1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-10-18 17:10:11 +00:00

Compare commits

..

13 Commits

Author SHA1 Message Date
1f9dce1870
use filepath.Join wrapper for different embed and os path styles
If using internal, "/" is used as a separator always, and with external,
filepath.Join is used.
2021-02-08 12:03:22 +00:00
1dfb1beba9
strip debug symbols in makefile 2021-02-05 13:01:45 +00:00
b3d7bc7704
refactor, should have been in main 2021-02-02 18:09:02 +00:00
d9354c7d6b
use embed.fs wrapper on data 2021-02-02 15:44:30 +00:00
bfab71c24b
use embed.fs wrapper for langFS so lang/ is not needed in paths
[files]lang_files is now the path to the lang directory, not path to a
directory containing it.
2021-02-02 15:19:43 +00:00
86bc26543a
Fix docker build, add GOBINARY flag for make
GOBINARY defaults to "go", but if you want to build on a normal system,
you'll likely set it to go1.16rc1 with "make all GOBINARY=go1.16rc1".
2021-02-01 20:55:17 +00:00
1efe3c17eb
drone image 2021-02-01 20:30:38 +00:00
a9b4a1d47f
update goreleaser 2021-02-01 20:28:01 +00:00
27530bff0a
add external/internal data options
"make all" will build with internal data, whereas "make debug"/"make
all-external" will make an external "data/" directory.
2021-02-01 20:25:20 +00:00
e2a68809fe
add to drone builds 2021-02-01 18:45:44 +00:00
e1af1eeab5
fully self-contained
paths are pretty janky, but it works. Also, [files]/lang_files now must
be the path to a directory CONTAINING a "lang/" directory. I'll work
around this at a later date.
2021-02-01 18:41:45 +00:00
1c06414320
Use fs for language, add lang_files option
The local app translations are loaded, and then if [files]/lang_files
is provided (a directory containing custom translations), any found
inside it are loaded over top. This makes customizing much easier.
2021-02-01 17:39:19 +00:00
88c7d8e606
use go1.16rc1 in make, remove ioutil, start switching to io/fs for file i/o
ioutil's contents are now in io and os.
Eventually jfa-go's files will be embedded in the binary with go1.16's
new embed feature. Using io/fs will provide abstraction for accessing
these files, and allow for both embedded and non-embedded versions.
Also, internal paths to things like email templates, etc. will be
prefixed with "jfa-go:" to indicate to use the app's own Filesystem
instead of reading the file normally. This also allows for custom files
to continue to be used as they are currently.
2021-01-31 23:12:50 +00:00
31 changed files with 101 additions and 455 deletions

View File

@ -9,7 +9,7 @@ steps:
commands: commands:
- git fetch --tags - git fetch --tags
- name: release - name: release
image: golang:1.16rc1-buster image: golang:latest
environment: environment:
GITHUB_TOKEN: GITHUB_TOKEN:
from_secret: github_token from_secret: github_token
@ -19,139 +19,11 @@ steps:
- (curl -sL https://deb.nodesource.com/setup_14.x | bash -) - (curl -sL https://deb.nodesource.com/setup_14.x | bash -)
- apt install nodejs - apt install nodejs
- curl -sL https://git.io/goreleaser | bash - curl -sL https://git.io/goreleaser | bash
trigger: when:
event: event: tag
- tag
branch:
- main
---
name: amd64-docker
kind: pipeline
type: docker
steps:
- name: fetch
image: docker:git
commands:
- git fetch --tags
- name: build
image: plugins/docker
settings:
username: hrfee
password:
from_secret: docker_key
repo: hrfee/jfa-go
tags: manifest-latest-amd64
trigger:
event:
- tag
branch:
- main
---
name: arm64-docker
kind: pipeline
type: docker
steps:
- name: arm64-ssh
image: appleboy/drone-ssh
volumes:
- name: ssh_key
path: /root/drone_rsa
settings:
host:
from_secret: ssh_host
username:
from_secret: ssh_username
port:
from_secret: ssh_port
volumes:
- /root/.ssh/docker-build:/root/drone_rsa
key_path: /root/drone_rsa
command_timeout: 50m
script:
- /home/rock64/jfa-go-build/build-stable.sh
trigger:
event:
- tag
branch:
- main
volumes:
- name: ssh_key
host:
path: /root/.ssh/docker-build
--- ---
name: armhf-docker name: jfa-go-1.16-git
kind: pipeline
type: docker
steps:
- name: armhf-ssh
image: appleboy/drone-ssh
volumes:
- name: ssh_key
path: /root/drone_rsa
settings:
host:
from_secret: ssh_host
username:
from_secret: ssh_username
port:
from_secret: ssh_port
volumes:
- /root/.ssh/docker-build:/root/drone_rsa
key_path: /root/drone_rsa
command_timeout: 50m
script:
- ssh pi /home/pi/jfa-go-build/build-stable.sh
trigger:
event:
- tag
branch:
- main
volumes:
- name: ssh_key
host:
path: /root/.ssh/docker-build
---
name: docker-manifest
kind: pipeline
type: docker
steps:
- name: manifest
image: appleboy/drone-ssh
volumes:
- name: ssh_key
path: /root/drone_rsa
settings:
host:
from_secret: ssh_host
username:
from_secret: ssh_username
port:
from_secret: ssh_port
volumes:
- /root/.ssh/docker-build:/root/drone_rsa
key_path: /root/drone_rsa
command_timeout: 50m
script:
- env DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create hrfee/jfa-go:latest --amend hrfee/jfa-go:manifest-latest-amd64 --amend hrfee/jfa-go:manifest-latest-arm64 --amend hrfee/jfa-go:manifest-latest-armhf
- env DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push hrfee/jfa-go:latest
trigger:
event:
- tag
branch:
- main
volumes:
- name: ssh_key
host:
path: /root/.ssh/docker-build
depends_on:
- amd64-docker
- arm64-docker
- armhf-docker
---
name: jfa-go-git
kind: pipeline kind: pipeline
type: docker type: docker
@ -182,146 +54,13 @@ trigger:
- pull_request - pull_request
--- ---
name: amd64-docker-git
kind: pipeline
type: docker
steps:
- name: build
image: plugins/docker
settings:
username: hrfee
password:
from_secret: docker_key
repo: hrfee/jfa-go
tags: manifest-unstable-amd64
trigger:
branch:
- main
event:
exclude:
- pull_request
---
name: arm64-docker-git
kind: pipeline
type: docker
steps:
- name: arm64-ssh
image: appleboy/drone-ssh
volumes:
- name: ssh_key
path: /root/drone_rsa
settings:
host:
from_secret: ssh_host
username:
from_secret: ssh_username
port:
from_secret: ssh_port
volumes:
- /root/.ssh/docker-build:/root/drone_rsa
key_path: /root/drone_rsa
command_timeout: 50m
script:
- /home/rock64/jfa-go-build/build.sh
trigger:
branch:
- main
event:
exclude:
- pull_request
volumes:
- name: ssh_key
host:
path: /root/.ssh/docker-build
---
name: armhf-docker-git
kind: pipeline
type: docker
steps:
- name: armhf-ssh
image: appleboy/drone-ssh
volumes:
- name: ssh_key
path: /root/drone_rsa
settings:
host:
from_secret: ssh_host
username:
from_secret: ssh_username
port:
from_secret: ssh_port
volumes:
- /root/.ssh/docker-build:/root/drone_rsa
key_path: /root/drone_rsa
command_timeout: 50m
script:
- ssh pi /home/pi/jfa-go-build/build.sh
trigger:
branch:
- main
event:
exclude:
- pull_request
volumes:
- name: ssh_key
host:
path: /root/.ssh/docker-build
---
name: docker-manifest-unstable
kind: pipeline
type: docker
steps:
- name: manifest
image: appleboy/drone-ssh
volumes:
- name: ssh_key
path: /root/drone_rsa
settings:
host:
from_secret: ssh_host
username:
from_secret: ssh_username
port:
from_secret: ssh_port
volumes:
- /root/.ssh/docker-build:/root/drone_rsa
key_path: /root/drone_rsa
command_timeout: 50m
script:
- env DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create hrfee/jfa-go:unstable --amend hrfee/jfa-go:manifest-unstable-amd64 --amend hrfee/jfa-go:manifest-unstable-arm64 --amend hrfee/jfa-go:manifest-unstable-armhf
- env DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push hrfee/jfa-go:unstable
depends_on:
- amd64-docker-git
- arm64-docker-git
- armhf-docker-git
trigger:
branch:
- main
event:
exclude:
- pull_request
volumes:
- name: ssh_key
host:
path: /root/.ssh/docker-build
---
name: jfa-go-pr name: jfa-go-pr
kind: pipeline kind: pipeline
type: docker type: docker
steps: steps:
- name: build - name: build
image: golang:1.16rc1-buster image: golang:latest
commands: commands:
- apt update -y - apt update -y
- apt install build-essential python3-pip curl software-properties-common sed upx -y - apt install build-essential python3-pip curl software-properties-common sed upx -y
@ -335,4 +74,3 @@ trigger:
event: event:
include: include:
- pull_request - pull_request

3
.github/FUNDING.yml vendored
View File

@ -1,3 +0,0 @@
# These are supported funding model platforms
github: hrfee

View File

@ -11,7 +11,6 @@ before:
- mkdir -p data - mkdir -p data
- cp -r static data/web - cp -r static data/web
- npm install - npm install
- npm install esbuild
- mkdir -p data/web/css - mkdir -p data/web/css
- npx esbuild --bundle css/base.css --outfile=./data/web/css/bundle.css --external:remixicon.css --minify - npx esbuild --bundle css/base.css --outfile=./data/web/css/bundle.css --external:remixicon.css --minify
- cp node_modules/remixicon/fonts/remixicon.css node_modules/remixicon/fonts/remixicon.woff2 data/web/css/ - cp node_modules/remixicon/fonts/remixicon.css node_modules/remixicon/fonts/remixicon.woff2 data/web/css/

View File

@ -6,7 +6,7 @@ RUN apt update -y \
&& apt install build-essential python3-pip curl software-properties-common sed upx -y \ && apt install build-essential python3-pip curl software-properties-common sed upx -y \
&& (curl -sL https://deb.nodesource.com/setup_14.x | bash -) \ && (curl -sL https://deb.nodesource.com/setup_14.x | bash -) \
&& apt install nodejs \ && apt install nodejs \
&& (cd /opt/build; make all-external GOESBUILD=on; make compress) \ && (cd /opt/build; make all-external; make compress) \
&& sed -i 's#id="password_resets-watch_directory" placeholder="/config/jellyfin"#id="password_resets-watch_directory" value="/jf" disabled#g' /opt/build/build/data/html/setup.html && sed -i 's#id="password_resets-watch_directory" placeholder="/config/jellyfin"#id="password_resets-watch_directory" value="/jf" disabled#g' /opt/build/build/data/html/setup.html
FROM golang:1.16rc1-buster FROM golang:1.16rc1-buster

View File

@ -1,19 +1,8 @@
GOESBUILD ?= off
ifeq ($(GOESBUILD), on)
ESBUILD := esbuild
else
ESBUILD := npx esbuild
endif
GOBINARY ?= go GOBINARY ?= go
npm: npm:
$(info installing npm dependencies) $(info installing npm dependencies)
npm install npm install
@if [ "$(GOESBUILD)" = "off" ]; then\
npm install esbuild;\
else\
go get -u github.com/evanw/esbuild/cmd/esbuild;\
fi
configuration: configuration:
$(info Fixing config-base) $(info Fixing config-base)
@ -29,16 +18,16 @@ email:
typescript: typescript:
$(info compiling typescript) $(info compiling typescript)
-mkdir -p data/web/js -mkdir -p data/web/js
-$(ESBUILD) --bundle ts/admin.ts --outfile=./data/web/js/admin.js --minify -npx esbuild --bundle ts/admin.ts --outfile=./data/web/js/admin.js --minify
-$(ESBUILD) --bundle ts/form.ts --outfile=./data/web/js/form.js --minify -npx esbuild --bundle ts/form.ts --outfile=./data/web/js/form.js --minify
-$(ESBUILD) --bundle ts/setup.ts --outfile=./data/web/js/setup.js --minify -npx esbuild --bundle ts/setup.ts --outfile=./data/web/js/setup.js --minify
ts-debug: ts-debug:
$(info compiling typescript w/ sourcemaps) $(info compiling typescript w/ sourcemaps)
-mkdir -p data/web/js -mkdir -p data/web/js
-$(ESBUILD) --bundle ts/admin.ts --sourcemap --outfile=./data/web/js/admin.js -npx esbuild --bundle ts/admin.ts --sourcemap --outfile=./data/web/js/admin.js
-$(ESBUILD) --bundle ts/form.ts --sourcemap --outfile=./data/web/js/form.js -npx esbuild --bundle ts/form.ts --sourcemap --outfile=./data/web/js/form.js
-$(ESBUILD) --bundle ts/setup.ts --sourcemap --outfile=./data/web/js/setup.js -npx esbuild --bundle ts/setup.ts --sourcemap --outfile=./data/web/js/setup.js
-rm -r data/web/js/ts -rm -r data/web/js/ts
$(info copying typescript) $(info copying typescript)
cp -r ts data/web/js cp -r ts data/web/js
@ -70,7 +59,7 @@ compress:
bundle-css: bundle-css:
-mkdir -p data/web/css -mkdir -p data/web/css
$(info bundling css) $(info bundling css)
$(ESBUILD) --bundle css/base.css --outfile=data/web/css/bundle.css --external:remixicon.css --minify npx esbuild --bundle css/base.css --outfile=data/web/css/bundle.css --external:remixicon.css --minify
copy: copy:
$(info copying fonts) $(info copying fonts)

1
go.mod
View File

@ -13,7 +13,6 @@ replace github.com/hrfee/jfa-go/ombi => ./ombi
require ( require (
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/evanw/esbuild v0.8.44 // indirect
github.com/fsnotify/fsnotify v1.4.9 github.com/fsnotify/fsnotify v1.4.9
github.com/gin-contrib/pprof v1.3.0 github.com/gin-contrib/pprof v1.3.0
github.com/gin-contrib/static v0.0.0-20200916080430-d45d9a37d28e github.com/gin-contrib/static v0.0.0-20200916080430-d45d9a37d28e

3
go.sum
View File

@ -34,8 +34,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473 h1:4
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanw/esbuild v0.8.44 h1:9svHk3MxC3T8ThKkUJ71GcPXYGMhxhO5iCfg2hrU0PU=
github.com/evanw/esbuild v0.8.44/go.mod h1:y2AFBAGVelPqPodpdtxWWqe6n2jYf5FrsJbligmRmuw=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
@ -422,7 +420,6 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 h1:sIky/MyNRSHTrdxfsiUSS4WIAMvInbeXljJz+jDjeYE= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 h1:sIky/MyNRSHTrdxfsiUSS4WIAMvInbeXljJz+jDjeYE=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed h1:J22ig1FUekjjkmZUM7pTKixYm8DvrYsvrBZdunYeIuQ= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed h1:J22ig1FUekjjkmZUM7pTKixYm8DvrYsvrBZdunYeIuQ=

View File

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class="{{ .cssClass }}"> <html lang="en" class="{{ .cssClass }}">
<head> <head>
<link rel="stylesheet" type="text/css" href="{{ .urlBase }}/css/bundle.css"> <link rel="stylesheet" type="text/css" href="css/bundle.css">
<script> <script>
window.URLBase = "{{ .urlBase }}"; window.URLBase = "{{ .urlBase }}";
window.notificationsEnabled = {{ .notifications }}; window.notificationsEnabled = {{ .notifications }};
@ -40,7 +40,7 @@
<div id="modal-about" class="modal"> <div id="modal-about" class="modal">
<div class="modal-content content card"> <div class="modal-content content card">
<span class="heading">{{ .strings.aboutProgram }} <span class="modal-close">&times;</span></span> <span class="heading">{{ .strings.aboutProgram }} <span class="modal-close">&times;</span></span>
<img src="{{ .urlBase }}/banner.svg" class="mt-1" alt="jfa-go banner"> <img src="/banner.svg" class="mt-1" alt="jfa-go banner">
<p><i class="icon ri-github-fill"></i><a href="https://github.com/hrfee/jfa-go">jfa-go</a></p> <p><i class="icon ri-github-fill"></i><a href="https://github.com/hrfee/jfa-go">jfa-go</a></p>
<p>{{ .strings.version }} <span class="code monospace">{{ .version }}</span></p> <p>{{ .strings.version }} <span class="code monospace">{{ .version }}</span></p>
<p>{{ .strings.commitNoun }} <span class="code monospace">{{ .commit }}</span></p> <p>{{ .strings.commitNoun }} <span class="code monospace">{{ .commit }}</span></p>
@ -292,6 +292,6 @@
</div> </div>
</div> </div>
</div> </div>
<script src="{{ .urlBase }}/js/admin.js" type="module"></script> <script src="js/admin.js" type="module"></script>
</body> </body>
</html> </html>

View File

@ -36,7 +36,7 @@
"sendDeleteNotificationEmail": "Send notification email", "sendDeleteNotificationEmail": "Send notification email",
"sendDeleteNotifiationExample": "Your account has been deleted.", "sendDeleteNotifiationExample": "Your account has been deleted.",
"settingsRestart": "Restart", "settingsRestart": "Restart",
"settingsRestarting": "Restarting", "settingsRestarting": "Restarting...",
"settingsRestartRequired": "Restart needed", "settingsRestartRequired": "Restart needed",
"settingsRestartRequiredDescription": "A restart is necessary to apply some settings you changed. Restart now or later?", "settingsRestartRequiredDescription": "A restart is necessary to apply some settings you changed. Restart now or later?",
"settingsApplyRestartLater": "Apply, restart later", "settingsApplyRestartLater": "Apply, restart later",
@ -55,6 +55,7 @@
"addProfileDescription": "Create a Jellyfin user and configure it, then select it below. When this profile is applied to an invite, new users will be created with the settings.", "addProfileDescription": "Create a Jellyfin user and configure it, then select it below. When this profile is applied to an invite, new users will be created with the settings.",
"addProfileNameOf": "Profile Name", "addProfileNameOf": "Profile Name",
"addProfileStoreHomescreenLayout": "Store homescreen layout", "addProfileStoreHomescreenLayout": "Store homescreen layout",
"inviteNoUsersCreated": "None yet!", "inviteNoUsersCreated": "None yet!",
"inviteUsersCreated": "Created users", "inviteUsersCreated": "Created users",
"inviteNoProfile": "No Profile", "inviteNoProfile": "No Profile",
@ -63,6 +64,7 @@
"inviteRemainingUses": "Remaining uses", "inviteRemainingUses": "Remaining uses",
"inviteNoInvites": "None", "inviteNoInvites": "None",
"inviteExpiresInTime": "Expires in {n}", "inviteExpiresInTime": "Expires in {n}",
"notifyEvent": "Notify on:", "notifyEvent": "Notify on:",
"notifyInviteExpiry": "On expiry", "notifyInviteExpiry": "On expiry",
"notifyUserCreation": "On user creation" "notifyUserCreation": "On user creation"
@ -96,6 +98,7 @@
"errorUserCreated": "Failed to create user {n}.", "errorUserCreated": "Failed to create user {n}.",
"errorSendWelcomeEmail": "Failed to send welcome email (check console/logs)" "errorSendWelcomeEmail": "Failed to send welcome email (check console/logs)"
}, },
"quantityStrings": { "quantityStrings": {
"modifySettingsFor": { "modifySettingsFor": {
"singular": "Modify Settings for {n} user", "singular": "Modify Settings for {n} user",

View File

@ -40,7 +40,7 @@
"settingsApplyRestartLater": "Appliquer, redémarrer plus tard", "settingsApplyRestartLater": "Appliquer, redémarrer plus tard",
"settingsApplyRestartNow": "Appliquer et redémarrer", "settingsApplyRestartNow": "Appliquer et redémarrer",
"settingsApplied": "Paramètres appliqués.", "settingsApplied": "Paramètres appliqués.",
"settingsRefreshPage": "Actualisez la page dans quelques secondes.", "settingsRefreshPage": "Actualisez la page dans quelques secondes",
"settingsRequiredOrRestartMessage": "Remarque: {n} indique un champ obligatoire, {n} indique que les modifications nécessitent un redémarrage.", "settingsRequiredOrRestartMessage": "Remarque: {n} indique un champ obligatoire, {n} indique que les modifications nécessitent un redémarrage.",
"settingsSave": "Sauver", "settingsSave": "Sauver",
"ombiUserDefaults": "Paramètres par défaut de l'utilisateur Ombi", "ombiUserDefaults": "Paramètres par défaut de l'utilisateur Ombi",
@ -64,9 +64,7 @@
"notifyEvent": "Notifier sur :", "notifyEvent": "Notifier sur :",
"notifyInviteExpiry": "À l'expiration", "notifyInviteExpiry": "À l'expiration",
"notifyUserCreation": "à la création de l'utilisateur", "notifyUserCreation": "à la création de l'utilisateur",
"label": "Etiquette", "label": "Etiquette"
"settingsRestarting": "Redémarrage…",
"settingsRestart": "Redémarrer"
}, },
"notifications": { "notifications": {
"changedEmailAddress": "Adresse e-mail modifiée de {n}.", "changedEmailAddress": "Adresse e-mail modifiée de {n}.",

View File

@ -39,7 +39,7 @@
"settingsApplyRestartLater": "Sla op, herstart later", "settingsApplyRestartLater": "Sla op, herstart later",
"settingsApplyRestartNow": "Sla op & herstart", "settingsApplyRestartNow": "Sla op & herstart",
"settingsApplied": "Wijzigingen doorgevoerd.", "settingsApplied": "Wijzigingen doorgevoerd.",
"settingsRefreshPage": "Ververs de pagina over enkele seconden.", "settingsRefreshPage": "Ververs de pagina over enkele seconden",
"settingsRequiredOrRestartMessage": "Opmerking: {n} is een verplicht veld, {n} geeft aan dat na wijzigen een herstart nodig is.", "settingsRequiredOrRestartMessage": "Opmerking: {n} is een verplicht veld, {n} geeft aan dat na wijzigen een herstart nodig is.",
"settingsSave": "Opslaan", "settingsSave": "Opslaan",
"ombiUserDefaults": "Ombi gebruiker standaardinstellingen", "ombiUserDefaults": "Ombi gebruiker standaardinstellingen",
@ -63,9 +63,7 @@
"notifyEvent": "Meldingen:", "notifyEvent": "Meldingen:",
"notifyInviteExpiry": "Bij verloop", "notifyInviteExpiry": "Bij verloop",
"notifyUserCreation": "Bij aanmaken gebruiker", "notifyUserCreation": "Bij aanmaken gebruiker",
"label": "Label", "label": "Label"
"settingsRestart": "Herstart",
"settingsRestarting": "Aan het herstarten…"
}, },
"notifications": { "notifications": {
"changedEmailAddress": "E-mailadres van {n} gewijzigd.", "changedEmailAddress": "E-mailadres van {n} gewijzigd.",

View File

@ -29,7 +29,7 @@
"newUser": "Novo Usuário", "newUser": "Novo Usuário",
"profile": "Perfil", "profile": "Perfil",
"unknown": "Desconhecido", "unknown": "Desconhecido",
"label": "Rótulo", "label": "",
"modifySettings": "Modificar configurações", "modifySettings": "Modificar configurações",
"modifySettingsDescription": "Aplique as configurações de um perfil existente ou obtenha-as diretamente de um usuário.", "modifySettingsDescription": "Aplique as configurações de um perfil existente ou obtenha-as diretamente de um usuário.",
"applyHomescreenLayout": "Aplicar layout na tela inicial", "applyHomescreenLayout": "Aplicar layout na tela inicial",
@ -40,7 +40,7 @@
"settingsApplyRestartLater": "Aplicar, reiniciar mais tarde", "settingsApplyRestartLater": "Aplicar, reiniciar mais tarde",
"settingsApplyRestartNow": "Aplicar e reiniciar", "settingsApplyRestartNow": "Aplicar e reiniciar",
"settingsApplied": "Configurações aplicada.", "settingsApplied": "Configurações aplicada.",
"settingsRefreshPage": "Atualize a página em alguns segundos.", "settingsRefreshPage": "Atualize a página em alguns segundos",
"settingsRequiredOrRestartMessage": "Nota: {n} indica campo obrigatório, {n} indica que as alterações requer um reinício.", "settingsRequiredOrRestartMessage": "Nota: {n} indica campo obrigatório, {n} indica que as alterações requer um reinício.",
"settingsSave": "Salve", "settingsSave": "Salve",
"ombiUserDefaults": "Padrões do usuário Ombi", "ombiUserDefaults": "Padrões do usuário Ombi",
@ -63,9 +63,7 @@
"inviteExpiresInTime": "Expira em {n}", "inviteExpiresInTime": "Expira em {n}",
"notifyEvent": "Notificar em:", "notifyEvent": "Notificar em:",
"notifyInviteExpiry": "No vencimento", "notifyInviteExpiry": "No vencimento",
"notifyUserCreation": "Na criação do usuário", "notifyUserCreation": "Na criação do usuário"
"settingsRestart": "Reiniciar",
"settingsRestarting": "Reiniciando…"
}, },
"notifications": { "notifications": {
"changedEmailAddress": "Endereço de e-mail alterado de {n}.", "changedEmailAddress": "Endereço de e-mail alterado de {n}.",
@ -88,7 +86,7 @@
"errorLoadUsers": "Falha ao carregar usuários.", "errorLoadUsers": "Falha ao carregar usuários.",
"errorSaveSettings": "Não foi possível salvar as configurações.", "errorSaveSettings": "Não foi possível salvar as configurações.",
"errorLoadSettings": "Falha ao carregar as configurações.", "errorLoadSettings": "Falha ao carregar as configurações.",
"errorSetOmbiDefaults": "Falha em armazenar os padrões ombi.", "errorSetOmbiDefaults": "Falha ao armazenar padrões de ombi.",
"errorLoadOmbiUsers": "Falha ao carregar usuários ombi.", "errorLoadOmbiUsers": "Falha ao carregar usuários ombi.",
"errorChangedEmailAddress": "Não foi possível alterar o endereço de e-mail de {n}.", "errorChangedEmailAddress": "Não foi possível alterar o endereço de e-mail de {n}.",
"errorFailureCheckLogs": "Falha (verificar console/logs)", "errorFailureCheckLogs": "Falha (verificar console/logs)",

View File

@ -43,10 +43,5 @@
"welcome": "Willkommen bei Jellyfin!", "welcome": "Willkommen bei Jellyfin!",
"youCanLoginWith": "Du kannst dich mit den mit den untenstehenden Zugangsdaten anmelden", "youCanLoginWith": "Du kannst dich mit den mit den untenstehenden Zugangsdaten anmelden",
"jellyfinURL": "URL" "jellyfinURL": "URL"
},
"emailConfirmation": {
"title": "Bestätige deine E-Mail - Jellyfin",
"clickBelow": "Klicke den untenstehenden Link, um deine E-Mail-Adresse zu bestätigen, und fange an, Jellyfin zu benutzen.",
"confirmEmail": "E-Mail bestätigen"
} }
} }

View File

@ -44,10 +44,5 @@
"title": "Bienvenue sur Jellyfin", "title": "Bienvenue sur Jellyfin",
"welcome": "Bienvenue sur Jellyfin !", "welcome": "Bienvenue sur Jellyfin !",
"jellyfinURL": "URL" "jellyfinURL": "URL"
},
"emailConfirmation": {
"title": "Confirmez votre adresse e-mail - Jellyfin",
"clickBelow": "Clique sur le lien ci-dessous pour confirmer ton adresse e-mail et commencer à utiliser Jellyfin.",
"confirmEmail": "Confirmer l'adresse e-mail"
} }
} }

View File

@ -43,10 +43,5 @@
"welcome": "Welkom bij Jellyfin!", "welcome": "Welkom bij Jellyfin!",
"youCanLoginWith": "Je kunt inloggen met onderstaande gegevens", "youCanLoginWith": "Je kunt inloggen met onderstaande gegevens",
"jellyfinURL": "URL" "jellyfinURL": "URL"
},
"emailConfirmation": {
"title": "Bevestig je e-mailadres - Jellyfin",
"clickBelow": "Klik op onderstaande link om je e-mailadres te bevestigen en te beginnen met Jellyfin.",
"confirmEmail": "Bevestig e-mailadres"
} }
} }

View File

@ -33,7 +33,7 @@
"inviteEmail": { "inviteEmail": {
"title": "Convite - Jellyfin", "title": "Convite - Jellyfin",
"hello": "Ola", "hello": "Ola",
"youHaveBeenInvited": "Você recebeu um convite para o Jellyfin.", "youHaveBeenInvited": "Você um convite para o Jellyfin.",
"toJoin": "Para participar, clique no link abaixo.", "toJoin": "Para participar, clique no link abaixo.",
"inviteExpiry": "Este convite expira em {n} às {n}, que é em {n}, então seja rápido.", "inviteExpiry": "Este convite expira em {n} às {n}, que é em {n}, então seja rápido.",
"linkButton": "Crie sua conta" "linkButton": "Crie sua conta"
@ -43,10 +43,5 @@
"welcome": "Bem vindo ao Jellyfin!", "welcome": "Bem vindo ao Jellyfin!",
"youCanLoginWith": "Você pode fazer o login com os detalhes abaixo", "youCanLoginWith": "Você pode fazer o login com os detalhes abaixo",
"jellyfinURL": "URL" "jellyfinURL": "URL"
},
"emailConfirmation": {
"title": "Confirme seu email - Jellyfin",
"clickBelow": "Clique no link abaixo para confirmar seu endereço de e-mail e começar a usar o Jellyfin.",
"confirmEmail": "Confirmar Email"
} }
} }

View File

@ -14,9 +14,7 @@
"createAccountButton": "Konto erstellen", "createAccountButton": "Konto erstellen",
"passwordRequirementsHeader": "Passwortanforderungen", "passwordRequirementsHeader": "Passwortanforderungen",
"successHeader": "Erfolg!", "successHeader": "Erfolg!",
"successContinueButton": "Weiter", "successContinueButton": "Weiter"
"confirmationRequired": "E-Mail-Bestätigung erforderlich",
"confirmationRequiredMessage": "Bitte überprüfe dein Posteingang und bestätige deine E-Mail-Adresse."
}, },
"validationStrings": { "validationStrings": {
"length": { "length": {

View File

@ -15,9 +15,7 @@
"createAccountButton": "Créer le compte", "createAccountButton": "Créer le compte",
"passwordRequirementsHeader": "Mot de passe requis", "passwordRequirementsHeader": "Mot de passe requis",
"successHeader": "Succes!", "successHeader": "Succes!",
"successContinueButton": "Continuer", "successContinueButton": "Continuer"
"confirmationRequired": "Confirmation de l'adresse e-mail requise",
"confirmationRequiredMessage": "Veuillez vérifier votre boite de réception pour confirmer votre adresse e-mail."
}, },
"validationStrings": { "validationStrings": {
"length": { "length": {

View File

@ -14,9 +14,7 @@
"createAccountButton": "Maak account aan", "createAccountButton": "Maak account aan",
"passwordRequirementsHeader": "Wachtwoordvereisten", "passwordRequirementsHeader": "Wachtwoordvereisten",
"successHeader": "Succes!", "successHeader": "Succes!",
"successContinueButton": "Doorgaan", "successContinueButton": "Doorgaan"
"confirmationRequired": "Bevestiging van e-mailadres verplicht",
"confirmationRequiredMessage": "Controleer je e-mail inbox om je adres te bevestigen."
}, },
"validationStrings": { "validationStrings": {
"length": { "length": {

View File

@ -14,9 +14,7 @@
"createAccountButton": "Criar Conta", "createAccountButton": "Criar Conta",
"passwordRequirementsHeader": "Requisitos da Senha", "passwordRequirementsHeader": "Requisitos da Senha",
"successHeader": "Sucesso!", "successHeader": "Sucesso!",
"successContinueButton": "Continuar", "successContinueButton": "Continuar"
"confirmationRequired": "Necessária confirmação de e-mail",
"confirmationRequiredMessage": "Verifique sua caixa de entrada no e-mail para verificar seu endereço."
}, },
"notifications": { "notifications": {
"errorUserExists": "Esse usuário já existe.", "errorUserExists": "Esse usuário já existe.",

View File

@ -25,7 +25,7 @@
}, },
"endPage": { "endPage": {
"finished": "Fertig!", "finished": "Fertig!",
"restartMessage": "Es gibt weitere Einstellungen, die du auf der Admin-Seite konfigurieren kannst. Klicke unten, um neu zu starten, dann aktualisiere die Seite.", "restartMessage": "Es gibt weitere Einstellungen, die du auf der Admin-Seite konfigurieren kannst. Drücke unten, um neu zu starten, dann aktualisiere die Seite.",
"refreshPage": "Aktualisieren" "refreshPage": "Aktualisieren"
}, },
"language": { "language": {
@ -42,7 +42,7 @@
"urlBaseNotice": "Nur erforderlich, wenn ein Reverse-Proxy auf einer Subdomain verwendet wird (z. B. 'jellyf.in/accounts').", "urlBaseNotice": "Nur erforderlich, wenn ein Reverse-Proxy auf einer Subdomain verwendet wird (z. B. 'jellyf.in/accounts').",
"lightTheme": "Hell", "lightTheme": "Hell",
"darkTheme": "Dunkel", "darkTheme": "Dunkel",
"useHTTPS": "HTTPS verwenden", "useHTTPS": "Verwende HTTPS",
"httpsPort": "HTTPS Port", "httpsPort": "HTTPS Port",
"useHTTPSNotice": "Nur empfohlen, wenn du keinen Reverse-Proxy verwendest.", "useHTTPSNotice": "Nur empfohlen, wenn du keinen Reverse-Proxy verwendest.",
"pathToCertificate": "Pfad zum Zertifikat", "pathToCertificate": "Pfad zum Zertifikat",
@ -51,10 +51,10 @@
"login": { "login": {
"title": "Anmelden", "title": "Anmelden",
"description": "Um auf die Admin-Seite zuzugreifen, musst du dich mit einer untenstehenden Methode anmelden:", "description": "Um auf die Admin-Seite zuzugreifen, musst du dich mit einer untenstehenden Methode anmelden:",
"authorizeWithJellyfin": "Mit Jellyfin/Emby autorisieren: Anmeldedaten werden mit Jellyfin geteilt, was mehrere Benutzer ermöglicht.", "authorizeWithJellyfin": "Autorisiere mit Jellyfin/Emby: Anmeldedaten werden mit Jellyfin geteilt, was mehrere Benutzer ermöglicht.",
"authorizeManual": "Benutzername und Passwort: Lege Benutzername und Passwort manuell fest.", "authorizeManual": "Benutzername und Passwort: Lege Benutzername und Passwort manuell fest.",
"adminOnly": "Nur Admin-Benutzer (empfohlen)", "adminOnly": "Nur Admin-Benutzer (empfohlen)",
"emailNotice": "Deine E-Mail-Adresse kann verwendet werden, um Benachrichtigungen zu erhalten." "emailNotice": "Eine E-Mail-Adresse kann verwendet werden, um Benachrichtigungen zu erhalten."
}, },
"jellyfinEmby": { "jellyfinEmby": {
"title": "Jellyfin/Emby", "title": "Jellyfin/Emby",
@ -69,8 +69,8 @@
}, },
"ombi": { "ombi": {
"title": "Ombi", "title": "Ombi",
"description": "Durch den Anschluss an Ombi wird sowohl ein Jellyfin-Konto als auch ein Ombi-Konto erstellt, wenn ein Benutzer durch jfa-go beitritt. Nachdem das Setup abgeschlossen ist, gehe zu den Einstellungen, um ein Standardprofil für neue Ombi-Benutzer festzulegen.", "description": "Durch den Anschluss an Ombi wird sowohl ein Jellyfin-Konto als auch ein Ombi-Konto erstellt, wenn ein Benutzer durch jfa-go beitritt. Nachdem das Setup abgeschlossen ist, gehe zu den Einstellungen um ein Standardprofil für neue Ombi-Benutzer festzulegen.",
"apiKeyNotice": "Finde dies in der ersten Registerkarte der Ombi-Einstellungen." "apiKeyNotice": "Finde das in der ersten Registerkarte der Ombi-Einstellungen."
}, },
"email": { "email": {
"title": "E-Mail", "title": "E-Mail",
@ -89,7 +89,7 @@
}, },
"notifications": { "notifications": {
"title": "Benachrichtigungen", "title": "Benachrichtigungen",
"description": "Wenn aktiviert, kannst du (pro Invite) wählen, eine E-Mail zu erhalten, wenn ein Invite abläuft oder ein Benutzer erstellt wird. Wenn du nicht die Jellyfin-Login-Methode gewählt hast, stelle sicher, dass du deine E-Mail-Adresse angegeben hast." "description": "Wenn aktiviert, kannst du (per Invite) wählen, eine E-Mail zu erhalten, wenn ein Invite abläuft oder ein Benutzer erstellt wird. Wenn du nicht die Jellyfin-Login-Methode gewählt hast, stelle sicher, dass du deine E-Mail-Adresse angegeben hast."
}, },
"welcomeEmails": { "welcomeEmails": {
"title": "Willkommens-E-Mails", "title": "Willkommens-E-Mails",

View File

@ -37,7 +37,7 @@
}, },
"general": { "general": {
"title": "Geral", "title": "Geral",
"listenAddress": "Endereço de Escuta", "listenAddress": "",
"urlBase": "URL Base", "urlBase": "URL Base",
"urlBaseNotice": "Necessário apenas se estiver usando um proxy reverso em um subdomínio (por exemplo, 'jellyf.in/accounts').", "urlBaseNotice": "Necessário apenas se estiver usando um proxy reverso em um subdomínio (por exemplo, 'jellyf.in/accounts').",
"lightTheme": "Claro", "lightTheme": "Claro",

View File

@ -10,7 +10,6 @@ import (
"io" "io"
"io/fs" "io/fs"
"log" "log"
"mime"
"net" "net"
"net/http" "net/http"
"os" "os"
@ -522,15 +521,13 @@ func start(asDaemon, firstCall bool) {
app.info.Fatalf("Failed to load language files: %+v\n", err) app.info.Fatalf("Failed to load language files: %+v\n", err)
} }
} }
cssHeader = app.loadCSSHeader()
// workaround for potentially broken windows mime types
mime.AddExtensionType(".js", "application/javascript")
app.info.Println("Initializing router") app.info.Println("Initializing router")
router := app.loadRouter(address, debugMode) router := app.loadRouter(address, debugMode)
app.info.Println("Loading routes") app.info.Println("Loading routes")
router.GET("/lang/:page", app.GetLanguages)
if !firstRun { if !firstRun {
app.loadRoutes(router) app.loadRoutes(router)
app.info.Printf("Starting router @ %s", address)
} else { } else {
app.loadSetup(router) app.loadSetup(router)
app.info.Printf("Loading setup @ %s", address) app.info.Printf("Loading setup @ %s", address)

6
package-lock.json generated
View File

@ -228,9 +228,9 @@
"integrity": "sha1-vfpzUplmTfr9NFKe1PhSKidf6lY=" "integrity": "sha1-vfpzUplmTfr9NFKe1PhSKidf6lY="
}, },
"esbuild": { "esbuild": {
"version": "0.8.44", "version": "0.7.22",
"resolved": "https://registry.npm.taobao.org/esbuild/download/esbuild-0.8.44.tgz", "resolved": "https://registry.npm.taobao.org/esbuild/download/esbuild-0.7.22.tgz",
"integrity": "sha1-KnT0j+IFeQgcnY/pm+b7jShIyIc=" "integrity": "sha1-kUm5A/gSi3xFp1QEbCQZnXa74I4="
}, },
"escalade": { "escalade": {
"version": "3.1.1", "version": "3.1.1",

View File

@ -18,7 +18,7 @@
"homepage": "https://github.com/hrfee/jfa-go#readme", "homepage": "https://github.com/hrfee/jfa-go#readme",
"dependencies": { "dependencies": {
"a17t": "^0.4.0", "a17t": "^0.4.0",
"esbuild": "^0.8.44", "esbuild": "^0.7.8",
"lodash": "^4.17.19", "lodash": "^4.17.19",
"mjml": "^4.8.0", "mjml": "^4.8.0",
"remixicon": "^2.5.0", "remixicon": "^2.5.0",

View File

@ -97,59 +97,45 @@ func (app *appContext) loadRouter(address string, debug bool) *gin.Engine {
} }
func (app *appContext) loadRoutes(router *gin.Engine) { func (app *appContext) loadRoutes(router *gin.Engine) {
routePrefixes := []string{app.URLBase} router.GET("/", app.AdminPage)
if app.URLBase != "" { router.GET("/accounts", app.AdminPage)
routePrefixes = append(routePrefixes, "") router.GET("/settings", app.AdminPage)
} router.GET("/lang/:page/:file", app.ServeLang)
for _, p := range routePrefixes { router.GET("/token/login", app.getTokenLogin)
router.GET(p+"/lang/:page", app.GetLanguages) router.GET("/token/refresh", app.getTokenRefresh)
router.Use(static.Serve(p+"/", app.webFS)) router.POST("/newUser", app.NewUser)
router.GET(p+"/", app.AdminPage) router.Use(static.Serve("/invite/", app.webFS))
router.GET(p+"/accounts", app.AdminPage) router.GET("/invite/:invCode", app.InviteProxy)
router.GET(p+"/settings", app.AdminPage)
router.GET(p+"/lang/:page/:file", app.ServeLang)
router.GET(p+"/token/login", app.getTokenLogin)
router.GET(p+"/token/refresh", app.getTokenRefresh)
router.POST(p+"/newUser", app.NewUser)
router.Use(static.Serve(p+"/invite/", app.webFS))
router.GET(p+"/invite/:invCode", app.InviteProxy)
}
if *SWAGGER { if *SWAGGER {
app.info.Print(aurora.Magenta("\n\nWARNING: Swagger should not be used on a public instance.\n\n")) app.info.Print(aurora.Magenta("\n\nWARNING: Swagger should not be used on a public instance.\n\n"))
for _, p := range routePrefixes { router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
router.GET(p+"/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
} }
api := router.Group("/", app.webAuth()) api := router.Group("/", app.webAuth())
for _, p := range routePrefixes { router.POST("/logout", app.Logout)
router.POST(p+"/logout", app.Logout) api.DELETE("/users", app.DeleteUser)
api.DELETE(p+"/users", app.DeleteUser) api.GET("/users", app.GetUsers)
api.GET(p+"/users", app.GetUsers) api.POST("/users", app.NewUserAdmin)
api.POST(p+"/users", app.NewUserAdmin) api.POST("/invites", app.GenerateInvite)
api.POST(p+"/invites", app.GenerateInvite) api.GET("/invites", app.GetInvites)
api.GET(p+"/invites", app.GetInvites) api.DELETE("/invites", app.DeleteInvite)
api.DELETE(p+"/invites", app.DeleteInvite) api.POST("/invites/profile", app.SetProfile)
api.POST(p+"/invites/profile", app.SetProfile) api.GET("/profiles", app.GetProfiles)
api.GET(p+"/profiles", app.GetProfiles) api.POST("/profiles/default", app.SetDefaultProfile)
api.POST(p+"/profiles/default", app.SetDefaultProfile) api.POST("/profiles", app.CreateProfile)
api.POST(p+"/profiles", app.CreateProfile) api.DELETE("/profiles", app.DeleteProfile)
api.DELETE(p+"/profiles", app.DeleteProfile) api.POST("/invites/notify", app.SetNotify)
api.POST(p+"/invites/notify", app.SetNotify) api.POST("/users/emails", app.ModifyEmails)
api.POST(p+"/users/emails", app.ModifyEmails) api.POST("/users/settings", app.ApplySettings)
// api.POST(p + "/setDefaults", app.SetDefaults) api.GET("/config", app.GetConfig)
api.POST(p+"/users/settings", app.ApplySettings) api.POST("/config", app.ModifyConfig)
api.GET(p+"/config", app.GetConfig) api.POST("/restart", app.restart)
api.POST(p+"/config", app.ModifyConfig)
api.POST(p+"/restart", app.restart)
if app.config.Section("ombi").Key("enabled").MustBool(false) { if app.config.Section("ombi").Key("enabled").MustBool(false) {
api.GET(p+"/ombi/users", app.OmbiUsers) api.GET("/ombi/users", app.OmbiUsers)
api.POST(p+"/ombi/defaults", app.SetOmbiDefaults) api.POST("/ombi/defaults", app.SetOmbiDefaults)
}
} }
} }
func (app *appContext) loadSetup(router *gin.Engine) { func (app *appContext) loadSetup(router *gin.Engine) {
router.GET("/lang/:page", app.GetLanguages)
router.GET("/", app.ServeSetup) router.GET("/", app.ServeSetup)
router.POST("/jellyfin/test", app.TestJF) router.POST("/jellyfin/test", app.TestJF)
router.POST("/config", app.ModifyConfig) router.POST("/config", app.ModifyConfig)

View File

@ -84,24 +84,23 @@ window.tabs.addTab("accounts", null, accounts.reload);
window.tabs.addTab("settings", null, settings.reload); window.tabs.addTab("settings", null, settings.reload);
for (let tab of ["invites", "accounts", "settings"]) { for (let tab of ["invites", "accounts", "settings"]) {
if (window.location.pathname == window.URLBase + "/" + tab) { if (window.location.pathname == "/" + tab) {
window.tabs.switch(tab, true); window.tabs.switch(tab, true);
} }
} }
if ((window.URLBase + "/").includes(window.location.pathname)) { if (window.location.pathname == "/") {
window.tabs.switch("invites", true); window.tabs.switch("invites", true);
} }
document.addEventListener("tab-change", (event: CustomEvent) => { document.addEventListener("tab-change", (event: CustomEvent) => {
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const lang = urlParams.get('lang'); const lang = urlParams.get('lang');
let tab = window.URLBase + "/" + event.detail; let tab = "/" + event.detail;
if (tab == window.URLBase + "/invites") { if (tab == "/invites") {
if (window.location.pathname == window.URLBase + "/") { if (window.location.pathname == "/") {
tab = window.URLBase + "/"; tab = "/";
} else if (window.URLBase) { tab = window.URLBase; } } else { tab = "../"; }
else { tab = "../"; }
} }
if (lang) { if (lang) {
tab += "?lang=" + lang tab += "?lang=" + lang

View File

@ -42,15 +42,7 @@ class user implements User {
} }
get email(): string { return this._emailAddress; } get email(): string { return this._emailAddress; }
set email(value: string) { set email(value: string) { this._email.value = value; this._emailAddress = value; }
this._emailAddress = value;
const input = this._email.querySelector("input");
if (input) {
input.value = value;
} else {
this._email.textContent = value;
}
}
get last_active(): string { return this._lastActive.textContent; } get last_active(): string { return this._lastActive.textContent; }
set last_active(value: string) { this._lastActive.textContent = value; } set last_active(value: string) { this._lastActive.textContent = value; }
@ -63,27 +55,20 @@ class user implements User {
this._row.innerHTML = ` this._row.innerHTML = `
<td><input type="checkbox" value=""></td> <td><input type="checkbox" value=""></td>
<td><span class="accounts-username"></span> <span class="accounts-admin"></span></td> <td><span class="accounts-username"></span> <span class="accounts-admin"></span></td>
<td><i class="icon ri-edit-line accounts-email-edit"></i><span class="accounts-email-container ml-half"></span></td> <td><i class="icon ri-edit-line accounts-email-edit"></i><input type="email" class="input ~neutral !normal stealth-input stealth-input-hidden accounts-email" readonly></td>
<td class="accounts-last-active"></td> <td class="accounts-last-active"></td>
`; `;
const emailEditor = `<input type="email" class="input ~neutral !normal stealth-input">`;
this._check = this._row.querySelector("input[type=checkbox]") as HTMLInputElement; this._check = this._row.querySelector("input[type=checkbox]") as HTMLInputElement;
this._username = this._row.querySelector(".accounts-username") as HTMLSpanElement; this._username = this._row.querySelector(".accounts-username") as HTMLSpanElement;
this._admin = this._row.querySelector(".accounts-admin") as HTMLSpanElement; this._admin = this._row.querySelector(".accounts-admin") as HTMLSpanElement;
this._email = this._row.querySelector(".accounts-email-container") as HTMLInputElement; this._email = this._row.querySelector(".accounts-email") as HTMLInputElement;
this._emailEditButton = this._row.querySelector(".accounts-email-edit") as HTMLElement; this._emailEditButton = this._row.querySelector(".accounts-email-edit") as HTMLElement;
this._lastActive = this._row.querySelector(".accounts-last-active") as HTMLTableDataCellElement; this._lastActive = this._row.querySelector(".accounts-last-active") as HTMLTableDataCellElement;
this._check.onchange = () => { this.selected = this._check.checked; } this._check.onchange = () => { this.selected = this._check.checked; }
const toggleStealthInput = () => { const toggleStealthInput = () => {
if (this._emailEditButton.classList.contains("ri-edit-line")) { this._email.classList.toggle("stealth-input-hidden");
this._email.innerHTML = emailEditor; this._email.readOnly = !this._email.readOnly;
this._email.querySelector("input").value = this._emailAddress;
this._email.classList.remove("ml-half");
} else {
this._email.textContent = this._emailAddress;
this._email.classList.add("ml-half");
}
this._emailEditButton.classList.toggle("ri-check-line"); this._emailEditButton.classList.toggle("ri-check-line");
this._emailEditButton.classList.toggle("ri-edit-line"); this._emailEditButton.classList.toggle("ri-edit-line");
}; };
@ -95,7 +80,7 @@ class user implements User {
} }
}; };
this._emailEditButton.onclick = () => { this._emailEditButton.onclick = () => {
if (this._emailEditButton.classList.contains("ri-edit-line")) { if (this._email.classList.contains("stealth-input-hidden")) {
document.addEventListener('click', outerClickListener); document.addEventListener('click', outerClickListener);
} else { } else {
this._updateEmail(); this._updateEmail();
@ -109,7 +94,7 @@ class user implements User {
private _updateEmail = () => { private _updateEmail = () => {
let oldEmail = this.email; let oldEmail = this.email;
this.email = this._email.querySelector("input").value; this.email = this._email.value;
let send = {}; let send = {};
send[this.id] = this.email; send[this.id] = this.email;
_post("/users/emails", send, (req: XMLHttpRequest) => { _post("/users/emails", send, (req: XMLHttpRequest) => {

View File

@ -45,7 +45,6 @@ export class DOMInvite implements Invite {
for (let split of ["#", "?"]) { for (let split of ["#", "?"]) {
codeLink = codeLink.split(split)[0]; codeLink = codeLink.split(split)[0];
} }
if (codeLink.slice(-1) != "/") { codeLink += "/"; }
this._codeLink = codeLink + "invite/" + code; this._codeLink = codeLink + "invite/" + code;
const linkEl = this._codeArea.querySelector("a") as HTMLAnchorElement; const linkEl = this._codeArea.querySelector("a") as HTMLAnchorElement;
if (this.label == "") { if (this.label == "") {

View File

@ -400,6 +400,7 @@ window.onpopstate = (event: PopStateEvent) => {
const card = cards[i]; const card = cards[i];
const back = card.getElementsByClassName("back")[0] as HTMLSpanElement; const back = card.getElementsByClassName("back")[0] as HTMLSpanElement;
const next = card.getElementsByClassName("next")[0] as HTMLSpanElement; const next = card.getElementsByClassName("next")[0] as HTMLSpanElement;
console.log(cards[i]);
const titleEl = cards[i].querySelector("span.heading") as HTMLElement; const titleEl = cards[i].querySelector("span.heading") as HTMLElement;
let title = titleEl.textContent.replace("/", "_").replace(" ", "-"); let title = titleEl.textContent.replace("/", "_").replace(" ", "-");
if (titleEl.classList.contains("welcome")) { if (titleEl.classList.contains("welcome")) {

View File

@ -11,26 +11,17 @@ import (
) )
var css = []string{"bundle.css", "remixicon.css"} var css = []string{"bundle.css", "remixicon.css"}
var cssHeader string var cssHeader = func() string {
func (app *appContext) loadCSSHeader() string {
l := len(css) l := len(css)
h := "" h := ""
for i, f := range css { for i, f := range css {
h += "<" + app.URLBase + "/css/" + f + ">; rel=preload; as=style" h += "</css/" + f + ">; rel=preload; as=style"
if l > 1 && i != (l-1) { if l > 1 && i != (l-1) {
h += ", " h += ", "
} }
} }
return h return h
} }()
func (app *appContext) getURLBase(gc *gin.Context) string {
if strings.HasPrefix(gc.Request.URL.String(), app.URLBase) {
return app.URLBase
}
return ""
}
func gcHTML(gc *gin.Context, code int, file string, templ gin.H) { func gcHTML(gc *gin.Context, code int, file string, templ gin.H) {
gc.Header("Cache-Control", "no-cache") gc.Header("Cache-Control", "no-cache")
@ -43,7 +34,7 @@ func (app *appContext) pushResources(gc *gin.Context, admin bool) {
if admin { if admin {
toPush := []string{"/js/admin.js", "/js/theme.js", "/js/lang.js", "/js/modal.js", "/js/tabs.js", "/js/invites.js", "/js/accounts.js", "/js/settings.js", "/js/profiles.js", "/js/common.js"} toPush := []string{"/js/admin.js", "/js/theme.js", "/js/lang.js", "/js/modal.js", "/js/tabs.js", "/js/invites.js", "/js/accounts.js", "/js/settings.js", "/js/profiles.js", "/js/common.js"}
for _, f := range toPush { for _, f := range toPush {
if err := pusher.Push(app.URLBase+f, nil); err != nil { if err := pusher.Push(f, nil); err != nil {
app.debug.Printf("Failed HTTP2 ServerPush of \"%s\": %+v", f, err) app.debug.Printf("Failed HTTP2 ServerPush of \"%s\": %+v", f, err)
} }
} }
@ -69,7 +60,7 @@ func (app *appContext) AdminPage(gc *gin.Context) {
notificationsEnabled, _ := app.config.Section("notifications").Key("enabled").Bool() notificationsEnabled, _ := app.config.Section("notifications").Key("enabled").Bool()
ombiEnabled := app.config.Section("ombi").Key("enabled").MustBool(false) ombiEnabled := app.config.Section("ombi").Key("enabled").MustBool(false)
gcHTML(gc, http.StatusOK, "admin.html", gin.H{ gcHTML(gc, http.StatusOK, "admin.html", gin.H{
"urlBase": app.getURLBase(gc), "urlBase": app.URLBase,
"cssClass": app.cssClass, "cssClass": app.cssClass,
"contactMessage": "", "contactMessage": "",
"email_enabled": emailEnabled, "email_enabled": emailEnabled,
@ -143,8 +134,9 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
Password: claims["password"].(string), Password: claims["password"].(string),
Code: claims["invite"].(string), Code: claims["invite"].(string),
} }
_, success := app.newUser(req, true) f, success := app.newUser(req, true)
if !success { if !success {
f(gc)
fail() fail()
return return
} }
@ -152,7 +144,6 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
"strings": app.storage.lang.Form[lang].Strings, "strings": app.storage.lang.Form[lang].Strings,
"successMessage": app.config.Section("ui").Key("success_message").String(), "successMessage": app.config.Section("ui").Key("success_message").String(),
"contactMessage": app.config.Section("ui").Key("contact_message").String(), "contactMessage": app.config.Section("ui").Key("contact_message").String(),
"jfLink": app.config.Section("jellyfin").Key("public_server").String(),
}) })
inv, ok := app.storage.invites[code] inv, ok := app.storage.invites[code]
if ok { if ok {
@ -167,7 +158,7 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
email = "" email = ""
} }
gcHTML(gc, http.StatusOK, "form-loader.html", gin.H{ gcHTML(gc, http.StatusOK, "form-loader.html", gin.H{
"urlBase": app.getURLBase(gc), "urlBase": app.URLBase,
"cssClass": app.cssClass, "cssClass": app.cssClass,
"contactMessage": app.config.Section("ui").Key("contact_message").String(), "contactMessage": app.config.Section("ui").Key("contact_message").String(),
"helpMessage": app.config.Section("ui").Key("help_message").String(), "helpMessage": app.config.Section("ui").Key("help_message").String(),