Compare commits
386 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f50ab36fd | ||
|
|
6124b9b3f3 | ||
|
|
a8ce35c13f | ||
|
|
26d9864051 | ||
|
|
a3a22d353c | ||
|
|
dd5eecf9f9 | ||
|
|
7e0e0b0520 | ||
|
|
8bee09cd01 | ||
|
|
deb117fc34 | ||
|
|
a9a0005007 | ||
|
|
4eb7afead6 | ||
|
|
d1b5b74060 | ||
|
|
cf91ee62ed | ||
|
|
277690ef79 | ||
|
|
f7f3530a33 | ||
|
|
2d3a5c739c | ||
|
|
3dbb993d35 | ||
|
|
508168b49e | ||
|
|
0e1cbd7e7b | ||
|
|
e73ecb7a52 | ||
|
|
62be8adc65 | ||
|
|
acc8892f26 | ||
|
|
0f0355fd01 | ||
|
|
a31f174375 | ||
|
|
18ae03554f | ||
|
|
57e6469564 | ||
|
|
cd2c37057d | ||
|
|
a35ca762e3 | ||
|
|
fd10b2600f | ||
|
|
1384091d95 | ||
|
|
ca29ea2d46 | ||
|
|
d8c9ae4ff6 | ||
|
|
4403ea8e18 | ||
|
|
528829ffda | ||
|
|
84429a3399 | ||
|
|
6be5d6cbcb | ||
|
|
c59ea2000c | ||
|
|
30ee554f56 | ||
|
|
c1d984b86d | ||
|
|
fe1570d0bc | ||
|
|
edfd295fb4 | ||
|
|
d57d33b620 | ||
|
|
aedea1bea6 | ||
|
|
535a100314 | ||
|
|
360c25d084 | ||
|
|
d47afe05f4 | ||
|
|
942792cdfa | ||
|
|
685254950e | ||
|
|
e6cc7fce1a | ||
|
|
d8b1f03ac4 | ||
|
|
d81679fbae | ||
|
|
ebb49fce97 | ||
|
|
0fd4f516b1 | ||
|
|
9fff5781f4 | ||
|
|
e19352a69f | ||
|
|
09b96e5983 | ||
|
|
bd9f4258e2 | ||
|
|
a37cdf43f3 | ||
|
|
4821b30634 | ||
|
|
dd8dfcb2b1 | ||
|
|
73c7f22bd1 | ||
|
|
e7ca335d83 | ||
|
|
3730775018 | ||
|
|
4fcba32f74 | ||
|
|
b39ad5c688 | ||
|
|
a41b382dba | ||
|
|
9092f42834 | ||
|
|
af563aa6e5 | ||
|
|
f37edcb751 | ||
|
|
5d33dcf68e | ||
|
|
947da02b3c | ||
|
|
838d108d25 | ||
|
|
4a19af3353 | ||
|
|
4da1c8c2b6 | ||
|
|
c988239fa8 | ||
|
|
94e3c13b3e | ||
|
|
36f3860c4c | ||
|
|
f78fa28822 | ||
|
|
2de7182c55 | ||
|
|
f3e1606440 | ||
|
|
b7236319ec | ||
|
|
556c31d4ea | ||
|
|
0bf8cd65cd | ||
|
|
4d27f7fc7a | ||
|
|
a4f59203b0 | ||
|
|
eeb9b07bce | ||
|
|
9ae16163bb | ||
|
|
c5ce66bd4d | ||
|
|
da8dd7def8 | ||
|
|
a4b5d6dea8 | ||
|
|
77799b2917 | ||
|
|
5ff3839239 | ||
|
|
d560df5b1e | ||
|
|
91c8ce8089 | ||
|
|
0fe72b41bf | ||
|
|
a1d93cd6af | ||
|
|
53ac01eda4 | ||
|
|
4cea755065 | ||
|
|
be4e83d69c | ||
|
|
1e58a33c68 | ||
|
|
15e1766920 | ||
|
|
a220ba8dfb | ||
|
|
b29c24a405 | ||
|
|
fbe3553b25 | ||
|
|
f727e2c5b2 | ||
|
|
6c6af623a6 | ||
|
|
548dceda28 | ||
|
|
e67b2e91fb | ||
|
|
412fe31da6 | ||
|
|
1bfec54c93 | ||
|
|
5b319d6612 | ||
|
|
626d623841 | ||
|
|
08343298fb | ||
|
|
7a343bbc08 | ||
|
|
2be34ed063 | ||
|
|
7e32c8c4f0 | ||
|
|
a0871e7c47 | ||
|
|
f6834ee63d | ||
|
|
54b8e30cff | ||
|
|
504d3e39de | ||
|
|
4e2dce7f18 | ||
|
|
b31506d94e | ||
|
|
5961bc6086 | ||
|
|
b0ffa457df | ||
|
|
9e3fa0ea9a | ||
|
|
ff69d0ba6d | ||
|
|
cf25472746 | ||
|
|
5e651d7b08 | ||
|
|
bcd1011b68 | ||
|
|
f8f0715a3b | ||
|
|
e27e08bfd7 | ||
|
|
ae4d47af43 | ||
|
|
b32271e375 | ||
|
|
753e233e27 | ||
|
|
d6669bfa70 | ||
|
|
94b288671d | ||
|
|
a444e53a99 | ||
|
|
792846f727 | ||
|
|
6b9fa7bf8a | ||
|
|
c4378d19db | ||
|
|
bdebd97dc0 | ||
|
|
cf2bd6c095 | ||
|
|
08f3675f71 | ||
|
|
8d53ddcf93 | ||
|
|
c20dc24ccf | ||
|
|
b60be0db6d | ||
|
|
756e7345cf | ||
|
|
eb3489b34f | ||
|
|
9693ce3dcd | ||
|
|
6e88c1f4fc | ||
|
|
67c60cb677 | ||
|
|
1a6b0d2b6e | ||
|
|
5aaab7904f | ||
|
|
9885c25a2e | ||
|
|
27ad7a4cf7 | ||
|
|
6551eeb938 | ||
|
|
36c23c1e4f | ||
|
|
6b4d4da455 | ||
|
|
aa2891fc87 | ||
|
|
db526fc611 | ||
|
|
a869acd5dc | ||
|
|
f763cfb53a | ||
|
|
25a8d3807e | ||
|
|
b10b558358 | ||
|
|
504b602c0b | ||
|
|
f04411e137 | ||
|
|
1336a87ae2 | ||
|
|
872c366384 | ||
|
|
762d5325fb | ||
|
|
7f37633423 | ||
|
|
8ec4031ba3 | ||
|
|
4c10996c09 | ||
|
|
4d3acb2c4c | ||
|
|
833d02b032 | ||
|
|
30198fab87 | ||
|
|
51768958c6 | ||
|
|
3e55cd1e31 | ||
|
|
35f0fead53 | ||
|
|
a95d8bff29 | ||
|
|
48332a4ffa | ||
|
|
2266bbc320 | ||
|
|
b682685a3b | ||
|
|
91411437e2 | ||
|
|
119bed7024 | ||
|
|
6d70a5b24b | ||
|
|
a99ee04aca | ||
|
|
3ca2315290 | ||
|
|
d4bcf229e9 | ||
|
|
3950455a3f | ||
|
|
7e8e242db0 | ||
|
|
cda90f20af | ||
|
|
8ba393ebc0 | ||
|
|
2de1570a98 | ||
|
|
6b01e0d44d | ||
|
|
af4dcd1e2a | ||
|
|
a8ce68959d | ||
|
|
05bc38565c | ||
|
|
574ca4734d | ||
|
|
0957dd58c2 | ||
|
|
4db5d96bb1 | ||
|
|
76c19731cb | ||
|
|
fea368aaae | ||
|
|
1f8bc027c8 | ||
|
|
f2240ebf0d | ||
|
|
9b9f34ae96 | ||
|
|
86559d5c76 | ||
|
|
40ec5b9933 | ||
|
|
fbb9f20026 | ||
|
|
d5a33cf242 | ||
|
|
c35fdc2cbe | ||
|
|
84d5bc8f67 | ||
|
|
b8d9d22545 | ||
|
|
788afa1025 | ||
|
|
6ca3ab899c | ||
|
|
d4096d0062 | ||
|
|
306ede47d6 | ||
|
|
fc0e86ffd8 | ||
|
|
729fc7baf7 | ||
|
|
2d83e9ff7e | ||
|
|
a0af76364a | ||
|
|
169622bf95 | ||
|
|
78b5136b9a | ||
|
|
e546f50141 | ||
|
|
e35fe8d425 | ||
|
|
6ed2f7aaa6 | ||
|
|
084e8ec432 | ||
|
|
fd7f74682b | ||
|
|
9950c158a1 | ||
|
|
21125033ff | ||
|
|
1dc0b2234a | ||
|
|
0ea5c7fdc0 | ||
|
|
b538922c05 | ||
|
|
f0f4e8118e | ||
|
|
2f501697db | ||
|
|
0a71d5b216 | ||
|
|
0014db44f0 | ||
|
|
885d2ebf0f | ||
|
|
6d089a9818 | ||
|
|
de81c7e29f | ||
|
|
e49996c401 | ||
|
|
aa40a72075 | ||
|
|
19b7341e80 | ||
|
|
73645a7569 | ||
|
|
a9dac8c04c | ||
|
|
43fbbbcd16 | ||
|
|
fc57a8c97f | ||
|
|
1fb2ef5675 | ||
|
|
e0d994c35c | ||
|
|
cab30eb628 | ||
|
|
71df011556 | ||
|
|
b2828110e3 | ||
|
|
50eb05776a | ||
|
|
19715f25f6 | ||
|
|
d41a281d53 | ||
|
|
a8229631bd | ||
|
|
0a2cf6132f | ||
|
|
d7ab01063a | ||
|
|
6fb8f1ed7f | ||
|
|
a9b11012bc | ||
|
|
e7cb1f516b | ||
|
|
555d5abf59 | ||
|
|
93937ec3b5 | ||
|
|
93c7a6e31b | ||
|
|
a9cabe3d74 | ||
|
|
d6fd1d6894 | ||
|
|
375022ba95 | ||
|
|
75fdf6ec3d | ||
|
|
561c461a18 | ||
|
|
59ebf52fe2 | ||
|
|
89fb3fa619 | ||
|
|
9bd6abadf4 | ||
|
|
953a66ec47 | ||
|
|
4e826f4167 | ||
|
|
e97b90d4d7 | ||
|
|
fb6256d1ed | ||
|
|
7035a3fe9c | ||
|
|
62c29d55cc | ||
|
|
a83dbcf3ab | ||
|
|
e48bdcc45b | ||
|
|
0b473ef01f | ||
|
|
e03525a1d1 | ||
|
|
087172c79e | ||
|
|
8fd919bf04 | ||
|
|
2ad84db482 | ||
|
|
85536ff79e | ||
|
|
8b62c91d13 | ||
|
|
e7d1693517 | ||
|
|
e78b4882b3 | ||
|
|
e01144950b | ||
|
|
86ef665b12 | ||
|
|
f419a57e6d | ||
|
|
d7e8ec95de | ||
|
|
5a9bc1c66f | ||
|
|
1f9af8df89 | ||
|
|
0676b6c41f | ||
|
|
ac842e6273 | ||
|
|
ce8cdced4d | ||
|
|
b8e3fc636c | ||
|
|
519a5615cc | ||
|
|
168b217553 | ||
|
|
7d698d63e3 | ||
|
|
035dbde819 | ||
|
|
c373d8b2d6 | ||
|
|
8698c3c6a4 | ||
|
|
0edd2ba68b | ||
|
|
b91f0b5a18 | ||
|
|
24fa841c0d | ||
|
|
44558b8109 | ||
|
|
478b40d0ff | ||
|
|
8b816dc725 | ||
|
|
81a58f628b | ||
|
|
e98c9b46f1 | ||
|
|
b3ce7acfcb | ||
|
|
9fac79b1f0 | ||
|
|
591e3c5ca1 | ||
|
|
35d407afef | ||
|
|
a6447165b7 | ||
|
|
23800bb892 | ||
|
|
b47cb91f55 | ||
|
|
2d9e3fbc1d | ||
|
|
bf67e27737 | ||
|
|
3427c97e3e | ||
|
|
81e69a7166 | ||
|
|
564098b9d8 | ||
|
|
ec659174fb | ||
|
|
1a42d8280c | ||
|
|
b14f10d79d | ||
|
|
ee8facd1bf | ||
|
|
811657b553 | ||
|
|
95936f7c29 | ||
|
|
613d4cd9af | ||
|
|
7beb3d9974 | ||
|
|
6f2bb7f0b5 | ||
|
|
315b5fda91 | ||
|
|
a6aa89e502 | ||
|
|
3bf722c5fe | ||
|
|
e931c09a34 | ||
|
|
f8f5f35cc1 | ||
|
|
524941da0c | ||
|
|
22bba922f9 | ||
|
|
d928df7ab2 | ||
|
|
4b11bbe21f | ||
|
|
18bcd55972 | ||
|
|
057f306ed9 | ||
|
|
76bbb3f147 | ||
|
|
0f3ad8bb69 | ||
|
|
1d47b9074f | ||
|
|
5167fde080 | ||
|
|
a62648ee68 | ||
|
|
5dee414596 | ||
|
|
8cf9b1f905 | ||
|
|
6bf1920160 | ||
|
|
33f8070e57 | ||
|
|
4d2a018032 | ||
|
|
ca7fb540ee | ||
|
|
beb0712ce9 | ||
|
|
a081b14794 | ||
|
|
e416acf6bd | ||
|
|
bf94f76509 | ||
|
|
ac239a309c | ||
|
|
0f12586166 | ||
|
|
b1b50ce561 | ||
|
|
8e2bf48ab4 | ||
|
|
6ec5022a0d | ||
|
|
ef97e0ac76 | ||
|
|
30736a055d | ||
|
|
d0905a29be | ||
|
|
fe5cf69b7a | ||
|
|
c560ec0f9f | ||
|
|
71554e0c85 | ||
|
|
0efd7c5718 | ||
|
|
901ad7529e | ||
|
|
b64bcc9738 | ||
|
|
fddb7b7584 | ||
|
|
b91302ddf8 | ||
|
|
ea0293bd4e | ||
|
|
51f2f4cc6a | ||
|
|
2d93b3b7ee | ||
|
|
0f41d1e6cf | ||
|
|
36edd4ab0d | ||
|
|
716d6a931a | ||
|
|
72bf280e2d | ||
|
|
326c2cf70a | ||
|
|
2816c6277d | ||
|
|
99875b9176 | ||
|
|
0e21942cd6 |
46
.drone.yml
@@ -9,23 +9,28 @@ steps:
|
||||
commands:
|
||||
- git fetch --tags
|
||||
- name: release
|
||||
image: golang:latest
|
||||
image: hrfee/jfa-go-build-docker:latest
|
||||
volumes:
|
||||
- name: ssh_key
|
||||
path: /id_rsa
|
||||
environment:
|
||||
BUILDRONE_KEY:
|
||||
from_secret: BUILDRONE_KEY
|
||||
GITHUB_TOKEN:
|
||||
from_secret: github_token
|
||||
commands:
|
||||
- apt-get update -y
|
||||
- apt-get install build-essential python3-pip curl software-properties-common sed upx -y
|
||||
- (curl -sL https://deb.nodesource.com/setup_14.x | bash -)
|
||||
- apt-get install nodejs
|
||||
- curl -sL https://git.io/goreleaser > ../goreleaser
|
||||
- chmod +x ../goreleaser
|
||||
- ./scripts/version.sh ../goreleaser
|
||||
- wget https://builds.hrfee.pw/upload.py -P ../
|
||||
- pip3 install requests
|
||||
- bash -c 'sftp -P 2022 -i /id_rsa -o StrictHostKeyChecking=no root@161.97.102.153:/repo/incoming <<< $"put dist/*.deb"'
|
||||
- bash -c 'ssh -i /id_rsa root@161.97.102.153 -p 2022 "repo-process-deb trusty"'
|
||||
- bash -c 'python3 ../upload.py https://builds.hrfee.pw hrfee jfa-go --tag internal=true'
|
||||
volumes:
|
||||
- name: ssh_key
|
||||
host:
|
||||
path: /root/.ssh/id_rsa_packaging
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
||||
@@ -71,22 +76,35 @@ type: docker
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: golang:latest
|
||||
image: hrfee/jfa-go-build-docker:latest
|
||||
volumes:
|
||||
- name: ssh_key
|
||||
path: /id_rsa
|
||||
- name: ssh_key2
|
||||
path: /id_rsa2
|
||||
commands:
|
||||
- apt-get update -y
|
||||
- apt-get install build-essential python3-pip curl software-properties-common sed upx -y
|
||||
- (curl -sL https://deb.nodesource.com/setup_14.x | bash -)
|
||||
- apt-get install nodejs
|
||||
- curl -sL https://git.io/goreleaser > goreleaser
|
||||
- chmod +x goreleaser
|
||||
- ./scripts/version.sh ./goreleaser --snapshot --skip-publish --rm-dist
|
||||
- wget https://builds.hrfee.pw/upload.py
|
||||
- pip3 install requests
|
||||
- bash -c 'python3 upload.py https://builds.hrfee.pw hrfee jfa-go --upload ./dist/*.tar.gz --tag internal-git=true'
|
||||
- bash -c 'sftp -i /id_rsa2 -o StrictHostKeyChecking=no root@161.97.102.153:/mnt/redoc <<< $"put docs/swagger.json jfa-go.json"'
|
||||
- bash -c 'sftp -P 2022 -i /id_rsa -o StrictHostKeyChecking=no root@161.97.102.153:/repo/incoming <<< $"put dist/*.deb"'
|
||||
# - bash -c 'ssh -i /id_rsa root@161.97.102.153 -p 2022 "reprepro -Vb /repo remove trusty-unstable jfa-go"'
|
||||
# - bash -c 'ssh -i /id_rsa root@161.97.102.153 -p 2022 "reprepro -Vb /repo remove trusty-unstable jfa-go-tray"'
|
||||
- bash -c 'ssh -i /id_rsa root@161.97.102.153 -p 2022 "repo-process-deb trusty"'
|
||||
- bash -c 'python3 upload.py https://builds.hrfee.pw hrfee jfa-go --upload ./dist/*.zip ./dist/*.rpm ./dist/*.apk --tag internal-git=true'
|
||||
environment:
|
||||
BUILDRONE_KEY:
|
||||
from_secret: BUILDRONE_KEY
|
||||
|
||||
volumes:
|
||||
- name: ssh_key
|
||||
host:
|
||||
path: /root/.ssh/id_rsa_packaging
|
||||
- name: ssh_key2
|
||||
host:
|
||||
path: /root/.ssh/docker-build
|
||||
trigger:
|
||||
branch:
|
||||
- main
|
||||
@@ -141,12 +159,8 @@ type: docker
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: golang:latest
|
||||
image: hrfee/jfa-go-build-docker:latest
|
||||
commands:
|
||||
- apt-get update -y
|
||||
- apt-get install build-essential python3-pip curl software-properties-common sed upx -y
|
||||
- (curl -sL https://deb.nodesource.com/setup_14.x | bash -)
|
||||
- apt-get install nodejs
|
||||
- curl -sL https://git.io/goreleaser > goreleaser
|
||||
- chmod +x goreleaser
|
||||
- ./scripts/version.sh ./goreleaser --snapshot --skip-publish --rm-dist
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -7,7 +7,7 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### Read the [FAQ](https://github.com/hrfee/jfa-go/wiki/FAQ) first!
|
||||
#### Read the [FAQ](https://wiki.jfa-go.com/docs/faq/) first!
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
@@ -19,8 +19,11 @@ What to do to reproduce the problem.
|
||||
|
||||
**Logs**
|
||||
|
||||
**If you're using a build with a tray icon, right-click on it and press "Open logs" to access your logs.**
|
||||
|
||||
When you notice the problem, check the output of `jfa-go`. If the problem is not obvious (e.g a panic (red text) or 'ERROR' log), re-run jfa-go with the `-debug` argument and reproduce the problem. You should then take a screenshot of the output, or paste it here, preferably between \`\`\` tags (e.g \`\`\``Log here`\`\`\`). Remember to censor any personal information.
|
||||
|
||||
|
||||
If nothing catches your eye in the log, access the admin page via your browser, go into the console (Right click > Inspect Element > Console), refresh, reproduce the problem then paste the output here in the same way as above.
|
||||
|
||||
**Configuration**
|
||||
|
||||
6
.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
node_modules/
|
||||
site/node_modules/
|
||||
site/out/
|
||||
mail/*.html
|
||||
dist/
|
||||
build/
|
||||
@@ -14,3 +16,7 @@ server.pem
|
||||
server.crt
|
||||
instructions-debian.txt
|
||||
cl.md
|
||||
./telegram/
|
||||
mautrix/
|
||||
tempts/
|
||||
matacc.txt
|
||||
|
||||
118
.goreleaser.yml
@@ -25,24 +25,81 @@ before:
|
||||
- npx esbuild --bundle ts/pwr.ts --outfile=./data/web/js/pwr.js --minify
|
||||
- npx esbuild --bundle ts/form.ts --outfile=./data/web/js/form.js --minify
|
||||
- npx esbuild --bundle ts/setup.ts --outfile=./data/web/js/setup.js --minify
|
||||
- npx esbuild --bundle ts/crash.ts --outfile=./data/crash.js --minify
|
||||
- cp html/crash.html data/
|
||||
- npx uncss data/crash.html --csspath web/css --output data/bundle.css
|
||||
- node scripts/inline.js root data data/crash.html data/crash.html
|
||||
- rm data/bundle.css
|
||||
- mv data/crash.html data/html/
|
||||
- go get -u github.com/swaggo/swag/cmd/swag
|
||||
- swag init -g main.go
|
||||
builds:
|
||||
- dir: ./
|
||||
- id: notray
|
||||
dir: ./
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
ldflags:
|
||||
- -s -w -X main.version={{.Env.JFA_GO_VERSION}} -X main.commit={{.ShortCommit}} -X main.updater=binary
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
- amd64
|
||||
- id: windows-tray
|
||||
dir: ./
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=x86_64-w64-mingw32-gcc
|
||||
- CXX=x86_64-w64-mingw32-g++
|
||||
flags:
|
||||
- -tags=tray
|
||||
ldflags:
|
||||
- -s -w -X main.version={{.Env.JFA_GO_VERSION}} -X main.commit={{.ShortCommit}} -X main.updater=binary -H=windowsgui
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- id: linux-tray
|
||||
dir: ./
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
flags:
|
||||
- -tags=tray
|
||||
ldflags:
|
||||
- -s -w -X main.version={{.Env.JFA_GO_VERSION}} -X main.commit={{.ShortCommit}} -X main.updater=binary
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
archives:
|
||||
- replacements:
|
||||
- id: windows-tray
|
||||
builds:
|
||||
- windows-tray
|
||||
format: zip
|
||||
name_template: "{{ .ProjectName }}_{{ .Version }}_TrayIcon_{{ .Os }}_{{ .Arch }}"
|
||||
replacements:
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
amd64: x86_64
|
||||
- id: linux-tray
|
||||
builds:
|
||||
- linux-tray
|
||||
format: zip
|
||||
name_template: "{{ .ProjectName }}_{{ .Version }}_TrayIcon_{{ .Os }}_{{ .Arch }}"
|
||||
replacements:
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
amd64: x86_64
|
||||
- id: notray
|
||||
builds:
|
||||
- notray
|
||||
format: zip
|
||||
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
|
||||
replacements:
|
||||
darwin: macOS
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
@@ -50,10 +107,61 @@ archives:
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
name_template: "git-{{.ShortCommit}}"
|
||||
name_template: "0.0.0-{{ .Env.JFA_GO_NFPM_EPOCH }}"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
nfpms:
|
||||
- id: notray
|
||||
file_name_template: '{{ .ProjectName }}{{ if .IsSnapshot }}-git{{ end }}_{{ .Arch }}_{{ if .IsSnapshot }}{{ .ShortCommit }}{{ else }}v{{ .Version }}{{ end }}'
|
||||
package_name: jfa-go
|
||||
homepage: https://github.com/hrfee/jfa-go
|
||||
description: A web app for managing users on Jellyfin
|
||||
maintainer: Harvey Tindall <hrfee@hrfee.dev>
|
||||
license: MIT
|
||||
vendor: hrfee.dev
|
||||
version_metadata: git
|
||||
builds:
|
||||
- notray
|
||||
contents:
|
||||
- src: ./LICENSE
|
||||
dst: /usr/share/licenses/jfa-go
|
||||
formats:
|
||||
- apk
|
||||
- deb
|
||||
- rpm
|
||||
- id: tray
|
||||
file_name_template: '{{ .ProjectName }}{{ if .IsSnapshot }}-git{{ end }}_TrayIcon_{{ .Arch }}_{{ if .IsSnapshot }}{{ .ShortCommit }}{{ else }}v{{ .Version }}{{ end }}'
|
||||
package_name: jfa-go-tray
|
||||
homepage: https://github.com/hrfee/jfa-go
|
||||
description: A web app for managing users on Jellyfin
|
||||
maintainer: Harvey Tindall <hrfee@hrfee.dev>
|
||||
license: MIT
|
||||
vendor: hrfee.dev
|
||||
version_metadata: git
|
||||
builds:
|
||||
- linux-tray
|
||||
contents:
|
||||
- src: ./LICENSE
|
||||
dst: /usr/share/licenses/jfa-go
|
||||
formats:
|
||||
- apk
|
||||
- deb
|
||||
- rpm
|
||||
overrides:
|
||||
deb:
|
||||
conflicts:
|
||||
- jfa-go
|
||||
replaces:
|
||||
- jfa-go
|
||||
dependencies:
|
||||
- libappindicator3-1
|
||||
rpm:
|
||||
dependencies:
|
||||
- libappindicator-gtk3
|
||||
apk:
|
||||
dependencies:
|
||||
- libappindicator
|
||||
|
||||
@@ -1,15 +1,44 @@
|
||||
#### Code
|
||||
---
|
||||
title: "Building/Contributing for developers"
|
||||
date: 2021-07-25T00:33:36+01:00
|
||||
draft: false
|
||||
---
|
||||
# Code
|
||||
I use 4 spaces for indentation. Go should ideally be formatted with `goimports` and/or `gofmt`. I don't use a formatter on typescript, so don't worry about that.
|
||||
|
||||
#### Compiling
|
||||
Code in Go should ideally use `PascalCase` for exported values, and `camelCase` for non-exported, JSON for transferring data should use `snake_case`, and Typescript should use `camelCase`. Forgive me for my many inconsistencies in this, and feel free to fix them if you want.
|
||||
|
||||
Prefix each of these with `make DEBUG=on INTERNAL=off `:
|
||||
Functions in Go that need to access `*appContext` should be generally be receivers, except when the behaviour could be seen as somewhat independent from it (`email.go` is the best example, its behaviour is broadly independent from the main app except from a couple config values).
|
||||
|
||||
|
||||
# Compiling
|
||||
|
||||
The Makefile is more suited towards development than other build methods, and provides separate build stages to speed up compilation when only making changes to specific aspects of the project.
|
||||
|
||||
Prefix each of these with `make DEBUG=on `:
|
||||
* `all` will download deps and build everything. The executable and data will be placed in `build`. This is only necessary the first time.
|
||||
* `npm` will download all node.js build-time dependencies.
|
||||
* `compile` will only compile go code into the `build/jfa-go` executable.
|
||||
* `typescript` will compile typescript w/ sourcemaps into `build/data/web/js`.
|
||||
* `bundle-css` will bundle CSS and place it in `build/data/web/css`.
|
||||
* `inline` will inline the css and javascript used in the single-file crash report webpage.
|
||||
* `configuration` will generate the `config-base.json` (used to render settings in the web ui) and `config-default.ini` and put them in `build/data`.
|
||||
* `email` will compile email mjml, and copy the text versions in to `build/data`.
|
||||
* `swagger`: generates swagger documentation for the API.
|
||||
* `copy` will copy iconography, html, language files and static data into `build/data`.
|
||||
|
||||
See the [wiki](https://github.com/hrfee/jfa-go/wiki/Build) for more info.
|
||||
## Environment variables
|
||||
|
||||
* `DEBUG=on/off`: If on, compiles with type-checking for typescript, sourcemaps, non-minified css and no symbol stripping.
|
||||
* `INTERNAL=on/off`: Whether or not to embed file assets into the binary itself, or store them separately beside the binary.
|
||||
* `UPDATER=on/off/docker`: Enable/Disable the updater, or set a special update type (currently only docker, which disables self-updating the binary).
|
||||
* `TRAY=on/off`: Enable/disable the tray icon, which lets you start/stop/autostart on login. For linux, requires `libappindicator3-dev` for debian or the equivalent on other distributions.
|
||||
* `GOESBUILD=on`: Use a locally installed `esbuild` binary. NPM doesn't provide builds for all os/architectures, so `npx esbuild` might not work for you, so the binary is compiled/installed with `go get`.
|
||||
* `GOBINARY=<path to go>`: Alternative path to go executable. Useful for testing with unstable go releases.
|
||||
* `VERSION=v<semver>`: Alternative verision number, useful to test update functionality.
|
||||
* `COMMIT=<short commit>`: Self explanatory.
|
||||
* `LDFLAGS=<ldflags>`: Passed to `go build -ldflags`.
|
||||
* `E2EE=on/off`: Enable/disable end-to-end encryption support for Matrix, which is currently very broken. Must subsequently be enabled (with Advanced settings enabled) in Settings > Matrix.
|
||||
* `TAGS=<tags>`: Passed to `go build -tags`.
|
||||
* `OS=<os>`: Unrelated to GOOS, if set to `windows`, `-H=windowsgui` is passed to ldflags, which stops a windows terminal popping up when run.
|
||||
* `RACE=on/off`: If on, compiles with the go race detector included.
|
||||
|
||||
@@ -6,7 +6,7 @@ RUN apt-get update -y \
|
||||
&& apt-get install build-essential python3-pip curl software-properties-common sed -y \
|
||||
&& (curl -sL https://deb.nodesource.com/setup_14.x | bash -) \
|
||||
&& apt-get install nodejs \
|
||||
&& (cd /opt/build; make configuration npm email typescript bundle-css swagger copy INTERNAL=off GOESBUILD=on) \
|
||||
&& (cd /opt/build; make configuration npm email typescript bundle-css inline swagger copy INTERNAL=off GOESBUILD=on) \
|
||||
&& 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
|
||||
|
||||
|
||||
|
||||
3
LICENSE
@@ -1,3 +1,5 @@
|
||||
---jfa-go---
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Harvey Tindall
|
||||
@@ -19,3 +21,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
78
Makefile
@@ -18,26 +18,58 @@ else ifneq ($(UPDATER), off)
|
||||
LDFLAGS := $(LDFLAGS) -X main.updater=$(UPDATER)
|
||||
endif
|
||||
|
||||
|
||||
|
||||
INTERNAL ?= on
|
||||
TRAY ?= off
|
||||
E2EE ?= off
|
||||
TAGS := -tags "
|
||||
|
||||
ifeq ($(INTERNAL), on)
|
||||
TAGS :=
|
||||
DATA := data
|
||||
else
|
||||
DATA := build/data
|
||||
TAGS := -tags external
|
||||
TAGS := $(TAGS) external
|
||||
endif
|
||||
|
||||
ifeq ($(TRAY), on)
|
||||
TAGS := $(TAGS) tray
|
||||
endif
|
||||
|
||||
ifeq ($(E2EE), on)
|
||||
TAGS := $(TAGS) e2ee
|
||||
endif
|
||||
|
||||
TAGS := $(TAGS)"
|
||||
|
||||
OS := $(shell go env GOOS)
|
||||
ifeq ($(TRAY)$(OS), onwindows)
|
||||
LDFLAGS := $(LDFLAGS) -H=windowsgui
|
||||
endif
|
||||
|
||||
DEBUG ?= off
|
||||
ifeq ($(DEBUG), on)
|
||||
LDFLAGS := -s -w $(LDFLAGS)
|
||||
SOURCEMAP := --sourcemap
|
||||
TYPECHECK := tsc -noEmit --project ts/tsconfig.json
|
||||
# jank
|
||||
COPYTS := rm -r $(DATA)/web/js/ts; cp -r ts $(DATA)/web/js
|
||||
COPYTS := rm -r $(DATA)/web/js/ts; cp -r tempts $(DATA)/web/js
|
||||
UNCSS := cp $(DATA)/web/css/bundle.css $(DATA)/bundle.css
|
||||
TAILWIND := --content ""
|
||||
else
|
||||
LDFLAGS := -s -w $(LDFLAGS)
|
||||
SOURCEMAP :=
|
||||
COPYTS :=
|
||||
TYPECHECK :=
|
||||
UNCSS := npx tailwindcss -i $(DATA)/web/css/bundle.css -o $(DATA)/bundle.css --content "html/crash.html"
|
||||
# UNCSS := npx uncss $(DATA)/crash.html --csspath web/css --output $(DATA)/bundle.css
|
||||
TAILWIND :=
|
||||
endif
|
||||
|
||||
RACE ?= off
|
||||
ifeq ($(RACE), on)
|
||||
RACEDETECTOR := -race
|
||||
else
|
||||
RACEDETECTOR :=
|
||||
endif
|
||||
|
||||
npm:
|
||||
@@ -62,16 +94,22 @@ email:
|
||||
|
||||
typescript:
|
||||
$(TYPECHECK)
|
||||
$(adding dark variants to typescript)
|
||||
-rm -r tempts
|
||||
cp -r ts tempts
|
||||
scripts/dark-variant.sh ts tempts
|
||||
scripts/dark-variant.sh ts tempts/modules
|
||||
$(info compiling typescript)
|
||||
-mkdir -p $(DATA)/web/js
|
||||
-$(ESBUILD) --bundle ts/admin.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/admin.js --minify
|
||||
-$(ESBUILD) --bundle ts/pwr.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/pwr.js --minify
|
||||
-$(ESBUILD) --bundle ts/form.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/form.js --minify
|
||||
-$(ESBUILD) --bundle ts/setup.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/setup.js --minify
|
||||
-$(ESBUILD) --bundle tempts/admin.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/admin.js --minify
|
||||
-$(ESBUILD) --bundle tempts/pwr.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/pwr.js --minify
|
||||
-$(ESBUILD) --bundle tempts/form.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/form.js --minify
|
||||
-$(ESBUILD) --bundle tempts/setup.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/setup.js --minify
|
||||
-$(ESBUILD) --bundle tempts/crash.ts --outfile=./$(DATA)/crash.js --minify
|
||||
$(COPYTS)
|
||||
|
||||
swagger:
|
||||
$(GOBINARY) get github.com/swaggo/swag/cmd/swag
|
||||
$(GOBINARY) install github.com/swaggo/swag/cmd/swag
|
||||
swag init -g main.go
|
||||
|
||||
compile:
|
||||
@@ -79,7 +117,7 @@ compile:
|
||||
$(GOBINARY) mod download
|
||||
$(info Building)
|
||||
mkdir -p build
|
||||
CGO_ENABLED=0 $(GOBINARY) build -ldflags="-s -w $(LDFLAGS)" $(TAGS) -o build/jfa-go
|
||||
$(GOBINARY) build $(RACEDETECTOR) -ldflags="$(LDFLAGS)" $(TAGS) -o build/jfa-go
|
||||
|
||||
compress:
|
||||
upx --lzma build/jfa-go
|
||||
@@ -88,12 +126,26 @@ bundle-css:
|
||||
-mkdir -p $(DATA)/web/css
|
||||
$(info bundling css)
|
||||
$(ESBUILD) --bundle css/base.css --outfile=$(DATA)/web/css/bundle.css --external:remixicon.css --minify
|
||||
npx tailwindcss -i $(DATA)/web/css/bundle.css -o $(DATA)/web/css/bundle.css $(TAILWIND)
|
||||
# npx postcss -o $(DATA)/web/css/bundle.css $(DATA)/web/css/bundle.css
|
||||
|
||||
inline:
|
||||
cp html/crash.html $(DATA)/crash.html
|
||||
$(UNCSS)
|
||||
node scripts/inline.js root $(DATA) $(DATA)/crash.html $(DATA)/crash.html
|
||||
rm $(DATA)/bundle.css
|
||||
|
||||
variants-html:
|
||||
$(info copying html)
|
||||
cp -r html $(DATA)/
|
||||
$(info adding dark variants to html)
|
||||
node scripts/missing-colors.js html $(DATA)/html
|
||||
|
||||
copy:
|
||||
$(info copying fonts)
|
||||
cp -r node_modules/remixicon/fonts/remixicon.css node_modules/remixicon/fonts/remixicon.woff2 $(DATA)/web/css/
|
||||
$(info copying html)
|
||||
cp -r html $(DATA)/
|
||||
$(info copying crash page)
|
||||
mv $(DATA)/crash.html $(DATA)/html/
|
||||
$(info copying static data)
|
||||
-mkdir -p $(DATA)/web
|
||||
cp -r static/* $(DATA)/web/
|
||||
@@ -122,4 +174,4 @@ clean:
|
||||
-rm docs/docs.go docs/swagger.json docs/swagger.yaml
|
||||
go clean
|
||||
|
||||
all: configuration npm email typescript bundle-css swagger copy compile
|
||||
all: configuration npm email typescript variants-html bundle-css inline swagger copy compile
|
||||
|
||||
92
README.md
@@ -1,15 +1,20 @@
|
||||

|
||||
[](https://drone.hrfee.dev/hrfee/jfa-go)
|
||||
[](https://hub.docker.com/r/hrfee/jfa-go)
|
||||
[](https://weblate.hrfee.pw/engage/jfa-go/)
|
||||
[](https://weblate.jfa-go.com/engage/jfa-go/)
|
||||
[](https://wiki.jfa-go.com)
|
||||
[](https://discord.com/invite/MrtvuQmyhP)
|
||||
|
||||
##### Downloads:
|
||||
##### [dockerhub](https://hub.docker.com/r/hrfee/jfa-go) | [stable](https://github.com/hrfee/jfa-go/releases) | [nightly](https://builds.hrfee.pw/view/hrfee/jfa-go) | [aur stable](https://aur.archlinux.org/packages/jfa-go) | [aur binary](https://aur.archlinux.org/packages/jfa-go-bin) | [aur nightly](https://aur.archlinux.org/packages/jfa-go-git)
|
||||
##### [docker](#docker) | [debian/ubuntu](#debian) | [arch (aur)](#aur) | [other platforms](#other-platforms)
|
||||
---
|
||||
jfa-go is a user management app for [Jellyfin](https://github.com/jellyfin/jellyfin) (and now [Emby](https://emby.media/)) that provides invite-based account creation as well as other features that make one's instance much easier to manage.
|
||||
|
||||
a rewrite of [jellyfin-accounts](https://github.com/hrfee/jellyfin-accounts) (original naming for both, ik
|
||||
😂).
|
||||
|
||||
#### Features
|
||||
* 🧑 Invite based account creation: Sends invites to your friends or family, and let them choose their own username and password without relying on you.
|
||||
* 🧑 Invite based account creation: Send invites to your friends or family, and let them choose their own username and password without relying on you.
|
||||
* Send invites via a link and/or email
|
||||
* Granular control over invites: Validity period as well as number of uses can be specified.
|
||||
* Account profiles: Assign settings profiles to invites so new users have your predefined permissions, homescreen layout, etc. applied to their account on creation.
|
||||
@@ -17,11 +22,12 @@ jfa-go is a user management app for [Jellyfin](https://github.com/jellyfin/jelly
|
||||
* ⌛ User expiry: Specify a validity period, and new users accounts will be disabled/deleted after it. The period can be manually extended too.
|
||||
* 🔗 Ombi Integration: Automatically creates Ombi accounts for new users using their email address and login details, and your own defined set of permissions.
|
||||
* Account management: Apply settings to your users individually or en masse, and delete users, optionally sending them an email notification with a reason.
|
||||
* Telegram/Discord/Matrix Integration: Verify users via a chat bot, and send Password Resets, Announcements, etc. through it.
|
||||
* 📨 Email storage: Add your existing users email addresses through the UI, and jfa-go will ask new users for them on account creation.
|
||||
* Email addresses can optionally be used instead of usernames
|
||||
* 🔑 Password resets: When users forget their passwords and request a change in Jellyfin, jfa-go reads the PIN from the created file and sends it straight to the user via email.
|
||||
* 🔑 Password resets: When users forget their passwords and request a change in Jellyfin, jfa-go reads the PIN from the created file and sends it straight to the user via email/telegram.
|
||||
* Notifications: Get notified when someone creates an account, or an invite expires.
|
||||
* 📣 Announcements: Bulk email your users with announcements about your server.
|
||||
* 📣 Announcements: Bulk message your users with announcements about your server.
|
||||
* Authentication via Jellyfin: Instead of using separate credentials for jfa-go and Jellyfin, jfa-go can use it as the authentication provider.
|
||||
* Enables the usage of jfa-go by multiple people
|
||||
* 🌓 Customizations
|
||||
@@ -42,7 +48,9 @@ jfa-go is a user management app for [Jellyfin](https://github.com/jellyfin/jelly
|
||||
|
||||
#### Install
|
||||
|
||||
The [Docker](https://hub.docker.com/r/hrfee/jfa-go) image is your best bet.
|
||||
**Note**: `TrayIcon` builds include a tray icon to start/stop/restart, and an option to automatically start when you log-in to your computer. For Linux users, these builds depend on the `libappindicator3-1`/`libappindicator-gtk3`/`libappindicator` package for Debian/Ubuntu, Fedora, and Alpine respectively.
|
||||
|
||||
##### [Docker](https://hub.docker.com/r/hrfee/jfa-go)
|
||||
```sh
|
||||
docker create \
|
||||
--name "jfa-go" \ # Whatever you want to name it
|
||||
@@ -53,9 +61,41 @@ docker create \
|
||||
-v /etc/localtime:/etc/localtime:ro \ # Makes sure time is correct
|
||||
hrfee/jfa-go # hrfee/jfa-go:unstable for latest build from git
|
||||
```
|
||||
Available on the AUR as [jfa-go](https://aur.archlinux.org/packages/jfa-go/), [jfa-go-bin](https://aur.archlinux.org/packages/jfa-go) or [jfa-go-git](https://aur.archlinux.org/packages/jfa-go-git/).
|
||||
|
||||
For other platforms, grab an archive from the release section for your platform (or nightly builds [here](https://builds.hrfee.dev/view/hrfee/jfa-go)), and extract the `jfa-go` executable to somewhere useful.
|
||||
##### [Debian/Ubuntu](https://apt.hrfee.dev)
|
||||
```sh
|
||||
sudo apt-get update && sudo apt-get install curl apt-transport-https gnupg
|
||||
curl https://apt.hrfee.dev/hrfee.pubkey.gpg | sudo apt-key add -
|
||||
|
||||
# For stable releases
|
||||
echo "deb https://apt.hrfee.dev trusty main" | sudo tee /etc/apt/sources.list.d/hrfee.list
|
||||
# ------
|
||||
# For unstable releases
|
||||
echo "deb https://apt.hrfee.dev trusty-unstable main" | sudo tee /etc/apt/sources.list.d/hrfee.list
|
||||
# ------
|
||||
|
||||
sudo apt-get update
|
||||
|
||||
# For servers
|
||||
sudo apt-get install jfa-go
|
||||
# ------
|
||||
# For desktops/servers with GUI (has dependencies)
|
||||
sudo apt-get install jfa-go-tray
|
||||
# ------
|
||||
```
|
||||
|
||||
##### Arch
|
||||
Available on the AUR as:
|
||||
* [jfa-go](https://aur.archlinux.org/packages/jfa-go/) (stable)
|
||||
* [jfa-go-bin](https://aur.archlinux.org/packages/jfa-go) (pre-compiled, stable)
|
||||
* [jfa-go-git](https://aur.archlinux.org/packages/jfa-go-git/) (nightly)
|
||||
|
||||
##### Other platforms
|
||||
Download precompiled binaries from:
|
||||
* [The releases section](https://github.com/hrfee/jfa-go/releases) (stable)
|
||||
* [Buildrone](https://builds.hrfee.dev/view/hrfee/jfa-go) (nightly)
|
||||
|
||||
unzip the `jfa-go`/`jfa-go.exe` executable to somewhere useful.
|
||||
* For \*nix/macOS users, `chmod +x jfa-go` then place it somewhere in your PATH like `/usr/bin`.
|
||||
|
||||
Run the executable to start.
|
||||
@@ -64,23 +104,34 @@ Run the executable to start.
|
||||
#### Build from source
|
||||
If you're using docker, a Dockerfile is provided that builds from source.
|
||||
|
||||
Otherwise, full build instructions can be found [here](https://github.com/hrfee/jfa-go/wiki/Build).
|
||||
Otherwise, full build instructions can be found [here](https://wiki.jfa-go.com/docs/build/).
|
||||
|
||||
#### Usage
|
||||
Simply run `jfa-go` to start the application. A setup wizard will start on `localhost:8056` (or your own specified address). Upon completion, refresh the page.
|
||||
|
||||
```
|
||||
Usage of ./jfa-go:
|
||||
-config string
|
||||
alternate path to config file. (default "~/.config/jfa-go/config.ini")
|
||||
-data string
|
||||
alternate path to data directory. (default "~/.config/jfa-go")
|
||||
Usage of jfa-go:
|
||||
start
|
||||
start jfa-go as a daemon and run in the background.
|
||||
stop
|
||||
stop a daemonized instance of jfa-go.
|
||||
systemd
|
||||
generate a systemd .service file.
|
||||
|
||||
-config, -c string
|
||||
alternate path to config file. (default "/home/hrfee/.config/jfa-go/config.ini")
|
||||
-data, -d string
|
||||
alternate path to data directory. (default "/home/hrfee/.config/jfa-go")
|
||||
-debug
|
||||
Enables debug logging and exposes pprof.
|
||||
Enables debug logging.
|
||||
-help, -h
|
||||
prints this message.
|
||||
-host string
|
||||
alternate address to host web ui on.
|
||||
-port int
|
||||
-port, -p int
|
||||
alternate port to host web ui on.
|
||||
-pprof
|
||||
Exposes pprof profiler on /debug/pprof.
|
||||
-swagger
|
||||
Enable swagger at /swagger/index.html
|
||||
```
|
||||
@@ -101,6 +152,11 @@ If you're switching from jellyfin-accounts, copy your existing `~/.jf-accounts`
|
||||
#### Contributing
|
||||
See [CONTRIBUTING.md](https://github.com/hrfee/jfa-go/blob/main/CONTRIBUTING.md).
|
||||
##### Translation
|
||||
[](https://weblate.hrfee.pw/engage/jfa-go/)
|
||||
[](https://weblate.jfa-go.com/engage/jfa-go/)
|
||||
|
||||
For translations, use the weblate instance [here](https://weblate.hrfee.pw/engage/jfa-go/). You can login with github.
|
||||
For translations, use the weblate instance [here](https://weblate.jfa-go.com/engage/jfa-go/). You can login with github.
|
||||
|
||||
#### Sponsors
|
||||
Big thanks to those who sponsor me. You can see them below:
|
||||
|
||||
[<img src="https://sponsors-endpoint.hrfee.pw/sponsor/avatar/0" width="35">](https://sponsors-endpoint.hrfee.pw/sponsor/profile/0)
|
||||
|
||||
14
auth.go
@@ -4,12 +4,11 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/lithammer/shortuuid/v3"
|
||||
)
|
||||
|
||||
@@ -23,17 +22,16 @@ func CreateToken(userId, jfId string) (string, string, error) {
|
||||
claims := jwt.MapClaims{
|
||||
"valid": true,
|
||||
"id": userId,
|
||||
"exp": strconv.FormatInt(time.Now().Add(time.Minute*20).Unix(), 10),
|
||||
"exp": time.Now().Add(time.Minute * 20).Unix(),
|
||||
"jfid": jfId,
|
||||
"type": "bearer",
|
||||
}
|
||||
|
||||
tk := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
token, err := tk.SignedString([]byte(os.Getenv("JFA_SECRET")))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
claims["exp"] = strconv.FormatInt(time.Now().Add(time.Hour*24).Unix(), 10)
|
||||
claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
|
||||
claims["type"] = "refresh"
|
||||
tk = jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
refresh, err = tk.SignedString([]byte(os.Getenv("JFA_SECRET")))
|
||||
@@ -58,7 +56,7 @@ func (app *appContext) authenticate(gc *gin.Context) {
|
||||
return
|
||||
}
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
expiryUnix, err := strconv.ParseInt(claims["exp"].(string), 10, 64)
|
||||
expiryUnix := int64(claims["exp"].(float64))
|
||||
if err != nil {
|
||||
app.debug.Printf("Auth denied: %s", err)
|
||||
respond(401, "Unauthorized", gc)
|
||||
@@ -102,7 +100,7 @@ type getTokenDTO struct {
|
||||
}
|
||||
|
||||
// @Summary Grabs an API token using username & password.
|
||||
// @description Click the lock icon next to this, login with your normal jfa-go credentials. Click 'try it out', then 'execute' and an API Key will be returned, copy it (not including quotes). On any of the other routes, click the lock icon and set the API key as "Bearer `your api key`".
|
||||
// @description If viewing docs locally, click the lock icon next to this, login with your normal jfa-go credentials. Click 'try it out', then 'execute' and an API Key will be returned, copy it (not including quotes). On any of the other routes, click the lock icon and set the API key as "Bearer `your api key`".
|
||||
// @Produce json
|
||||
// @Success 200 {object} getTokenDTO
|
||||
// @Failure 401 {object} stringResponse
|
||||
@@ -200,7 +198,7 @@ func (app *appContext) getTokenRefresh(gc *gin.Context) {
|
||||
return
|
||||
}
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
expiryUnix, err := strconv.ParseInt(claims["exp"].(string), 10, 64)
|
||||
expiryUnix := int64(claims["exp"].(float64))
|
||||
if err != nil {
|
||||
app.debug.Printf("getTokenRefresh: Invalid token expiry: %s", err)
|
||||
respond(401, "Invalid token", gc)
|
||||
|
||||
69
autostart.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// +build tray
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/emersion/go-autostart"
|
||||
"github.com/getlantern/systray"
|
||||
)
|
||||
|
||||
type Autostart struct {
|
||||
as *autostart.App
|
||||
enabled bool
|
||||
menuitem *systray.MenuItem
|
||||
clicked chan bool
|
||||
}
|
||||
|
||||
func NewAutostart(name, displayname, trayName, trayTooltip string) *Autostart {
|
||||
a := &Autostart{
|
||||
as: &autostart.App{
|
||||
Name: name,
|
||||
DisplayName: displayname,
|
||||
},
|
||||
enabled: true,
|
||||
clicked: make(chan bool),
|
||||
}
|
||||
a.menuitem = systray.AddMenuItemCheckbox(trayName, trayTooltip, a.as.IsEnabled())
|
||||
command := os.Args
|
||||
command[0], _ = filepath.Abs(command[0])
|
||||
// Make sure to replace any relative paths with absolute ones
|
||||
pathArgs := []string{"-d", "-data", "-c", "-config"}
|
||||
for i := 1; i < len(command); i++ {
|
||||
isPath := false
|
||||
for _, p := range pathArgs {
|
||||
if command[i-1] == p {
|
||||
isPath = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if isPath {
|
||||
command[i], _ = filepath.Abs(command[i])
|
||||
}
|
||||
}
|
||||
a.as.Exec = command
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Autostart) HandleCheck() {
|
||||
for range a.menuitem.ClickedCh {
|
||||
if !a.menuitem.Checked() {
|
||||
if err := a.as.Enable(); err != nil {
|
||||
log.Printf("Failed to enable autostart on login: %v", err)
|
||||
} else {
|
||||
a.menuitem.Check()
|
||||
log.Printf("Enabled autostart")
|
||||
}
|
||||
} else {
|
||||
if err := a.as.Disable(); err != nil {
|
||||
log.Printf("Failed to disable autostart on login: %v", err)
|
||||
} else {
|
||||
a.menuitem.Uncheck()
|
||||
log.Printf("Disabled autostart")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ type TimeoutHandler func()
|
||||
func NewTimeoutHandler(name, addr string, noFail bool) TimeoutHandler {
|
||||
return func() {
|
||||
if r := recover(); r != nil {
|
||||
out := fmt.Sprintf("Failed to authenticate with %s @ %s: Timed out", name, addr)
|
||||
out := fmt.Sprintf("Failed to authenticate with %s @ \"%s\": Timed out", name, addr)
|
||||
if noFail {
|
||||
log.Print(out)
|
||||
} else {
|
||||
|
||||
52
config.go
@@ -12,6 +12,10 @@ import (
|
||||
)
|
||||
|
||||
var emailEnabled = false
|
||||
var messagesEnabled = false
|
||||
var telegramEnabled = false
|
||||
var discordEnabled = false
|
||||
var matrixEnabled = false
|
||||
|
||||
func (app *appContext) GetPath(sect, key string) (fs.FS, string) {
|
||||
val := app.config.Section(sect).Key(key).MustString("")
|
||||
@@ -35,14 +39,19 @@ func (app *appContext) loadConfig() error {
|
||||
|
||||
app.MustSetValue("jellyfin", "public_server", app.config.Section("jellyfin").Key("server").String())
|
||||
|
||||
app.MustSetValue("ui", "redirect_url", app.config.Section("jellyfin").Key("public_server").String())
|
||||
|
||||
for _, key := range app.config.Section("files").Keys() {
|
||||
if name := key.Name(); name != "html_templates" && name != "lang_files" {
|
||||
key.SetValue(key.MustString(filepath.Join(app.dataPath, (key.Name() + ".json"))))
|
||||
}
|
||||
}
|
||||
for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template", "invites", "emails", "user_template", "custom_emails", "users"} {
|
||||
for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template", "invites", "emails", "user_template", "custom_emails", "users", "telegram_users", "discord_users", "matrix_users", "announcements"} {
|
||||
app.config.Section("files").Key(key).SetValue(app.config.Section("files").Key(key).MustString(filepath.Join(app.dataPath, (key + ".json"))))
|
||||
}
|
||||
for _, key := range []string{"matrix_sql"} {
|
||||
app.config.Section("files").Key(key).SetValue(app.config.Section("files").Key(key).MustString(filepath.Join(app.dataPath, (key + ".db"))))
|
||||
}
|
||||
app.URLBase = strings.TrimSuffix(app.config.Section("ui").Key("url_base").MustString(""), "/")
|
||||
app.config.Section("email").Key("no_username").SetValue(strconv.FormatBool(app.config.Section("email").Key("no_username").MustBool(false)))
|
||||
|
||||
@@ -64,6 +73,14 @@ func (app *appContext) loadConfig() error {
|
||||
app.MustSetValue("deletion", "email_html", "jfa-go:"+"deleted.html")
|
||||
app.MustSetValue("deletion", "email_text", "jfa-go:"+"deleted.txt")
|
||||
|
||||
app.MustSetValue("smtp", "hello_hostname", "localhost")
|
||||
app.MustSetValue("smtp", "cert_validation", "true")
|
||||
|
||||
jfUrl := app.config.Section("jellyfin").Key("server").String()
|
||||
if !(strings.HasPrefix(jfUrl, "http://") || strings.HasPrefix(jfUrl, "https://")) {
|
||||
app.config.Section("jellyfin").Key("server").SetValue("http://" + jfUrl)
|
||||
}
|
||||
|
||||
// Deletion template is good enough for these as well.
|
||||
app.MustSetValue("disable_enable", "disabled_html", "jfa-go:"+"deleted.html")
|
||||
app.MustSetValue("disable_enable", "disabled_text", "jfa-go:"+"deleted.txt")
|
||||
@@ -80,15 +97,40 @@ func (app *appContext) loadConfig() error {
|
||||
app.MustSetValue("user_expiry", "email_html", "jfa-go:"+"user-expired.html")
|
||||
app.MustSetValue("user_expiry", "email_text", "jfa-go:"+"user-expired.txt")
|
||||
|
||||
app.MustSetValue("matrix", "topic", "Jellyfin notifications")
|
||||
app.MustSetValue("matrix", "show_on_reg", "true")
|
||||
|
||||
app.MustSetValue("discord", "show_on_reg", "true")
|
||||
|
||||
app.MustSetValue("telegram", "show_on_reg", "true")
|
||||
|
||||
app.config.Section("jellyfin").Key("version").SetValue(version)
|
||||
app.config.Section("jellyfin").Key("device").SetValue("jfa-go")
|
||||
app.config.Section("jellyfin").Key("device_id").SetValue(fmt.Sprintf("jfa-go-%s-%s", version, commit))
|
||||
|
||||
if app.config.Section("email").Key("method").MustString("") == "" {
|
||||
// These two settings are pretty much the same
|
||||
url1 := app.config.Section("invite_emails").Key("url_base").String()
|
||||
url2 := app.config.Section("password_resets").Key("url_base").String()
|
||||
app.MustSetValue("password_resets", "url_base", strings.TrimSuffix(url1, "/invite"))
|
||||
app.MustSetValue("invite_emails", "url_base", url2)
|
||||
|
||||
messagesEnabled = app.config.Section("messages").Key("enabled").MustBool(false)
|
||||
telegramEnabled = app.config.Section("telegram").Key("enabled").MustBool(false)
|
||||
discordEnabled = app.config.Section("discord").Key("enabled").MustBool(false)
|
||||
matrixEnabled = app.config.Section("matrix").Key("enabled").MustBool(false)
|
||||
if !messagesEnabled {
|
||||
emailEnabled = false
|
||||
telegramEnabled = false
|
||||
discordEnabled = false
|
||||
matrixEnabled = false
|
||||
} else if app.config.Section("email").Key("method").MustString("") == "" {
|
||||
emailEnabled = false
|
||||
} else {
|
||||
emailEnabled = true
|
||||
}
|
||||
if !emailEnabled && !telegramEnabled && !discordEnabled && !matrixEnabled {
|
||||
messagesEnabled = false
|
||||
}
|
||||
|
||||
app.MustSetValue("updates", "enabled", "true")
|
||||
releaseChannel := app.config.Section("updates").Key("channel").String()
|
||||
@@ -117,6 +159,11 @@ func (app *appContext) loadConfig() error {
|
||||
|
||||
substituteStrings = app.config.Section("jellyfin").Key("substitute_jellyfin_strings").MustString("")
|
||||
|
||||
if substituteStrings != "" {
|
||||
v := app.config.Section("ui").Key("success_message")
|
||||
v.SetValue(strings.ReplaceAll(v.String(), "Jellyfin", substituteStrings))
|
||||
}
|
||||
|
||||
oldFormLang := app.config.Section("ui").Key("language").MustString("")
|
||||
if oldFormLang != "" {
|
||||
app.storage.lang.chosenFormLang = oldFormLang
|
||||
@@ -128,6 +175,7 @@ func (app *appContext) loadConfig() error {
|
||||
app.storage.lang.chosenAdminLang = app.config.Section("ui").Key("language-admin").MustString("en-us")
|
||||
app.storage.lang.chosenEmailLang = app.config.Section("email").Key("language").MustString("en-us")
|
||||
app.storage.lang.chosenPWRLang = app.config.Section("password_resets").Key("language").MustString("en-us")
|
||||
app.storage.lang.chosenTelegramLang = app.config.Section("telegram").Key("language").MustString("en-us")
|
||||
|
||||
app.email = NewEmailer(app)
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
["en-us", "English (US)"]
|
||||
],
|
||||
"value": "en-us",
|
||||
"description": "Default Account Form Language. Visit weblate.hrfee.dev if you'd like to translate."
|
||||
"description": "Default Account Form Language. Visit weblate.jfa-go.com if you'd like to translate."
|
||||
},
|
||||
"language-admin": {
|
||||
"name": "Default Admin Language",
|
||||
@@ -135,7 +135,7 @@
|
||||
["en-us", "English (US)"]
|
||||
],
|
||||
"value": "en-us",
|
||||
"description": "Default Admin page Language. Settings has not been translated. Visit weblate.hrfee.dev if you'd like to translate."
|
||||
"description": "Default Admin page Language. Settings has not been translated. Visit weblate.jfa-go.com if you'd like to translate."
|
||||
},
|
||||
"theme": {
|
||||
"name": "Default Look",
|
||||
@@ -246,7 +246,16 @@
|
||||
"requires_restart": true,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "URL base for when running jfa-go with a reverse proxy in a subfolder."
|
||||
"description": "URL base for when running jfa-go with a reverse proxy in a subfolder. include preceding /, e.g \"/accounts\"."
|
||||
},
|
||||
"redirect_url": {
|
||||
"name": "Form success redirect URL",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"advanced": true,
|
||||
"description": "Set a different URL for the sign-up form to redirect the user to when they've signed up. Default to 'Public Server' or 'Server' in the Jellyfin tab."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -345,33 +354,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"messages": {
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "Email",
|
||||
"description": "General email settings."
|
||||
"name": "Messages/Notifications",
|
||||
"description": "General settings for emails/messages."
|
||||
},
|
||||
"settings": {
|
||||
"language": {
|
||||
"name": "Email Language",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"depends_true": "method",
|
||||
"type": "select",
|
||||
"options": [
|
||||
["en-us", "English (US)"]
|
||||
],
|
||||
"value": "en-us",
|
||||
"description": "Default email language. Submit a PR on github if you'd like to translate."
|
||||
},
|
||||
"no_username": {
|
||||
"name": "Use email addresses as username",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"depends_true": "method",
|
||||
"enabled": {
|
||||
"name": "Enabled",
|
||||
"required": true,
|
||||
"requires_restart": true,
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Use email address from invite form as username on Jellyfin."
|
||||
"value": true,
|
||||
"description": "Enable the sending of emails/messages such as password resets, announcements, etc."
|
||||
},
|
||||
"use_24h": {
|
||||
"name": "Use 24h time",
|
||||
@@ -399,6 +395,37 @@
|
||||
"type": "text",
|
||||
"value": "Need help? contact me.",
|
||||
"description": "Message displayed at bottom of emails."
|
||||
}
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "Email",
|
||||
"description": "General email settings.",
|
||||
"depends_true": "messages|enabled"
|
||||
},
|
||||
"settings": {
|
||||
"language": {
|
||||
"name": "Email Language",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"depends_true": "method",
|
||||
"type": "select",
|
||||
"options": [
|
||||
["en-us", "English (US)"]
|
||||
],
|
||||
"value": "en-us",
|
||||
"description": "Default email language. Submit a PR on github if you'd like to translate."
|
||||
},
|
||||
"no_username": {
|
||||
"name": "Use email addresses as username",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"depends_true": "method",
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Use email address from invite form as username on Jellyfin."
|
||||
},
|
||||
"method": {
|
||||
"name": "Email method",
|
||||
@@ -440,6 +467,359 @@
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Send emails as plain text instead of HTML."
|
||||
},
|
||||
"required": {
|
||||
"name": "Require on sign-up",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"depends_true": "method",
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Require an email address on sign-up."
|
||||
}
|
||||
}
|
||||
},
|
||||
"mailgun": {
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "Mailgun (Email)",
|
||||
"description": "Mailgun API connection settings",
|
||||
"depends_true": "email|method"
|
||||
},
|
||||
"settings": {
|
||||
"api_url": {
|
||||
"name": "API URL",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "https://api.mailgun.net..."
|
||||
},
|
||||
"api_key": {
|
||||
"name": "API Key",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "your api key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"smtp": {
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "SMTP (Email)",
|
||||
"description": "SMTP Server connection settings.",
|
||||
"depends_true": "email|method"
|
||||
},
|
||||
"settings": {
|
||||
"username": {
|
||||
"name": "Username",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Username for SMTP. Leave blank to user send from address as username."
|
||||
},
|
||||
"encryption": {
|
||||
"name": "Encryption Method",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "select",
|
||||
"options": [
|
||||
["ssl_tls", "SSL/TLS"],
|
||||
["starttls", "STARTTLS"]
|
||||
],
|
||||
"value": "starttls",
|
||||
"description": "Your email provider should provide different ports for each encryption method. Generally 465 for ssl_tls, 587 for starttls."
|
||||
},
|
||||
"server": {
|
||||
"name": "Server address",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "smtp.jellyf.in",
|
||||
"description": "SMTP Server address."
|
||||
},
|
||||
"port": {
|
||||
"name": "Port",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "number",
|
||||
"value": 465
|
||||
},
|
||||
"password": {
|
||||
"name": "Password",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "password",
|
||||
"value": "smtp password"
|
||||
},
|
||||
"hello_hostname": {
|
||||
"name": "HELLO Hostname",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"advanced": true,
|
||||
"type": "text",
|
||||
"value": "localhost",
|
||||
"description": "Hostname sent when sending HELLO to the SMTP server. Some servers don't like the default \"localhost\" value, such as smtp-relay.gmail.com."
|
||||
},
|
||||
"ssl_cert": {
|
||||
"name": "Path to custom SSL certificate",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"advanced": true,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Use if your SMTP server's SSL Certificate is not trusted by the system."
|
||||
},
|
||||
"cert_validation": {
|
||||
"name": "Verify certificate",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"advanced": true,
|
||||
"type": "bool",
|
||||
"value": true,
|
||||
"description": "Warning, disabling this makes you much more vulnerable to man-in-the-middle attacks"
|
||||
}
|
||||
}
|
||||
},
|
||||
"discord": {
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "Discord",
|
||||
"description": "Settings for Discord invites/signup/notifications"
|
||||
},
|
||||
"settings": {
|
||||
"enabled": {
|
||||
"name": "Enabled",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Enable signup verification through Discord and the sending of notifications through it.\nSee the jfa-go wiki for setting up a bot."
|
||||
},
|
||||
"show_on_reg": {
|
||||
"name": "Show on user registration",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"type": "bool",
|
||||
"depends_true": "enabled",
|
||||
"value": true,
|
||||
"description": "Allow users to link their Discord on the registration page."
|
||||
},
|
||||
"required": {
|
||||
"name": "Require on sign-up",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Require Discord connection on sign-up. See the jfa-go wiki for info on setting this up."
|
||||
},
|
||||
"token": {
|
||||
"name": "API Token",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Discord Bot API Token."
|
||||
},
|
||||
"start_command": {
|
||||
"name": "Start command",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "!start",
|
||||
"description": "Command to start the user verification process."
|
||||
},
|
||||
"channel": {
|
||||
"name": "Channel to monitor",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Only listen to commands in specified channel. Leave blank to monitor all."
|
||||
},
|
||||
"provide_invite": {
|
||||
"name": "Provide server invite",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Generate a one-time discord server invite for the account creation form. Required Bot permission \"Create instant invite\", you may need to re-add the bot to your server after."
|
||||
},
|
||||
"invite_channel": {
|
||||
"name": "Invite channel",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "provide_invite",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Channel to invite new users to."
|
||||
},
|
||||
"language": {
|
||||
"name": "Language",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"depends_true": "enabled",
|
||||
"type": "select",
|
||||
"options": [
|
||||
["en-us", "English (US)"]
|
||||
],
|
||||
"value": "en-us",
|
||||
"description": "Default Discord message language. Visit weblate if you'd like to translate."
|
||||
}
|
||||
}
|
||||
},
|
||||
"telegram": {
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "Telegram",
|
||||
"description": "Settings for Telegram signup/notifications. See the jfa-go wiki for info on setting this up."
|
||||
},
|
||||
"settings": {
|
||||
"enabled": {
|
||||
"name": "Enabled",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Enable signup verification through Telegram and the sending of notifications through it.\nSee the jfa-go wiki for setting up a bot."
|
||||
},
|
||||
"show_on_reg": {
|
||||
"name": "Show on user registration",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"type": "bool",
|
||||
"depends_true": "enabled",
|
||||
"value": true,
|
||||
"description": "Allow users to link their Telegram on the registration page."
|
||||
},
|
||||
"required": {
|
||||
"name": "Require on sign-up",
|
||||
"required": false,
|
||||
"required_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Require telegram connection on sign-up."
|
||||
},
|
||||
"token": {
|
||||
"name": "API Token",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Telegram Bot API Token."
|
||||
},
|
||||
"language": {
|
||||
"name": "Language",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"depends_true": "enabled",
|
||||
"type": "select",
|
||||
"options": [
|
||||
["en-us", "English (US)"]
|
||||
],
|
||||
"value": "en-us",
|
||||
"description": "Default telegram message language. Visit weblate if you'd like to translate."
|
||||
}
|
||||
}
|
||||
},
|
||||
"matrix": {
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "Matrix",
|
||||
"description": "Settings for Matrix invites/signup/notifications. See the jfa-go wiki for info on setting this up."
|
||||
},
|
||||
"settings": {
|
||||
"enabled": {
|
||||
"name": "Enabled",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Enable signup verification through Matrix and the sending of notifications through it.\nSee the jfa-go wiki for setting up a bot."
|
||||
},
|
||||
"show_on_reg": {
|
||||
"name": "Show on user registration",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"type": "bool",
|
||||
"depends_true": "enabled",
|
||||
"value": true,
|
||||
"description": "Allow users to link their Matrix on the registration page."
|
||||
},
|
||||
"required": {
|
||||
"name": "Require on sign-up",
|
||||
"required": false,
|
||||
"required_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Require Matrix connection on sign-up."
|
||||
},
|
||||
"homeserver": {
|
||||
"name": "Home Server URL",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Matrix Home server URL."
|
||||
},
|
||||
"token": {
|
||||
"name": "Access Token",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Matrix Bot API Token."
|
||||
},
|
||||
"user_id": {
|
||||
"name": "Bot User ID",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "User ID of bot account (Example: @jfa-bot:riot.im)"
|
||||
},
|
||||
"topic": {
|
||||
"name": "Chat topic",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "Jellyfin notifications",
|
||||
"description": "Topic of Matrix private chats."
|
||||
},
|
||||
"language": {
|
||||
"name": "Language",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"depends_true": "enabled",
|
||||
"type": "select",
|
||||
"options": [
|
||||
["en-us", "English (US)"]
|
||||
],
|
||||
"value": "en-us",
|
||||
"description": "Default Matrix message language. Visit weblate if you'd like to translate."
|
||||
},
|
||||
"encryption": {
|
||||
"name": "End-to-end encryption (experimental)",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "enabled",
|
||||
"advanced": true,
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Enable end-to-end encryption for messages. Very experimental, currently does not support receiving commands (e.g !lang)."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -448,7 +828,7 @@
|
||||
"meta": {
|
||||
"name": "Password Resets",
|
||||
"description": "Settings for the password reset handler.",
|
||||
"depends_true": "email|method"
|
||||
"depends_true": "messages|enabled"
|
||||
},
|
||||
"settings": {
|
||||
"enabled": {
|
||||
@@ -477,6 +857,24 @@
|
||||
"value": false,
|
||||
"description": "Send users a link to reset their password instead of a PIN. Must be enabled to reset Ombi password at the same time as the Jellyfin password."
|
||||
},
|
||||
"set_password": {
|
||||
"name": "Set password through link",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"depends_true": "link_reset",
|
||||
"type": "bool",
|
||||
"value": false,
|
||||
"description": "Instead of automatically setting the user's password to the PIN, allow them to set a new password through the reset link."
|
||||
},
|
||||
"url_base": {
|
||||
"name": "URL Base",
|
||||
"required": true,
|
||||
"requires_restart": false,
|
||||
"depends_true": "link_reset",
|
||||
"type": "text",
|
||||
"value": "http://accounts.jellyf.in:8056",
|
||||
"description": "Base URL for jfa-go. You can leave this if you have one set in \"Invite Emails\". This is necessary because using a reverse proxy means the program has no way of knowing the URL itself."
|
||||
},
|
||||
"language": {
|
||||
"name": "Default reset link language",
|
||||
"required": false,
|
||||
@@ -570,17 +968,47 @@
|
||||
"requires_restart": false,
|
||||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "http://accounts.jellyf.in:8056/invite",
|
||||
"value": "http://accounts.jellyf.in:8056",
|
||||
"description": "Base URL for jfa-go. This is necessary because using a reverse proxy means the program has no way of knowing the URL itself."
|
||||
}
|
||||
}
|
||||
},
|
||||
"template_email": {
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "Custom email template",
|
||||
"description": "Settings for the template used for announcements & custom messages. HTML should include {{ .text }}, Plaintext should include {{ .plaintext }}, and either can have {{ .message }} to include the contact message.",
|
||||
"advanced": true
|
||||
},
|
||||
"settings": {
|
||||
"email_html": {
|
||||
"name": "Custom template email (HTML)",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"advanced": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Path to custom email HTML template for announcements/custom messages."
|
||||
},
|
||||
"email_text": {
|
||||
"name": "Custom template email (plaintext)",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"advanced": true,
|
||||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Path to custom email text template for announcements/custom messages."
|
||||
}
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "Notifications",
|
||||
"description": "Notification related settings.",
|
||||
"depends_true": "email|method"
|
||||
"depends_true": "messages|enabled"
|
||||
},
|
||||
"settings": {
|
||||
"enabled": {
|
||||
@@ -633,96 +1061,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"mailgun": {
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "Mailgun (Email)",
|
||||
"description": "Mailgun API connection settings",
|
||||
"depends_true": "email|method"
|
||||
},
|
||||
"settings": {
|
||||
"api_url": {
|
||||
"name": "API URL",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "https://api.mailgun.net..."
|
||||
},
|
||||
"api_key": {
|
||||
"name": "API Key",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "your api key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"smtp": {
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "SMTP (Email)",
|
||||
"description": "SMTP Server connection settings.",
|
||||
"depends_true": "email|method"
|
||||
},
|
||||
"settings": {
|
||||
"username": {
|
||||
"name": "Username",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Username for SMTP. Leave blank to user send from address as username."
|
||||
},
|
||||
"encryption": {
|
||||
"name": "Encryption Method",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "select",
|
||||
"options": [
|
||||
["ssl_tls", "SSL/TLS"],
|
||||
["starttls", "STARTTLS"]
|
||||
],
|
||||
"value": "starttls",
|
||||
"description": "Your email provider should provide different ports for each encryption method. Generally 465 for ssl_tls, 587 for starttls."
|
||||
},
|
||||
"server": {
|
||||
"name": "Server address",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "smtp.jellyf.in",
|
||||
"description": "SMTP Server address."
|
||||
},
|
||||
"port": {
|
||||
"name": "Port",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "number",
|
||||
"value": 465
|
||||
},
|
||||
"password": {
|
||||
"name": "Password",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "password",
|
||||
"value": "smtp password"
|
||||
},
|
||||
"ssl_cert": {
|
||||
"name": "Path to custom SSL certificate",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"advanced": true,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Use if your SMTP server's SSL Certificate is not trusted by the system."
|
||||
}
|
||||
}
|
||||
},
|
||||
"ombi": {
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "Ombi Integration",
|
||||
"description": "Connect to Ombi to automatically create both Ombi and Jellyfin accounts for new users. You'll need to create a user template for this to work. Once enabled, refresh to see an option in settings for this. To handle password resets for Ombi & Jellyfin, enable \"Use reset link instead of PIN\"."
|
||||
"description": "Connect to Ombi to automatically create both Ombi and Jellyfin accounts for new users. You'll need to add a ombi template to an existing User Profile for accounts to be created, which you can do by refreshing then checking Settings > User Profiles. To handle password resets for Ombi & Jellyfin, enable \"Use reset link instead of PIN\"."
|
||||
},
|
||||
"settings": {
|
||||
"enabled": {
|
||||
@@ -756,9 +1099,9 @@
|
||||
"welcome_email": {
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "Welcome Emails",
|
||||
"description": "Optionally send a welcome email to new users with the Jellyfin URL and their username.",
|
||||
"depends_true": "email|method"
|
||||
"name": "Welcome Message",
|
||||
"description": "Optionally send a welcome message to new users with the Jellyfin URL and their username.",
|
||||
"depends_true": "messages|enabled"
|
||||
},
|
||||
"settings": {
|
||||
"enabled": {
|
||||
@@ -865,14 +1208,14 @@
|
||||
"requires_restart": false,
|
||||
"type": "bool",
|
||||
"value": true,
|
||||
"depends_true": "email|method",
|
||||
"depends_true": "messages|enabled",
|
||||
"description": "Send an email when a user's account expires."
|
||||
},
|
||||
"subject": {
|
||||
"name": "Email subject",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"depends_true": "email|method",
|
||||
"depends_true": "messages|enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Subject of user expiry emails."
|
||||
@@ -882,7 +1225,7 @@
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"advanced": true,
|
||||
"depends_true": "email|method",
|
||||
"depends_true": "messages|enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Path to custom email html"
|
||||
@@ -892,7 +1235,7 @@
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"advanced": true,
|
||||
"depends_true": "email|method",
|
||||
"depends_true": "messages|enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Path to custom email in plain text"
|
||||
@@ -904,7 +1247,7 @@
|
||||
"meta": {
|
||||
"name": "Account Disabling/Enabling",
|
||||
"description": "Subject/email files for account disabling/enabling emails.",
|
||||
"depends_true": "email|method"
|
||||
"depends_true": "messages|enabled"
|
||||
},
|
||||
"settings": {
|
||||
"subject_disabled": {
|
||||
@@ -966,7 +1309,7 @@
|
||||
"meta": {
|
||||
"name": "Account Deletion",
|
||||
"description": "Subject/email files for account deletion emails.",
|
||||
"depends_true": "email|method"
|
||||
"depends_true": "messages|enabled"
|
||||
},
|
||||
"settings": {
|
||||
"subject": {
|
||||
@@ -1013,6 +1356,14 @@
|
||||
"value": "",
|
||||
"description": "Location of stored invites (json)."
|
||||
},
|
||||
"password_resets": {
|
||||
"name": "Password Resets",
|
||||
"required": false,
|
||||
"requires_restart": true,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Location of stored non-Jellyfin password resets (json)."
|
||||
},
|
||||
"emails": {
|
||||
"name": "Email Addresses",
|
||||
"required": false,
|
||||
@@ -1068,6 +1419,46 @@
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "JSON file generated by program in settings, different from email_html/email_text. See wiki for more info."
|
||||
},
|
||||
"telegram_users": {
|
||||
"name": "Telegram users",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Stores telegram user IDs and language preferences."
|
||||
},
|
||||
"matrix_users": {
|
||||
"name": "Matrix users",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Stores matrix user IDs and language preferences."
|
||||
},
|
||||
"matrix_sql": {
|
||||
"name": "Matrix encryption DB",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Stores cryptographic material for Matrix end-to-end encryption."
|
||||
},
|
||||
"discord_users": {
|
||||
"name": "Discord users",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Stores discord user IDs and language preferences."
|
||||
},
|
||||
"announcements": {
|
||||
"name": "Announcement templates",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Stores custom announcement templates."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
209
css/base.css
@@ -1,4 +1,7 @@
|
||||
@import "../node_modules/a17t/dist/a17t.css";
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@import "remixicon.css";
|
||||
@import "./modal.css";
|
||||
@import "./dark.css";
|
||||
@@ -12,14 +15,39 @@
|
||||
--border-width-8: 8px;
|
||||
}
|
||||
|
||||
.light-theme {
|
||||
.light {
|
||||
--settings-section-button-filter: 90%;
|
||||
}
|
||||
|
||||
.body {
|
||||
.dark {
|
||||
--settings-section-button-filter: 80%;
|
||||
}
|
||||
|
||||
.dark body {
|
||||
background-color: #101010;
|
||||
}
|
||||
|
||||
.dark select, .dark option, .dark input {
|
||||
background: #202020;
|
||||
}
|
||||
|
||||
html:not(.dark) .card.\@low:not(.\~neutral):not(.\~positive):not(.\~urge):not(.\~warning):not(.\~info):not(.\~critical) {
|
||||
--color-fill: va(--color-fill);
|
||||
--color-content: var(--color-content);
|
||||
--color-accent: var(--color-accent);
|
||||
--color-muted: var(--color-muted);
|
||||
background-color: #fff;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.light-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dark-only {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
margin: 5% 20% 5% 20%;
|
||||
}
|
||||
@@ -27,24 +55,32 @@
|
||||
@media (max-width: 1100px) {
|
||||
.page-container {
|
||||
margin: 2%;
|
||||
margin-top: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 750px) {
|
||||
@media screen and (max-width: 1000px) {
|
||||
:root {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.table-responsive table {
|
||||
min-width: 660px;
|
||||
min-width: 800px;
|
||||
}
|
||||
}
|
||||
|
||||
.chip.btn:hover:not([disabled]):not(.textarea),
|
||||
.chip.btn:focus:not([disabled]):not(.textarea) {
|
||||
filter: brightness(var(--button-filter-brightness,95%));
|
||||
}
|
||||
|
||||
.banner {
|
||||
margin: calc(-1 * var(--spacing-4,1rem));
|
||||
}
|
||||
|
||||
.banner.header {
|
||||
margin-bottom: var(--spacing-4,1rem);
|
||||
max-width: calc(100% + 2.2rem); /* no idea why this works */
|
||||
margin-left: -1.1rem;
|
||||
}
|
||||
|
||||
.banner.footer {
|
||||
@@ -65,54 +101,6 @@ div.card:contains(section.banner.footer) {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.mb-half {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.mb-1 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.mt-half {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.mt-1 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.ml-1 {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.ml-half {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.mr-half {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.mr-1 {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.p-1 {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.pb-1 {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.pl-1 {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.al {
|
||||
text-align: left;
|
||||
}
|
||||
@@ -121,6 +109,18 @@ div.card:contains(section.banner.footer) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.ac {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.w-100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.h-100 {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -162,6 +162,12 @@ div.card:contains(section.banner.footer) {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
p.sm,
|
||||
span.sm:not(.heading) {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
|
||||
.col.sm {
|
||||
margin: .25rem;
|
||||
}
|
||||
@@ -180,15 +186,6 @@ div.card:contains(section.banner.footer) {
|
||||
}
|
||||
}
|
||||
|
||||
.fr {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.monospace {
|
||||
background-color: inherit; /* so we can use a17t code blocks */
|
||||
font-family: Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;
|
||||
}
|
||||
|
||||
sup.\~critical, .text-critical {
|
||||
color: var(--color-critical-normal-content);
|
||||
}
|
||||
@@ -354,10 +351,8 @@ sup.\~critical, .text-critical {
|
||||
}
|
||||
|
||||
.settings-section-button {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 2.5rem;
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
.settings-section-button:hover, .settings-section-button:focus {
|
||||
@@ -369,8 +364,6 @@ sup.\~critical, .text-critical {
|
||||
}
|
||||
|
||||
.settings-section-button.selected {
|
||||
background-color: var(--color-neutral-normal-fill);
|
||||
--buton-filter-brightness: var(--settings-section-button-filter);
|
||||
filter: brightness(var(--settings-section-button-filter)) !important;
|
||||
}
|
||||
|
||||
@@ -398,17 +391,28 @@ select, textarea {
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
html.dark textarea {
|
||||
background-color: #202020
|
||||
}
|
||||
|
||||
input {
|
||||
color: inherit;
|
||||
border: 0 solid var(--color-neutral-300);
|
||||
}
|
||||
|
||||
table {
|
||||
color: var(--color-content);
|
||||
}
|
||||
|
||||
|
||||
|
||||
p.top {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
overflow-x: auto;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
#notification-box {
|
||||
@@ -423,12 +427,23 @@ p.top {
|
||||
margin-bottom: -0.5rem;
|
||||
}
|
||||
|
||||
.dropdown-display.lg {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dropdown-display.above {
|
||||
top: auto;
|
||||
bottom: 115%;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap; /* css-3 */
|
||||
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 */
|
||||
white-space: -o-pre-wrap; /* Opera 7 */
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
background-color: var(--color-content-high) !important;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
.circle {
|
||||
@@ -459,7 +474,69 @@ a:hover:not(.lang-link):not(.\~urge), a:active:not(.lang-link):not(.\~urge) {
|
||||
color: var(--color-urge-200);
|
||||
}
|
||||
|
||||
.link-center {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.search {
|
||||
max-width: 15rem;
|
||||
min-width: 10rem;
|
||||
}
|
||||
|
||||
td.img-circle {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
span.img-circle.lg {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
span.shield.img-circle {
|
||||
padding: 0.2rem;
|
||||
}
|
||||
|
||||
img.img-circle {
|
||||
border-radius: 50%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table td.sm {
|
||||
padding-top: 0.1rem;
|
||||
padding-bottom: 0.1rem;
|
||||
}
|
||||
|
||||
.table-inline {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
div.card:contains(section.banner.footer) {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.card.sectioned {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.card.sectioned .section {
|
||||
padding: var(--spacing-4, 1rem);
|
||||
}
|
||||
|
||||
.button.discord.\@low {
|
||||
background-color: rgba(88, 101, 242,60%);
|
||||
}
|
||||
|
||||
.button.discord.\@low:not(.lang-link) {
|
||||
color: rgba(38, 51, 192, 90%);
|
||||
}
|
||||
|
||||
.pb-0i {
|
||||
padding-bottom: 0px !important
|
||||
}
|
||||
|
||||
.text-center-i {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
91
css/dark.css
@@ -1,91 +0,0 @@
|
||||
.dark-theme {
|
||||
|
||||
--settings-section-button-filter: 110%;
|
||||
|
||||
--color-neutral-900: rgba(255, 255, 255, 0.87);
|
||||
--color-neutral-800: rgba(255, 255, 255, 0.8);
|
||||
--color-neutral-700: rgba(255, 255, 255, 0.73);
|
||||
--color-neutral-600: rgba(255, 255, 255, 0.66);
|
||||
--color-neutral-500: rgb(153, 153, 153);
|
||||
--color-neutral-400: #383838;
|
||||
--color-neutral-300: #303030;
|
||||
--color-neutral-200: #292929;
|
||||
--color-neutral-100: #242424;
|
||||
--color-neutral-50: #202020;
|
||||
--color-neutral-000: #101010;
|
||||
|
||||
--color-critical-900: #fef2f2;
|
||||
--color-critical-800: #fee2e2;
|
||||
--color-critical-700: #fecaca;
|
||||
--color-critical-600: #fca5a5;
|
||||
--color-critical-500: #f87171;
|
||||
--color-critical-400: #ef4444;
|
||||
--color-critical-300: #dc2626;
|
||||
--color-critical-200: #b91c1c;
|
||||
--color-critical-100: #991b1b;
|
||||
--color-critical-50: #7f1d1d;
|
||||
--color-critical-000: #441313;
|
||||
|
||||
--color-warning-900: #fffbeb;
|
||||
--color-warning-800: #fef3c7;
|
||||
--color-warning-700: #fde68a;
|
||||
--color-warning-600: #fcd34d;
|
||||
--color-warning-500: #fbbf24;
|
||||
--color-warning-400: #f59e0b;
|
||||
--color-warning-300: #d97706;
|
||||
--color-warning-200: #b45309;
|
||||
--color-warning-100: #92400e;
|
||||
--color-warning-50: #783900;
|
||||
--color-warning-000: #411e01;
|
||||
|
||||
--color-positive-900: #f0fdf4;
|
||||
--color-positive-800: #dcfce7;
|
||||
--color-positive-700: #bbf7d0;
|
||||
--color-positive-600: #86efac;
|
||||
--color-positive-500: #4ade80;
|
||||
--color-positive-400: #22c55e;
|
||||
--color-positive-300: #16a34a;
|
||||
--color-positive-200: #15803d;
|
||||
--color-positive-100: #166534;
|
||||
--color-positive-50: #14532d;
|
||||
--color-positive-000: #0f2e1b;
|
||||
|
||||
--color-urge-900: #e0ffff;
|
||||
--color-urge-800: #c0fbff;
|
||||
--color-urge-700: #a0f4ff;
|
||||
--color-urge-600: #80e9ff;
|
||||
--color-urge-500: #60dbfb;
|
||||
--color-urge-400: #40cbf3;
|
||||
--color-urge-300: #20b9e9;
|
||||
--color-urge-200: #00a4dc; /* tab buttons */
|
||||
--color-urge-100: #0054bc;
|
||||
--color-urge-50: #00169a;
|
||||
--color-urge-000: #050076;
|
||||
|
||||
--color-info-900: #f5f3ff;
|
||||
--color-info-800: #ede9fe;
|
||||
--color-info-700: #ddd6fe;
|
||||
--color-info-600: #c4b5fd;
|
||||
--color-info-500: #a78bfa;
|
||||
--color-info-400: #8b5cf6;
|
||||
--color-info-300: #7c3aed;
|
||||
--color-info-200: #6d28d9;
|
||||
--color-info-100: #5b21b6;
|
||||
--color-info-50: #4c1d95;
|
||||
--color-info-000: #240e44;
|
||||
|
||||
|
||||
--color-neutral-normal-content: #ffffff;
|
||||
}
|
||||
|
||||
.light-only {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dark-only {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
.dark-theme select option {
|
||||
background: #202020;
|
||||
}
|
||||
|
||||
351
css/dark.js
Normal file
@@ -0,0 +1,351 @@
|
||||
var c = {
|
||||
inherit: 'inherit',
|
||||
current: 'currentColor',
|
||||
transparent: 'transparent',
|
||||
black: '#000',
|
||||
white: '#fff',
|
||||
d_neutral: {
|
||||
900: "rgba(255, 255, 255, 0.87)",
|
||||
800: "rgba(255, 255, 255, 0.8)",
|
||||
700: "rgba(255, 255, 255, 0.73)",
|
||||
600: "rgba(255, 255, 255, 0.66)",
|
||||
500: "rgb(153, 153, 153)",
|
||||
400: "#383838",
|
||||
300: "#303030",
|
||||
200: "#292929",
|
||||
100: "#242424",
|
||||
50: "#202020",
|
||||
000: "#101010"
|
||||
},
|
||||
d_critical: {
|
||||
900: "#fef2f2",
|
||||
800: "#fee2e2",
|
||||
700: "#fecaca",
|
||||
600: "#fca5a5",
|
||||
500: "#f87171",
|
||||
400: "#ef4444",
|
||||
300: "#dc2626",
|
||||
200: "#b91c1c",
|
||||
100: "#991b1b",
|
||||
50: "#7f1d1d",
|
||||
000: "#441313"
|
||||
},
|
||||
d_warning: {
|
||||
900: "#fffbeb",
|
||||
800: "#fef3c7",
|
||||
700: "#fde68a",
|
||||
600: "#fcd34d",
|
||||
500: "#fbbf24",
|
||||
400: "#f59e0b",
|
||||
300: "#d97706",
|
||||
200: "#b45309",
|
||||
100: "#92400e",
|
||||
50: "#783900",
|
||||
000: "#411e01"
|
||||
},
|
||||
d_positive: {
|
||||
900: "#f0fdf4",
|
||||
800: "#dcfce7",
|
||||
700: "#bbf7d0",
|
||||
600: "#86efac",
|
||||
500: "#4ade80",
|
||||
400: "#22c55e",
|
||||
300: "#16a34a",
|
||||
200: "#15803d",
|
||||
100: "#166534",
|
||||
50: "#14532d",
|
||||
000: "#0f2e1b"
|
||||
},
|
||||
d_urge: {
|
||||
900: "#e0ffff",
|
||||
800: "#c0fbff",
|
||||
700: "#a0f4ff",
|
||||
600: "#80e9ff",
|
||||
500: "#60dbfb",
|
||||
400: "#40cbf3",
|
||||
300: "#20b9e9",
|
||||
200: "#00a4dc",
|
||||
100: "#0054bc",
|
||||
50: "#00169a",
|
||||
000: "#050076"
|
||||
},
|
||||
d_info: {
|
||||
900: "#f5f3ff",
|
||||
800: "#ede9fe",
|
||||
700: "#ddd6fe",
|
||||
600: "#c4b5fd",
|
||||
500: "#a78bfa",
|
||||
400: "#8b5cf6",
|
||||
300: "#7c3aed",
|
||||
200: "#6d28d9",
|
||||
100: "#5b21b6",
|
||||
50: "#4c1d95",
|
||||
000: "#240e44"
|
||||
},
|
||||
slate: {
|
||||
50: '#f8fafc',
|
||||
100: '#f1f5f9',
|
||||
200: '#e2e8f0',
|
||||
300: '#cbd5e1',
|
||||
400: '#94a3b8',
|
||||
500: '#64748b',
|
||||
600: '#475569',
|
||||
700: '#334155',
|
||||
800: '#1e293b',
|
||||
900: '#0f172a'
|
||||
},
|
||||
gray: {
|
||||
50: '#f9fafb',
|
||||
100: '#f3f4f6',
|
||||
200: '#e5e7eb',
|
||||
300: '#d1d5db',
|
||||
400: '#9ca3af',
|
||||
500: '#6b7280',
|
||||
600: '#4b5563',
|
||||
700: '#374151',
|
||||
800: '#1f2937',
|
||||
900: '#111827'
|
||||
},
|
||||
zinc: {
|
||||
50: '#fafafa',
|
||||
100: '#f4f4f5',
|
||||
200: '#e4e4e7',
|
||||
300: '#d4d4d8',
|
||||
400: '#a1a1aa',
|
||||
500: '#71717a',
|
||||
600: '#52525b',
|
||||
700: '#3f3f46',
|
||||
800: '#27272a',
|
||||
900: '#18181b'
|
||||
},
|
||||
neutral: {
|
||||
50: '#fafafa',
|
||||
100: '#f5f5f5',
|
||||
200: '#e5e5e5',
|
||||
300: '#d4d4d4',
|
||||
400: '#a3a3a3',
|
||||
500: '#737373',
|
||||
600: '#525252',
|
||||
700: '#404040',
|
||||
800: '#262626',
|
||||
900: '#171717'
|
||||
},
|
||||
stone: {
|
||||
50: '#fafaf9',
|
||||
100: '#f5f5f4',
|
||||
200: '#e7e5e4',
|
||||
300: '#d6d3d1',
|
||||
400: '#a8a29e',
|
||||
500: '#78716c',
|
||||
600: '#57534e',
|
||||
700: '#44403c',
|
||||
800: '#292524',
|
||||
900: '#1c1917'
|
||||
},
|
||||
red: {
|
||||
50: '#fef2f2',
|
||||
100: '#fee2e2',
|
||||
200: '#fecaca',
|
||||
300: '#fca5a5',
|
||||
400: '#f87171',
|
||||
500: '#ef4444',
|
||||
600: '#dc2626',
|
||||
700: '#b91c1c',
|
||||
800: '#991b1b',
|
||||
900: '#7f1d1d'
|
||||
},
|
||||
orange: {
|
||||
50: '#fff7ed',
|
||||
100: '#ffedd5',
|
||||
200: '#fed7aa',
|
||||
300: '#fdba74',
|
||||
400: '#fb923c',
|
||||
500: '#f97316',
|
||||
600: '#ea580c',
|
||||
700: '#c2410c',
|
||||
800: '#9a3412',
|
||||
900: '#7c2d12'
|
||||
},
|
||||
amber: {
|
||||
50: '#fffbeb',
|
||||
100: '#fef3c7',
|
||||
200: '#fde68a',
|
||||
300: '#fcd34d',
|
||||
400: '#fbbf24',
|
||||
500: '#f59e0b',
|
||||
600: '#d97706',
|
||||
700: '#b45309',
|
||||
800: '#92400e',
|
||||
900: '#78350f'
|
||||
},
|
||||
yellow: {
|
||||
50: '#fefce8',
|
||||
100: '#fef9c3',
|
||||
200: '#fef08a',
|
||||
300: '#fde047',
|
||||
400: '#facc15',
|
||||
500: '#eab308',
|
||||
600: '#ca8a04',
|
||||
700: '#a16207',
|
||||
800: '#854d0e',
|
||||
900: '#713f12'
|
||||
},
|
||||
lime: {
|
||||
50: '#f7fee7',
|
||||
100: '#ecfccb',
|
||||
200: '#d9f99d',
|
||||
300: '#bef264',
|
||||
400: '#a3e635',
|
||||
500: '#84cc16',
|
||||
600: '#65a30d',
|
||||
700: '#4d7c0f',
|
||||
800: '#3f6212',
|
||||
900: '#365314'
|
||||
},
|
||||
green: {
|
||||
50: '#f0fdf4',
|
||||
100: '#dcfce7',
|
||||
200: '#bbf7d0',
|
||||
300: '#86efac',
|
||||
400: '#4ade80',
|
||||
500: '#22c55e',
|
||||
600: '#16a34a',
|
||||
700: '#15803d',
|
||||
800: '#166534',
|
||||
900: '#14532d'
|
||||
},
|
||||
emerald: {
|
||||
50: '#ecfdf5',
|
||||
100: '#d1fae5',
|
||||
200: '#a7f3d0',
|
||||
300: '#6ee7b7',
|
||||
400: '#34d399',
|
||||
500: '#10b981',
|
||||
600: '#059669',
|
||||
700: '#047857',
|
||||
800: '#065f46',
|
||||
900: '#064e3b'
|
||||
},
|
||||
teal: {
|
||||
50: '#f0fdfa',
|
||||
100: '#ccfbf1',
|
||||
200: '#99f6e4',
|
||||
300: '#5eead4',
|
||||
400: '#2dd4bf',
|
||||
500: '#14b8a6',
|
||||
600: '#0d9488',
|
||||
700: '#0f766e',
|
||||
800: '#115e59',
|
||||
900: '#134e4a'
|
||||
},
|
||||
cyan: {
|
||||
50: '#ecfeff',
|
||||
100: '#cffafe',
|
||||
200: '#a5f3fc',
|
||||
300: '#67e8f9',
|
||||
400: '#22d3ee',
|
||||
500: '#06b6d4',
|
||||
600: '#0891b2',
|
||||
700: '#0e7490',
|
||||
800: '#155e75',
|
||||
900: '#164e63'
|
||||
},
|
||||
sky: {
|
||||
50: '#f0f9ff',
|
||||
100: '#e0f2fe',
|
||||
200: '#bae6fd',
|
||||
300: '#7dd3fc',
|
||||
400: '#38bdf8',
|
||||
500: '#0ea5e9',
|
||||
600: '#0284c7',
|
||||
700: '#0369a1',
|
||||
800: '#075985',
|
||||
900: '#0c4a6e'
|
||||
},
|
||||
blue: {
|
||||
50: '#eff6ff',
|
||||
100: '#dbeafe',
|
||||
200: '#bfdbfe',
|
||||
300: '#93c5fd',
|
||||
400: '#60a5fa',
|
||||
500: '#3b82f6',
|
||||
600: '#2563eb',
|
||||
700: '#1d4ed8',
|
||||
800: '#1e40af',
|
||||
900: '#1e3a8a'
|
||||
},
|
||||
indigo: {
|
||||
50: '#eef2ff',
|
||||
100: '#e0e7ff',
|
||||
200: '#c7d2fe',
|
||||
300: '#a5b4fc',
|
||||
400: '#818cf8',
|
||||
500: '#6366f1',
|
||||
600: '#4f46e5',
|
||||
700: '#4338ca',
|
||||
800: '#3730a3',
|
||||
900: '#312e81'
|
||||
},
|
||||
violet: {
|
||||
50: '#f5f3ff',
|
||||
100: '#ede9fe',
|
||||
200: '#ddd6fe',
|
||||
300: '#c4b5fd',
|
||||
400: '#a78bfa',
|
||||
500: '#8b5cf6',
|
||||
600: '#7c3aed',
|
||||
700: '#6d28d9',
|
||||
800: '#5b21b6',
|
||||
900: '#4c1d95'
|
||||
},
|
||||
purple: {
|
||||
50: '#faf5ff',
|
||||
100: '#f3e8ff',
|
||||
200: '#e9d5ff',
|
||||
300: '#d8b4fe',
|
||||
400: '#c084fc',
|
||||
500: '#a855f7',
|
||||
600: '#9333ea',
|
||||
700: '#7e22ce',
|
||||
800: '#6b21a8',
|
||||
900: '#581c87'
|
||||
},
|
||||
fuchsia: {
|
||||
50: '#fdf4ff',
|
||||
100: '#fae8ff',
|
||||
200: '#f5d0fe',
|
||||
300: '#f0abfc',
|
||||
400: '#e879f9',
|
||||
500: '#d946ef',
|
||||
600: '#c026d3',
|
||||
700: '#a21caf',
|
||||
800: '#86198f',
|
||||
900: '#701a75'
|
||||
},
|
||||
pink: {
|
||||
50: '#fdf2f8',
|
||||
100: '#fce7f3',
|
||||
200: '#fbcfe8',
|
||||
300: '#f9a8d4',
|
||||
400: '#f472b6',
|
||||
500: '#ec4899',
|
||||
600: '#db2777',
|
||||
700: '#be185d',
|
||||
800: '#9d174d',
|
||||
900: '#831843'
|
||||
},
|
||||
rose: {
|
||||
50: '#fff1f2',
|
||||
100: '#ffe4e6',
|
||||
200: '#fecdd3',
|
||||
300: '#fda4af',
|
||||
400: '#fb7185',
|
||||
500: '#f43f5e',
|
||||
600: '#e11d48',
|
||||
700: '#be123c',
|
||||
800: '#9f1239',
|
||||
900: '#881337'
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = c;
|
||||
@@ -1,6 +1,6 @@
|
||||
.loader {
|
||||
height: auto;
|
||||
color: rgba(0, 0, 0, 0);
|
||||
color: rgba(0, 0, 0, 0) !important;
|
||||
}
|
||||
|
||||
.loader .dot {
|
||||
|
||||
410
discord.go
Normal file
@@ -0,0 +1,410 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
dg "github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
type DiscordDaemon struct {
|
||||
Stopped bool
|
||||
ShutdownChannel chan string
|
||||
bot *dg.Session
|
||||
username string
|
||||
tokens []string
|
||||
verifiedTokens map[string]DiscordUser // Map of tokens to discord users.
|
||||
channelID, channelName, inviteChannelID, inviteChannelName string
|
||||
guildID string
|
||||
serverChannelName, serverName string
|
||||
users map[string]DiscordUser // Map of user IDs to users. Added to on first interaction, and loaded from app.storage.discord on start.
|
||||
app *appContext
|
||||
}
|
||||
|
||||
func newDiscordDaemon(app *appContext) (*DiscordDaemon, error) {
|
||||
token := app.config.Section("discord").Key("token").String()
|
||||
if token == "" {
|
||||
return nil, fmt.Errorf("token was blank")
|
||||
}
|
||||
bot, err := dg.New("Bot " + token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dd := &DiscordDaemon{
|
||||
Stopped: false,
|
||||
ShutdownChannel: make(chan string),
|
||||
bot: bot,
|
||||
tokens: []string{},
|
||||
verifiedTokens: map[string]DiscordUser{},
|
||||
users: map[string]DiscordUser{},
|
||||
app: app,
|
||||
}
|
||||
for _, user := range app.storage.discord {
|
||||
dd.users[user.ID] = user
|
||||
}
|
||||
|
||||
return dd, nil
|
||||
}
|
||||
|
||||
// NewAuthToken generates an 8-character pin in the form "A1-2B-CD".
|
||||
func (d *DiscordDaemon) NewAuthToken() string {
|
||||
pin := genAuthToken()
|
||||
d.tokens = append(d.tokens, pin)
|
||||
return pin
|
||||
}
|
||||
|
||||
func (d *DiscordDaemon) NewUnknownUser(channelID, userID, discrim, username string) DiscordUser {
|
||||
user := DiscordUser{
|
||||
ChannelID: channelID,
|
||||
ID: userID,
|
||||
Username: username,
|
||||
Discriminator: discrim,
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
func (d *DiscordDaemon) MustGetUser(channelID, userID, discrim, username string) DiscordUser {
|
||||
if user, ok := d.users[userID]; ok {
|
||||
return user
|
||||
}
|
||||
return d.NewUnknownUser(channelID, userID, discrim, username)
|
||||
}
|
||||
|
||||
func (d *DiscordDaemon) run() {
|
||||
d.bot.AddHandler(d.messageHandler)
|
||||
d.bot.Identify.Intents = dg.IntentsGuildMessages | dg.IntentsDirectMessages | dg.IntentsGuildMembers | dg.IntentsGuildInvites
|
||||
if err := d.bot.Open(); err != nil {
|
||||
d.app.err.Printf("Discord: Failed to start daemon: %v", err)
|
||||
return
|
||||
}
|
||||
// Wait for everything to populate, it's slow sometimes.
|
||||
for d.bot.State == nil {
|
||||
continue
|
||||
}
|
||||
for d.bot.State.User == nil {
|
||||
continue
|
||||
}
|
||||
d.username = d.bot.State.User.Username
|
||||
for d.bot.State.Guilds == nil {
|
||||
continue
|
||||
}
|
||||
// Choose the last guild (server), for now we don't really support multiple anyway
|
||||
d.guildID = d.bot.State.Guilds[len(d.bot.State.Guilds)-1].ID
|
||||
guild, err := d.bot.Guild(d.guildID)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to get guild: %v", err)
|
||||
}
|
||||
d.serverChannelName = guild.Name
|
||||
d.serverName = guild.Name
|
||||
if channel := d.app.config.Section("discord").Key("channel").String(); channel != "" {
|
||||
d.channelName = channel
|
||||
d.serverChannelName += "/" + channel
|
||||
}
|
||||
if d.app.config.Section("discord").Key("provide_invite").MustBool(false) {
|
||||
if invChannel := d.app.config.Section("discord").Key("invite_channel").String(); invChannel != "" {
|
||||
d.inviteChannelName = invChannel
|
||||
}
|
||||
}
|
||||
defer d.bot.Close()
|
||||
<-d.ShutdownChannel
|
||||
d.ShutdownChannel <- "Down"
|
||||
return
|
||||
}
|
||||
|
||||
// NewTempInvite creates an invite link, and returns the invite URL, as well as the URL for the server icon.
|
||||
func (d *DiscordDaemon) NewTempInvite(ageSeconds, maxUses int) (inviteURL, iconURL string) {
|
||||
var inv *dg.Invite
|
||||
var err error
|
||||
if d.inviteChannelName == "" {
|
||||
d.app.err.Println("Discord: Cannot create invite without channel specified in settings.")
|
||||
return
|
||||
}
|
||||
if d.inviteChannelID == "" {
|
||||
channels, err := d.bot.GuildChannels(d.guildID)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Couldn't get channel list: %v", err)
|
||||
return
|
||||
}
|
||||
found := false
|
||||
for _, channel := range channels {
|
||||
// channel, err := d.bot.Channel(ch.ID)
|
||||
// if err != nil {
|
||||
// d.app.err.Printf("Discord: Couldn't get channel: %v", err)
|
||||
// return
|
||||
// }
|
||||
if channel.Name == d.inviteChannelName {
|
||||
d.inviteChannelID = channel.ID
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
d.app.err.Printf("Discord: Couldn't find invite channel \"%s\"", d.inviteChannelName)
|
||||
return
|
||||
}
|
||||
}
|
||||
// channel, err := d.bot.Channel(d.inviteChannelID)
|
||||
// if err != nil {
|
||||
// d.app.err.Printf("Discord: Couldn't get invite channel: %v", err)
|
||||
// return
|
||||
// }
|
||||
inv, err = d.bot.ChannelInviteCreate(d.inviteChannelID, dg.Invite{
|
||||
// Guild: d.bot.State.Guilds[len(d.bot.State.Guilds)-1],
|
||||
// Channel: channel,
|
||||
// Inviter: d.bot.State.User,
|
||||
MaxAge: ageSeconds,
|
||||
MaxUses: maxUses,
|
||||
Temporary: false,
|
||||
})
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to create invite: %v", err)
|
||||
return
|
||||
}
|
||||
inviteURL = "https://discord.gg/" + inv.Code
|
||||
guild, err := d.bot.Guild(d.guildID)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to get guild: %v", err)
|
||||
return
|
||||
}
|
||||
iconURL = guild.IconURL()
|
||||
return
|
||||
}
|
||||
|
||||
// Returns the user(s) roughly corresponding to the username (if they are in the guild).
|
||||
// if no discriminator (#xxxx) is given in the username and there are multiple corresponding users, a list of all matching users is returned.
|
||||
func (d *DiscordDaemon) GetUsers(username string) []*dg.Member {
|
||||
members, err := d.bot.GuildMembers(
|
||||
d.guildID,
|
||||
"",
|
||||
1000,
|
||||
)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to get members: %v", err)
|
||||
return nil
|
||||
}
|
||||
hasDiscriminator := strings.Contains(username, "#")
|
||||
var users []*dg.Member
|
||||
for _, member := range members {
|
||||
if hasDiscriminator {
|
||||
if member.User.Username+"#"+member.User.Discriminator == username {
|
||||
return []*dg.Member{member}
|
||||
}
|
||||
}
|
||||
if strings.Contains(member.User.Username, username) {
|
||||
users = append(users, member)
|
||||
}
|
||||
}
|
||||
return users
|
||||
}
|
||||
|
||||
func (d *DiscordDaemon) NewUser(ID string) (user DiscordUser, ok bool) {
|
||||
u, err := d.bot.User(ID)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to get user: %v", err)
|
||||
return
|
||||
}
|
||||
user.ID = ID
|
||||
user.Username = u.Username
|
||||
user.Contact = true
|
||||
user.Discriminator = u.Discriminator
|
||||
channel, err := d.bot.UserChannelCreate(ID)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to create DM channel: %v", err)
|
||||
return
|
||||
}
|
||||
user.ChannelID = channel.ID
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
func (d *DiscordDaemon) Shutdown() {
|
||||
d.Stopped = true
|
||||
d.ShutdownChannel <- "Down"
|
||||
<-d.ShutdownChannel
|
||||
close(d.ShutdownChannel)
|
||||
}
|
||||
|
||||
func (d *DiscordDaemon) messageHandler(s *dg.Session, m *dg.MessageCreate) {
|
||||
if m.GuildID != "" && d.channelName != "" {
|
||||
if d.channelID == "" {
|
||||
channel, err := s.Channel(m.ChannelID)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Couldn't get channel, will monitor all: %v", err)
|
||||
d.channelName = ""
|
||||
}
|
||||
if channel.Name == d.channelName {
|
||||
d.channelID = channel.ID
|
||||
}
|
||||
}
|
||||
if d.channelID != m.ChannelID {
|
||||
d.app.debug.Printf("Discord: Ignoring message as not in specified channel")
|
||||
return
|
||||
}
|
||||
}
|
||||
if m.Author.ID == s.State.User.ID {
|
||||
return
|
||||
}
|
||||
sects := strings.Split(m.Content, " ")
|
||||
if len(sects) == 0 {
|
||||
return
|
||||
}
|
||||
lang := d.app.storage.lang.chosenTelegramLang
|
||||
if user, ok := d.users[m.Author.ID]; ok {
|
||||
if _, ok := d.app.storage.lang.Telegram[user.Lang]; ok {
|
||||
lang = user.Lang
|
||||
}
|
||||
}
|
||||
switch msg := sects[0]; msg {
|
||||
case d.app.config.Section("discord").Key("start_command").MustString("!start"):
|
||||
d.commandStart(s, m, lang)
|
||||
case "!lang":
|
||||
d.commandLang(s, m, sects, lang)
|
||||
default:
|
||||
d.commandPIN(s, m, sects, lang)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DiscordDaemon) commandStart(s *dg.Session, m *dg.MessageCreate, lang string) {
|
||||
channel, err := s.UserChannelCreate(m.Author.ID)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to create private channel with \"%s\": %v", m.Author.Username, err)
|
||||
return
|
||||
}
|
||||
user := d.MustGetUser(channel.ID, m.Author.ID, m.Author.Discriminator, m.Author.Username)
|
||||
d.users[m.Author.ID] = user
|
||||
content := d.app.storage.lang.Telegram[lang].Strings.get("startMessage") + "\n"
|
||||
content += d.app.storage.lang.Telegram[lang].Strings.template("languageMessage", tmpl{"command": "!lang"})
|
||||
_, err = s.ChannelMessageSend(channel.ID, content)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to send message to \"%s\": %v", m.Author.Username, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DiscordDaemon) commandLang(s *dg.Session, m *dg.MessageCreate, sects []string, lang string) {
|
||||
if len(sects) == 1 {
|
||||
list := "!lang <lang>\n"
|
||||
for code := range d.app.storage.lang.Telegram {
|
||||
list += fmt.Sprintf("%s: %s\n", code, d.app.storage.lang.Telegram[code].Meta.Name)
|
||||
}
|
||||
_, err := s.ChannelMessageSendReply(
|
||||
m.ChannelID,
|
||||
list,
|
||||
m.Reference(),
|
||||
)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to send message to \"%s\": %v", m.Author.Username, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if _, ok := d.app.storage.lang.Telegram[sects[1]]; ok {
|
||||
var user DiscordUser
|
||||
for jfID, user := range d.app.storage.discord {
|
||||
if user.ID == m.Author.ID {
|
||||
user.Lang = sects[1]
|
||||
d.app.storage.discord[jfID] = user
|
||||
if err := d.app.storage.storeDiscordUsers(); err != nil {
|
||||
d.app.err.Printf("Failed to store Discord users: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
d.users[m.Author.ID] = user
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DiscordDaemon) commandPIN(s *dg.Session, m *dg.MessageCreate, sects []string, lang string) {
|
||||
if _, ok := d.users[m.Author.ID]; ok {
|
||||
channel, err := s.Channel(m.ChannelID)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to get channel: %v", err)
|
||||
return
|
||||
}
|
||||
if channel.Type != dg.ChannelTypeDM {
|
||||
d.app.debug.Println("Discord: Ignoring message as not a DM")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
d.app.debug.Println("Discord: Ignoring message as user was not found")
|
||||
return
|
||||
}
|
||||
tokenIndex := -1
|
||||
for i, token := range d.tokens {
|
||||
if sects[0] == token {
|
||||
tokenIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if tokenIndex == -1 {
|
||||
_, err := s.ChannelMessageSend(
|
||||
m.ChannelID,
|
||||
d.app.storage.lang.Telegram[lang].Strings.get("invalidPIN"),
|
||||
)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to send message to \"%s\": %v", m.Author.Username, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
_, err := s.ChannelMessageSend(
|
||||
m.ChannelID,
|
||||
d.app.storage.lang.Telegram[lang].Strings.get("pinSuccess"),
|
||||
)
|
||||
if err != nil {
|
||||
d.app.err.Printf("Discord: Failed to send message to \"%s\": %v", m.Author.Username, err)
|
||||
}
|
||||
d.verifiedTokens[sects[0]] = d.users[m.Author.ID]
|
||||
d.tokens[len(d.tokens)-1], d.tokens[tokenIndex] = d.tokens[tokenIndex], d.tokens[len(d.tokens)-1]
|
||||
d.tokens = d.tokens[:len(d.tokens)-1]
|
||||
}
|
||||
|
||||
func (d *DiscordDaemon) SendDM(message *Message, userID ...string) error {
|
||||
channels := make([]string, len(userID))
|
||||
for i, id := range userID {
|
||||
channel, err := d.bot.UserChannelCreate(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
channels[i] = channel.ID
|
||||
}
|
||||
return d.Send(message, channels...)
|
||||
}
|
||||
|
||||
func (d *DiscordDaemon) Send(message *Message, channelID ...string) error {
|
||||
msg := ""
|
||||
var embeds []*dg.MessageEmbed
|
||||
if message.Markdown != "" {
|
||||
msg, embeds = StripAltText(message.Markdown, true)
|
||||
} else {
|
||||
msg = message.Text
|
||||
}
|
||||
for _, id := range channelID {
|
||||
var err error
|
||||
if len(embeds) != 0 {
|
||||
_, err = d.bot.ChannelMessageSendComplex(
|
||||
id,
|
||||
&dg.MessageSend{
|
||||
Content: msg,
|
||||
Embed: embeds[0],
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 1; i < len(embeds); i++ {
|
||||
_, err := d.bot.ChannelMessageSendEmbed(id, embeds[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_, err := d.bot.ChannelMessageSend(
|
||||
id,
|
||||
msg,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
449
email.go
@@ -9,102 +9,40 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/smtp"
|
||||
"io/fs"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
textTemplate "text/template"
|
||||
"time"
|
||||
|
||||
"github.com/gomarkdown/markdown"
|
||||
"github.com/gomarkdown/markdown/html"
|
||||
"github.com/itchyny/timefmt-go"
|
||||
jEmail "github.com/jordan-wright/email"
|
||||
"github.com/mailgun/mailgun-go/v4"
|
||||
sMail "github.com/xhit/go-simple-mail/v2"
|
||||
)
|
||||
|
||||
// implements email sending, right now via smtp or mailgun.
|
||||
type emailClient interface {
|
||||
send(fromName, fromAddr string, email *Email, address ...string) error
|
||||
var renderer = html.NewRenderer(html.RendererOptions{Flags: html.Smartypants})
|
||||
|
||||
// EmailClient implements email sending, right now via smtp, mailgun or a dummy client.
|
||||
type EmailClient interface {
|
||||
Send(fromName, fromAddr string, message *Message, address ...string) error
|
||||
}
|
||||
|
||||
type dummyClient struct{}
|
||||
|
||||
func (dc *dummyClient) send(fromName, fromAddr string, email *Email, address ...string) error {
|
||||
fmt.Printf("FROM: %s <%s>\nTO: %s\nTEXT: %s\n", fromName, fromAddr, strings.Join(address, ", "), email.Text)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mailgun client implements emailClient.
|
||||
type Mailgun struct {
|
||||
client *mailgun.MailgunImpl
|
||||
}
|
||||
|
||||
func (mg *Mailgun) send(fromName, fromAddr string, email *Email, address ...string) error {
|
||||
message := mg.client.NewMessage(
|
||||
fmt.Sprintf("%s <%s>", fromName, fromAddr),
|
||||
email.Subject,
|
||||
email.Text,
|
||||
)
|
||||
for _, a := range address {
|
||||
// Adding variable tells mailgun to do a batch send, so users don't see other recipients.
|
||||
message.AddRecipientAndVariables(a, map[string]interface{}{"unique_id": a})
|
||||
}
|
||||
message.SetHtml(email.HTML)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||
defer cancel()
|
||||
_, _, err := mg.client.Send(ctx, message)
|
||||
return err
|
||||
}
|
||||
|
||||
// SMTP supports SSL/TLS and STARTTLS; implements emailClient.
|
||||
type SMTP struct {
|
||||
sslTLS bool
|
||||
server string
|
||||
port int
|
||||
auth smtp.Auth
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
func (sm *SMTP) send(fromName, fromAddr string, email *Email, address ...string) error {
|
||||
server := fmt.Sprintf("%s:%d", sm.server, sm.port)
|
||||
from := fmt.Sprintf("%s <%s>", fromName, fromAddr)
|
||||
var wg sync.WaitGroup
|
||||
var err error
|
||||
for _, addr := range address {
|
||||
wg.Add(1)
|
||||
go func(addr string) {
|
||||
defer wg.Done()
|
||||
e := jEmail.NewEmail()
|
||||
e.Subject = email.Subject
|
||||
e.From = from
|
||||
e.Text = []byte(email.Text)
|
||||
e.HTML = []byte(email.HTML)
|
||||
e.To = []string{addr}
|
||||
if sm.sslTLS {
|
||||
err = e.SendWithTLS(server, sm.auth, sm.tlsConfig)
|
||||
} else {
|
||||
err = e.SendWithStartTLS(server, sm.auth, sm.tlsConfig)
|
||||
}
|
||||
}(addr)
|
||||
}
|
||||
wg.Wait()
|
||||
return err
|
||||
}
|
||||
|
||||
// Emailer contains the email sender, email content, and methods to construct message content.
|
||||
// Emailer contains the email sender, translations, and methods to construct messages.
|
||||
type Emailer struct {
|
||||
fromAddr, fromName string
|
||||
lang emailLang
|
||||
sender emailClient
|
||||
sender EmailClient
|
||||
}
|
||||
|
||||
// Email stores content.
|
||||
type Email struct {
|
||||
Subject string `json:"subject"`
|
||||
HTML string `json:"html"`
|
||||
Text string `json:"text"`
|
||||
// Message stores content.
|
||||
type Message struct {
|
||||
Subject string `json:"subject"`
|
||||
HTML string `json:"html"`
|
||||
Text string `json:"text"`
|
||||
Markdown string `json:"markdown"`
|
||||
}
|
||||
|
||||
func (emailer *Emailer) formatExpiry(expiry time.Time, tzaware bool, datePattern, timePattern string) (d, t, expiresIn string) {
|
||||
@@ -137,28 +75,114 @@ func NewEmailer(app *appContext) *Emailer {
|
||||
}
|
||||
method := app.config.Section("email").Key("method").String()
|
||||
if method == "smtp" {
|
||||
sslTls := false
|
||||
sslTLS := false
|
||||
if app.config.Section("smtp").Key("encryption").String() == "ssl_tls" {
|
||||
sslTls = true
|
||||
sslTLS = true
|
||||
}
|
||||
username := ""
|
||||
if u := app.config.Section("smtp").Key("username").MustString(""); u != "" {
|
||||
username = u
|
||||
} else {
|
||||
username := app.config.Section("smtp").Key("username").MustString("")
|
||||
password := app.config.Section("smtp").Key("password").String()
|
||||
if username == "" && password != "" {
|
||||
username = emailer.fromAddr
|
||||
}
|
||||
err := emailer.NewSMTP(app.config.Section("smtp").Key("server").String(), app.config.Section("smtp").Key("port").MustInt(465), username, app.config.Section("smtp").Key("password").String(), sslTls, app.config.Section("smtp").Key("ssl_cert").MustString(""))
|
||||
err := emailer.NewSMTP(app.config.Section("smtp").Key("server").String(), app.config.Section("smtp").Key("port").MustInt(465), username, password, sslTLS, app.config.Section("smtp").Key("ssl_cert").MustString(""), app.config.Section("smtp").Key("hello_hostname").String(), app.config.Section("smtp").Key("cert_validation").MustBool(true))
|
||||
if err != nil {
|
||||
app.err.Printf("Error while initiating SMTP mailer: %v", err)
|
||||
}
|
||||
} else if method == "mailgun" {
|
||||
emailer.NewMailgun(app.config.Section("mailgun").Key("api_url").String(), app.config.Section("mailgun").Key("api_key").String())
|
||||
} else if method == "dummy" {
|
||||
emailer.sender = &dummyClient{}
|
||||
emailer.sender = &DummyClient{}
|
||||
}
|
||||
return emailer
|
||||
}
|
||||
|
||||
// DummyClient just logs the email to the console for debugging purposes. It can be used by settings [email]/method to "dummy".
|
||||
type DummyClient struct{}
|
||||
|
||||
func (dc *DummyClient) Send(fromName, fromAddr string, email *Message, address ...string) error {
|
||||
fmt.Printf("FROM: %s <%s>\nTO: %s\nTEXT: %s\n", fromName, fromAddr, strings.Join(address, ", "), email.Text)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SMTP supports SSL/TLS and STARTTLS; implements EmailClient.
|
||||
type SMTP struct {
|
||||
Client *sMail.SMTPServer
|
||||
}
|
||||
|
||||
// NewSMTP returns an SMTP emailClient.
|
||||
func (emailer *Emailer) NewSMTP(server string, port int, username, password string, sslTLS bool, certPath string, helloHostname string, validateCertificate bool) (err error) {
|
||||
sender := &SMTP{}
|
||||
sender.Client = sMail.NewSMTPClient()
|
||||
if sslTLS {
|
||||
sender.Client.Encryption = sMail.EncryptionSSLTLS
|
||||
} else {
|
||||
sender.Client.Encryption = sMail.EncryptionSTARTTLS
|
||||
}
|
||||
if username != "" || password != "" {
|
||||
sender.Client.Authentication = sMail.AuthLogin
|
||||
sender.Client.Username = username
|
||||
sender.Client.Password = password
|
||||
}
|
||||
sender.Client.Helo = helloHostname
|
||||
sender.Client.ConnectTimeout, sender.Client.SendTimeout = 15*time.Second, 15*time.Second
|
||||
sender.Client.Host = server
|
||||
sender.Client.Port = port
|
||||
sender.Client.KeepAlive = false
|
||||
// x509.SystemCertPool is unavailable on windows
|
||||
if PLATFORM == "windows" {
|
||||
sender.Client.TLSConfig = &tls.Config{
|
||||
InsecureSkipVerify: !validateCertificate,
|
||||
ServerName: server,
|
||||
}
|
||||
emailer.sender = sender
|
||||
return
|
||||
}
|
||||
rootCAs, err := x509.SystemCertPool()
|
||||
if rootCAs == nil || err != nil {
|
||||
rootCAs = x509.NewCertPool()
|
||||
}
|
||||
if certPath != "" {
|
||||
var cert []byte
|
||||
cert, err = os.ReadFile(certPath)
|
||||
if rootCAs.AppendCertsFromPEM(cert) == false {
|
||||
err = errors.New("Failed to append cert to pool")
|
||||
}
|
||||
}
|
||||
sender.Client.TLSConfig = &tls.Config{
|
||||
InsecureSkipVerify: !validateCertificate,
|
||||
ServerName: server,
|
||||
RootCAs: rootCAs,
|
||||
}
|
||||
emailer.sender = sender
|
||||
return
|
||||
}
|
||||
|
||||
func (sm *SMTP) Send(fromName, fromAddr string, email *Message, address ...string) error {
|
||||
from := fmt.Sprintf("%s <%s>", fromName, fromAddr)
|
||||
var cli *sMail.SMTPClient
|
||||
var err error
|
||||
cli, err = sm.Client.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
e := sMail.NewMSG()
|
||||
e.SetFrom(from)
|
||||
e.SetSubject(email.Subject)
|
||||
e.AddTo(address...)
|
||||
e.SetBody(sMail.TextPlain, email.Text)
|
||||
if email.HTML != "" {
|
||||
e.AddAlternative(sMail.TextHTML, email.HTML)
|
||||
}
|
||||
err = e.Send(cli)
|
||||
return err
|
||||
}
|
||||
|
||||
// Mailgun client implements EmailClient.
|
||||
type Mailgun struct {
|
||||
client *mailgun.MailgunImpl
|
||||
}
|
||||
|
||||
// NewMailgun returns a Mailgun emailClient.
|
||||
func (emailer *Emailer) NewMailgun(url, key string) {
|
||||
sender := &Mailgun{
|
||||
@@ -173,38 +197,28 @@ func (emailer *Emailer) NewMailgun(url, key string) {
|
||||
emailer.sender = sender
|
||||
}
|
||||
|
||||
// NewSMTP returns an SMTP emailClient.
|
||||
func (emailer *Emailer) NewSMTP(server string, port int, username, password string, sslTLS bool, certPath string) (err error) {
|
||||
rootCAs, err := x509.SystemCertPool()
|
||||
if rootCAs == nil || err != nil {
|
||||
rootCAs = x509.NewCertPool()
|
||||
func (mg *Mailgun) Send(fromName, fromAddr string, email *Message, address ...string) error {
|
||||
message := mg.client.NewMessage(
|
||||
fmt.Sprintf("%s <%s>", fromName, fromAddr),
|
||||
email.Subject,
|
||||
email.Text,
|
||||
)
|
||||
for _, a := range address {
|
||||
// Adding variable tells mailgun to do a batch send, so users don't see other recipients.
|
||||
message.AddRecipientAndVariables(a, map[string]interface{}{"unique_id": a})
|
||||
}
|
||||
if certPath != "" {
|
||||
var cert []byte
|
||||
cert, err = os.ReadFile(certPath)
|
||||
if rootCAs.AppendCertsFromPEM(cert) == false {
|
||||
err = errors.New("Failed to append cert to pool")
|
||||
}
|
||||
}
|
||||
emailer.sender = &SMTP{
|
||||
auth: smtp.PlainAuth("", username, password, server),
|
||||
server: server,
|
||||
port: port,
|
||||
sslTLS: sslTLS,
|
||||
tlsConfig: &tls.Config{
|
||||
InsecureSkipVerify: false,
|
||||
ServerName: server,
|
||||
RootCAs: rootCAs,
|
||||
},
|
||||
}
|
||||
return
|
||||
message.SetHtml(email.HTML)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||
defer cancel()
|
||||
_, _, err := mg.client.Send(ctx, message)
|
||||
return err
|
||||
}
|
||||
|
||||
type templ interface {
|
||||
Execute(wr io.Writer, data interface{}) error
|
||||
}
|
||||
|
||||
func (emailer *Emailer) construct(app *appContext, section, keyFragment string, data map[string]interface{}) (html, text string, err error) {
|
||||
func (emailer *Emailer) construct(app *appContext, section, keyFragment string, data map[string]interface{}) (html, text, markdown string, err error) {
|
||||
var tpl templ
|
||||
if substituteStrings == "" {
|
||||
data["jellyfin"] = "Jellyfin"
|
||||
@@ -212,14 +226,30 @@ func (emailer *Emailer) construct(app *appContext, section, keyFragment string,
|
||||
data["jellyfin"] = substituteStrings
|
||||
}
|
||||
var keys []string
|
||||
if app.config.Section("email").Key("plaintext").MustBool(false) {
|
||||
keys = []string{"text"}
|
||||
text = ""
|
||||
plaintext := app.config.Section("email").Key("plaintext").MustBool(false)
|
||||
if plaintext {
|
||||
if telegramEnabled || discordEnabled {
|
||||
keys = []string{"text"}
|
||||
text, markdown = "", ""
|
||||
} else {
|
||||
keys = []string{"text"}
|
||||
text = ""
|
||||
}
|
||||
} else {
|
||||
keys = []string{"html", "text"}
|
||||
if telegramEnabled || discordEnabled {
|
||||
keys = []string{"html", "text", "markdown"}
|
||||
} else {
|
||||
keys = []string{"html", "text"}
|
||||
}
|
||||
}
|
||||
for _, key := range keys {
|
||||
filesystem, fpath := app.GetPath(section, keyFragment+key)
|
||||
var filesystem fs.FS
|
||||
var fpath string
|
||||
if key == "markdown" {
|
||||
filesystem, fpath = app.GetPath(section, keyFragment+"text")
|
||||
} else {
|
||||
filesystem, fpath = app.GetPath(section, keyFragment+key)
|
||||
}
|
||||
if key == "html" {
|
||||
tpl, err = template.ParseFS(filesystem, fpath)
|
||||
} else {
|
||||
@@ -228,15 +258,28 @@ func (emailer *Emailer) construct(app *appContext, section, keyFragment string,
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// For constructTemplate, if "md" is found in data it's used in stead of "text".
|
||||
foundMarkdown := false
|
||||
if key == "markdown" {
|
||||
_, foundMarkdown = data["md"]
|
||||
if foundMarkdown {
|
||||
data["plaintext"], data["md"] = data["md"], data["plaintext"]
|
||||
}
|
||||
}
|
||||
var tplData bytes.Buffer
|
||||
err = tpl.Execute(&tplData, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if foundMarkdown {
|
||||
data["plaintext"], data["md"] = data["md"], data["plaintext"]
|
||||
}
|
||||
if key == "html" {
|
||||
html = tplData.String()
|
||||
} else {
|
||||
} else if key == "text" {
|
||||
text = tplData.String()
|
||||
} else {
|
||||
markdown = tplData.String()
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -257,8 +300,11 @@ func (emailer *Emailer) confirmationValues(code, username, key string, app *appC
|
||||
template[v] = "{" + v + "}"
|
||||
}
|
||||
} else {
|
||||
message := app.config.Section("email").Key("message").String()
|
||||
message := app.config.Section("messages").Key("message").String()
|
||||
inviteLink := app.config.Section("invite_emails").Key("url_base").String()
|
||||
if !strings.HasSuffix(inviteLink, "/invite") {
|
||||
inviteLink += "/invite"
|
||||
}
|
||||
inviteLink = fmt.Sprintf("%s/%s?key=%s", inviteLink, code, key)
|
||||
template["helloUser"] = emailer.lang.Strings.template("helloUser", tmpl{"username": username})
|
||||
template["confirmationURL"] = inviteLink
|
||||
@@ -267,8 +313,8 @@ func (emailer *Emailer) confirmationValues(code, username, key string, app *appC
|
||||
return template
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructConfirmation(code, username, key string, app *appContext, noSub bool) (*Email, error) {
|
||||
email := &Email{
|
||||
func (emailer *Emailer) constructConfirmation(code, username, key string, app *appContext, noSub bool) (*Message, error) {
|
||||
email := &Message{
|
||||
Subject: app.config.Section("email_confirmation").Key("subject").MustString(emailer.lang.EmailConfirmation.get("title")),
|
||||
}
|
||||
var err error
|
||||
@@ -282,7 +328,7 @@ func (emailer *Emailer) constructConfirmation(code, username, key string, app *a
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "email_confirmation", "email_", template)
|
||||
email.HTML, email.Text, email.Markdown, err = emailer.construct(app, "email_confirmation", "email_", template)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -290,18 +336,27 @@ func (emailer *Emailer) constructConfirmation(code, username, key string, app *a
|
||||
return email, nil
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructTemplate(subject, md string, app *appContext) (*Email, error) {
|
||||
email := &Email{Subject: subject}
|
||||
renderer := html.NewRenderer(html.RendererOptions{Flags: html.Smartypants})
|
||||
// username is optional, but should only be passed once.
|
||||
func (emailer *Emailer) constructTemplate(subject, md string, app *appContext, username ...string) (*Message, error) {
|
||||
if len(username) != 0 {
|
||||
md = templateEmail(md, []string{"{username}"}, nil, map[string]interface{}{"username": username[0]})
|
||||
subject = templateEmail(subject, []string{"{username}"}, nil, map[string]interface{}{"username": username[0]})
|
||||
}
|
||||
email := &Message{Subject: subject}
|
||||
html := markdown.ToHTML([]byte(md), nil, renderer)
|
||||
text := stripMarkdown(md)
|
||||
message := app.config.Section("email").Key("message").String()
|
||||
message := app.config.Section("messages").Key("message").String()
|
||||
var err error
|
||||
email.HTML, email.Text, err = emailer.construct(app, "template_email", "email_", map[string]interface{}{
|
||||
data := map[string]interface{}{
|
||||
"text": template.HTML(html),
|
||||
"plaintext": text,
|
||||
"message": message,
|
||||
})
|
||||
"md": md,
|
||||
}
|
||||
if len(username) != 0 {
|
||||
data["username"] = username[0]
|
||||
}
|
||||
email.HTML, email.Text, email.Markdown, err = emailer.construct(app, "template_email", "email_", data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -311,8 +366,11 @@ func (emailer *Emailer) constructTemplate(subject, md string, app *appContext) (
|
||||
func (emailer *Emailer) inviteValues(code string, invite Invite, app *appContext, noSub bool) map[string]interface{} {
|
||||
expiry := invite.ValidTill
|
||||
d, t, expiresIn := emailer.formatExpiry(expiry, false, app.datePattern, app.timePattern)
|
||||
message := app.config.Section("email").Key("message").String()
|
||||
message := app.config.Section("messages").Key("message").String()
|
||||
inviteLink := app.config.Section("invite_emails").Key("url_base").String()
|
||||
if !strings.HasSuffix(inviteLink, "/invite") {
|
||||
inviteLink += "/invite"
|
||||
}
|
||||
inviteLink = fmt.Sprintf("%s/%s", inviteLink, code)
|
||||
template := map[string]interface{}{
|
||||
"hello": emailer.lang.InviteEmail.get("hello"),
|
||||
@@ -338,8 +396,8 @@ func (emailer *Emailer) inviteValues(code string, invite Invite, app *appContext
|
||||
return template
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructInvite(code string, invite Invite, app *appContext, noSub bool) (*Email, error) {
|
||||
email := &Email{
|
||||
func (emailer *Emailer) constructInvite(code string, invite Invite, app *appContext, noSub bool) (*Message, error) {
|
||||
email := &Message{
|
||||
Subject: app.config.Section("invite_emails").Key("subject").MustString(emailer.lang.InviteEmail.get("title")),
|
||||
}
|
||||
template := emailer.inviteValues(code, invite, app, noSub)
|
||||
@@ -353,7 +411,7 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "invite_emails", "email_", template)
|
||||
email.HTML, email.Text, email.Markdown, err = emailer.construct(app, "invite_emails", "email_", template)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -377,8 +435,8 @@ func (emailer *Emailer) expiryValues(code string, invite Invite, app *appContext
|
||||
return template
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appContext, noSub bool) (*Email, error) {
|
||||
email := &Email{
|
||||
func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appContext, noSub bool) (*Message, error) {
|
||||
email := &Message{
|
||||
Subject: emailer.lang.InviteExpiry.get("title"),
|
||||
}
|
||||
var err error
|
||||
@@ -392,7 +450,7 @@ func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appCont
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "notifications", "expiry_", template)
|
||||
email.HTML, email.Text, email.Markdown, err = emailer.construct(app, "notifications", "expiry_", template)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -431,8 +489,8 @@ func (emailer *Emailer) createdValues(code, username, address string, invite Inv
|
||||
return template
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructCreated(code, username, address string, invite Invite, app *appContext, noSub bool) (*Email, error) {
|
||||
email := &Email{
|
||||
func (emailer *Emailer) constructCreated(code, username, address string, invite Invite, app *appContext, noSub bool) (*Message, error) {
|
||||
email := &Message{
|
||||
Subject: emailer.lang.UserCreated.get("title"),
|
||||
}
|
||||
template := emailer.createdValues(code, username, address, invite, app, noSub)
|
||||
@@ -446,7 +504,7 @@ func (emailer *Emailer) constructCreated(code, username, address string, invite
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "notifications", "created_", template)
|
||||
email.HTML, email.Text, email.Markdown, err = emailer.construct(app, "notifications", "created_", template)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -454,9 +512,21 @@ func (emailer *Emailer) constructCreated(code, username, address string, invite
|
||||
return email, nil
|
||||
}
|
||||
|
||||
// GenResetLink generates and returns a password reset link.
|
||||
func (app *appContext) GenResetLink(pin string) (string, error) {
|
||||
url := app.config.Section("password_resets").Key("url_base").String()
|
||||
var pinLink string
|
||||
if url == "" {
|
||||
return pinLink, fmt.Errorf("disabled as no URL Base provided. Set in Settings > Password Resets.")
|
||||
}
|
||||
// Strip /invite from end of this URL, ik it's ugly.
|
||||
pinLink = fmt.Sprintf("%s/reset?pin=%s", url, pin)
|
||||
return pinLink, nil
|
||||
}
|
||||
|
||||
func (emailer *Emailer) resetValues(pwr PasswordReset, app *appContext, noSub bool) map[string]interface{} {
|
||||
d, t, expiresIn := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern)
|
||||
message := app.config.Section("email").Key("message").String()
|
||||
message := app.config.Section("messages").Key("message").String()
|
||||
template := map[string]interface{}{
|
||||
"someoneHasRequestedReset": emailer.lang.PasswordReset.get("someoneHasRequestedReset"),
|
||||
"ifItWasNotYou": emailer.lang.Strings.get("ifItWasNotYou"),
|
||||
@@ -484,17 +554,16 @@ func (emailer *Emailer) resetValues(pwr PasswordReset, app *appContext, noSub bo
|
||||
} else {
|
||||
template["helloUser"] = emailer.lang.Strings.template("helloUser", tmpl{"username": pwr.Username})
|
||||
template["codeExpiry"] = emailer.lang.PasswordReset.template("codeExpiry", tmpl{"date": d, "time": t, "expiresInMinutes": expiresIn})
|
||||
inviteLink := app.config.Section("invite_emails").Key("url_base").String()
|
||||
if linkResetEnabled {
|
||||
if inviteLink != "" {
|
||||
pinLink, err := app.GenResetLink(pwr.Pin)
|
||||
if err == nil {
|
||||
// Strip /invite form end of this URL, ik its ugly.
|
||||
template["link_reset"] = true
|
||||
pinLink := fmt.Sprintf("%s/reset?pin=%s", strings.Replace(inviteLink, "/invite", "", 1), pwr.Pin)
|
||||
template["pin"] = pinLink
|
||||
// Only used in html email.
|
||||
template["pin_code"] = pwr.Pin
|
||||
} else {
|
||||
app.info.Println("Password Reset link disabled as no URL Base provided. Set in Settings > Invite Emails.")
|
||||
app.info.Println("Couldn't generate PWR link: %v", err)
|
||||
template["pin"] = pwr.Pin
|
||||
}
|
||||
} else {
|
||||
@@ -505,8 +574,8 @@ func (emailer *Emailer) resetValues(pwr PasswordReset, app *appContext, noSub bo
|
||||
return template
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext, noSub bool) (*Email, error) {
|
||||
email := &Email{
|
||||
func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext, noSub bool) (*Message, error) {
|
||||
email := &Message{
|
||||
Subject: app.config.Section("password_resets").Key("subject").MustString(emailer.lang.PasswordReset.get("title")),
|
||||
}
|
||||
template := emailer.resetValues(pwr, app, noSub)
|
||||
@@ -520,7 +589,7 @@ func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext, noSub
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "password_resets", "email_", template)
|
||||
email.HTML, email.Text, email.Markdown, err = emailer.construct(app, "password_resets", "email_", template)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -541,13 +610,14 @@ func (emailer *Emailer) deletedValues(reason string, app *appContext, noSub bool
|
||||
}
|
||||
} else {
|
||||
template["reason"] = reason
|
||||
template["message"] = app.config.Section("email").Key("message").String()
|
||||
template["message"] = app.config.Section("messages").Key("message").String()
|
||||
}
|
||||
fmt.Println("TTTT", template)
|
||||
return template
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructDeleted(reason string, app *appContext, noSub bool) (*Email, error) {
|
||||
email := &Email{
|
||||
func (emailer *Emailer) constructDeleted(reason string, app *appContext, noSub bool) (*Message, error) {
|
||||
email := &Message{
|
||||
Subject: app.config.Section("deletion").Key("subject").MustString(emailer.lang.UserDeleted.get("title")),
|
||||
}
|
||||
var err error
|
||||
@@ -561,7 +631,7 @@ func (emailer *Emailer) constructDeleted(reason string, app *appContext, noSub b
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "deletion", "email_", template)
|
||||
email.HTML, email.Text, email.Markdown, err = emailer.construct(app, "deletion", "email_", template)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -582,13 +652,13 @@ func (emailer *Emailer) disabledValues(reason string, app *appContext, noSub boo
|
||||
}
|
||||
} else {
|
||||
template["reason"] = reason
|
||||
template["message"] = app.config.Section("email").Key("message").String()
|
||||
template["message"] = app.config.Section("messages").Key("message").String()
|
||||
}
|
||||
return template
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructDisabled(reason string, app *appContext, noSub bool) (*Email, error) {
|
||||
email := &Email{
|
||||
func (emailer *Emailer) constructDisabled(reason string, app *appContext, noSub bool) (*Message, error) {
|
||||
email := &Message{
|
||||
Subject: app.config.Section("disable_enable").Key("subject_disabled").MustString(emailer.lang.UserDisabled.get("title")),
|
||||
}
|
||||
var err error
|
||||
@@ -602,7 +672,7 @@ func (emailer *Emailer) constructDisabled(reason string, app *appContext, noSub
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "disable_enable", "disabled_", template)
|
||||
email.HTML, email.Text, email.Markdown, err = emailer.construct(app, "disable_enable", "disabled_", template)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -623,13 +693,13 @@ func (emailer *Emailer) enabledValues(reason string, app *appContext, noSub bool
|
||||
}
|
||||
} else {
|
||||
template["reason"] = reason
|
||||
template["message"] = app.config.Section("email").Key("message").String()
|
||||
template["message"] = app.config.Section("messages").Key("message").String()
|
||||
}
|
||||
return template
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructEnabled(reason string, app *appContext, noSub bool) (*Email, error) {
|
||||
email := &Email{
|
||||
func (emailer *Emailer) constructEnabled(reason string, app *appContext, noSub bool) (*Message, error) {
|
||||
email := &Message{
|
||||
Subject: app.config.Section("disable_enable").Key("subject_enabled").MustString(emailer.lang.UserEnabled.get("title")),
|
||||
}
|
||||
var err error
|
||||
@@ -643,7 +713,7 @@ func (emailer *Emailer) constructEnabled(reason string, app *appContext, noSub b
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "disable_enable", "enabled_", template)
|
||||
email.HTML, email.Text, email.Markdown, err = emailer.construct(app, "disable_enable", "enabled_", template)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -668,7 +738,7 @@ func (emailer *Emailer) welcomeValues(username string, expiry time.Time, app *ap
|
||||
} else {
|
||||
template["jellyfinURL"] = app.config.Section("jellyfin").Key("public_server").String()
|
||||
template["username"] = username
|
||||
template["message"] = app.config.Section("email").Key("message").String()
|
||||
template["message"] = app.config.Section("messages").Key("message").String()
|
||||
exp := app.formatDatetime(expiry)
|
||||
if !expiry.IsZero() {
|
||||
if custom {
|
||||
@@ -683,8 +753,8 @@ func (emailer *Emailer) welcomeValues(username string, expiry time.Time, app *ap
|
||||
return template
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructWelcome(username string, expiry time.Time, app *appContext, noSub bool) (*Email, error) {
|
||||
email := &Email{
|
||||
func (emailer *Emailer) constructWelcome(username string, expiry time.Time, app *appContext, noSub bool) (*Message, error) {
|
||||
email := &Message{
|
||||
Subject: app.config.Section("welcome_email").Key("subject").MustString(emailer.lang.WelcomeEmail.get("title")),
|
||||
}
|
||||
var err error
|
||||
@@ -708,7 +778,7 @@ func (emailer *Emailer) constructWelcome(username string, expiry time.Time, app
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "welcome_email", "email_", template)
|
||||
email.HTML, email.Text, email.Markdown, err = emailer.construct(app, "welcome_email", "email_", template)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -723,13 +793,13 @@ func (emailer *Emailer) userExpiredValues(app *appContext, noSub bool) map[strin
|
||||
"message": "",
|
||||
}
|
||||
if !noSub {
|
||||
template["message"] = app.config.Section("email").Key("message").String()
|
||||
template["message"] = app.config.Section("messages").Key("message").String()
|
||||
}
|
||||
return template
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructUserExpired(app *appContext, noSub bool) (*Email, error) {
|
||||
email := &Email{
|
||||
func (emailer *Emailer) constructUserExpired(app *appContext, noSub bool) (*Message, error) {
|
||||
email := &Message{
|
||||
Subject: app.config.Section("user_expiry").Key("subject").MustString(emailer.lang.UserExpired.get("title")),
|
||||
}
|
||||
var err error
|
||||
@@ -743,7 +813,7 @@ func (emailer *Emailer) constructUserExpired(app *appContext, noSub bool) (*Emai
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "user_expiry", "email_", template)
|
||||
email.HTML, email.Text, email.Markdown, err = emailer.construct(app, "user_expiry", "email_", template)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -752,6 +822,53 @@ func (emailer *Emailer) constructUserExpired(app *appContext, noSub bool) (*Emai
|
||||
}
|
||||
|
||||
// calls the send method in the underlying emailClient.
|
||||
func (emailer *Emailer) send(email *Email, address ...string) error {
|
||||
return emailer.sender.send(emailer.fromName, emailer.fromAddr, email, address...)
|
||||
func (emailer *Emailer) send(email *Message, address ...string) error {
|
||||
return emailer.sender.Send(emailer.fromName, emailer.fromAddr, email, address...)
|
||||
}
|
||||
|
||||
func (app *appContext) sendByID(email *Message, ID ...string) error {
|
||||
for _, id := range ID {
|
||||
var err error
|
||||
if tgChat, ok := app.storage.telegram[id]; ok && tgChat.Contact && telegramEnabled {
|
||||
err = app.telegram.Send(email, tgChat.ChatID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if dcChat, ok := app.storage.discord[id]; ok && dcChat.Contact && discordEnabled {
|
||||
err = app.discord.Send(email, dcChat.ChannelID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if mxChat, ok := app.storage.matrix[id]; ok && mxChat.Contact && matrixEnabled {
|
||||
err = app.matrix.Send(email, mxChat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if address, ok := app.storage.emails[id]; ok && address.Contact && emailEnabled {
|
||||
err = app.email.send(email, address.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *appContext) getAddressOrName(jfID string) string {
|
||||
if dcChat, ok := app.storage.discord[jfID]; ok && dcChat.Contact && discordEnabled {
|
||||
return dcChat.Username + "#" + dcChat.Discriminator
|
||||
}
|
||||
if tgChat, ok := app.storage.telegram[jfID]; ok && tgChat.Contact && telegramEnabled {
|
||||
return "@" + tgChat.Username
|
||||
}
|
||||
if addr, ok := app.storage.emails[jfID]; ok {
|
||||
return addr.Addr
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
87
exit.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/browser"
|
||||
)
|
||||
|
||||
// https://gist.github.com/swdunlop/9629168
|
||||
func identifyPanic() string {
|
||||
var name, file string
|
||||
var line int
|
||||
var pc [16]uintptr
|
||||
|
||||
n := runtime.Callers(4, pc[:])
|
||||
for _, pc := range pc[:n] {
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
continue
|
||||
}
|
||||
file, line = fn.FileLine(pc)
|
||||
name = fn.Name()
|
||||
if !strings.HasPrefix(name, "runtime.") {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case name != "":
|
||||
return fmt.Sprintf("%v:%v", name, line)
|
||||
case file != "":
|
||||
return fmt.Sprintf("%v:%v", file, line)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("pc:%x", pc)
|
||||
}
|
||||
|
||||
// Exit dumps the last 100 lines of output to a crash file in /tmp (or equivalent), and generates a prettier HTML file containing it that is opened in the browser if possible.
|
||||
func Exit(err interface{}) {
|
||||
tmpl, err2 := template.ParseFS(localFS, "html/crash.html", "html/header.html")
|
||||
if err2 != nil {
|
||||
log.Fatalf("Failed to load template: %v", err)
|
||||
}
|
||||
logCache := lineCache.String()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
logCache += "\n" + fmt.Sprint(err)
|
||||
}
|
||||
logCache += "\n" + string(debug.Stack())
|
||||
sanitized := sanitizeLog(logCache)
|
||||
data := map[string]interface{}{
|
||||
"Log": logCache,
|
||||
"SanitizedLog": sanitized,
|
||||
}
|
||||
if err != nil {
|
||||
data["Err"] = fmt.Sprintf("%s %v", identifyPanic(), err)
|
||||
}
|
||||
fpath := filepath.Join(temp, "jfa-go-crash-"+time.Now().Local().Format("2006-01-02T15:04:05"))
|
||||
err2 = os.WriteFile(fpath+".txt", []byte(logCache), 0666)
|
||||
if err2 != nil {
|
||||
log.Fatalf("Failed to write crash dump file: %v", err2)
|
||||
}
|
||||
log.Printf("\n------\nA crash report has been saved to \"%s\".\n------", fpath+".txt")
|
||||
f, err2 := os.OpenFile(fpath+".html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err2 != nil {
|
||||
log.Fatalf("Failed to open crash dump file: %v", err2)
|
||||
}
|
||||
defer f.Close()
|
||||
err2 = tmpl.Execute(f, data)
|
||||
if err2 != nil {
|
||||
log.Fatalf("Failed to execute template: %v", err2)
|
||||
}
|
||||
browser.OpenFile(fpath + ".html")
|
||||
if TRAY {
|
||||
QuitTray()
|
||||
} else {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
22
external.go
@@ -12,8 +12,8 @@ import (
|
||||
|
||||
const binaryType = "external"
|
||||
|
||||
var localFS fs.FS
|
||||
var langFS fs.FS
|
||||
var localFS dirFS
|
||||
var langFS dirFS
|
||||
|
||||
// When using os.DirFS, even on Windows the separator seems to be '/'.
|
||||
// func FSJoin(elem ...string) string { return filepath.Join(elem...) }
|
||||
@@ -29,9 +29,23 @@ func FSJoin(elem ...string) string {
|
||||
return strings.TrimSuffix(path, sep)
|
||||
}
|
||||
|
||||
type dirFS string
|
||||
|
||||
func (dir dirFS) Open(name string) (fs.File, error) {
|
||||
return os.Open(string(dir) + "/" + name)
|
||||
}
|
||||
|
||||
func (dir dirFS) ReadFile(name string) ([]byte, error) {
|
||||
return os.ReadFile(string(dir) + "/" + name)
|
||||
}
|
||||
|
||||
func (dir dirFS) ReadDir(name string) ([]fs.DirEntry, error) {
|
||||
return os.ReadDir(string(dir) + "/" + name)
|
||||
}
|
||||
|
||||
func loadFilesystems() {
|
||||
log.Println("Using external storage")
|
||||
executable, _ := os.Executable()
|
||||
localFS = os.DirFS(filepath.Join(filepath.Dir(executable), "data"))
|
||||
langFS = os.DirFS(filepath.Join(filepath.Dir(executable), "data", "lang"))
|
||||
localFS = dirFS(filepath.Join(filepath.Dir(executable), "data"))
|
||||
langFS = dirFS(filepath.Join(filepath.Dir(executable), "data", "lang"))
|
||||
}
|
||||
|
||||
80
go.mod
@@ -10,42 +10,56 @@ replace github.com/hrfee/jfa-go/ombi => ./ombi
|
||||
|
||||
replace github.com/hrfee/jfa-go/logger => ./logger
|
||||
|
||||
replace github.com/hrfee/jfa-go/linecache => ./linecache
|
||||
|
||||
require (
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/fatih/color v1.10.0
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/bwmarrin/discordgo v0.23.2
|
||||
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/fsnotify/fsnotify v1.5.1
|
||||
github.com/getlantern/golog v0.0.0-20210606115803-bce9f9fe5a5f // indirect
|
||||
github.com/getlantern/hidden v0.0.0-20201229170000-e66e7f878730 // indirect
|
||||
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6 // indirect
|
||||
github.com/getlantern/systray v1.1.0
|
||||
github.com/gin-contrib/pprof v1.3.0
|
||||
github.com/gin-contrib/static v0.0.0-20200916080430-d45d9a37d28e
|
||||
github.com/gin-gonic/gin v1.6.3
|
||||
github.com/go-chi/chi v4.1.2+incompatible // indirect
|
||||
github.com/go-openapi/spec v0.20.3 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-playground/validator/v10 v10.4.1 // indirect
|
||||
github.com/golang/protobuf v1.4.3 // indirect
|
||||
github.com/gomarkdown/markdown v0.0.0-20210208175418-bda154fe17d8
|
||||
github.com/google/uuid v1.1.2 // indirect
|
||||
github.com/hrfee/jfa-go/common v0.0.0-20210105184019-fdc97b4e86cc
|
||||
github.com/hrfee/jfa-go/docs v0.0.0-20201112212552-b6f3cd7c1f71
|
||||
github.com/hrfee/jfa-go/logger v0.0.0-00010101000000-000000000000 // indirect
|
||||
github.com/hrfee/jfa-go/ombi v0.0.0-20201112212552-b6f3cd7c1f71
|
||||
github.com/hrfee/mediabrowser v0.3.3
|
||||
github.com/itchyny/timefmt-go v0.1.2
|
||||
github.com/jordan-wright/email v4.0.1-0.20200917010138-e1c00e156980+incompatible
|
||||
github.com/lithammer/shortuuid/v3 v3.0.4
|
||||
github.com/mailgun/mailgun-go/v4 v4.3.0
|
||||
github.com/gin-contrib/static v0.0.1
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
github.com/go-playground/validator/v10 v10.9.0 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/gomarkdown/markdown v0.0.0-20211212230626-5af6ad2f47df
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/hrfee/jfa-go/common v0.0.0-20211222231100-d47afe05f49c
|
||||
github.com/hrfee/jfa-go/docs v0.0.0-20211222231100-d47afe05f49c
|
||||
github.com/hrfee/jfa-go/linecache v0.0.0-20211222231100-d47afe05f49c
|
||||
github.com/hrfee/jfa-go/logger v0.0.0-20211222231100-d47afe05f49c
|
||||
github.com/hrfee/jfa-go/ombi v0.0.0-20211222231100-d47afe05f49c
|
||||
github.com/hrfee/mediabrowser v0.3.8
|
||||
github.com/itchyny/timefmt-go v0.1.3
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/lithammer/shortuuid/v3 v3.0.7
|
||||
github.com/mailgun/mailgun-go/v4 v4.6.0
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14
|
||||
github.com/swaggo/gin-swagger v1.3.0
|
||||
github.com/swaggo/swag v1.7.0 // indirect
|
||||
github.com/ugorji/go v1.2.0 // indirect
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2
|
||||
github.com/swaggo/gin-swagger v1.3.3
|
||||
github.com/swaggo/swag v1.7.6 // indirect
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.4 // indirect
|
||||
github.com/ugorji/go v1.2.6 // indirect
|
||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible
|
||||
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 // indirect
|
||||
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c // indirect
|
||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54 // indirect
|
||||
golang.org/x/tools v0.1.0 // indirect
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0
|
||||
github.com/xhit/go-simple-mail/v2 v2.10.0
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
||||
golang.org/x/tools v0.1.8 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/ini.v1 v1.66.2
|
||||
maunium.net/go/mautrix v0.10.7
|
||||
)
|
||||
|
||||
392
go.sum
@@ -1,6 +1,3 @@
|
||||
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
@@ -9,58 +6,81 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs=
|
||||
github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
|
||||
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4=
|
||||
github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473 h1:4cmBvAEBNJaGARUEs3/suWRyfyBfhf7I60WBZq+bv2w=
|
||||
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/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:M88ob4TyDnEqNuL3PgsE/p3bDujfspnulR+0dQWNYZs=
|
||||
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:buzQsO8HHkZX2Q45fdfGH1xejPjuDQaXH8btcYMFzPM=
|
||||
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/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4=
|
||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
|
||||
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
|
||||
github.com/getlantern/errors v1.0.1 h1:XukU2whlh7OdpxnkXhNH9VTLVz0EVPGKDV5K0oWhvzw=
|
||||
github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
|
||||
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7/go.mod h1:zx/1xUUeYPy3Pcmet8OSXLbF47l+3y6hIPpyLWoR9oc=
|
||||
github.com/getlantern/golog v0.0.0-20210606115803-bce9f9fe5a5f h1:wsVt3P/boVKkPFEZkWxgNgRq/+mD7sWHc17+Vw2PgH8=
|
||||
github.com/getlantern/golog v0.0.0-20210606115803-bce9f9fe5a5f/go.mod h1:ZyIjgH/1wTCl+B+7yH1DqrWp6MPJqESmwmEQ89ZfhvA=
|
||||
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0=
|
||||
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o=
|
||||
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
|
||||
github.com/getlantern/hidden v0.0.0-20201229170000-e66e7f878730 h1:oKJVQbWZ2CAJ71jYnm6A3+e6h5bkPJ0okIMwkaYB5HI=
|
||||
github.com/getlantern/hidden v0.0.0-20201229170000-e66e7f878730/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
|
||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6 h1:QthAQCekS1YOeYWSvoHI6ZatlG4B+GBDLxV/2ZkBsTA=
|
||||
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||
github.com/getlantern/systray v1.1.0 h1:U0wCEqseLi2ok1fE6b88gJklzriavPJixZysZPkZd/Y=
|
||||
github.com/getlantern/systray v1.1.0/go.mod h1:AecygODWIsBquJCJFop8MEQcJbWFfw/1yWbVabNgpCM=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc=
|
||||
github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=
|
||||
github.com/gin-contrib/gzip v0.0.3 h1:etUaeesHhEORpZMp18zoOhepboiWnFtXrBZxszWUn4k=
|
||||
github.com/gin-contrib/gzip v0.0.3/go.mod h1:YxxswVZIqOvcHEQpsSn+QF5guQtO1dCfy0shBPy4jFc=
|
||||
github.com/gin-contrib/pprof v1.3.0 h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0=
|
||||
github.com/gin-contrib/pprof v1.3.0/go.mod h1:waMjT1H9b179t3CxuG1cV3DHpga6ybizwfBaM5OXaB0=
|
||||
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-contrib/static v0.0.0-20200916080430-d45d9a37d28e h1:8bZpGwoPxkaivQPrAbWl+7zjjUcbFUnYp7yQcx2r2N0=
|
||||
github.com/gin-contrib/static v0.0.0-20200916080430-d45d9a37d28e/go.mod h1:VhW/Ch/3FhimwZb8Oj+qJmdMmoB8r7lmJ5auRjm50oQ=
|
||||
github.com/gin-contrib/static v0.0.1 h1:JVxuvHPuUfkoul12N7dtQw7KRn/pSMq7Ue1Va9Swm1U=
|
||||
github.com/gin-contrib/static v0.0.1/go.mod h1:CSxeF+wep05e0kCOsqWdAWbSszmc31zTIbD8TvWl7Hs=
|
||||
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
|
||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||
github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/go-chi/chi v4.0.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
|
||||
github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
||||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
@@ -70,164 +90,194 @@ github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3Hfo
|
||||
github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
|
||||
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
|
||||
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
|
||||
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
|
||||
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
|
||||
github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.14/go.mod h1:gwrgJS15eCUgjLpMjBJmbZezCsw88LmgeEip0M63doA=
|
||||
github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ=
|
||||
github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg=
|
||||
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
|
||||
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY=
|
||||
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A=
|
||||
github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
||||
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/gomarkdown/markdown v0.0.0-20210208175418-bda154fe17d8 h1:nWU6p08f1VgIalT6iZyqXi4o5cZsz4X6qa87nusfcsc=
|
||||
github.com/gomarkdown/markdown v0.0.0-20210208175418-bda154fe17d8/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/gomarkdown/markdown v0.0.0-20211212230626-5af6ad2f47df h1:M7mdNDTRraBcrHZg2aOYiFP9yTDajb6fquRZRpXnbVA=
|
||||
github.com/gomarkdown/markdown v0.0.0-20211212230626-5af6ad2f47df/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/hrfee/mediabrowser v0.3.3 h1:7E05uiol8hh2ytKn3WVLrUIvHAyifYEIy3Y5qtuNh8I=
|
||||
github.com/hrfee/mediabrowser v0.3.3/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U=
|
||||
github.com/itchyny/timefmt-go v0.1.2 h1:q0Xa4P5it6K6D7ISsbLAMwx1PnWlixDcJL6/sFs93Hs=
|
||||
github.com/itchyny/timefmt-go v0.1.2/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A=
|
||||
github.com/jordan-wright/email v4.0.1-0.20200917010138-e1c00e156980+incompatible h1:CL0ooBNfbNyJTJATno+m0h+zM5bW6v7fKlboKUGP/dI=
|
||||
github.com/jordan-wright/email v4.0.1-0.20200917010138-e1c00e156980+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hrfee/mediabrowser v0.3.7 h1:F57Cmwst4fOfhPuOlanKiOuek9zCVcXm78/zP/1WB2s=
|
||||
github.com/hrfee/mediabrowser v0.3.7/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U=
|
||||
github.com/hrfee/mediabrowser v0.3.8 h1:y0iBCb6jE3QKcsiCJSYva2fFPHRn4UA+sGRzoPuJ/Dk=
|
||||
github.com/hrfee/mediabrowser v0.3.8/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U=
|
||||
github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU=
|
||||
github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lithammer/shortuuid/v3 v3.0.4 h1:uj4xhotfY92Y1Oa6n6HUiFn87CdoEHYUlTy0+IgbLrs=
|
||||
github.com/lithammer/shortuuid/v3 v3.0.4/go.mod h1:RviRjexKqIzx/7r1peoAITm6m7gnif/h+0zmolKJjzw=
|
||||
github.com/mailgun/mailgun-go/v4 v4.3.0 h1:9nAF7LI3k6bfDPbMZQMMl63Q8/vs+dr1FUN8eR1XMhk=
|
||||
github.com/mailgun/mailgun-go/v4 v4.3.0/go.mod h1:fWuBI2iaS/pSSyo6+EBpHjatQO3lV8onwqcRy7joSJI=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJVuvyJQQ8=
|
||||
github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts=
|
||||
github.com/mailgun/mailgun-go/v4 v4.6.0 h1:qSrgT3wP5fU7wF/tNUp4xeYe8wSUy+8V5NJPYnB6Hxo=
|
||||
github.com/mailgun/mailgun-go/v4 v4.6.0/go.mod h1:FJlF9rI5cQT+mrwujtJjPMbIVy3Ebor9bKTVsJ0QU40=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
|
||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE=
|
||||
github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U=
|
||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
|
||||
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 h1:+iNTcqQJy0OZ5jk6a5NLib47eqXK8uYcPX+O4+cBpEM=
|
||||
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
|
||||
github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=
|
||||
github.com/swaggo/gin-swagger v1.3.0 h1:eOmp7r57oUgZPw2dJOjcGNMse9cvXcI4tTqBcnZtPsI=
|
||||
github.com/swaggo/gin-swagger v1.3.0/go.mod h1:oy1BRA6WvgtCp848lhxce7BnWH4C8Bxa0m5SkWx+cS0=
|
||||
github.com/swaggo/gin-swagger v1.3.3 h1:XHyYmeNVFG5PbyWHG4jXtxOm2P4kiZapDCWsyDDiQ/I=
|
||||
github.com/swaggo/gin-swagger v1.3.3/go.mod h1:ymsZuGpbbu+S7ZoQ49QPpZoDBj6uqhb8WizgQPVgWl0=
|
||||
github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
|
||||
github.com/swaggo/swag v1.6.7/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc=
|
||||
github.com/swaggo/swag v1.7.0 h1:5bCA/MTLQoIqDXXyHfOpMeDvL9j68OY/udlK4pQoo4E=
|
||||
github.com/swaggo/swag v1.7.0/go.mod h1:BdPIL73gvS9NBsdi7M1JOxLvlbfvNRaBP8m6WT6Aajo=
|
||||
github.com/swaggo/swag v1.7.4/go.mod h1:zD8h6h4SPv7t3l+4BKdRquqW1ASWjKZgT6Qv9z3kNqI=
|
||||
github.com/swaggo/swag v1.7.6 h1:UbAqHyXkW2J+cDjs5S43MkuYR7a6stB7Am7SK8NBmRg=
|
||||
github.com/swaggo/swag v1.7.6/go.mod h1:7vLqNYEtYoIsD14wXgy9oDS65MNiDANrPtbk9rnLuj0=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
|
||||
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo=
|
||||
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.3/go.mod h1:5WdjKx3AQMvCJ4RG6/2UYT7dLrGvJUV1x4jdTAyGvZs=
|
||||
github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc=
|
||||
github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go v1.1.13/go.mod h1:jxau1n+/wyTGLQoCkjok9r5zFa/FxT6eI5HiHKQszjc=
|
||||
github.com/ugorji/go v1.2.0 h1:6eXlzYLLwZwXroJx9NyqbYcbv/d93twiOzQLDewE6qM=
|
||||
github.com/ugorji/go v1.2.0/go.mod h1:1ny++pKMXhLWrwWV5Nf+CbOuZJhMoaFD+0GMFfd8fEc=
|
||||
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
|
||||
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
|
||||
github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU=
|
||||
github.com/ugorji/go/codec v1.2.0 h1:As6RccOIlbm9wHuWYMlB30dErcI+4WiKWsYsmPkyrUw=
|
||||
github.com/ugorji/go/codec v1.2.0/go.mod h1:dXvG35r7zTX6QImXOSFhGMmKtX+wJ7VTWzGvYQGIjBs=
|
||||
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
|
||||
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
@@ -235,29 +285,27 @@ github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw=
|
||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE=
|
||||
github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
|
||||
github.com/xhit/go-simple-mail/v2 v2.10.0 h1:nib6RaJ4qVh5HD9UE9QJqnUZyWp3upv+Z6CFxaMj0V8=
|
||||
github.com/xhit/go-simple-mail/v2 v2.10.0/go.mod h1:kA1XbQfCI4JxQ9ccSN6VFyIEkkugOm7YiPkA5hKiQn4=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead h1:jeP6FgaSLNTMP+Yri3qjlACywQLye+huGLmNGhBzm6k=
|
||||
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU=
|
||||
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4 h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@@ -266,104 +314,92 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c h1:KHUzaHIpjWVlVVNh65G3hhuj3KB1HnjY6Cq5cTvRQT8=
|
||||
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM=
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54 h1:rF3Ohx8DRyl8h2zw9qojyLHLhrJpEMgyPOImREEryf0=
|
||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
maunium.net/go/maulogger/v2 v2.3.1 h1:fwBYJne0pHvJrrIPHK+TAPfyxxbBEz46oVGez2x0ODE=
|
||||
maunium.net/go/maulogger/v2 v2.3.1/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
|
||||
maunium.net/go/mautrix v0.10.7 h1:QV5vbCY4g50N7r1ihdG6zEPfaPn/EVYjM5H+qfLy4RM=
|
||||
maunium.net/go/mautrix v0.10.7/go.mod h1:k4Ng5oci83MEbqPL6KOjPdbU7f8v01KlMjR/zTQ+7mA=
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
</head>
|
||||
<body class="section">
|
||||
<div class="page-container">
|
||||
<h1 class="heading">Page not found.</h1>
|
||||
<p class="content">
|
||||
{{ .contactMessage }}
|
||||
</p>
|
||||
<div class="card">
|
||||
<h1 class="heading">Page not found.</h1>
|
||||
<p class="content">
|
||||
{{ .contactMessage }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
448
html/admin.html
@@ -5,10 +5,14 @@
|
||||
<script>
|
||||
window.URLBase = "{{ .urlBase }}";
|
||||
window.notificationsEnabled = {{ .notifications }};
|
||||
window.emailEnabled = {{ .email_enabled }};
|
||||
window.emailEnabled = {{ .emailEnabled }};
|
||||
window.telegramEnabled = {{ .telegramEnabled }};
|
||||
window.discordEnabled = {{ .discordEnabled }};
|
||||
window.matrixEnabled = {{ .matrixEnabled }};
|
||||
window.ombiEnabled = {{ .ombiEnabled }};
|
||||
window.usernameEnabled = {{ .username }};
|
||||
window.langFile = JSON.parse({{ .language }});
|
||||
window.linkResetEnabled = {{ .linkResetEnabled }};
|
||||
window.language = "{{ .langName }}";
|
||||
</script>
|
||||
{{ template "header.html" . }}
|
||||
@@ -18,23 +22,23 @@
|
||||
<div id="modal-login" class="modal">
|
||||
<form class="modal-content card" id="form-login" href="">
|
||||
<span class="heading">{{ .strings.login }}</span>
|
||||
<input type="text" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.username }}" id="login-user">
|
||||
<input type="password" class="field input ~neutral !high mb-1" placeholder="{{ .strings.password }}" id="login-password">
|
||||
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.username }}" id="login-user">
|
||||
<input type="password" class="field input ~neutral @high mb-4" placeholder="{{ .strings.password }}" id="login-password">
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge !normal full-width center supra submit">{{ .strings.login }}</span>
|
||||
<span class="button ~urge @low full-width center supra submit">{{ .strings.login }}</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
<div id="modal-add-user" class="modal">
|
||||
<form class="modal-content card" id="form-add-user" href="">
|
||||
<span class="heading">{{ .strings.newUser }} <span class="modal-close">×</span></span>
|
||||
<input type="text" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.username }}" id="add-user-user">
|
||||
<input type="email" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.emailAddress }}">
|
||||
<input type="password" class="field input ~neutral !high mb-1" placeholder="{{ .strings.password }}" id="add-user-password">
|
||||
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.username }}" id="add-user-user">
|
||||
<input type="email" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.emailAddress }}">
|
||||
<input type="password" class="field input ~neutral @high mb-4" placeholder="{{ .strings.password }}" id="add-user-password">
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge !normal full-width center supra submit">{{ .strings.create }}</span>
|
||||
<span class="button ~urge @low full-width center supra submit">{{ .strings.create }}</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
@@ -42,68 +46,73 @@
|
||||
<div class="modal-content content card">
|
||||
<img src="{{ .urlBase }}/banner.svg" class="banner header" alt="jfa-go banner">
|
||||
<span class="heading"><span class="modal-close">×</span></span>
|
||||
<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.commitNoun }} <span class="code monospace">{{ .commit }}</span></p>
|
||||
<div class="dropdown" tabindex="0">
|
||||
<span class="button ~info dropdown-button">
|
||||
<i class="ri-hand-heart-line mr-half"></i>
|
||||
{{ .strings.donate }}
|
||||
<span class="ml-1 chev"></span>
|
||||
</span>
|
||||
<div class="dropdown-display">
|
||||
<div class="card ~neutral !low">
|
||||
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button input ~neutral field mb-half lang-link">GitHub</a>
|
||||
<a href="https://ko-fi.com/hrfee" target="_blank" class="button input ~neutral field mb-half lang-link">Ko-fi</a>
|
||||
<p>{{ .strings.version }} <span class="code font-mono bg-inherit">{{ .version }}</span></p>
|
||||
<p>{{ .strings.commitNoun }} <span class="code font-mono bg-inherit">{{ .commit }}</span></p>
|
||||
<div class="row col flex">
|
||||
<a class="button ~neutral mr-2 mt-4 mb-4 lang-link" href="https://github.com/hrfee/jfa-go"><i class="ri-github-line mr-2"></i>github</a>
|
||||
<a class="button ~urge mt-4 mb-4 mr-2 lang-link" href="https://wiki.jfa-go.com">wiki/docs</a>
|
||||
<a class="button ~positive mt-4 mb-4 mr-2 lang-link" href="https://weblate.jfa-go.com">translation</a>
|
||||
<div class="dropdown mr-2" tabindex="0">
|
||||
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button ~info mt-4 mb-4 dropdown-button lang-link">
|
||||
<i class="ri-hand-heart-line mr-2"></i>
|
||||
donate
|
||||
<span class="ml-2 chev"></span>
|
||||
</a>
|
||||
<div class="dropdown-display">
|
||||
<div class="card ~neutral @low">
|
||||
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button input ~neutral field mb-2 lang-link">GitHub</a>
|
||||
<a href="https://ko-fi.com/hrfee" target="_blank" class="button input ~neutral field mb-2 lang-link">Ko-fi</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="button ~urge mt-4 mb-4 @low discord lang-link" href="https://discord.com/invite/MrtvuQmyhP" target="_blank"><i class="ri-discord-line mr-2"></i>discord</a>
|
||||
</div>
|
||||
<p><a href="https://github.com/hrfee/jfa-go/blob/main/LICENSE">Available under the MIT License.</a></p>
|
||||
<pre class="monospace">{{ .license }}</pre>
|
||||
<pre class="font-mono bg-inherit">{{ .license }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal-modify-user" class="modal">
|
||||
<form class="modal-content card" id="form-modify-user" href="">
|
||||
<span class="heading"><span id="header-modify-user"></span> <span class="modal-close">×</span></span>
|
||||
<p class="content">{{ .strings.modifySettingsDescription }}</p>
|
||||
<div class="flex-row mb-1">
|
||||
<label class="flex-row-group mr-1">
|
||||
<p class="content my-4">{{ .strings.modifySettingsDescription }}</p>
|
||||
<div class="flex-row mb-4">
|
||||
<label class="flex-row-group mr-2">
|
||||
<input type="radio" name="modify-user-source" class="unfocused" id="radio-use-profile" checked>
|
||||
<span class="button ~neutral !high supra full-width center">{{ .strings.profile }}</span>
|
||||
<span class="button ~neutral @high supra full-width center">{{ .strings.profile }}</span>
|
||||
</label>
|
||||
<label class="flex-row-group ml-1">
|
||||
<label class="flex-row-group ml-2">
|
||||
<input type="radio" name="modify-user-source" class="unfocused" id="radio-use-user">
|
||||
<span class="button ~neutral !normal supra full-width center">{{ .strings.user }}</span>
|
||||
<span class="button ~neutral @low supra full-width center">{{ .strings.user }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="select ~neutral !normal mb-1">
|
||||
<div class="select ~neutral @low mb-4">
|
||||
<select id="modify-user-profiles"></select>
|
||||
</div>
|
||||
<div class="select ~neutral !normal mb-1 unfocused">
|
||||
<div class="select ~neutral @low mb-4 unfocused">
|
||||
<select id="modify-user-users"></select>
|
||||
</div>
|
||||
<label class="switch mb-1">
|
||||
<label class="switch mb-4">
|
||||
<input type="checkbox" id="modify-user-homescreen" checked>
|
||||
<span>{{ .strings.applyHomescreenLayout }}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge !normal full-width center supra submit">{{ .strings.apply }}</span>
|
||||
<span class="button ~urge @low full-width center supra submit">{{ .strings.apply }}</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
<div id="modal-delete-user" class="modal">
|
||||
<form class="modal-content card" id="form-delete-user" href="">
|
||||
<span class="heading"><span id="header-delete-user"></span> <span class="modal-close">×</span></span>
|
||||
<div class="content mt-half">
|
||||
<label class="switch mb-1">
|
||||
<div class="content mt-8">
|
||||
<label class="switch mb-4">
|
||||
<input type="checkbox" id="delete-user-notify" checked>
|
||||
<span>{{ .strings.sendDeleteNotificationEmail }}</span>
|
||||
</label>
|
||||
<textarea id="textarea-delete-user" class="textarea full-width ~neutral !normal mb-1" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
|
||||
<textarea id="textarea-delete-user" class="textarea full-width ~neutral @low mb-4" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~critical !normal full-width center supra submit">{{ .strings.delete }}</span>
|
||||
<span class="button ~critical @low full-width center supra submit">{{ .strings.delete }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
@@ -111,11 +120,11 @@
|
||||
<div id="modal-extend-expiry" class="modal">
|
||||
<form class="modal-content card" id="form-extend-expiry" href="">
|
||||
<span class="heading"><span id="header-extend-expiry"></span> <span class="modal-close">×</span></span>
|
||||
<div class="content mt-half">
|
||||
<div class="content mt-8">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label supra" for="extend-expiry-months">{{ .strings.inviteMonths }}</label>
|
||||
<div class="select ~neutral !normal mb-1 mt-half">
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="extend-expiry-months">
|
||||
<option>0</option>
|
||||
</select>
|
||||
@@ -123,7 +132,7 @@
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label supra" for="extend-expiry-days">{{ .strings.inviteDays }}</label>
|
||||
<div class="select ~neutral !normal mb-1 mt-half">
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="extend-expiry-days">
|
||||
<option>0</option>
|
||||
</select>
|
||||
@@ -133,7 +142,7 @@
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label supra" for="extend-expiry-hours">{{ .strings.inviteHours }}</label>
|
||||
<div class="select ~neutral !normal mb-1 mt-half">
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="extend-expiry-hours">
|
||||
<option>0</option>
|
||||
</select>
|
||||
@@ -141,16 +150,21 @@
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label supra" for="extend-expiry-minutes">{{ .strings.inviteMinutes }}</label>
|
||||
<div class="select ~neutral !normal mb-1 mt-half">
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="extend-expiry-minutes">
|
||||
<option>0</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="switch mb-4">
|
||||
<input type="checkbox" id="expiry-extend-enable" checked>
|
||||
<span>{{ .strings.sendDeleteNotificationEmail }}</span>
|
||||
</label>
|
||||
<textarea id="textarea-extend-enable" class="textarea full-width ~neutral @low mb-4" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~critical !normal full-width center supra submit">{{ .strings.submit }}</span>
|
||||
<span class="button ~critical @low full-width center supra submit">{{ .strings.submit }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
@@ -159,34 +173,47 @@
|
||||
<form class="modal-content wide card" id="form-announce" href="">
|
||||
<span class="heading"><span id="header-announce"></span> <span class="modal-close">×</span></span>
|
||||
<div class="row">
|
||||
<div class="col flex-col content mt-half">
|
||||
<label class="label supra" for="announce-subject"> {{ .strings.subject }}</label>
|
||||
<input type="text" id="announce-subject" class="input ~neutral !normal mb-1 mt-half">
|
||||
<label class="label supra" for="textarea-announce">{{ .strings.message }}</label>
|
||||
<textarea id="textarea-announce" class="textarea full-width ~neutral !normal mt-half monospace"></textarea>
|
||||
<p class="support mt-half mb-1">{{ .strings.markdownSupported }}</p>
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge !normal full-width center supra submit">{{ .strings.submit }}</span>
|
||||
<div class="col card ~neutral @low">
|
||||
<div id="announce-details">
|
||||
<span class="label supra" for="editor-variables" id="label-editor-variables">{{ .strings.variables }}</span>
|
||||
<div id="announce-variables">
|
||||
<span class="button ~urge @low mb-2 mt-4" id="announce-variables-username" style="margin-left: 0.25rem; margin-right: 0.25rem;"><span class="font-mono bg-inherit">{username}</span></span>
|
||||
</div>
|
||||
<label class="label supra" for="announce-subject"> {{ .strings.subject }}</label>
|
||||
<input type="text" id="announce-subject" class="input ~neutral @low mb-2 mt-4">
|
||||
<label class="label supra" for="textarea-announce">{{ .strings.message }}</label>
|
||||
<textarea id="textarea-announce" class="textarea full-width ~neutral @low mt-4 font-mono"></textarea>
|
||||
<p class="support mt-4 mb-2">{{ .strings.markdownSupported }}</p>
|
||||
</div>
|
||||
<label class="label unfocused" id="announce-name"><p class="supra">{{ .strings.name }}</p>
|
||||
<input type="text" class="input ~neutral @low mb-2 mt-4">
|
||||
<p class="support">{{ .strings.templateEnterName }}</p>
|
||||
</label>
|
||||
<div class="row flex-expand">
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge @low center supra submit">{{ .strings.send }}</span>
|
||||
</label>
|
||||
<span class="button ~info @low center supra" id="save-announce">{{ .strings.saveAsTemplate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col card ~neutral !low">
|
||||
<div class="col card ~neutral @low">
|
||||
<span class="subheading supra">{{ .strings.preview }}</span>
|
||||
<div class="mt-half" id="announce-preview"></div>
|
||||
<div class="mt-8" id="announce-preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="modal-customize" class="modal">
|
||||
<div class="modal-content card">
|
||||
<span class="heading">{{ .strings.customizeEmails }} <span class="modal-close">×</span></span>
|
||||
<p class="content">{{ .strings.customizeEmailsDescription }}</p>
|
||||
<span class="heading">{{ .strings.customizeMessages }} <span class="modal-close">×</span></span>
|
||||
<p class="content my-4">{{ .strings.customizeMessagesDescription }}</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ .strings.name }}</th>
|
||||
<th>{{ .strings.reset }}</th>
|
||||
<th class="table-inline justify-center">{{ .strings.reset }}</th>
|
||||
<th>{{ .strings.edit }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -199,70 +226,80 @@
|
||||
<form class="modal-content wide card" id="form-editor" href="">
|
||||
<span class="heading"><span id="header-editor"></span> <span class="modal-close">×</span></span>
|
||||
<div class="row">
|
||||
<div class="col flex-col content mt-half">
|
||||
<div class="col card ~neutral @low">
|
||||
<span class="label supra" for="editor-variables" id="label-editor-variables">{{ .strings.variables }}</span>
|
||||
<div id="editor-variables"></div>
|
||||
<div id="editor-variables" class="mt-4"></div>
|
||||
<span class="label supra" for="editor-conditionals" id="label-editor-conditionals">{{ .strings.conditionals }}</span>
|
||||
<div id="editor-conditionals"></div>
|
||||
<label class="label supra" for="textarea-editor">{{ .strings.message }}</label>
|
||||
<textarea id="textarea-editor" class="textarea full-width flex-auto ~neutral !normal mt-half monospace"></textarea>
|
||||
<p class="support mt-half mb-1">{{ .strings.markdownSupported }}</p>
|
||||
<textarea id="textarea-editor" class="textarea full-width flex-auto ~neutral @low mt-4 font-mono"></textarea>
|
||||
<p class="support mt-4 mb-2">{{ .strings.markdownSupported }}</p>
|
||||
<div class="flex-row">
|
||||
<label class="full-width ml-half">
|
||||
<label class="full-width ml-2">
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge !normal full-width center supra submit">{{ .strings.submit }}</span>
|
||||
<span class="button ~urge @low full-width center supra submit">{{ .strings.submit }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col card ~neutral !low">
|
||||
<div class="col card ~neutral @low">
|
||||
<span class="subheading supra">{{ .strings.preview }}</span>
|
||||
<div class="mt-half" id="editor-preview"></div>
|
||||
<div class="mt-8" id="editor-preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="modal-restart" class="modal">
|
||||
<div class="modal-content card ~critical !low">
|
||||
<div class="modal-content card ~critical @low">
|
||||
<span class="heading">{{ .strings.settingsRestartRequired }} <span class="modal-close">×</span></span>
|
||||
<p class="content pb-1">{{ .strings.settingsRestartRequiredDescription }}</p>
|
||||
<div class="fr">
|
||||
<span class="button ~info !normal mb-half" id="settings-apply-no-restart">{{ .strings.settingsApplyRestartLater }}</span>
|
||||
<span class="button ~critical !normal" id="settings-apply-restart">{{ .strings.settingsApplyRestartNow }}</span>
|
||||
<p class="content my-4">{{ .strings.settingsRestartRequiredDescription }}</p>
|
||||
<div class="float-right">
|
||||
<span class="button ~info @low mb-2" id="settings-apply-no-restart">{{ .strings.settingsApplyRestartLater }}</span>
|
||||
<span class="button ~critical @low" id="settings-apply-restart">{{ .strings.settingsApplyRestartNow }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal-refresh" class="modal">
|
||||
<div class="modal-content card ~neutral !normal">
|
||||
<div class="modal-content card ~neutral @low">
|
||||
<span class="heading">{{ .strings.settingsApplied }}</span>
|
||||
<p class="content">{{ .strings.settingsRefreshPage }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal-ombi-defaults" class="modal">
|
||||
<div id="modal-send-pwr" class="modal">
|
||||
<div class="modal-content card ~neutral @low">
|
||||
<span class="heading">{{ .strings.sendPWR }}</span>
|
||||
<p class="content my-2" id="send-pwr-note"></p>
|
||||
<span class="button ~urge @low mt-2" id="send-pwr-link">{{ .strings.copy }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal-ombi-profile" class="modal">
|
||||
<form class="modal-content card" id="form-ombi-defaults" href="">
|
||||
<span class="heading">{{ .strings.ombiUserDefaults }} <span class="modal-close">×</span></span>
|
||||
<p class="content">{{ .strings.ombiUserDefaultsDescription }}</p>
|
||||
<div class="select ~neutral !normal mb-1">
|
||||
<span class="heading">{{ .strings.ombiProfile }} <span class="modal-close">×</span></span>
|
||||
<p class="content my-4">{{ .strings.ombiUserDefaultsDescription }}</p>
|
||||
<div class="select ~neutral @low mb-4">
|
||||
<select></select>
|
||||
</div>
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge !normal full-width center supra submit">{{ .strings.submit }}</span>
|
||||
<span class="button ~urge @low full-width center supra submit">{{ .strings.submit }}</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
<div id="modal-user-profiles" class="modal">
|
||||
<div class="modal-content wide card">
|
||||
<span class="heading">{{ .strings.userProfiles }} <span class="modal-close">×</span></span>
|
||||
<p class="support lg">{{ .strings.userProfilesDescription }}</p>
|
||||
<p class="content my-4">{{ .strings.userProfilesDescription }}</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ .strings.name }}</th>
|
||||
<th>{{ .strings.userProfilesIsDefault }}</th>
|
||||
{{ if .ombiEnabled }}
|
||||
<th>Ombi</th>
|
||||
{{ end }}
|
||||
<th>{{ .strings.from }}</th>
|
||||
<th>{{ .strings.userProfilesLibraries }}</th>
|
||||
<th><span class="button ~neutral !high" id="button-profile-create">{{ .strings.create }}</span></th>
|
||||
<th><span class="button ~neutral @high" id="button-profile-create">{{ .strings.create }}</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="table-profiles"></tbody>
|
||||
@@ -273,23 +310,23 @@
|
||||
<div id="modal-add-profile" class="modal">
|
||||
<form class="modal-content card" id="form-add-profile" href="">
|
||||
<span class="heading">{{ .strings.addProfile }} <span class="modal-close">×</span></span>
|
||||
<p class="content">{{ .strings.addProfileDescription }}</p>
|
||||
<p class="content my-4">{{ .strings.addProfileDescription }}</p>
|
||||
<label>
|
||||
<span class="supra">{{ .strings.addProfileNameOf }} </span>
|
||||
<input type="text" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.name }}" id="add-profile-name">
|
||||
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.name }}" id="add-profile-name">
|
||||
<label>
|
||||
<span class="supra">{{ .strings.user }}</span>
|
||||
<div class="select ~neutral !normal mt-half mb-1">
|
||||
<div class="select ~neutral @low mt-4 mb-2">
|
||||
<select id="add-profile-user"></select>
|
||||
</div>
|
||||
</label>
|
||||
<label class="switch mb-1">
|
||||
<label class="switch mb-4">
|
||||
<input type="checkbox" id="add-profile-homescreen" checked>
|
||||
<span>{{ .strings.addProfileStoreHomescreenLayout }}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge !normal full-width center supra submit">{{ .strings.create }}</span>
|
||||
<span class="button ~urge @low full-width center supra submit">{{ .strings.create }}</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
@@ -298,76 +335,121 @@
|
||||
<span class="heading">{{ .strings.updates }} <span class="modal-close">×</span></span>
|
||||
<p class="content">
|
||||
<h2>
|
||||
<a id="update-version"></a> (<span class="monospace" id="update-commit"></span>)
|
||||
<a id="update-version"></a> (<span class="font-mono bg-inherit" id="update-commit"></span>)
|
||||
</h2>
|
||||
<p class="content" id="update-description"></p>
|
||||
<p class="support" id="update-date"></p>
|
||||
<div class="content markdown-box" id="update-changelog"></div>
|
||||
</p>
|
||||
<span class="button ~info !normal full-width center" id="update-download">{{ .strings.download }}</span>
|
||||
<span class="button ~urge !normal full-width center" id="update-update">{{ .strings.update }}</span>
|
||||
<span class="button ~info @low full-width center" id="update-download">{{ .strings.download }}</span>
|
||||
<span class="button ~urge @low full-width center" id="update-update">{{ .strings.update }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="notification-box"></div>
|
||||
<span class="dropdown" tabindex="0" id="lang-dropdown">
|
||||
<span class="button ~urge dropdown-button">
|
||||
<i class="ri-global-line"></i>
|
||||
<span class="ml-1 chev"></span>
|
||||
</span>
|
||||
<div class="dropdown-display">
|
||||
<div class="card ~neutral !low">
|
||||
<label class="switch pb-1">
|
||||
<input type="radio" name="lang-time" id="lang-12h">
|
||||
<span>{{ .strings.time12h }}</span>
|
||||
</label>
|
||||
<label class="switch pb-1">
|
||||
<input type="radio" name="lang-time" id="lang-24h">
|
||||
<span>{{ .strings.time24h }}</span>
|
||||
</label>
|
||||
<div id="lang-list"></div>
|
||||
</div>
|
||||
{{ if .telegramEnabled }}
|
||||
<div id="modal-telegram" class="modal">
|
||||
<div class="modal-content card">
|
||||
<span class="heading mb-4">{{ .strings.linkTelegram }}</span>
|
||||
<p class="content mb-4">{{ .strings.sendPIN }}</p>
|
||||
<h1 class="ac" id="telegram-pin"></h1>
|
||||
<a class="subheading link-center" id="telegram-link" target="_blank">
|
||||
<span class="shield ~info mr-2">
|
||||
<span class="icon">
|
||||
<i class="ri-telegram-line"></i>
|
||||
</span>
|
||||
</span>
|
||||
@<span id="telegram-username">
|
||||
</a>
|
||||
<span class="button ~info @low full-width center mt-4" id="telegram-waiting">{{ .strings.success }}</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if .discordEnabled }}
|
||||
<div id="modal-discord" class="modal">
|
||||
<div class="modal-content card">
|
||||
<span class="heading mb-4"><span id="discord-header"></span><span class="modal-close">×</span></span>
|
||||
<p class="content mb-4" id="discord-description"></p>
|
||||
<div class="row">
|
||||
<input type="search" class="col sm field ~neutral @low input" id="discord-search" placeholder="user#1234">
|
||||
</div>
|
||||
<table class="table"><tbody id="discord-list"></tbody></table>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
<div id="modal-matrix" class="modal">
|
||||
<form class="modal-content card" id="form-matrix" href="">
|
||||
<span class="heading">{{ .strings.linkMatrix }}</span>
|
||||
<p class="content my-4">{{ .strings.linkMatrixDescription }}</p>
|
||||
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.matrixHomeServer }}" id="matrix-homeserver">
|
||||
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.username }}" id="matrix-user">
|
||||
<input type="password" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.password }}" id="matrix-password">
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge @low full-width center supra submit">{{ .strings.submit }}</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
<div id="notification-box"></div>
|
||||
<div class="top-4 left-4 absolute">
|
||||
<span class="dropdown" tabindex="0" id="lang-dropdown">
|
||||
<span class="button ~urge dropdown-button">
|
||||
<i class="ri-global-line"></i>
|
||||
<span class="ml-2 chev"></span>
|
||||
</span>
|
||||
<div class="dropdown-display">
|
||||
<div class="card ~neutral @low">
|
||||
<label class="switch pb-4">
|
||||
<input type="radio" name="lang-time" id="lang-12h">
|
||||
<span>{{ .strings.time12h }}</span>
|
||||
</label>
|
||||
<label class="switch pb-4">
|
||||
<input type="radio" name="lang-time" id="lang-24h">
|
||||
<span>{{ .strings.time24h }}</span>
|
||||
</label>
|
||||
<div id="lang-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<span class="button ~warning" alt="{{ .strings.theme }}" id="button-theme"><i class="ri-sun-line"></i></span>
|
||||
</div>
|
||||
<div class="page-container">
|
||||
<div class="mb-1">
|
||||
<div class="mb-4">
|
||||
<header class="flex flex-wrap items-center justify-between">
|
||||
<div class="text-neutral-700">
|
||||
<span id="button-tab-invites" class="tab-button portal">{{ .strings.invites }}</span>
|
||||
<span id="button-tab-accounts" class="tab-button portal">{{ .strings.accounts }}</span>
|
||||
<span id="button-tab-settings" class="tab-button portal">{{ .strings.settings }}</span>
|
||||
<div>
|
||||
<span id="button-tab-invites" class="text-3xl button portal ~neutral dark:~d_neutral @low mr-2 px-5">{{ .strings.invites }}</span>
|
||||
<span id="button-tab-accounts" class="text-3xl button portal ~neutral dark:~d_neutral @low mr-2 px-5">{{ .strings.accounts }}</span>
|
||||
<span id="button-tab-settings" class="text-3xl button portal ~neutral dark:~d_neutral @low mr-2 px-5">{{ .strings.settings }}</span>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<div class="text-neutral-700">
|
||||
<span class="button ~critical !normal mb-1 unfocused" id="logout-button">{{ .strings.logout }}</span>
|
||||
<span id="button-theme" class="button ~neutral !normal mb-1">{{ .strings.theme }}</span>
|
||||
<div class="mb-4">
|
||||
<div>
|
||||
<span class="button ~critical @low mb-4 unfocused" id="logout-button">{{ .strings.logout }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tab-invites">
|
||||
<div class="card ~neutral !low invites mb-1">
|
||||
<div class="card @low invites dark:~d_neutral mb-4">
|
||||
<span class="heading">{{ .strings.invites }}</span>
|
||||
<div id="invites"></div>
|
||||
</div>
|
||||
<div class="card ~neutral !low">
|
||||
<div class="card @low dark:~d_neutral">
|
||||
<span class="heading">{{ .strings.create }}</span>
|
||||
<div class="row" id="create-inv">
|
||||
<div class="card ~neutral !normal col">
|
||||
<div class="row mb-1">
|
||||
<label class="col mr-1">
|
||||
<div class="card ~neutral @low col">
|
||||
<div class="row mb-2">
|
||||
<label class="col mr-2">
|
||||
<input type="radio" name="duration" class="unfocused" id="radio-inv-duration" checked>
|
||||
<span class="button ~neutral !high supra full-width center">{{ .strings.inviteDuration }}</span>
|
||||
<span class="button ~neutral @high supra full-width center">{{ .strings.inviteDuration }}</span>
|
||||
</label>
|
||||
<label class="col ml-1">
|
||||
<label class="col ml-2">
|
||||
<input type="radio" name="duration" class="unfocused" id="radio-user-expiry">
|
||||
<span class="button ~neutral !normal supra full-width center">{{ .strings.userExpiry }}</span>
|
||||
<span class="button ~neutral @low supra full-width center">{{ .strings.userExpiry }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="inv-duration">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label supra" for="create-months">{{ .strings.inviteMonths }}</label>
|
||||
<div class="select ~neutral !normal mb-1 mt-half">
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="create-months">
|
||||
<option>0</option>
|
||||
</select>
|
||||
@@ -375,7 +457,7 @@
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label supra" for="create-days">{{ .strings.inviteDays }}</label>
|
||||
<div class="select ~neutral !normal mb-1 mt-half">
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="create-days">
|
||||
<option>0</option>
|
||||
</select>
|
||||
@@ -385,7 +467,7 @@
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label supra" for="create-hours">{{ .strings.inviteHours }}</label>
|
||||
<div class="select ~neutral !normal mb-1 mt-half">
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="create-hours">
|
||||
<option>0</option>
|
||||
</select>
|
||||
@@ -393,7 +475,7 @@
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label supra" for="create-minutes">{{ .strings.inviteMinutes }}</label>
|
||||
<div class="select ~neutral !normal mb-1 mt-half">
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="create-minutes">
|
||||
<option>0</option>
|
||||
</select>
|
||||
@@ -402,17 +484,17 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="user-expiry" class="unfocused">
|
||||
<p class="support">{{ .strings.userExpiryDescription }}</p>
|
||||
<div class="mb-half">
|
||||
<label for="create-user-expiry-enabled" class="button ~neutral !normal">
|
||||
<p class="support mb-2">{{ .strings.userExpiryDescription }}</p>
|
||||
<div class="mb-2">
|
||||
<label for="create-user-expiry-enabled" class="button ~neutral @low">
|
||||
<input type="checkbox" id="create-user-expiry-enabled" aria-label="User duration enabled">
|
||||
<span class="ml-half">{{ .strings.enabled }} </span>
|
||||
<span class="ml-2">{{ .strings.enabled }} </span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label supra" for="user-months">{{ .strings.inviteMonths }}</label>
|
||||
<div class="select ~neutral !normal mb-1 mt-half">
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="user-months">
|
||||
<option>0</option>
|
||||
</select>
|
||||
@@ -420,7 +502,7 @@
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label supra" for="user-days">{{ .strings.inviteDays }}</label>
|
||||
<div class="select ~neutral !normal mb-1 mt-half">
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="user-days">
|
||||
<option>0</option>
|
||||
</select>
|
||||
@@ -430,7 +512,7 @@
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label supra" for="user-hours">{{ .strings.inviteHours }}</label>
|
||||
<div class="select ~neutral !normal mb-1 mt-half">
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="user-hours">
|
||||
<option>0</option>
|
||||
</select>
|
||||
@@ -438,7 +520,7 @@
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label supra" for="user-minutes">{{ .strings.inviteMinutes }}</label>
|
||||
<div class="select ~neutral !normal mb-1 mt-half">
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="user-minutes">
|
||||
<option>0</option>
|
||||
</select>
|
||||
@@ -448,61 +530,93 @@
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label supra" for="create-label"> {{ .strings.label }}</label>
|
||||
<input type="text" id="create-label" class="input ~neutral !normal mb-1 mt-half">
|
||||
<input type="text" id="create-label" class="input ~neutral @low mb-2 mt-4">
|
||||
</div>
|
||||
</div>
|
||||
<div class="card ~neutral !normal col">
|
||||
<div class="card ~neutral @low col">
|
||||
<label class="label supra" for="create-uses">{{ .strings.inviteNumberOfUses }}</label>
|
||||
<div class="flex-expand mb-1 mt-half">
|
||||
<input type="number" min="0" id="create-uses" class="input ~neutral !normal mr-1" value=1>
|
||||
<label for="create-inf-uses" class="button ~neutral !normal" title="Set uses to infinite">
|
||||
<span>∞</span>
|
||||
<div class="flex-expand mb-2 mt-4">
|
||||
<input type="number" min="0" id="create-uses" class="input ~neutral @low mr-2" value=1>
|
||||
<label for="create-inf-uses" class="button ~neutral @low" title="Set uses to infinite">
|
||||
<span>∞</span>
|
||||
<input type="checkbox" class="unfocused" id="create-inf-uses" aria-label="Set uses to infinite">
|
||||
</label>
|
||||
</div>
|
||||
<p class="support unfocused" id="create-inf-uses-warning"><span class="badge ~critical">{{ .strings.warning }}</span> {{ .strings.inviteInfiniteUsesWarning }}</p>
|
||||
<p class="support unfocused my-2" id="create-inf-uses-warning"><span class="badge ~critical">{{ .strings.warning }}</span> {{ .strings.inviteInfiniteUsesWarning }}</p>
|
||||
<label class="label supra">{{ .strings.profile }}</label>
|
||||
<div class="select ~neutral !normal mb-1 mt-half">
|
||||
<div class="select ~neutral @low mb-2 mt-4">
|
||||
<select id="create-profile">
|
||||
</select>
|
||||
</div>
|
||||
<div id="create-send-to-container">
|
||||
<label class="label supra">{{ .strings.inviteSendToEmail }}</label>
|
||||
<div class="flex-expand mb-1 mt-half">
|
||||
<input type="email" id="create-send-to" class="input ~neutral !normal mr-1" placeholder="example@example.com">
|
||||
<label for="create-send-to-enabled" class="button ~neutral !normal">
|
||||
<div class="flex-expand mb-2 mt-4">
|
||||
{{ if .discordEnabled }}
|
||||
<input type="text" id="create-send-to" class="input ~neutral @low mr-2" placeholder="example@example.com | user#1234">
|
||||
<span id="create-send-to-search" class="button ~neutral @low mr-2">
|
||||
<i class="icon ri-search-2-line" title="{{ .strings.search }}"></i>
|
||||
</span>
|
||||
{{ else }}
|
||||
<input type="email" id="create-send-to" class="input ~neutral @low mr-2" placeholder="example@example.com">
|
||||
{{ end }}
|
||||
<label for="create-send-to-enabled" class="button ~neutral @low">
|
||||
<input type="checkbox" id="create-send-to-enabled" aria-label="Send to address enabled">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<span class="button ~urge !normal supra full-width center lg" id="create-submit">{{ .strings.create }}</span>
|
||||
<span class="button ~urge @low supra full-width center lg" id="create-submit">{{ .strings.create }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tab-accounts" class="unfocused">
|
||||
<div class="card ~neutral !low accounts mb-1">
|
||||
<div class="card @low dark:~d_neutral accounts mb-4">
|
||||
<div class="flex-expand row">
|
||||
<div class="row">
|
||||
<span class="heading mr-1 col sm">{{ .strings.accounts }}</span>
|
||||
<input type="search" class="col sm field ~neutral !normal input search ml-1 mr-1" id="accounts-search" placeholder="{{ .strings.search }}">
|
||||
<span class="text-3xl font-bold mr-2 col">{{ .strings.accounts }}</span>
|
||||
<input type="search" class="col sm field ~neutral @low input search ml-2 mr-2" id="accounts-search" placeholder="{{ .strings.search }}">
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="col sm button ~neutral !normal center mb-half" id="accounts-add-user">{{ .quantityStrings.addUser.Singular }}</span>
|
||||
<span class="col sm button ~info !normal center mb-half" id="accounts-announce">{{ .strings.announce }}</span>
|
||||
<span class="col sm button ~urge !normal center mb-half" id="accounts-modify-user">{{ .strings.modifySettings }}</span>
|
||||
<span class="col sm button ~warning !normal center mb-half" id="accounts-extend-expiry">{{ .strings.extendExpiry }}</span>
|
||||
<span class="col sm button ~positive !normal center mb-half" id="accounts-disable-enable">{{ .strings.disable }}</span>
|
||||
<span class="col sm button ~critical !normal center mb-half" id="accounts-delete-user">{{ .quantityStrings.deleteUser.Singular }}</span>
|
||||
<span class="col sm button ~neutral @low center mb-2" id="accounts-add-user">{{ .quantityStrings.addUser.Singular }}</span>
|
||||
<div id="accounts-announce-dropdown" class="col sm dropdown pb-0i" tabindex="0">
|
||||
<span class="h-100 sm button ~info @low center mb-2" id="accounts-announce">{{ .strings.announce }}</span>
|
||||
<div class="dropdown-display">
|
||||
<div class="card ~neutral @low">
|
||||
<span class="supra sm">{{ .strings.templates }}</span>
|
||||
<div id="accounts-announce-templates"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="col sm button ~urge @low center mb-2" id="accounts-modify-user">{{ .strings.modifySettings }}</span>
|
||||
<span class="col sm button ~warning @low center mb-2" id="accounts-extend-expiry">{{ .strings.extendExpiry }}</span>
|
||||
<div id="accounts-disable-enable-dropdown" class="col sm dropdown manual pb-0i" tabindex="0">
|
||||
<span class="h-100 sm button ~positive @low center mb-2" id="accounts-disable-enable">{{ .strings.disable }}</span>
|
||||
<div class="dropdown-display">
|
||||
<div class="card ~neutral @low">
|
||||
<span class="button ~urge sm full-width accounts-announce-template-button" id="accounts-enable-expiry">{{ .strings.setExpiry }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="col sm button ~info @low center mb-2 unfocused" id="accounts-send-pwr">{{ .strings.sendPWR }}</span>
|
||||
<span class="col sm button ~critical @low center mb-2" id="accounts-delete-user">{{ .quantityStrings.deleteUser.Singular }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card ~neutral !normal accounts-header table-responsive mt-half">
|
||||
<table class="table">
|
||||
<div class="card @low accounts-header table-responsive mt-8">
|
||||
<table class="table text-base leading-4">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" value="" id="accounts-select-all"></th>
|
||||
<th>{{ .strings.username }}</th>
|
||||
<th class="table-inline my-2">{{ .strings.username }}</th>
|
||||
<th>{{ .strings.emailAddress }}</th>
|
||||
{{ if .telegramEnabled }}
|
||||
<th class="text-center-i">Telegram</th>
|
||||
{{ end }}
|
||||
{{ if .matrixEnabled }}
|
||||
<th class="text-center-i">Matrix</th>
|
||||
{{ end }}
|
||||
{{ if .discordEnabled }}
|
||||
<th class="text-center-i">Discord</th>
|
||||
{{ end }}
|
||||
<th>{{ .strings.expiry }}</th>
|
||||
<th>{{ .strings.lastActiveTime }}</th>
|
||||
</tr>
|
||||
@@ -513,27 +627,27 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="tab-settings" class="unfocused">
|
||||
<div class="card ~neutral !low settings overflow">
|
||||
<div class="card @low dark:~d_neutral settings overflow">
|
||||
<div class="flex-expand">
|
||||
<div class="flex-row">
|
||||
<span class="heading">{{ .strings.settings }}</span>
|
||||
<label for="settings-advanced-enabled" class="button ~neutral !normal ml-1">
|
||||
<label for="settings-advanced-enabled" class="button ~neutral @low ml-2">
|
||||
<input type="checkbox" id="settings-advanced-enabled" aria-label="Advanced settings enabled">
|
||||
<span class="ml-half">{{ .strings.advancedSettings }} </span>
|
||||
<span class="ml-2">{{ .strings.advancedSettings }} </span>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<span class="button ~neutral !normal" id="settings-restart">{{ .strings.settingsRestart }}</span>
|
||||
<span class="button ~urge !normal unfocused" id="settings-save">{{ .strings.settingsSave }}</span>
|
||||
<span class="button ~neutral @low" id="settings-restart">{{ .strings.settingsRestart }}</span>
|
||||
<span class="button ~urge @low unfocused" id="settings-save">{{ .strings.settingsSave }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="card ~neutral !normal col" id="settings-sidebar">
|
||||
<aside class="aside sm ~info mb-half" id="settings-message">Note: <span class="badge ~critical">*</span> indicates a required field, <span class="badge ~info">R</span> indicates changes require a restart.</aside>
|
||||
<span class="button ~neutral !low settings-section-button mb-half" id="setting-about"><span class="flex">{{ .strings.aboutProgram }} <i class="ri-information-line ml-half"></i></span></span>
|
||||
<span class="button ~neutral !low settings-section-button mb-half" id="setting-profiles"><span class="flex">{{ .strings.userProfiles }} <i class="ri-user-line ml-half"></i></span></span>
|
||||
<div class="card @low dark:~d_neutral col" id="settings-sidebar">
|
||||
<aside class="aside sm ~urge dark:~d_info mb-2 @low" id="settings-message">Note: <span class="badge ~critical">*</span> indicates a required field, <span class="badge ~info dark:~d_warning">R</span> indicates changes require a restart.</aside>
|
||||
<span class="button ~neutral @low settings-section-button justify-between mb-2" id="setting-about"><span class="flex">{{ .strings.aboutProgram }} <i class="ri-information-line ml-2"></i></span></span>
|
||||
<span class="button ~neutral @low settings-section-button justify-between mb-2" id="setting-profiles"><span class="flex">{{ .strings.userProfiles }} <i class="ri-user-line ml-2"></i></span></span>
|
||||
</div>
|
||||
<div class="card ~neutral !normal col overflow" id="settings-panel"></div>
|
||||
<div class="card ~neutral @low col overflow" id="settings-panel"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
45
html/crash.html
Normal file
@@ -0,0 +1,45 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link inline rel="stylesheet" type="text/css" href="bundle.css">
|
||||
{{ template "header.html" . }}
|
||||
<title>Crash report</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-container">
|
||||
<div class="card ~critical sectioned">
|
||||
<section class="section ~critical">
|
||||
<span class="heading">Crash report for jfa-go</span>
|
||||
{{ if .Err }}
|
||||
<div class="font-mono bg-inherit pre-line mt-4 mb-4">
|
||||
Error: {{ .Err }}
|
||||
</div>
|
||||
{{ end }}
|
||||
<a class="button ~critical mb-4" target="_blank" href="https://github.com/hrfee/jfa-go/issues/new/choose">Create an Issue</a>
|
||||
</section>
|
||||
<section class="section ~neutral @low">
|
||||
<div class="flex-expand">
|
||||
<span class="subheading">Full Log</span>
|
||||
<span class="button ~urge ml-4" id="copy-log">Copy</span>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<label class="col mr-4">
|
||||
<span class="button ~neutral @high supra full-width center" id="button-log-normal">Normal</span>
|
||||
</label>
|
||||
<label class="col mr-4">
|
||||
<span class="button ~neutral @low supra full-width center" id="button-log-sanitized">Sanitized</span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="log-normal">
|
||||
<pre class="font-mono bg-inherit pre-line">{{ .Log }}</pre>
|
||||
</div>
|
||||
<div id="log-sanitized" class="unfocused">
|
||||
<p class="subheading">An attempt has been made to remove sensitive info, but make sure to check yourself.</p>
|
||||
<pre class="font-mono bg-inherit pre-line">{{ .SanitizedLog }}</pre>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<script inline src="crash.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,14 +3,14 @@
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="css/bundle.css">
|
||||
{{ template "header.html" . }}
|
||||
<title>{{ .strings.successHeader }} - jfa-go</title>
|
||||
<title>{{ .strings.successHeader }} - jfa-go</title>
|
||||
</head>
|
||||
<body class="section">
|
||||
<div class="page-container">
|
||||
<div class="card ~neutral !normal mb-1">
|
||||
<span class="heading mb-1">{{ .strings.successHeader }}</span>
|
||||
<p class="content mb-1">{{ .successMessage }}</p>
|
||||
<a class="button ~urge !normal full-width center supra submit" href="{{ .jfLink }}" id="create-success-button">{{ .strings.successContinueButton }}</a>
|
||||
<div class="card ~neutral @low mb-4">
|
||||
<span class="heading mb-4">{{ .strings.successHeader }}</span>
|
||||
<p class="content my-4">{{ .successMessage }}</p>
|
||||
<a class="button ~urge @high full-width center supra submit" href="{{ .jfLink }}" id="create-success-button">{{ .strings.continue }}</a>
|
||||
</div>
|
||||
<i class="content">{{ .contactMessage }}</i>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,22 @@
|
||||
window.userExpiryHours = {{ .userExpiryHours }};
|
||||
window.userExpiryMinutes = {{ .userExpiryMinutes }};
|
||||
window.userExpiryMessage = {{ .userExpiryMessage }};
|
||||
window.telegramEnabled = {{ .telegramEnabled }};
|
||||
window.telegramRequired = {{ .telegramRequired }};
|
||||
window.telegramPIN = "{{ .telegramPIN }}";
|
||||
window.emailRequired = {{ .emailRequired }};
|
||||
window.discordEnabled = {{ .discordEnabled }};
|
||||
window.discordRequired = {{ .discordRequired }};
|
||||
window.discordPIN = "{{ .discordPIN }}";
|
||||
window.discordInviteLink = {{ .discordInviteLink }};
|
||||
window.discordServerName = "{{ .discordServerName }}";
|
||||
window.matrixEnabled = {{ .matrixEnabled }};
|
||||
window.matrixRequired = {{ .matrixRequired }};
|
||||
window.matrixUserID = "{{ .matrixUser }}";
|
||||
</script>
|
||||
{{ if .passwordReset }}
|
||||
<script src="js/pwr.js" type="module"></script>
|
||||
{{ else }}
|
||||
<script src="js/form.js" type="module"></script>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
161
html/form.html
@@ -3,65 +3,171 @@
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="css/bundle.css">
|
||||
{{ template "header.html" . }}
|
||||
<title>{{ .strings.pageTitle }}</title>
|
||||
<title>
|
||||
{{ if .passwordReset }}
|
||||
{{ .strings.passwordReset }}
|
||||
{{ else }}
|
||||
{{ .strings.pageTitle }}
|
||||
{{ end }}
|
||||
</title>
|
||||
</head>
|
||||
<body class="max-w-full overflow-x-hidden section">
|
||||
<div id="modal-success" class="modal">
|
||||
<div class="modal-content card">
|
||||
<span class="heading mb-1">{{ .strings.successHeader }}</span>
|
||||
<p class="content mb-1">{{ .successMessage }}</p>
|
||||
<a class="button ~urge !normal full-width center supra submit" href="{{ .jfLink }}" id="create-success-button">{{ .strings.successContinueButton }}</a>
|
||||
<span class="heading mb-4">{{ if .passwordReset }}{{ .strings.passwordReset }}{{ else }}{{ .strings.successHeader }}{{ end }}</span>
|
||||
<p class="content mb-4">{{ if .passwordReset }}{{ .strings.youCanLoginPassword }}{{ else }}{{ .successMessage }}{{ end }}</p>
|
||||
<a class="button ~urge @low full-width center supra submit" href="{{ .jfLink }}" id="create-success-button">{{ .strings.continue }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal-confirmation" class="modal">
|
||||
<div class="modal-content card">
|
||||
<span class="heading mb-1">{{ .strings.confirmationRequired }}</span>
|
||||
<p class="content mb-1">{{ .strings.confirmationRequiredMessage }}</p>
|
||||
<span class="heading mb-4">{{ .strings.confirmationRequired }}</span>
|
||||
<p class="content mb-4">{{ .strings.confirmationRequiredMessage }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="dropdown" tabindex="0" id="lang-dropdown">
|
||||
<span class="button ~urge dropdown-button">
|
||||
<i class="ri-global-line"></i>
|
||||
<span class="ml-1 chev"></span>
|
||||
</span>
|
||||
<div class="dropdown-display">
|
||||
<div class="card ~neutral !low" id="lang-list">
|
||||
</div>
|
||||
{{ if .telegramEnabled }}
|
||||
<div id="modal-telegram" class="modal">
|
||||
<div class="modal-content card">
|
||||
<span class="heading mb-4">{{ .strings.linkTelegram }}</span>
|
||||
<p class="content mb-4">{{ .strings.sendPIN }}</p>
|
||||
<p class="text-center text-2xl mb-2">{{ .telegramPIN }}</p>
|
||||
<a class="subheading link-center" href="{{ .telegramURL }}" target="_blank">
|
||||
<span class="shield ~info mr-4">
|
||||
<span class="icon">
|
||||
<i class="ri-telegram-line"></i>
|
||||
</span>
|
||||
</span>
|
||||
@{{ .telegramUsername }}
|
||||
</a>
|
||||
<span class="button ~info @low full-width center mt-4" id="telegram-waiting">{{ .strings.success }}</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if .discordEnabled }}
|
||||
<div id="modal-discord" class="modal">
|
||||
<div class="modal-content card">
|
||||
<span class="heading mb-4">{{ .strings.linkDiscord }}</span>
|
||||
<p class="content mb-4"> {{ .discordSendPINMessage }}</p>
|
||||
<h1 class="text-center text-2xl mb-2">{{ .discordPIN }}</h1>
|
||||
<a id="discord-invite"></a>
|
||||
<span class="button ~info @low full-width center mt-4" id="discord-waiting">{{ .strings.success }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if .matrixEnabled }}
|
||||
<div id="modal-matrix" class="modal">
|
||||
<div class="modal-content card">
|
||||
<span class="heading mb-4">{{ .strings.linkMatrix }}</span>
|
||||
<p class="content mb-4"> {{ .strings.matrixEnterUser }}</p>
|
||||
<input type="text" class="input ~neutral @high" placeholder="@user:riot.im" id="matrix-userid">
|
||||
<div class="subheading link-center mt-4">
|
||||
<span class="shield ~info mr-4">
|
||||
<span class="icon">
|
||||
<i class="ri-chat-3-line"></i>
|
||||
</span>
|
||||
</span>
|
||||
{{ .matrixUser }}
|
||||
</div>
|
||||
<span class="button ~info @low full-width center mt-4" id="matrix-send">{{ .strings.submit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="top-4 left-4 absolute">
|
||||
<span class="dropdown" tabindex="0" id="lang-dropdown">
|
||||
<span class="button ~urge dropdown-button">
|
||||
<i class="ri-global-line"></i>
|
||||
<span class="ml-2 chev"></span>
|
||||
</span>
|
||||
<div class="dropdown-display">
|
||||
<div class="card ~neutral @low" id="lang-list">
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div id="notification-box"></div>
|
||||
<div class="page-container">
|
||||
<div class="card ~neutral !low">
|
||||
<div class="card dark:~d_neutral @low">
|
||||
<div class="row baseline">
|
||||
<span class="col heading">{{ .strings.createAccountHeader }}</span>
|
||||
<span class="col subheading"> {{ .helpMessage }}</span>
|
||||
<span class="col heading">
|
||||
{{ if .passwordReset }}
|
||||
{{ .strings.passwordReset }}
|
||||
{{ else }}
|
||||
{{ .strings.createAccountHeader }}
|
||||
{{ end }}
|
||||
</span>
|
||||
<span class="col subheading">
|
||||
{{ if .passwordReset }}
|
||||
{{ .strings.enterYourPassword }}
|
||||
{{ else }}
|
||||
{{ .helpMessage }}
|
||||
{{ end }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{{ if .userExpiry }}
|
||||
<aside class="col aside sm ~warning" id="user-expiry-message"></aside>
|
||||
{{ end }}
|
||||
<form class="card ~neutral !normal" id="form-create" href="">
|
||||
<form class="card dark:~d_neutral @low" id="form-create" href="">
|
||||
{{ if not .passwordReset }}
|
||||
<label class="label supra">
|
||||
{{ .strings.username }}
|
||||
<input type="text" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.username }}" id="create-username" aria-label="{{ .strings.username }}">
|
||||
<input type="text" class="input ~neutral @high mt-2 mb-4" placeholder="{{ .strings.username }}" id="create-username" aria-label="{{ .strings.username }}">
|
||||
</label>
|
||||
|
||||
<label class="label supra" for="create-email">{{ .strings.emailAddress }}</label>
|
||||
<input type="email" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.emailAddress }}" id="create-email" aria-label="{{ .strings.emailAddress }}" value="{{ .email }}">
|
||||
|
||||
<input type="email" class="input ~neutral @high mt-2 mb-4" placeholder="{{ .strings.emailAddress }}" id="create-email" aria-label="{{ .strings.emailAddress }}" value="{{ .email }}">
|
||||
{{ if .telegramEnabled }}
|
||||
<span class="button ~info @low full-width center mb-4" id="link-telegram">{{ .strings.linkTelegram }}</span>
|
||||
{{ end }}
|
||||
{{ if .discordEnabled }}
|
||||
<span class="button ~info @low full-width center mb-4" id="link-discord">{{ .strings.linkDiscord }}</span>
|
||||
{{ end }}
|
||||
{{ if .matrixEnabled }}
|
||||
<span class="button ~info @low full-width center mb-4" id="link-matrix">{{ .strings.linkMatrix }}</span>
|
||||
{{ end }}
|
||||
{{ if or (.telegramEnabled) (or .discordEnabled .matrixEnabled) }}
|
||||
<div id="contact-via" class="unfocused">
|
||||
<label class="row switch pb-4">
|
||||
<input type="radio" name="contact-via" value="email"><span>Contact through Email</span>
|
||||
</label>
|
||||
{{ if .telegramEnabled }}
|
||||
<label class="row switch pb-4">
|
||||
<input type="radio" name="contact-via" value="telegram" id="contact-via-telegram"><span>Contact through Telegram</span>
|
||||
</label>
|
||||
{{ end }}
|
||||
{{ if .discordEnabled }}
|
||||
<label class="row switch pb-4">
|
||||
<input type="radio" name="contact-via" value="discord" id="contact-via-discord"><span>Contact through Discord</span>
|
||||
</label>
|
||||
{{ end }}
|
||||
{{ if .matrixEnabled }}
|
||||
<label class="row switch pb-4">
|
||||
<input type="radio" name="contact-via" value="matrix" id="contact-via-matrix"><span>Contact through Matrix</span>
|
||||
</label>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
<label class="label supra" for="create-password">{{ .strings.password }}</label>
|
||||
<input type="password" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.password }}" id="create-password" aria-label="{{ .strings.password }}">
|
||||
<input type="password" class="input ~neutral @high mt-2 mb-4" placeholder="{{ .strings.password }}" id="create-password" aria-label="{{ .strings.password }}">
|
||||
|
||||
<label class="label supra" for="create-reenter-password">{{ .strings.reEnterPassword }}</label>
|
||||
<input type="password" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.password }}" id="create-reenter-password" aria-label="{{ .strings.reEnterPassword }}">
|
||||
<input type="password" class="input ~neutral @high mt-2 mb-4" placeholder="{{ .strings.password }}" id="create-reenter-password" aria-label="{{ .strings.reEnterPassword }}">
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge !normal full-width center supra submit">{{ .strings.createAccountButton }}</span>
|
||||
<span class="button ~urge @low full-width center supra submit">
|
||||
{{ if .passwordReset }}
|
||||
{{ .strings.reset }}
|
||||
{{ else }}
|
||||
{{ .strings.createAccountButton }}
|
||||
{{ end }}
|
||||
</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card ~neutral !normal">
|
||||
<div class="card ~neutral @low mb-4">
|
||||
<span class="label supra" for="inv-uses">{{ .strings.passwordRequirementsHeader }}</span>
|
||||
<ul>
|
||||
{{ range $key, $value := .requirements }}
|
||||
@@ -72,7 +178,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
{{ if .contactMessage }}
|
||||
<aside class="col aside sm ~info">{{ .contactMessage }}</aside>
|
||||
<aside class="col aside sm ~info mt-4">{{ .contactMessage }}</aside>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -81,4 +187,3 @@
|
||||
{{ template "form-base" . }}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
</head>
|
||||
<body class="section">
|
||||
<div class="page-container">
|
||||
<h1 class="heading">Invalid invite code.</h1>
|
||||
<p class="content">The code above was either incorrect, or has expired.</p>
|
||||
<p class="content">
|
||||
{{ .contactMessage }}
|
||||
</p>
|
||||
<div class="card">
|
||||
<h1 class="text-3xl font-semibold">Invalid invite code.</h1>
|
||||
<p class="content">The code above was either incorrect, or has expired.</p>
|
||||
<p class="content">
|
||||
{{ .contactMessage }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -12,15 +12,15 @@
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="page-container">
|
||||
<div class="card ~neutral !normal mb-1">
|
||||
<span class="heading mb-1">
|
||||
<div class="card ~neutral @low mb-4">
|
||||
<span class="heading mb-4">
|
||||
{{ if .success }}
|
||||
{{ .strings.passwordReset }}
|
||||
{{ else }}
|
||||
{{ .strings.resetFailed }}
|
||||
{{ end }}
|
||||
</span>
|
||||
<p class="content mb-1">
|
||||
<p class="content mb-4">
|
||||
{{ if .success }}
|
||||
{{ if .ombiEnabled }}
|
||||
{{ .strings.youCanLoginOmbi }}
|
||||
@@ -35,11 +35,11 @@
|
||||
<aside class="aside ~warning">
|
||||
{{ .strings.changeYourPassword }}
|
||||
</aside>
|
||||
<span class="button ~urge !normal full-width center supra p-1 mt-1" id="pin" title="{{ .strings.copy }}">{{ .pin }}</span>
|
||||
<span class="button ~urge @low w-100 text-center text-xl p-1 mt-4" id="pin" title="{{ .strings.copy }}">{{ .pin }}</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
<i class="content">{{ .contactMessage }}</i>
|
||||
</div>
|
||||
<script src="{{ .urlBase }}/js/pwr.js" type="module"></script>
|
||||
<script src="{{ .urlBase }}/js/pwr-pin.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
487
html/setup.html
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="light-theme">
|
||||
<html lang="en" class="light">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="css/bundle.css">
|
||||
{{ template "header.html" . }}
|
||||
@@ -7,18 +7,20 @@
|
||||
</head>
|
||||
<body class="max-w-full overflow-x-hidden section">
|
||||
<div id="notification-box"></div>
|
||||
<span class="dropdown" tabindex="0" id="lang-dropdown">
|
||||
<span class="button ~urge dropdown-button">
|
||||
<i class="ri-global-line"></i>
|
||||
<span class="ml-1 chev"></span>
|
||||
</span>
|
||||
<div class="dropdown-display">
|
||||
<div class="card ~neutral !low" id="lang-list">
|
||||
<div class="top-4 left-4 absolute">
|
||||
<span class="dropdown" tabindex="0" id="lang-dropdown">
|
||||
<span class="button ~urge dropdown-button">
|
||||
<i class="ri-global-line"></i>
|
||||
<span class="ml-2 chev"></span>
|
||||
</span>
|
||||
<div class="dropdown-display">
|
||||
<div class="card ~neutral @low" id="lang-list">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="page-container" id="page-container">
|
||||
<div class="card ~neutral !low mb-1">
|
||||
<div class="card ~neutral @low mb-2">
|
||||
<div class="row">
|
||||
<img class="banner header" src="banner.svg" alt="jfa-go" />
|
||||
</div>
|
||||
@@ -26,70 +28,70 @@
|
||||
<span class="heading welcome">{{ .lang.StartPage.welcome }}</span>
|
||||
</div>
|
||||
<div class="row col flex center">
|
||||
<p class="content">{{ .lang.StartPage.pressStart }}</p>
|
||||
<p class="content my-2">{{ .lang.StartPage.pressStart }}</p>
|
||||
</div>
|
||||
<section class="section ~neutral banner footer flex-expand middle">
|
||||
<span class="support">{{ .lang.StartPage.httpsNotice }}</span>
|
||||
<span class="button ~urge !normal next">{{ .lang.StartPage.start }}</span>
|
||||
<span class="button ~urge @low next">{{ .lang.StartPage.start }}</span>
|
||||
</section>
|
||||
</div>
|
||||
<div class="card ~neutral !low mb-1 unfocused">
|
||||
<div class="card ~neutral @low mb-2 unfocused">
|
||||
<span class="heading">{{ .lang.Language.title }}</span>
|
||||
<p class="content" id="language-description"></p>
|
||||
<p class="content my-2" id="language-description"></p>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Language.defaultAdminLang }}</span>
|
||||
<div class="select ~neutral !normal mt-half mb-1">
|
||||
<span class="mt-4">{{ .lang.Language.defaultAdminLang }}</span>
|
||||
<div class="select ~neutral @low mt-4 mb-2">
|
||||
<select id="ui-language-admin">
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Language.defaultFormLang }}</span>
|
||||
<div class="select ~neutral !normal mt-half mb-1">
|
||||
<span class="mt-4">{{ .lang.Language.defaultFormLang }}</span>
|
||||
<div class="select ~neutral @low mt-4 mb-2">
|
||||
<select id="ui-language-form">
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Language.defaultEmailLang }}</span>
|
||||
<div class="select ~neutral !normal mt-half mb-1">
|
||||
<span class="mt-4">{{ .lang.Language.defaultEmailLang }}</span>
|
||||
<div class="select ~neutral @low mt-4 mb-2">
|
||||
<select id="email-language">
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
<section class="section ~neutral banner footer flex-expand middle">
|
||||
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
|
||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||
</section>
|
||||
</div>
|
||||
<div class="card ~neutral !low mb-1 unfocused">
|
||||
<div class="card ~neutral @low mb-2 unfocused">
|
||||
<span class="heading">{{ .lang.General.title }}</span>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.General.listenAddress }}</span>
|
||||
<input type="url" class="input ~neutral !normal mt-half mb-1" id="ui-host" value="0.0.0.0">
|
||||
<span class="mt-4">{{ .lang.General.listenAddress }}</span>
|
||||
<input type="url" class="input ~neutral @low mt-4 mb-2" id="ui-host" value="0.0.0.0">
|
||||
</label>
|
||||
<label class="row switch">
|
||||
<input type="checkbox" id="advanced-tls"><span>{{ .lang.General.useHTTPS }}</span>
|
||||
<input type="checkbox" class="mr-2" id="advanced-tls"><span>{{ .lang.General.useHTTPS }}</span>
|
||||
</label>
|
||||
<p class="support mb-1">{{ .lang.General.useHTTPSNotice }}</p>
|
||||
<p class="support mb-2 mt-1">{{ .lang.General.useHTTPSNotice }}</p>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.General.pathToCertificate }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half mb-1" id="advanced-tls_cert">
|
||||
<span class="mt-4">{{ .lang.General.pathToCertificate }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="advanced-tls_cert">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.General.pathToKeyFile }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half mb-1" id="advanced-tls_key">
|
||||
<span class="mt-4">{{ .lang.General.pathToKeyFile }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="advanced-tls_key">
|
||||
</label>
|
||||
<span class="heading">{{ .lang.Updates.title }}</span>
|
||||
<p class="content" id="updates-description"></p>
|
||||
<label class="row switch pb-1">
|
||||
<input type="checkbox" id="updates-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
|
||||
<p class="content my-2" id="updates-description"></p>
|
||||
<label class="row switch pb-4">
|
||||
<input type="checkbox" class="mr-2" id="updates-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span>{{ .lang.Updates.updateChannel }}</span>
|
||||
<div class="select ~neutral !normal mt-half mb-1">
|
||||
<div class="select ~neutral @low mt-4 mb-2">
|
||||
<select id="updates-channel">
|
||||
<option value="stable">{{ .lang.Updates.stable }}</option>
|
||||
<option value="unstable">{{ .lang.Updates.unstable }}</option>
|
||||
@@ -99,21 +101,21 @@
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.port }}</span>
|
||||
<input type="number" class="input ~neutral !normal mt-half mb-1" id="ui-port" value="8056">
|
||||
<span class="mt-4">{{ .lang.Strings.port }}</span>
|
||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="ui-port" value="8056">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.General.httpsPort }}</span>
|
||||
<input type="number" class="input ~neutral !normal mt-half mb-1" id="advanced-tls_port" value="8057">
|
||||
<span class="mt-4">{{ .lang.General.httpsPort }}</span>
|
||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="advanced-tls_port" value="8057">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.General.urlBase }} ({{ .lang.Strings.optional }})</span>
|
||||
<input type="url" class="input ~neutral !normal mt-half" id="ui-url_base">
|
||||
<p class="support mb-1">{{ .lang.General.urlBaseNotice }}</p>
|
||||
<span class="mt-4">{{ .lang.General.urlBase }} ({{ .lang.Strings.optional }})</span>
|
||||
<input type="url" class="input ~neutral @low mt-4" id="ui-url_base">
|
||||
<p class="support mb-2 mt-1">{{ .lang.General.urlBaseNotice }}</p>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span>{{ .lang.Strings.theme }}</span>
|
||||
<div class="select ~neutral !normal mt-half mb-1">
|
||||
<div class="select ~neutral @low mt-4 mb-2">
|
||||
<select id="ui-theme">
|
||||
<option value="Jellyfin (Dark)">{{ .lang.General.darkTheme }}</option>
|
||||
<option value="Default (Light)">{{ .lang.General.lightTheme }}</option>
|
||||
@@ -123,355 +125,366 @@
|
||||
</div>
|
||||
</div>
|
||||
<section class="section ~neutral banner footer flex-expand middle">
|
||||
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
|
||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||
</section>
|
||||
</div>
|
||||
<div class="card ~neutral !low mb-1 unfocused">
|
||||
<div class="card ~neutral @low mb-2 unfocused">
|
||||
<span class="heading">{{ .lang.Login.title }}</span>
|
||||
<p class="content">{{ .lang.Login.description }}</p>
|
||||
<div class="pl-1">
|
||||
<label class="row switch pb-1">
|
||||
<input type="radio" name="ui-jellyfin_login" value="true" checked><span>{{ .lang.Login.authorizeWithJellyfin }}</span>
|
||||
<p class="content my-2">{{ .lang.Login.description }}</p>
|
||||
<div class="pl-4">
|
||||
<label class="row switch pb-4">
|
||||
<input type="radio" class="mr-2" name="ui-jellyfin_login" value="true" checked><span>{{ .lang.Login.authorizeWithJellyfin }}</span>
|
||||
</label>
|
||||
<label class="row switch pl-1 pb-1">
|
||||
<input type="checkbox" id="ui-admin_only"><span>{{ .lang.Login.adminOnly }}</span>
|
||||
<label class="row switch pl-4 pb-4">
|
||||
<input type="checkbox" class="mr-2" id="ui-admin_only"><span>{{ .lang.Login.adminOnly }}</span>
|
||||
</label>
|
||||
<label class="row switch pb-1">
|
||||
<input type="radio" name="ui-jellyfin_login" value="false"><span>{{ .lang.Login.authorizeManual }}</span>
|
||||
<label class="row switch pb-4">
|
||||
<input type="radio" class="mr-2" name="ui-jellyfin_login" value="false"><span>{{ .lang.Login.authorizeManual }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="login-manual">
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.username }}</span>
|
||||
<input type="text" id="ui-username" class="input ~neutral !normal mt-half mb-1" placeholder="{{ .lang.Strings.username }}">
|
||||
<span class="mt-4">{{ .lang.Strings.username }}</span>
|
||||
<input type="text" id="ui-username" class="input ~neutral @low mt-4 mb-2" placeholder="{{ .lang.Strings.username }}">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span>{{ .lang.Strings.password }}</span>
|
||||
<input type="password" id="ui-password" class="input ~neutral !normal mt-half mb-1" placeholder="{{ .lang.Strings.password }}">
|
||||
<input type="password" id="ui-password" class="input ~neutral @low mt-4 mb-2" placeholder="{{ .lang.Strings.password }}">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span>{{ .lang.Strings.emailAddress }} ({{ .lang.Strings.optional }})</span>
|
||||
<input type="email" id="ui-email" class="input ~neutral !normal mt-half" placeholder="email@address">
|
||||
<span class="support mb-1">{{ .lang.Login.emailNotice }}</span>
|
||||
<input type="email" id="ui-email" class="input ~neutral @low mt-4" placeholder="email@address">
|
||||
<span class="support mb-2 mt-1">{{ .lang.Login.emailNotice }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<section class="section ~neutral banner footer flex-expand middle">
|
||||
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
|
||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||
</section>
|
||||
</div>
|
||||
<div class="card ~neutral !low mb-1 unfocused">
|
||||
<div class="card ~neutral @low mb-2 unfocused">
|
||||
<span class="heading">{{ .lang.JellyfinEmby.title }}</span>
|
||||
<p class="content">{{ .lang.JellyfinEmby.description }}</p>
|
||||
<p class="content my-2">{{ .lang.JellyfinEmby.description }}</p>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label">
|
||||
<span>{{ .lang.Strings.serverType }}</span>
|
||||
<div class="select ~neutral !normal mt-half">
|
||||
<div class="select ~neutral @low mt-4">
|
||||
<select id="jellyfin-type">
|
||||
<option value="jellyfin">Jellyfin</option>
|
||||
<option value="emby">Emby</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="support mb-1">{{ .lang.JellyfinEmby.embyNotice }}</p>
|
||||
<p class="support mb-2 mt-1">{{ .lang.JellyfinEmby.embyNotice }}</p>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.JellyfinEmby.replaceJellyfin }} ({{ .lang.Strings.optional }})</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half" id="jellyfin-substitute_jellyfin_strings">
|
||||
<p class="support mb-1">{{ .lang.JellyfinEmby.replaceJellyfinNotice }}</p>
|
||||
<span class="mt-4">{{ .lang.JellyfinEmby.replaceJellyfin }} ({{ .lang.Strings.optional }})</span>
|
||||
<input type="text" class="input ~neutral @low mt-4" id="jellyfin-substitute_jellyfin_strings">
|
||||
<p class="support mb-2 mt-1">{{ .lang.JellyfinEmby.replaceJellyfinNotice }}</p>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.username }}</span>
|
||||
<input type="text" id="jellyfin-username" class="input ~neutral !normal mt-half mb-1" placeholder="{{ .lang.Strings.username }}">
|
||||
<span class="mt-4">{{ .lang.Strings.username }}</span>
|
||||
<input type="text" id="jellyfin-username" class="input ~neutral @low mt-4 mb-2" placeholder="{{ .lang.Strings.username }}">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span>{{ .lang.Strings.password }}</span>
|
||||
<input type="password" id="jellyfin-password" class="input ~neutral !normal mt-half mb-1" placeholder="{{ .lang.Strings.password }}">
|
||||
<input type="password" id="jellyfin-password" class="input ~neutral @low mt-4 mb-2" placeholder="{{ .lang.Strings.password }}">
|
||||
</label>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.serverAddress }} ({{ .lang.JellyfinEmby.internal }})</span>
|
||||
<input type="url" class="input ~neutral !normal mt-half mb-1" id="jellyfin-server" placeholder="http://jellyf.in:80">
|
||||
<span class="mt-4">{{ .lang.Strings.serverAddress }} ({{ .lang.JellyfinEmby.internal }})</span>
|
||||
<input type="url" class="input ~neutral @low mt-4 mb-2" id="jellyfin-server" placeholder="http://jellyf.in:80">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.serverAddress }} ({{ .lang.JellyfinEmby.external }})</span>
|
||||
<input type="url" class="input ~neutral !normal mt-half" id="jellyfin-public_server" placeholder="https://jellyf.in">
|
||||
<p class="support mb-1">{{ .lang.JellyfinEmby.addressExternalNotice }}</p>
|
||||
<span class="mt-4">{{ .lang.Strings.serverAddress }} ({{ .lang.JellyfinEmby.external }})</span>
|
||||
<input type="url" class="input ~neutral @low mt-4" id="jellyfin-public_server" placeholder="https://jellyf.in">
|
||||
<p class="support mb-2 mt-1">{{ .lang.JellyfinEmby.addressExternalNotice }}</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<section class="section ~neutral banner footer flex-expand middle">
|
||||
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||
<div>
|
||||
<span class="button ~urge !normal" id="jellyfin-test-connection">{{ .lang.JellyfinEmby.testConnection }}</span>
|
||||
<span class="button ~urge !normal next" disabled>{{ .lang.Strings.next }}</span>
|
||||
<span class="button ~urge @low" id="jellyfin-test-connection">{{ .lang.JellyfinEmby.testConnection }}</span>
|
||||
<span class="button ~urge @low next" disabled>{{ .lang.Strings.next }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="card ~neutral !low mb-1 unfocused">
|
||||
<div class="card ~neutral @low mb-2 unfocused">
|
||||
<span class="heading">{{ .lang.Ombi.title }}</span>
|
||||
<p class="content">{{ .lang.Ombi.description }}</p>
|
||||
<label class="row switch pb-1">
|
||||
<input type="checkbox" id="ombi-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
||||
<p class="content my-2">{{ .lang.Ombi.description }}</p>
|
||||
<label class="row switch pb-4">
|
||||
<input type="checkbox" class="mr-2" id="ombi-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.serverAddress }}</span>
|
||||
<input type="url" class="input ~neutral !normal mt-half mb-1" id="ombi-server" placeholder="ombi.jellyf.in">
|
||||
<span class="mt-4">{{ .lang.Strings.serverAddress }}</span>
|
||||
<input type="url" class="input ~neutral @low mt-4 mb-2" id="ombi-server" placeholder="ombi.jellyf.in">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.apiKey }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half" id="ombi-api_key">
|
||||
<p class="support mb-1">{{ .lang.Ombi.apiKeyNotice }}</p>
|
||||
<span class="mt-4">{{ .lang.Strings.apiKey }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4" id="ombi-api_key">
|
||||
<p class="support mb-2 mt-1">{{ .lang.Ombi.apiKeyNotice }}</p>
|
||||
</label>
|
||||
<section class="section ~neutral banner footer flex-expand middle">
|
||||
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||
<div>
|
||||
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
|
||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="card ~neutral !low mb-1 unfocused">
|
||||
<span class="heading">{{ .lang.Email.title }}</span>
|
||||
<p class="content" id="email-description"></p>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label">
|
||||
<span>{{ .lang.Email.method }}</span>
|
||||
<div class="select ~neutral !normal mt-half mb-1">
|
||||
<select id="email-method">
|
||||
<option value="">{{ .lang.Strings.disabled }}</option>
|
||||
<option value="smtp">SMTP</option>
|
||||
<option value="mailgun">Mailgun</option>
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
<label class="row switch">
|
||||
<input type="checkbox" id="email-no_username"><span>{{ .lang.Email.useEmailAsUsername }}</span>
|
||||
<p class="support mb-1">{{ .lang.Email.useEmailAsUsernameNotice }}</p>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Email.fromAddress }}</span>
|
||||
<input type="email" class="input ~neutral !normal mt-half mb-1" id="email-address" placeholder="mail@jellyf.in">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Email.senderName }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half mb-1" id="email-from" value="Jellyfin">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Email.dateFormat }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half" id="email-date_format" value="%d/%m/%y">
|
||||
<p class="support mb-1" id="email-dateformat-notice"></p>
|
||||
</label>
|
||||
<div>
|
||||
<label class="row switch pb-1">
|
||||
<input type="radio" name="email-24h" value="true" checked><span>{{ .lang.Strings.time24h }}</span>
|
||||
</label>
|
||||
<label class="row switch pb-1">
|
||||
<input type="radio" name="email-24h" value="false"><span>{{ .lang.Strings.time12h }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div id="email-smtp">
|
||||
<p class="subheading">SMTP</p>
|
||||
<div class="card ~neutral @low mb-2 unfocused">
|
||||
<span class="heading">{{ .lang.Messages.title }}</span>
|
||||
<p class="content my-2" id="messages-description"></p>
|
||||
<label class="row switch pb-4">
|
||||
<input type="checkbox" class="mr-2" id="messages-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-4">{{ .lang.Email.dateFormat }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4" id="email-date_format" value="%d/%m/%y">
|
||||
<p class="support mb-2 mt-1" id="email-dateformat-notice"></p>
|
||||
</label>
|
||||
<div>
|
||||
<label class="row switch pb-4">
|
||||
<input type="radio" class="mr-2" name="email-24h" value="true" checked><span>{{ .lang.Strings.time24h }}</span>
|
||||
</label>
|
||||
<label class="row switch pb-4">
|
||||
<input type="radio" class="mr-2" name="email-24h" value="false"><span>{{ .lang.Strings.time12h }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="email-sect">
|
||||
<span class="heading">{{ .lang.Email.title }}</span>
|
||||
<p class="content my-2" id="email-description"></p>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="label">
|
||||
<span>{{ .lang.Email.encryption }}</span>
|
||||
<div class="select ~neutral !normal mt-half mb-1">
|
||||
<select id="smtp-encryption">
|
||||
<option value="starttls">STARTTLS ({{ .lang.Strings.port }} 587)</option>
|
||||
<option value="ssl_tls">SSL/TLS ({{ .lang.Strings.port }} 465)</option>
|
||||
<span>{{ .lang.Email.method }}</span>
|
||||
<div class="select ~neutral @low mt-4 mb-2">
|
||||
<select id="email-method">
|
||||
<option value="">{{ .lang.Strings.disabled }}</option>
|
||||
<option value="smtp">SMTP</option>
|
||||
<option value="mailgun">Mailgun</option>
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.serverAddress }}</span>
|
||||
<input type="url" class="input ~neutral !normal mt-half mb-1" id="smtp-server" placeholder="smtp.jellyf.in">
|
||||
<label class="row switch">
|
||||
<input type="checkbox" class="mr-2" id="email-no_username"><span>{{ .lang.Email.useEmailAsUsername }}</span>
|
||||
<p class="support mb-2 mt-1">{{ .lang.Email.useEmailAsUsernameNotice }}</p>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.port }}</span>
|
||||
<input type="number" class="input ~neutral !normal mt-half mb-1" id="smtp-port" placeholder="587">
|
||||
<span class="mt-4">{{ .lang.Email.fromAddress }}</span>
|
||||
<input type="email" class="input ~neutral @low mt-4 mb-2" id="email-address" placeholder="mail@jellyf.in">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.username }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half mb-1" id="smtp-username">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.password }}</span>
|
||||
<input type="password" class="input ~neutral !normal mt-half mb-1" id="smtp-password">
|
||||
<span class="mt-4">{{ .lang.Email.senderName }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="email-from" value="Jellyfin">
|
||||
</label>
|
||||
</div>
|
||||
<div id="email-mailgun">
|
||||
<p class="subheading">Mailgun</p>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Email.mailgunApiURL }}</span>
|
||||
<input type="url" class="input ~neutral !normal mt-half mb-1" id="mailgun-api_url" placeholder="https://api.eu.mailgun.net/v3/mail.jellyf.in/messages">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.apiKey }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half mb-1" id="mailgun-api_key">
|
||||
</label>
|
||||
<div class="col">
|
||||
<div id="email-smtp">
|
||||
<p class="text-2xl font-semibold mb-2">SMTP</p>
|
||||
<label class="label">
|
||||
<span>{{ .lang.Email.encryption }}</span>
|
||||
<div class="select ~neutral @low mt-4 mb-2">
|
||||
<select id="smtp-encryption">
|
||||
<option value="starttls">STARTTLS ({{ .lang.Strings.port }} 587)</option>
|
||||
<option value="ssl_tls">SSL/TLS ({{ .lang.Strings.port }} 465)</option>
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-4">{{ .lang.Strings.serverAddress }}</span>
|
||||
<input type="url" class="input ~neutral @low mt-4 mb-2" id="smtp-server" placeholder="smtp.jellyf.in">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-4">{{ .lang.Strings.port }}</span>
|
||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="smtp-port" placeholder="587">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-4">{{ .lang.Strings.username }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="smtp-username">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-4">{{ .lang.Strings.password }}</span>
|
||||
<input type="password" class="input ~neutral @low mt-4 mb-2" id="smtp-password">
|
||||
</label>
|
||||
</div>
|
||||
<div id="email-mailgun">
|
||||
<p class="text-2xl font-semibold mb-2">Mailgun</p>
|
||||
<label class="label">
|
||||
<span class="mt-4">{{ .lang.Email.mailgunApiURL }}</span>
|
||||
<input type="url" class="input ~neutral @low mt-4 mb-2" id="mailgun-api_url" placeholder="https://api.eu.mailgun.net/v3/mail.jellyf.in/messages">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-4">{{ .lang.Strings.apiKey }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="mailgun-api_key">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section class="section ~neutral banner footer flex-expand middle">
|
||||
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||
<div>
|
||||
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
|
||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="card ~neutral !low mb-1 unfocused related-to-email">
|
||||
<div class="card ~neutral @low mb-2 unfocused related-to-email">
|
||||
<span class="heading">{{ .lang.Notifications.title }}</span>
|
||||
<p class="content">{{ .lang.Notifications.description }}</p>
|
||||
<label class="row switch pb-1">
|
||||
<input type="checkbox" id="notifications-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
||||
<p class="content my-2">{{ .lang.Notifications.description }}</p>
|
||||
<label class="row switch pb-4">
|
||||
<input type="checkbox" class="mr-2" id="notifications-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
||||
</label>
|
||||
<span class="heading">{{ .lang.WelcomeEmails.title }}</span>
|
||||
<p class="content">{{ .lang.WelcomeEmails.description }}</p>
|
||||
<label class="row switch pb-1">
|
||||
<input type="checkbox" id="welcome_email-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
||||
<p class="content my-2">{{ .lang.WelcomeEmails.description }}</p>
|
||||
<label class="row switch pb-4">
|
||||
<input type="checkbox" class="mr-2" id="welcome_email-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.emailSubject }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half mb-1" id="welcome_email-subject" placeholder="{{ .emailLang.WelcomeEmail.title }}">
|
||||
<span class="mt-4">{{ .lang.Strings.emailSubject }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="welcome_email-subject" placeholder="{{ .emailLang.WelcomeEmail.title }}">
|
||||
</label>
|
||||
<section class="section ~neutral banner footer flex-expand middle">
|
||||
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||
<div>
|
||||
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
|
||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="card ~neutral !low mb-1 unfocused related-to-email">
|
||||
<div class="card ~neutral @low mb-2 unfocused related-to-email">
|
||||
<span class="heading">{{ .lang.InviteEmails.title }}</span>
|
||||
<p class="content">{{ .lang.InviteEmails.description }}</p>
|
||||
<label class="row switch pb-1">
|
||||
<input type="checkbox" id="invite_emails-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
||||
<p class="content my-2">{{ .lang.InviteEmails.description }}</p>
|
||||
<label class="row switch pb-4">
|
||||
<input type="checkbox" class="mr-2" id="invite_emails-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.URL }}</span>
|
||||
<input type="url" class="input ~neutral !normal mt-half mb-1" id="invite_emails-url_base" placeholder="https://accounts.jellyf.in/invite">
|
||||
<span class="mt-4">{{ .lang.Strings.URL }}</span>
|
||||
<input type="url" class="input ~neutral @low mt-4 mb-2" id="invite_emails-url_base" placeholder="https://accounts.jellyf.in/invite">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.Strings.emailSubject }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half mb-1" id="invite_emails-subject" placeholder="{{ .emailLang.InviteEmail.title }}">
|
||||
<span class="mt-4">{{ .lang.Strings.emailSubject }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="invite_emails-subject" placeholder="{{ .emailLang.InviteEmail.title }}">
|
||||
</label>
|
||||
<section class="section ~neutral banner footer flex-expand middle">
|
||||
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||
<div>
|
||||
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
|
||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div id="password-resets" class="card ~neutral !low mb-1 unfocused related-to-email">
|
||||
<div id="password-resets" class="card ~neutral @low mb-2 unfocused related-to-email">
|
||||
<span class="heading">{{ .lang.PasswordResets.title }}</span>
|
||||
<p class="content">{{ .lang.PasswordResets.description }}</p>
|
||||
<label class="row switch pb-1">
|
||||
<input type="checkbox" id="password_resets-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
||||
<p class="content my-2">{{ .lang.PasswordResets.description }}</p>
|
||||
<label class="row switch pb-4">
|
||||
<input type="checkbox" class="mr-2" id="password_resets-enabled"><span>{{ .lang.Strings.enabled }}</span>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.PasswordResets.pathToJellyfin }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half" id="password_resets-watch_directory" placeholder="/config/jellyfin">
|
||||
<p class="support mb-1">{{ .lang.PasswordResets.pathToJellyfinNotice }}</p>
|
||||
<span class="mt-4">{{ .lang.PasswordResets.pathToJellyfin }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4" id="password_resets-watch_directory" placeholder="/config/jellyfin">
|
||||
<p class="support mb-2 mt-1">{{ .lang.PasswordResets.pathToJellyfinNotice }}</p>
|
||||
</label>
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="password_resets-link_reset"><span>{{ .lang.PasswordResets.resetLinks }}</span>
|
||||
<p class="support mb-1">{{ .lang.PasswordResets.resetLinksNotice }}</p>
|
||||
<input type="checkbox" class="mr-2" id="password_resets-link_reset"><span>{{ .lang.PasswordResets.resetLinks }}</span>
|
||||
<p class="support mb-2 mt-1">{{ .lang.PasswordResets.resetLinksNotice }}</p>
|
||||
</label>
|
||||
<label class="row label">
|
||||
<p class="mt-half">{{ .lang.PasswordResets.resetLinksLanguage }}</p>
|
||||
<div class="select ~neutral !normal mt-half mb-1">
|
||||
<label class="switch">
|
||||
<input type="checkbox" class="mr-2" id="password_resets-set_password"><span>{{ .lang.PasswordResets.setPassword }}</span>
|
||||
<p class="support mb-2 mt-1">{{ .lang.PasswordResets.setPasswordNotice }}</p>
|
||||
</label>
|
||||
<label class="label">
|
||||
<p class="mt-4">{{ .lang.PasswordResets.resetLinksLanguage }}</p>
|
||||
<div class="select ~neutral @low mt-4 mb-2">
|
||||
<select id="password_resets-language">
|
||||
</select>
|
||||
</div>
|
||||
</label>
|
||||
<label class="row label">
|
||||
<span class="mt-half">{{ .lang.Strings.emailSubject }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half mb-1" id="password_resets-subject" placeholder="{{ .emailLang.PasswordReset.title }}">
|
||||
<span class="mt-4">{{ .lang.Strings.emailSubject }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4 mb-2" id="password_resets-subject" placeholder="{{ .emailLang.PasswordReset.title }}">
|
||||
</label>
|
||||
<section class="section ~neutral banner footer flex-expand middle">
|
||||
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||
<div>
|
||||
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
|
||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="card ~neutral !low mb-1 unfocused">
|
||||
<div class="card ~neutral @low mb-2 unfocused">
|
||||
<span class="heading">{{ .lang.PasswordValidation.title }}</span>
|
||||
<p class="content">{{ .lang.PasswordValidation.description }}</p>
|
||||
<label class="row switch pb-1">
|
||||
<input type="checkbox" id="password_validation-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
|
||||
<p class="content my-2">{{ .lang.PasswordValidation.description }}</p>
|
||||
<label class="row switch pb-4">
|
||||
<input type="checkbox" class="mr-2" id="password_validation-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.PasswordValidation.length }}</span>
|
||||
<input type="number" class="input ~neutral !normal mt-half mb-1" id="password_validation-min_length" value="8">
|
||||
<span class="mt-4">{{ .lang.PasswordValidation.length }}</span>
|
||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-min_length" value="8">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.PasswordValidation.uppercase }}</span>
|
||||
<input type="number" class="input ~neutral !normal mt-half mb-1" id="password_validation-upper" value="1">
|
||||
<span class="mt-4">{{ .lang.PasswordValidation.uppercase }}</span>
|
||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-upper" value="1">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.PasswordValidation.lowercase }}</span>
|
||||
<input type="number" class="input ~neutral !normal mt-half mb-1" id="password_validation-lower" value="0">
|
||||
<span class="mt-4">{{ .lang.PasswordValidation.lowercase }}</span>
|
||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-lower" value="0">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.PasswordValidation.numbers }}</span>
|
||||
<input type="number" class="input ~neutral !normal mt-half mb-1" id="password_validation-number" value="0">
|
||||
<span class="mt-4">{{ .lang.PasswordValidation.numbers }}</span>
|
||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-number" value="0">
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.PasswordValidation.special }}</span>
|
||||
<input type="number" class="input ~neutral !normal mt-half mb-1" id="password_validation-special" value="0">
|
||||
<span class="mt-4">{{ .lang.PasswordValidation.special }}</span>
|
||||
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-special" value="0">
|
||||
</label>
|
||||
<section class="section ~neutral banner footer flex-expand middle">
|
||||
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||
<div>
|
||||
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
|
||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="card ~neutral !low mb-1 unfocused">
|
||||
<div class="card ~neutral @low mb-2 unfocused">
|
||||
<span class="heading">{{ .lang.HelpMessages.title }}</span>
|
||||
<p class="content">{{ .lang.HelpMessages.description }}</p>
|
||||
<p class="content my-2">{{ .lang.HelpMessages.description }}</p>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.HelpMessages.contactMessage }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half" id="ui-contact_message">
|
||||
<p class="support mb-1">{{ .lang.HelpMessages.contactMessageNotice }}</p>
|
||||
<span class="mt-4">{{ .lang.HelpMessages.contactMessage }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4" id="ui-contact_message">
|
||||
<p class="support mb-2 mt-1">{{ .lang.HelpMessages.contactMessageNotice }}</p>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.HelpMessages.helpMessage }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half" id="ui-help_message">
|
||||
<p class="support mb-1">{{ .lang.HelpMessages.helpMessageNotice }}</p>
|
||||
<span class="mt-4">{{ .lang.HelpMessages.helpMessage }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4" id="ui-help_message">
|
||||
<p class="support mb-2 mt-1">{{ .lang.HelpMessages.helpMessageNotice }}</p>
|
||||
</label>
|
||||
<label class="label">
|
||||
<span class="mt-half">{{ .lang.HelpMessages.successMessage }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half" id="ui-success_message">
|
||||
<p class="support mb-1">{{ .lang.HelpMessages.successMessageNotice }}</p>
|
||||
<span class="mt-4">{{ .lang.HelpMessages.successMessage }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4" id="ui-success_message">
|
||||
<p class="support mb-2 mt-1">{{ .lang.HelpMessages.successMessageNotice }}</p>
|
||||
</label>
|
||||
<label class="label related-to-email">
|
||||
<span class="mt-half">{{ .lang.HelpMessages.emailMessage }}</span>
|
||||
<input type="text" class="input ~neutral !normal mt-half" id="email-message">
|
||||
<p class="support mb-1">{{ .lang.HelpMessages.emailMessageNotice }}</p>
|
||||
<span class="mt-4">{{ .lang.HelpMessages.emailMessage }}</span>
|
||||
<input type="text" class="input ~neutral @low mt-4" id="email-message">
|
||||
<p class="support mb-2 mt-1">{{ .lang.HelpMessages.emailMessageNotice }}</p>
|
||||
</label>
|
||||
<section class="section ~neutral banner footer flex-expand middle">
|
||||
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
|
||||
<div>
|
||||
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
|
||||
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="card ~neutral !low mb-1 unfocused">
|
||||
<div class="card ~neutral @low mb-2 unfocused">
|
||||
<div class="row col flex center">
|
||||
<span class="heading">{{ .lang.EndPage.finished }}</span>
|
||||
</div>
|
||||
<div class="row col flex center">
|
||||
<p class="content">{{ .lang.EndPage.restartMessage }}</p>
|
||||
<p class="content my-2">{{ .lang.EndPage.restartMessage }}</p>
|
||||
</div>
|
||||
<div class="row col flex center">
|
||||
<span class="button ~neutral !normal back mr-1">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~urge !normal" id="restart">{{ .lang.Strings.submit }}</span>
|
||||
<span class="button ~urge !normal unfocused" id="refresh">{{ .lang.EndPage.refreshPage }}</span>
|
||||
<span class="button ~neutral @low back mr-4">{{ .lang.Strings.back }}</span>
|
||||
<span class="button ~urge @low" id="restart">{{ .lang.Strings.submit }}</span>
|
||||
<span class="button ~urge @low unfocused" id="refresh">{{ .lang.EndPage.refreshPage }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 66 KiB |
BIN
images/discord/1.jpg
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
images/discord/2.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
images/discord/3.jpg
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
images/discord/4.jpg
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
images/discord/5.jpg
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
images/discord/6.jpg
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
images/discord/7.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
images/discord/8.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
images/matrix/1.png
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
images/matrix/2.png
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
images/matrix/3.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
images/matrix/4.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
images/tg-settings.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
images/tg.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
images/thumb-white.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
203
images/thumb-white.svg
Normal file
|
After Width: | Height: | Size: 128 KiB |
@@ -13,7 +13,7 @@ const binaryType = "internal"
|
||||
//go:embed data data/html data/web data/web/css data/web/js
|
||||
var loFS embed.FS
|
||||
|
||||
//go:embed lang/common lang/admin lang/email lang/form lang/setup lang/pwreset
|
||||
//go:embed lang/common lang/admin lang/email lang/form lang/setup lang/pwreset lang/telegram
|
||||
var laFS embed.FS
|
||||
|
||||
var langFS rewriteFS
|
||||
|
||||
@@ -42,7 +42,7 @@ func (rt *inviteDaemon) run() {
|
||||
}
|
||||
}
|
||||
|
||||
func (rt *inviteDaemon) shutdown() {
|
||||
func (rt *inviteDaemon) Shutdown() {
|
||||
rt.Stopped = true
|
||||
rt.ShutdownChannel <- "Down"
|
||||
<-rt.ShutdownChannel
|
||||
18
lang.go
@@ -117,6 +117,7 @@ type setupLang struct {
|
||||
JellyfinEmby langSection `json:"jellyfinEmby"`
|
||||
Ombi langSection `json:"ombi"`
|
||||
Email langSection `json:"email"`
|
||||
Messages langSection `json:"messages"`
|
||||
Notifications langSection `json:"notifications"`
|
||||
WelcomeEmails langSection `json:"welcomeEmails"`
|
||||
PasswordResets langSection `json:"passwordResets"`
|
||||
@@ -136,6 +137,23 @@ func (ls *setupLangs) getOptions() [][2]string {
|
||||
return opts
|
||||
}
|
||||
|
||||
type telegramLangs map[string]telegramLang
|
||||
|
||||
type telegramLang struct {
|
||||
Meta langMeta `json:"meta"`
|
||||
Strings langSection `json:"strings"`
|
||||
}
|
||||
|
||||
func (ts *telegramLangs) getOptions() [][2]string {
|
||||
opts := make([][2]string, len(*ts))
|
||||
i := 0
|
||||
for key, lang := range *ts {
|
||||
opts[i] = [2]string{key, lang.Meta.Name}
|
||||
i++
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
type langSection map[string]string
|
||||
type tmpl map[string]string
|
||||
|
||||
|
||||
204
lang/admin/da-dk.json
Normal file
@@ -0,0 +1,204 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "Dansk"
|
||||
},
|
||||
"strings": {
|
||||
"invites": "Invitationer",
|
||||
"accounts": "Konti",
|
||||
"settings": "Indstillinger",
|
||||
"inviteMonths": "Måneder",
|
||||
"inviteDays": "Dage",
|
||||
"inviteHours": "Timer",
|
||||
"inviteMinutes": "Minutter",
|
||||
"inviteNumberOfUses": "Antal anvendelser",
|
||||
"inviteDuration": "Invitations varighed",
|
||||
"warning": "Advarsel",
|
||||
"inviteInfiniteUsesWarning": "invitationer med uendelig brug kan blive misbrugt",
|
||||
"inviteSendToEmail": "Send til",
|
||||
"login": "Log på",
|
||||
"logout": "Log ud",
|
||||
"create": "Opret",
|
||||
"apply": "Anvend",
|
||||
"delete": "Slet",
|
||||
"add": "Tilføj",
|
||||
"select": "Vælg",
|
||||
"name": "Navn",
|
||||
"date": "Dato",
|
||||
"enabled": "Aktiveret",
|
||||
"disabled": "Deaktiveret",
|
||||
"reEnable": "Genaktiver",
|
||||
"disable": "Deaktiver",
|
||||
"admin": "Administrator",
|
||||
"updates": "Opdateringer",
|
||||
"update": "Opdatering",
|
||||
"download": "Hent",
|
||||
"search": "Søg",
|
||||
"advancedSettings": "Avanceret Indstillinger",
|
||||
"lastActiveTime": "Sidst Aktiv",
|
||||
"from": "Fra",
|
||||
"user": "Bruger",
|
||||
"expiry": "Udløb",
|
||||
"userExpiry": "Brugerens Udløb",
|
||||
"userExpiryDescription": "En specificeret tid efter hver tilmelding, sletter/deaktiverer jfa-go kontoen. Du kan ændre denne adfærd i indstillingerne.",
|
||||
"aboutProgram": "Om",
|
||||
"version": "Version",
|
||||
"commitNoun": "Commit",
|
||||
"newUser": "Ny Bruger",
|
||||
"profile": "Profil",
|
||||
"unknown": "Ukendt",
|
||||
"label": "Etiket",
|
||||
"announce": "Annoncere",
|
||||
"subject": "Emne",
|
||||
"message": "Meddelelse",
|
||||
"variables": "Variabler",
|
||||
"conditionals": "Betingelser",
|
||||
"preview": "Eksempel",
|
||||
"reset": "Nulstil",
|
||||
"edit": "Rediger",
|
||||
"donate": "Doner",
|
||||
"contactThrough": "Kontakt gennem:",
|
||||
"extendExpiry": "Forlæng udløb",
|
||||
"customizeMessages": "Tilpas Meddelelser",
|
||||
"customizeMessagesDescription": "Hvis du ikke vil bruge jfa-go's meddelelses skabeloner, kan du oprette din egen ved hjælp af Markdown.",
|
||||
"markdownSupported": "Markdown understøttes.",
|
||||
"modifySettings": "Rediger indstillinger",
|
||||
"modifySettingsDescription": "Anvend indstillinger fra en eksisterende profil, eller hent dem direkte fra en bruger.",
|
||||
"applyHomescreenLayout": "Anvend startskærmens layout",
|
||||
"sendDeleteNotificationEmail": "Send notifikations meddelelse",
|
||||
"sendDeleteNotifiationExample": "Din konto er blevet slettet.",
|
||||
"settingsRestart": "Genstart",
|
||||
"settingsRestarting": "Genstarter…",
|
||||
"settingsRestartRequired": "Genstart nødvendig",
|
||||
"settingsRestartRequiredDescription": "En genstart er nødvendig for at anvende nogle indstillinger du har ændret. Genstart nu eller senere?",
|
||||
"settingsApplyRestartLater": "Anvend, genstart senere",
|
||||
"settingsApplyRestartNow": "Anvend & genstart",
|
||||
"settingsApplied": "Indstillingerne anvendt.",
|
||||
"settingsRefreshPage": "Opdater siden om få sekunder.",
|
||||
"settingsRequiredOrRestartMessage": "Bemærk: {n} angiver et obligatorisk felt, {n} angiver at ændringer kræver genstart.",
|
||||
"settingsSave": "Gem",
|
||||
"ombiUserDefaults": "Ombi bruger standarder",
|
||||
"ombiUserDefaultsDescription": "Opret en Ombi bruger og konfigurer den, vælg den derefter nedenfor. Brugerens indstillinger/tilladelser gemmes og anvendes på nye Ombi brugere oprettet af jfa-go",
|
||||
"userProfiles": "Bruger Profiler",
|
||||
"userProfilesDescription": "Profiler anvendes på brugere når de opretter en konto. En profil inkluderer adgangsrettigheder til biblioteket og layout på startskærmen.",
|
||||
"userProfilesIsDefault": "Standard",
|
||||
"userProfilesLibraries": "Biblioteker",
|
||||
"addProfile": "Tilføj Profil",
|
||||
"addProfileDescription": "Opret en Jellyfin bruger og konfigurer den, vælg den derefter nedenfor. Når denne profil anvendes på en invitation, oprettes nye brugere med indstillingerne.",
|
||||
"addProfileNameOf": "Profil Navn",
|
||||
"addProfileStoreHomescreenLayout": "Gem startskærmens layout",
|
||||
"inviteNoUsersCreated": "Ingen endnu!",
|
||||
"inviteUsersCreated": "Oprettet brugere",
|
||||
"inviteNoProfile": "Ingen Profil",
|
||||
"inviteDateCreated": "Oprettet",
|
||||
"inviteRemainingUses": "Resterende anvendelser",
|
||||
"inviteNoInvites": "Ingen",
|
||||
"inviteExpiresInTime": "Udløber om {n}",
|
||||
"notifyEvent": "Meddel den:",
|
||||
"notifyInviteExpiry": "Ved udløb",
|
||||
"notifyUserCreation": "Ved oprettelse af brugere",
|
||||
"sendPIN": "Bed brugeren om at sende pinkoden nedenfor til botten.",
|
||||
"searchDiscordUser": "Begynd at skrive Discord brugernavnet for at finde brugeren.",
|
||||
"findDiscordUser": "Find Discord bruger",
|
||||
"linkMatrixDescription": "Indtast brugernavnet og adgangskoden til den bruger der skal bruges som en bot. Når indsendt, genstarter appen.",
|
||||
"matrixHomeServer": "Hjemme server adresse",
|
||||
"saveAsTemplate": "Gem som skabelon",
|
||||
"templates": "Skabeloner",
|
||||
"deleteTemplate": "Slet skabelon",
|
||||
"templateEnterName": "Indtast et navn for at gemme denne skabelon."
|
||||
},
|
||||
"notifications": {
|
||||
"changedEmailAddress": "Ændret e-mail adresse på {n}.",
|
||||
"userCreated": "Bruger {n} oprettet.",
|
||||
"createProfile": "Oprettede profil {n}.",
|
||||
"saveSettings": "Indstillingerne blev gemt",
|
||||
"saveEmail": "E-mail gemt.",
|
||||
"sentAnnouncement": "Meddelelse sendt.",
|
||||
"setOmbiDefaults": "Ombi standarder gemt.",
|
||||
"updateApplied": "Opdatering anvendt, genstart.",
|
||||
"updateAppliedRefresh": "Opdatering anvendt, genindlæs venligst siden.",
|
||||
"telegramVerified": "Telegram konto verificeret.",
|
||||
"accountConnected": "Konto tilsluttet.",
|
||||
"errorConnection": "Kunne ikke oprette forbindelse til jfa-go.",
|
||||
"error401Unauthorized": "Adgang nægtet. Prøv at genindlæse siden.",
|
||||
"errorSettingsAppliedNoHomescreenLayout": "Indstillingerne blev anvendt, men anvendelse af startskærmens layout mislykkedes muligvis.",
|
||||
"errorHomescreenAppliedNoSettings": "Startskærmens layout blev anvendt, men anvendelsen af indstillingerne mislykkedes muligvis.",
|
||||
"errorSettingsFailed": "Ansøgningen mislykkedes.",
|
||||
"errorLoginBlank": "Brugernavnet og/eller adgangskoden blev efterladt tomme.",
|
||||
"errorUnknown": "Ukendt fejl.",
|
||||
"errorSaveEmail": "Kunne ikke gemme e-mail.",
|
||||
"errorBlankFields": "Felter blev efterladt tomme",
|
||||
"errorDeleteProfile": "Kunne ikke slette profilen {n}",
|
||||
"errorLoadProfiles": "Profiler kunne ikke indlæses.",
|
||||
"errorCreateProfile": "Kunne ikke oprette profilen {n}",
|
||||
"errorSetDefaultProfile": "Standard profilen kunne ikke indstilles.",
|
||||
"errorLoadUsers": "Kunne ikke indlæse brugere.",
|
||||
"errorSaveSettings": "Kunne ikke gemme indstillingerne.",
|
||||
"errorLoadSettings": "Indstillingerne kunne ikke indlæses.",
|
||||
"errorSetOmbiDefaults": "Ombi standarderne kunne ikke gemmes.",
|
||||
"errorLoadOmbiUsers": "Kunne ikke indlæse ombi brugere.",
|
||||
"errorChangedEmailAddress": "Kunne ikke ændre e-mail adressen på {n}.",
|
||||
"errorFailureCheckLogs": "Mislykkedes (tjek konsol/logfiler)",
|
||||
"errorPartialFailureCheckLogs": "Delvis fejl (tjek konsol/logfiler)",
|
||||
"errorUserCreated": "Kunne ikke oprette bruger {n}.",
|
||||
"errorSendWelcomeEmail": "Kunne ikke sende velkomst meddelelse (tjek konsol/logfiler",
|
||||
"errorApplyUpdate": "Kunne ikke anvende opdateringen, prøv manuelt.",
|
||||
"errorCheckUpdate": "Kunne ikke kontrollere for opdatering.",
|
||||
"updateAvailable": "En ny opdatering er tilgængelig, tjek indstillingerne.",
|
||||
"noUpdatesAvailable": "Ingen nye opdateringer tilgængelige.",
|
||||
"savedAnnouncement": "Meddelelse gemt."
|
||||
},
|
||||
"quantityStrings": {
|
||||
"modifySettingsFor": {
|
||||
"singular": "Rediger indstillinger for {n} bruger",
|
||||
"plural": "Rediger indstillinger for {n} brugere"
|
||||
},
|
||||
"deleteNUsers": {
|
||||
"singular": "Slet {n} bruger",
|
||||
"plural": "Slet {n} brugere"
|
||||
},
|
||||
"disableUsers": {
|
||||
"singular": "Deaktiver {n} bruger",
|
||||
"plural": "Deaktiver {n} brugere"
|
||||
},
|
||||
"reEnableUsers": {
|
||||
"singular": "Genaktiver {n} bruger",
|
||||
"plural": "Genaktiver {n} brugere"
|
||||
},
|
||||
"addUser": {
|
||||
"singular": "Tilføj bruger",
|
||||
"plural": "Tilføj brugere"
|
||||
},
|
||||
"deleteUser": {
|
||||
"singular": "Slet bruger",
|
||||
"plural": "Slet brugere"
|
||||
},
|
||||
"deletedUser": {
|
||||
"singular": "Slettede {n} bruger.",
|
||||
"plural": "Slettede {n} brugere."
|
||||
},
|
||||
"disabledUser": {
|
||||
"singular": "Deaktiveret {n} bruger.",
|
||||
"plural": "Deaktiverede {n} brugere."
|
||||
},
|
||||
"enabledUser": {
|
||||
"singular": "Aktiveret {n} bruger.",
|
||||
"plural": "Aktiveret {n} brugere."
|
||||
},
|
||||
"announceTo": {
|
||||
"singular": "Annoncer til {n} bruger",
|
||||
"plural": "Annoncer til {n} brugere"
|
||||
},
|
||||
"appliedSettings": {
|
||||
"singular": "Anvendte indstillinger til {n} bruger.",
|
||||
"plural": "Anvendte indstillinger til {n} brugere."
|
||||
},
|
||||
"extendExpiry": {
|
||||
"singular": "Forlæng udløbet for {n} bruger",
|
||||
"plural": "Forlæng udløbet for {n} brugere"
|
||||
},
|
||||
"extendedExpiry": {
|
||||
"singular": "Forlængede udløb for {n} bruger.",
|
||||
"plural": "Forlængede udløb for {n} brugere."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@
|
||||
"modifySettings": "Einstellungen ändern",
|
||||
"modifySettingsDescription": "Wende Einstellungen von einem bestehenden Profil an, oder beziehe sie direkt von einem Benutzer.",
|
||||
"applyHomescreenLayout": "Startbildschirmlayout anwenden",
|
||||
"sendDeleteNotificationEmail": "Benachrichtigungs-E-Mail senden",
|
||||
"sendDeleteNotificationEmail": "Benachrichtigung senden",
|
||||
"sendDeleteNotifiationExample": "Dein Konto wurde gelöscht.",
|
||||
"settingsRestartRequired": "Neustart erforderlich",
|
||||
"settingsRestartRequiredDescription": "Ein Neustart ist notwendig, um einige Einstellungen anzuwenden, die du geändert hast. Jetzt oder später neu starten?",
|
||||
@@ -69,10 +69,10 @@
|
||||
"preview": "Vorschau",
|
||||
"reset": "Zurücksetzen",
|
||||
"edit": "Bearbeiten",
|
||||
"customizeEmails": "E-Mails anpassen",
|
||||
"customizeEmailsDescription": "Wenn du jfa-go's E-Mail-Vorlagen nicht benutzen willst, kannst du deinen eigenen unter Verwendung von Markdown erstellen.",
|
||||
"customizeMessages": "E-Mails anpassen",
|
||||
"customizeMessagesDescription": "Wenn du jfa-go's E-Mail-Vorlagen nicht benutzen willst, kannst du deinen eigenen unter Verwendung von Markdown erstellen.",
|
||||
"announce": "Ankündigen",
|
||||
"subject": "E-Mail-Betreff",
|
||||
"subject": "Betreff",
|
||||
"message": "Nachricht",
|
||||
"markdownSupported": "Markdown wird unterstützt.",
|
||||
"advancedSettings": "Erweiterte Einstellungen",
|
||||
@@ -87,7 +87,21 @@
|
||||
"update": "Aktualisieren",
|
||||
"updates": "Aktualisierungen",
|
||||
"expiry": "Ablaufdatum",
|
||||
"extendExpiry": "Ablaufdatum verlängern"
|
||||
"extendExpiry": "Ablaufdatum verlängern",
|
||||
"reEnable": "Wieder aktivieren",
|
||||
"disable": "Deaktivieren",
|
||||
"donate": "Spenden",
|
||||
"conditionals": "Bedingungen",
|
||||
"contactThrough": "Kontakt über:",
|
||||
"sendPIN": "Bitte den Benutzer, die unten stehende PIN an den Bot zu senden.",
|
||||
"inviteMonths": "Monate",
|
||||
"add": "Hinzufügen",
|
||||
"select": "Auswählen",
|
||||
"searchDiscordUser": "Gib den Discord-Benutzername ein, um den Benutzer zu finden.",
|
||||
"findDiscordUser": "Suche Discord-Benutzer",
|
||||
"linkMatrixDescription": "Gib den Benutzernamen und das Passwort des Benutzers ein, der als Bot verwendet werden soll. Nach dem Absenden wird die App neu gestartet.",
|
||||
"matrixHomeServer": "Adresse des Homeservers",
|
||||
"templates": "Vorlagen"
|
||||
},
|
||||
"notifications": {
|
||||
"changedEmailAddress": "E-Mail-Adresse von {n} geändert.",
|
||||
@@ -116,7 +130,7 @@
|
||||
"errorFailureCheckLogs": "Fehlgeschlagen (überprüfe die Konsole/Logs)",
|
||||
"errorPartialFailureCheckLogs": "Teilweiser Fehlschlag (überprüfe die Konsole/Logs)",
|
||||
"errorUserCreated": "Fehler beim Erstellen des Benutzers {n}.",
|
||||
"errorSendWelcomeEmail": "Fehler beim Senden der Willkommens-E-Mail (überprüfe die Konsole/Logs)",
|
||||
"errorSendWelcomeEmail": "Fehler beim Senden der Willkommensnachricht (überprüfe die Konsole/Logs)",
|
||||
"saveEmail": "E-Mail gespeichert.",
|
||||
"errorSaveEmail": "Fehler beim Speichern der E-Mail.",
|
||||
"sentAnnouncement": "Ankündigung gesendet.",
|
||||
@@ -124,7 +138,11 @@
|
||||
"errorApplyUpdate": "Fehler beim Anwenden der Aktualisierung, versuche es manuell.",
|
||||
"errorCheckUpdate": "Fehler beim Suchen nach Aktualisierungen.",
|
||||
"updateAvailable": "Eine neue Aktualisierung ist verfügbar, überprüfe die Einstellungen.",
|
||||
"noUpdatesAvailable": "Keinen neuen Aktualisierungen verfügbar."
|
||||
"noUpdatesAvailable": "Keinen neuen Aktualisierungen verfügbar.",
|
||||
"updateAppliedRefresh": "Update angewendet, bitte aktualisieren.",
|
||||
"telegramVerified": "Telegram-Konto verifiziert.",
|
||||
"accountConnected": "Konto verbunden.",
|
||||
"savedAnnouncement": "Ankündigung gespeichert."
|
||||
},
|
||||
"quantityStrings": {
|
||||
"modifySettingsFor": {
|
||||
@@ -162,6 +180,22 @@
|
||||
"extendedExpiry": {
|
||||
"singular": "Ablaufdatum für {n} Benutzer verlängern.",
|
||||
"plural": "Ablaufdatum für {n} Benutzer verlängern."
|
||||
},
|
||||
"disabledUser": {
|
||||
"plural": "Benutzer {n} Deaktiviert.",
|
||||
"singular": "Benutzer {n} Deaktiviert."
|
||||
},
|
||||
"enabledUser": {
|
||||
"singular": "Benutzer {n} Aktiviert.",
|
||||
"plural": "Benutzer {n} Aktiviert."
|
||||
},
|
||||
"disableUsers": {
|
||||
"singular": "Benutzer {n} deaktivieren",
|
||||
"plural": "Deaktiviere {n} Benutzer"
|
||||
},
|
||||
"reEnableUsers": {
|
||||
"singular": "Benutzer {n} wieder aktivieren",
|
||||
"plural": "Benutzer {n} wieder aktivieren"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,9 +69,9 @@
|
||||
"preview": "Προεπισκόπηση",
|
||||
"reset": "Επαναφορά",
|
||||
"edit": "Επεξεργασία",
|
||||
"customizeEmails": "Παραμετροποίηση Emails",
|
||||
"customizeMessages": "Παραμετροποίηση Emails",
|
||||
"advancedSettings": "Προχωρημένες Ρυθμίσεις",
|
||||
"customizeEmailsDescription": "Αν δεν θέλετε να ζρησιμοποιήσετε τα πρότυπα email του jfa-go, μπορείτε να δημιουργήσετε τα δικά σας με χρήση Markdown.",
|
||||
"customizeMessagesDescription": "Αν δεν θέλετε να ζρησιμοποιήσετε τα πρότυπα email του jfa-go, μπορείτε να δημιουργήσετε τα δικά σας με χρήση Markdown.",
|
||||
"updates": "Ενημερώσεις",
|
||||
"update": "Ενημέρωση",
|
||||
"download": "Λήψη",
|
||||
|
||||
@@ -20,11 +20,14 @@
|
||||
"create": "Create",
|
||||
"apply": "Apply",
|
||||
"delete": "Delete",
|
||||
"add": "Add",
|
||||
"select": "Select",
|
||||
"name": "Name",
|
||||
"date": "Date",
|
||||
"enabled": "Enabled",
|
||||
"disabled": "Disabled",
|
||||
"reEnable": "Re-enable",
|
||||
"setExpiry": "Set expiry",
|
||||
"disable": "Disable",
|
||||
"admin": "Admin",
|
||||
"updates": "Updates",
|
||||
@@ -46,7 +49,8 @@
|
||||
"unknown": "Unknown",
|
||||
"label": "Label",
|
||||
"announce": "Announce",
|
||||
"subject": "Email Subject",
|
||||
"templates": "Templates",
|
||||
"subject": "Subject",
|
||||
"message": "Message",
|
||||
"variables": "Variables",
|
||||
"conditionals": "Conditionals",
|
||||
@@ -54,14 +58,20 @@
|
||||
"reset": "Reset",
|
||||
"edit": "Edit",
|
||||
"donate": "Donate",
|
||||
"sendPWR": "Send Password Reset",
|
||||
"contactThrough": "Contact through:",
|
||||
"extendExpiry": "Extend expiry",
|
||||
"customizeEmails": "Customize Emails",
|
||||
"customizeEmailsDescription": "If you don't want to use jfa-go's email templates, you can create your own using Markdown.",
|
||||
"sendPWRManual": "User {n} has no method of contact, press copy to get a link to send to them.",
|
||||
"sendPWRSuccess": "Password reset link sent.",
|
||||
"sendPWRSuccessManual": "If the user hasn't received it, press copy to get a link to manually send to them.",
|
||||
"sendPWRValidFor": "The link is valid for 30m.",
|
||||
"customizeMessages": "Customize Messages",
|
||||
"customizeMessagesDescription": "If you don't want to use jfa-go's message templates, you can create your own using Markdown.",
|
||||
"markdownSupported": "Markdown is supported.",
|
||||
"modifySettings": "Modify Settings",
|
||||
"modifySettingsDescription": "Apply settings from an existing profile, or source them directly from a user.",
|
||||
"applyHomescreenLayout": "Apply homescreen layout",
|
||||
"sendDeleteNotificationEmail": "Send notification email",
|
||||
"sendDeleteNotificationEmail": "Send notification message",
|
||||
"sendDeleteNotifiationExample": "Your account has been deleted.",
|
||||
"settingsRestart": "Restart",
|
||||
"settingsRestarting": "Restarting…",
|
||||
@@ -73,8 +83,8 @@
|
||||
"settingsRefreshPage": "Refresh the page in a few seconds.",
|
||||
"settingsRequiredOrRestartMessage": "Note: {n} indicates a required field, {n} indicates changes require a restart.",
|
||||
"settingsSave": "Save",
|
||||
"ombiUserDefaults": "Ombi user defaults",
|
||||
"ombiUserDefaultsDescription": "Create an Ombi user and configure it, then select it below. It's settings/permissions will be stored and applied to new Ombi users created by jfa-go",
|
||||
"ombiProfile": "Ombi user profile",
|
||||
"ombiUserDefaultsDescription": "Create an Ombi user and configure it, then select it below. It's settings/permissions will be stored and applied to new Ombi users created by jfa-go when this profile is selected.",
|
||||
"userProfiles": "User Profiles",
|
||||
"userProfilesDescription": "Profiles are applied to users when they create an account. A profile include library access rights and homescreen layout.",
|
||||
"userProfilesIsDefault": "Default",
|
||||
@@ -92,7 +102,15 @@
|
||||
"inviteExpiresInTime": "Expires in {n}",
|
||||
"notifyEvent": "Notify on:",
|
||||
"notifyInviteExpiry": "On expiry",
|
||||
"notifyUserCreation": "On user creation"
|
||||
"notifyUserCreation": "On user creation",
|
||||
"sendPIN": "Ask the user to send the PIN below to the bot.",
|
||||
"searchDiscordUser": "Start typing the Discord username to find the user.",
|
||||
"findDiscordUser": "Find Discord user",
|
||||
"linkMatrixDescription": "Enter the username and password of the user to use as a bot. Once submitted, the app will restart.",
|
||||
"matrixHomeServer": "Home server address",
|
||||
"saveAsTemplate": "Save as template",
|
||||
"deleteTemplate": "Delete template",
|
||||
"templateEnterName": "Enter a name to save this template."
|
||||
},
|
||||
"notifications": {
|
||||
"changedEmailAddress": "Changed email address of {n}.",
|
||||
@@ -101,8 +119,12 @@
|
||||
"saveSettings": "Settings were saved",
|
||||
"saveEmail": "Email saved.",
|
||||
"sentAnnouncement": "Announcement sent.",
|
||||
"setOmbiDefaults": "Stored ombi defaults.",
|
||||
"savedAnnouncement": "Announcement saved.",
|
||||
"setOmbiProfile": "Stored ombi profile.",
|
||||
"updateApplied": "Update applied, please restart.",
|
||||
"updateAppliedRefresh": "Update applied, please refresh.",
|
||||
"telegramVerified": "Telegram account verified.",
|
||||
"accountConnected": "Account connected.",
|
||||
"errorConnection": "Couldn't connect to jfa-go.",
|
||||
"error401Unauthorized": "Unauthorized. Try refreshing the page.",
|
||||
"errorSettingsAppliedNoHomescreenLayout": "Settings were applied, but applying homescreen layout may have failed.",
|
||||
@@ -119,13 +141,13 @@
|
||||
"errorLoadUsers": "Failed to load users.",
|
||||
"errorSaveSettings": "Couldn't save settings.",
|
||||
"errorLoadSettings": "Failed to load settings.",
|
||||
"errorSetOmbiDefaults": "Failed to store ombi defaults.",
|
||||
"errorSetOmbiProfile": "Failed to store ombi profile.",
|
||||
"errorLoadOmbiUsers": "Failed to load ombi users.",
|
||||
"errorChangedEmailAddress": "Couldn't change email address of {n}.",
|
||||
"errorFailureCheckLogs": "Failed (check console/logs)",
|
||||
"errorPartialFailureCheckLogs": "Partial failure (check console/logs)",
|
||||
"errorUserCreated": "Failed to create user {n}.",
|
||||
"errorSendWelcomeEmail": "Failed to send welcome email (check console/logs)",
|
||||
"errorSendWelcomeEmail": "Failed to send welcome message (check console/logs)",
|
||||
"errorApplyUpdate": "Failed to apply update, try manually.",
|
||||
"errorCheckUpdate": "Failed to check for update.",
|
||||
"updateAvailable": "A new update is available, check settings.",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "Español(ES)"
|
||||
"name": "Español (ES)"
|
||||
},
|
||||
"strings": {
|
||||
"invites": "Invitaciones",
|
||||
@@ -13,11 +13,11 @@
|
||||
"inviteNumberOfUses": "Números de usos",
|
||||
"inviteDuration": "Duración de invitación",
|
||||
"warning": "Advertencia",
|
||||
"inviteInfiniteUsesWarning": "Las invitaciones con usos infinitos pueden usarse abusivamente",
|
||||
"inviteInfiniteUsesWarning": "Las invitaciones con usos infinitos se pueden usar de forma abusiva",
|
||||
"inviteSendToEmail": "Enviar a",
|
||||
"login": "Acceso",
|
||||
"logout": "Cerrar sesión",
|
||||
"create": "Cerrar sesión",
|
||||
"create": "Crear",
|
||||
"apply": "Aplicar",
|
||||
"delete": "Eliminar",
|
||||
"name": "Nombre",
|
||||
@@ -37,10 +37,10 @@
|
||||
"user": "Usuario",
|
||||
"expiry": "Expiración",
|
||||
"userExpiry": "Caducidad del usuario",
|
||||
"userExpiryDescription": "Una cantidad específica de tiempo después de cada registro, jfa-go eliminará/deshabilitará la cuenta. Puede cambiar este comportamiento en la configuración.",
|
||||
"userExpiryDescription": "Una cantidad de tiempo específica después de cada registro, jfa-go eliminará / deshabilitará la cuenta. Puede cambiar este comportamiento en la configuración.",
|
||||
"aboutProgram": "Acerca de",
|
||||
"version": "Versión",
|
||||
"commitNoun": "Cometer",
|
||||
"commitNoun": "Revisión",
|
||||
"newUser": "Nuevo usuario",
|
||||
"profile": "Perfil",
|
||||
"unknown": "Desconocido",
|
||||
@@ -49,38 +49,38 @@
|
||||
"subject": "Asunto del email",
|
||||
"message": "Mensaje",
|
||||
"variables": "Variables",
|
||||
"preview": "Previsualizar",
|
||||
"preview": "Vista previa",
|
||||
"reset": "Reiniciar",
|
||||
"edit": "Editar",
|
||||
"extendExpiry": "Extender el vencimiento",
|
||||
"customizeEmails": "Personalizar emails",
|
||||
"customizeEmailsDescription": "Si no desea utilizar las plantillas de correo electrónico de jfa-go, puede crear las suyas propias con Markdown.",
|
||||
"customizeMessages": "Personalizar mensajes",
|
||||
"customizeMessagesDescription": "Si no desea utilizar las plantillas de mensajes de jfa-go, puede crear las suyas con Markdown.",
|
||||
"markdownSupported": "Se admite Markdown.",
|
||||
"modifySettings": "Modificar configuración",
|
||||
"modifySettingsDescription": "Aplique la configuración de un perfil existente u obténgalos directamente de un usuario.",
|
||||
"applyHomescreenLayout": "Aplicar el diseño de la pantalla de inicio",
|
||||
"sendDeleteNotificationEmail": "Enviar notificación a correo",
|
||||
"sendDeleteNotificationEmail": "Enviar mensaje de notificación",
|
||||
"sendDeleteNotifiationExample": "Tu cuenta ha sido eliminada.",
|
||||
"settingsRestart": "Reiniciar",
|
||||
"settingsRestarting": "Reiniciando…",
|
||||
"settingsRestartRequired": "Reinicio necesario",
|
||||
"settingsRestartRequiredDescription": "Es necesario reiniciar para aplicar algunas configuraciones que cambió. ¿Reiniciar ahora o más tarde?",
|
||||
"settingsApplyRestartLater": "Aplicar, reiniciar más tarde",
|
||||
"settingsApplyRestartNow": "Aplicar, reiniciar más tarde",
|
||||
"settingsApplyRestartNow": "Aplicar y reiniciar",
|
||||
"settingsApplied": "Se aplicó la configuración.",
|
||||
"settingsRefreshPage": "Actualiza la página en unos segundos.",
|
||||
"settingsRequiredOrRestartMessage": "Nota: {n} indica un campo obligatorio, {n} indica que los cambios requieren un reinicio.",
|
||||
"settingsSave": "Guardar",
|
||||
"ombiUserDefaults": "Valores predeterminados de usuario de Ombi",
|
||||
"ombiUserDefaultsDescription": "Cree un usuario Ombi y configúrelo, luego selecciónelo a continuación. Sus configuraciones / permisos se almacenarán y aplicarán a los nuevos usuarios de Ombi creados por jfa-go",
|
||||
"ombiUserDefaultsDescription": "Cree un usuario Ombi y configúrelo, luego selecciónelo a continuación. Sus configuraciones/permisos se almacenarán y aplicarán a los nuevos usuarios de Ombi creados por jfa-go cuando se seleccione este perfil.",
|
||||
"userProfiles": "Perfiles de usuario",
|
||||
"userProfilesDescription": "Los perfiles se aplican a los usuarios cuando crean una cuenta. Un perfil incluye los derechos de acceso a la biblioteca y el diseño de la pantalla de inicio.",
|
||||
"userProfilesIsDefault": "Defecto",
|
||||
"userProfilesIsDefault": "Por defecto",
|
||||
"userProfilesLibraries": "Bibliotecas",
|
||||
"addProfile": "Agregar perfil",
|
||||
"addProfile": "Agregar Perfil",
|
||||
"addProfileDescription": "Cree un usuario de Jellyfin y configúrelo, luego selecciónelo a continuación. Cuando este perfil se aplica a una invitación, se crearán nuevos usuarios con la configuración.",
|
||||
"addProfileNameOf": "Nombre de perfil",
|
||||
"addProfileStoreHomescreenLayout": "Diseño de la pantalla de inicio de la tienda",
|
||||
"addProfileStoreHomescreenLayout": "Guardar el diseño de la pantalla de inicio",
|
||||
"inviteNoUsersCreated": "¡Ninguno todavía!",
|
||||
"inviteUsersCreated": "Usuarios creados",
|
||||
"inviteNoProfile": "Sin perfil",
|
||||
@@ -91,7 +91,27 @@
|
||||
"notifyEvent": "Notificar en:",
|
||||
"notifyInviteExpiry": "Al vencimiento",
|
||||
"notifyUserCreation": "Sobre la creación de usuarios",
|
||||
"conditionals": "Condicionales"
|
||||
"conditionals": "Condicionales",
|
||||
"donate": "Donar",
|
||||
"add": "Agregar",
|
||||
"templates": "Plantillas",
|
||||
"contactThrough": "Contactar a través de:",
|
||||
"select": "Seleccionar",
|
||||
"sendPIN": "Pídale al usuario que envíe el PIN a continuación al bot.",
|
||||
"searchDiscordUser": "Comienza a escribir el nombre de usuario de Discord para encontrar al usuario.",
|
||||
"findDiscordUser": "Encontrar usuario de Discord",
|
||||
"linkMatrixDescription": "Ingrese el nombre de usuario y la contraseña del usuario para usar como bot. Una vez enviada, la aplicación se reiniciará.",
|
||||
"matrixHomeServer": "Dirección del servidor de inicio",
|
||||
"saveAsTemplate": "Guardar como plantilla",
|
||||
"deleteTemplate": "Eliminar plantilla",
|
||||
"templateEnterName": "Ingrese un nombre para guardar esta plantilla.",
|
||||
"setExpiry": "Establecer vencimiento",
|
||||
"sendPWR": "Enviar restablecimiento de contraseña",
|
||||
"sendPWRSuccess": "Se envió el enlace para restablecer la contraseña.",
|
||||
"sendPWRSuccessManual": "Si el usuario no lo ha recibido, presione copiar para generar el enlace y enviárselo manualmente.",
|
||||
"sendPWRValidFor": "El enlace es válido por 30m.",
|
||||
"sendPWRManual": "El usuario {n} no tiene ningún método de contacto, presione copiar para generar el enlace para enviarle.",
|
||||
"ombiProfile": "Perfil de usuario de Ombi"
|
||||
},
|
||||
"notifications": {
|
||||
"changedEmailAddress": "Se cambió la dirección de correo electrónico de {n}.",
|
||||
@@ -105,7 +125,7 @@
|
||||
"errorConnection": "No se pudo conectar a jfa-go.",
|
||||
"error401Unauthorized": "No autorizado. Intente actualizar la página.",
|
||||
"errorSettingsAppliedNoHomescreenLayout": "Se aplicó la configuración, pero es posible que no se haya aplicado el diseño de la pantalla de inicio.",
|
||||
"errorHomescreenAppliedNoSettings": "Se aplicó el diseño de la pantalla de inicio, pero es posible que no se haya aplicado la configuración.",
|
||||
"errorHomescreenAppliedNoSettings": "Se aplicó el diseño de la pantalla de inicio, pero es posible que la aplicación de la configuración haya fallado.",
|
||||
"errorSettingsFailed": "La aplicación falló.",
|
||||
"errorLoginBlank": "El nombre de usuario y/o la contraseña se dejaron en blanco.",
|
||||
"errorUnknown": "Error desconocido.",
|
||||
@@ -119,16 +139,22 @@
|
||||
"errorSaveSettings": "No se pudo guardar la configuración.",
|
||||
"errorLoadSettings": "No se pudo cargar la configuración.",
|
||||
"errorSetOmbiDefaults": "No se pudieron almacenar los valores predeterminados de ombi.",
|
||||
"errorLoadOmbiUsers": "No se pudieron cargar los usuarios de ombi.",
|
||||
"errorLoadOmbiUsers": "No se pudieron cargar los usuarios de Ombi.",
|
||||
"errorChangedEmailAddress": "No se pudo cambiar la dirección de correo electrónico de {n}.",
|
||||
"errorFailureCheckLogs": "Fallido (ver consola / registros)",
|
||||
"errorPartialFailureCheckLogs": "Fallo parcial (ver consola / registros)",
|
||||
"errorFailureCheckLogs": "Fallido (ver consola/registros)",
|
||||
"errorPartialFailureCheckLogs": "Fallo parcial (ver consola/registros)",
|
||||
"errorUserCreated": "No se pudo crear el usuario {n}.",
|
||||
"errorSendWelcomeEmail": "No se pudo enviar el correo electrónico de bienvenida (verifique la consola / registros)",
|
||||
"errorSendWelcomeEmail": "No se pudo enviar el mensaje de bienvenida (verifique la consola/registros)",
|
||||
"errorApplyUpdate": "No se pudo aplicar la actualización, intente manualmente.",
|
||||
"errorCheckUpdate": "No se pudo comprobar la actualización.",
|
||||
"updateAvailable": "Hay una nueva actualización disponible, verifique la configuración.",
|
||||
"noUpdatesAvailable": "No hay nuevas actualizaciones disponibles."
|
||||
"noUpdatesAvailable": "No hay nuevas actualizaciones disponibles.",
|
||||
"updateAppliedRefresh": "Actualización aplicada, por favor actualice.",
|
||||
"accountConnected": "Cuenta vinculada.",
|
||||
"savedAnnouncement": "Anuncio guardado.",
|
||||
"telegramVerified": "Cuenta de Telegram verificada.",
|
||||
"setOmbiProfile": "Perfil de Ombi guardado.",
|
||||
"errorSetOmbiProfile": "No se pudo guardar el perfil de Ombi."
|
||||
},
|
||||
"quantityStrings": {
|
||||
"modifySettingsFor": {
|
||||
@@ -140,8 +166,8 @@
|
||||
"plural": "Eliminar {n} usuarios"
|
||||
},
|
||||
"disableUsers": {
|
||||
"singular": "Deshabilitar {n} usuario",
|
||||
"plural": "Inhabilitar {n} usuarios"
|
||||
"singular": "Desactivar {n} usuario",
|
||||
"plural": "Desactivar {n} usuarios"
|
||||
},
|
||||
"reEnableUsers": {
|
||||
"singular": "Reactivar {n} usuario",
|
||||
@@ -156,32 +182,32 @@
|
||||
"plural": "Borrar usuarios"
|
||||
},
|
||||
"deletedUser": {
|
||||
"singular": "Usuario eliminado {n}.",
|
||||
"plural": "Usuarios eliminados {n}."
|
||||
"singular": "{n} usuario eliminado.",
|
||||
"plural": "{n} usuarios eliminados."
|
||||
},
|
||||
"disabledUser": {
|
||||
"singular": "Usuario deshabilitado {n}.",
|
||||
"plural": "Usuarios deshabilitados {n}."
|
||||
"singular": "{n} usuario desactivado.",
|
||||
"plural": "{n} usuarios desactivados."
|
||||
},
|
||||
"enabledUser": {
|
||||
"singular": "Usuario {n} habilitado.",
|
||||
"plural": "Usuarios {n} habilitados."
|
||||
"singular": "{n} usuario activado.",
|
||||
"plural": "{n} usuarios activados."
|
||||
},
|
||||
"announceTo": {
|
||||
"singular": "Anunciar al usuario {n}",
|
||||
"plural": "Anunciar a los usuarios {n}"
|
||||
"singular": "Anunciar a {n} usuario",
|
||||
"plural": "Anunciar a {n} usuarios"
|
||||
},
|
||||
"appliedSettings": {
|
||||
"singular": "Se aplicó la configuración al usuario {n}.",
|
||||
"plural": "Se aplicó la configuración a los usuarios {n}."
|
||||
"singular": "Se aplicó la configuración a {n} usuario.",
|
||||
"plural": "Se aplicó la configuración a {n} usuarios."
|
||||
},
|
||||
"extendExpiry": {
|
||||
"singular": "Extender la expiración para el usuario {n}",
|
||||
"plural": "Extender la expiración para los usuarios {n}"
|
||||
"singular": "Extender la expiración para {n} usuario",
|
||||
"plural": "Extender la expiración para {n} usuarios"
|
||||
},
|
||||
"extendedExpiry": {
|
||||
"singular": "Caducidad extendida para el usuario {n}.",
|
||||
"plural": "Caducidad extendida para los usuarios {n}."
|
||||
"singular": "Caducidad extendida para {n} usuario.",
|
||||
"plural": "Caducidad extendida para {n} usuarios."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"modifySettings": "Modifier les paramètres",
|
||||
"modifySettingsDescription": "Appliquez les paramètres à partir d'un profil existant ou obtenez-les directement auprès d'un utilisateur.",
|
||||
"applyHomescreenLayout": "Appliquer la disposition de l'écran d'accueil",
|
||||
"sendDeleteNotificationEmail": "Envoyer un e-mail de notification",
|
||||
"sendDeleteNotificationEmail": "Envoyer un message de notification",
|
||||
"sendDeleteNotifiationExample": "Votre compte a été supprimé.",
|
||||
"settingsRestartRequired": "Redémarrage nécessaire",
|
||||
"settingsRestartRequiredDescription": "Un redémarrage est nécessaire pour appliquer certains paramètres que vous avez modifiés. Redémarrer maintenant ou plus tard ?",
|
||||
@@ -45,7 +45,7 @@
|
||||
"settingsRequiredOrRestartMessage": "Remarque : {n} indique un champ obligatoire, {n} indique que les modifications nécessitent un redémarrage.",
|
||||
"settingsSave": "Sauver",
|
||||
"ombiUserDefaults": "Paramètres par défaut de l'utilisateur Ombi",
|
||||
"ombiUserDefaultsDescription": "Créez un utilisateur Ombi et configurez-le, puis sélectionnez-le ci-dessous. Ses paramètres/autorisations seront stockés et appliqués aux nouveaux utilisateurs Ombi créés par jfa-go",
|
||||
"ombiUserDefaultsDescription": "Créez un utilisateur Ombi et configurez-le, puis sélectionnez-le ci-dessous. Ses paramètres/autorisations seront stockés et appliqués aux nouveaux utilisateurs Ombi créés par jfa-go lorsque ce profil est sélectionné.",
|
||||
"userProfiles": "Profils d'utilisateurs",
|
||||
"userProfilesDescription": "Les profils sont appliqués aux utilisateurs lorsqu'ils créent un compte. Un profil inclut les droits d'accès à la bibliothèque et la disposition de l'écran d'accueil.",
|
||||
"userProfilesIsDefault": "Défaut",
|
||||
@@ -68,15 +68,15 @@
|
||||
"settingsRestarting": "Redémarrage…",
|
||||
"settingsRestart": "Redémarrer",
|
||||
"announce": "Annoncer",
|
||||
"subject": "Sujet du courriel",
|
||||
"subject": "Sujet",
|
||||
"message": "Message",
|
||||
"markdownSupported": "Markdown est pris en charge.",
|
||||
"customizeEmailsDescription": "Si vous ne souhaitez pas utiliser les modèles d'e-mails de jfa-go, vous pouvez créer les vôtres à l'aide de Markdown.",
|
||||
"customizeMessagesDescription": "Si vous ne souhaitez pas utiliser les modèles d'e-mails de jfa-go, vous pouvez créer les vôtres à l'aide de Markdown.",
|
||||
"variables": "Variables",
|
||||
"preview": "Aperçu",
|
||||
"reset": "Réinitialiser",
|
||||
"reset": "Réinitialisation",
|
||||
"edit": "Éditer",
|
||||
"customizeEmails": "Personnaliser les e-mails",
|
||||
"customizeMessages": "Personnaliser les e-mails",
|
||||
"inviteDuration": "Durée de l'invitation",
|
||||
"enabled": "Activé",
|
||||
"disabled": "Désactivé",
|
||||
@@ -90,7 +90,29 @@
|
||||
"update": "Mise à jour",
|
||||
"download": "Téléchargement",
|
||||
"search": "Recherche",
|
||||
"conditionals": "Conditions"
|
||||
"conditionals": "Conditions",
|
||||
"userExpiryDescription": "Un laps de temps spécifié après chaque inscription, jfa-go supprimera / désactivera le compte. Vous pouvez modifier ce comportement dans les paramètres.",
|
||||
"donate": "Faire un don",
|
||||
"extendExpiry": "Prolonger l'expiration",
|
||||
"contactThrough": "Contacté par :",
|
||||
"sendPIN": "Demandez à l'utilisateur d'envoyer le code PIN ci-dessous au bot.",
|
||||
"add": "Ajouter",
|
||||
"select": "Sélectionner",
|
||||
"findDiscordUser": "Trouver l'utilisateur Discord",
|
||||
"linkMatrixDescription": "Entrez le nom d'utilisateur et le mot de passe de l'utilisateur pour l’utilisateur comme bot. Une fois soumis, l'application va redémarrer.",
|
||||
"searchDiscordUser": "Commencez à taper le nom d'utilisateur Discord pour trouver l'utilisateur.",
|
||||
"matrixHomeServer": "Adresse du serveur domestique",
|
||||
"saveAsTemplate": "Sauvegarder comme modèle",
|
||||
"templateEnterName": "Entrez un nom pour sauvegarder ce modèle.",
|
||||
"deleteTemplate": "Supprimer le modèle",
|
||||
"templates": "Modèles",
|
||||
"setExpiry": "Paramétrer l'expiration",
|
||||
"sendPWRSuccess": "Réinitialisation du mot de passe envoyée.",
|
||||
"sendPWR": "Envoyer une réinitialisation du mot de passe",
|
||||
"sendPWRValidFor": "Ce lien est valable 30min.",
|
||||
"sendPWRManual": "L'utilisateur {n} n'a pas indiqué de méthode de contact, appuyez sur copier pour recevoir un lien à lui envoyer.",
|
||||
"sendPWRSuccessManual": "Si l'utilisateur ne l'a pas reçu, appuyez sur copier pour recevoir un lien à lui envoyer manuellement.",
|
||||
"ombiProfile": "Profil d'utilisateur Ombi"
|
||||
},
|
||||
"notifications": {
|
||||
"changedEmailAddress": "Adresse e-mail modifiée de {n}.",
|
||||
@@ -119,11 +141,21 @@
|
||||
"errorFailureCheckLogs": "Échec (vérifier la console / les journaux)",
|
||||
"errorPartialFailureCheckLogs": "Panne partielle (vérifier la console / les journaux)",
|
||||
"errorUserCreated": "Echec lors de la création de l'utilisateur {n}.",
|
||||
"errorSendWelcomeEmail": "Echec lors de l'envoi du mail de bienvenue (vérifier la console/les journaux)",
|
||||
"errorSendWelcomeEmail": "Echec lors de l'envoi du message de bienvenue (vérifier la console/les journaux)",
|
||||
"sentAnnouncement": "Annonce envoyée.",
|
||||
"saveEmail": "Email enregistré.",
|
||||
"errorSaveEmail": "Échec de l'enregistrement de l'e-mail.",
|
||||
"updateApplied": "Mise à jour appliquée, veuillez redémarrer."
|
||||
"updateApplied": "Mise à jour appliquée, veuillez redémarrer.",
|
||||
"errorApplyUpdate": "Échec de l'application de la mise à jour, essayez manuellement.",
|
||||
"errorCheckUpdate": "Échec de la vérification de la mise à jour.",
|
||||
"updateAvailable": "Une nouvelle mise à jour est disponible, vérifiez les paramètres.",
|
||||
"noUpdatesAvailable": "Aucune nouvelle mise à jour disponible.",
|
||||
"telegramVerified": "Compte Telegram vérifié.",
|
||||
"updateAppliedRefresh": "Mise à jour appliquée, veuillez actualiser.",
|
||||
"accountConnected": "Compte connecté.",
|
||||
"savedAnnouncement": "Annonce enregistrée.",
|
||||
"setOmbiProfile": "Profil ombi enregistré.",
|
||||
"errorSetOmbiProfile": "Echec de la sauvegarde du profil ombi."
|
||||
},
|
||||
"quantityStrings": {
|
||||
"modifySettingsFor": {
|
||||
@@ -153,6 +185,30 @@
|
||||
"announceTo": {
|
||||
"singular": "Annonce à {n} utilisateur",
|
||||
"plural": "Annonce à {n} utilisateurs"
|
||||
},
|
||||
"enabledUser": {
|
||||
"plural": "{n} utilisateurs activés.",
|
||||
"singular": "{n} utilisateur activé."
|
||||
},
|
||||
"extendExpiry": {
|
||||
"singular": "Prolonger l'expiration pour {n} utilisateur",
|
||||
"plural": "Prolonger l'expiration pour {n} utilisateurs"
|
||||
},
|
||||
"extendedExpiry": {
|
||||
"singular": "Expiration prolongée pour {n} utilisateur.",
|
||||
"plural": "Expiration prolongée pour {n} utilisateurs."
|
||||
},
|
||||
"disableUsers": {
|
||||
"singular": "Désactiver {n} utilisateur",
|
||||
"plural": "Désactiver {n} utilisateurs"
|
||||
},
|
||||
"reEnableUsers": {
|
||||
"singular": "Ré-activer {n} utilisateur",
|
||||
"plural": "Ré-activer {n} utilisateurs"
|
||||
},
|
||||
"disabledUser": {
|
||||
"singular": "{n} utilisateur désactivé.",
|
||||
"plural": "{n} utilisateurs désactivés."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,8 +69,8 @@
|
||||
"preview": "Pratinjau",
|
||||
"reset": "Setel ulang",
|
||||
"edit": "Edit",
|
||||
"customizeEmails": "Sesuaikan Email",
|
||||
"customizeEmailsDescription": "Jika Anda tidak ingin menggunakan templat email jfa-go, Anda dapat membuatnya sendiri menggunakan Markdown.",
|
||||
"customizeMessages": "Sesuaikan Email",
|
||||
"customizeMessagesDescription": "Jika Anda tidak ingin menggunakan templat email jfa-go, Anda dapat membuatnya sendiri menggunakan Markdown.",
|
||||
"announce": "Mengumumkan",
|
||||
"subject": "Subjek Email",
|
||||
"message": "Pesan",
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"modifySettings": "Instellingen aanpassen",
|
||||
"modifySettingsDescription": "Pas instellingen van een bestaand profiel toe, of neem ze direct over van een gebruiker.",
|
||||
"applyHomescreenLayout": "Sla startpagina indeling op",
|
||||
"sendDeleteNotificationEmail": "Stuur meldingse-mail",
|
||||
"sendDeleteNotificationEmail": "Stuur melding",
|
||||
"sendDeleteNotifiationExample": "Je account is verwijderd.",
|
||||
"settingsRestartRequired": "Herstart nodig",
|
||||
"settingsRestartRequiredDescription": "Er is een herstart nodig om de wijzigingen door te voeren. Herstart nu of later?",
|
||||
@@ -43,7 +43,7 @@
|
||||
"settingsRequiredOrRestartMessage": "Opmerking: {n} is een verplicht veld, {n} geeft aan dat na wijzigen een herstart nodig is.",
|
||||
"settingsSave": "Opslaan",
|
||||
"ombiUserDefaults": "Ombi gebruiker standaardinstellingen",
|
||||
"ombiUserDefaultsDescription": "Maak een Ombi gebruiker aan met de gewenste instellingen, en selecteer deze hieronder. Deze instellingen/rechten worden opgeslagen en toegepast voor nieuwe Ombi gebruikers die jfa-go aanmaakt",
|
||||
"ombiUserDefaultsDescription": "Maak een Ombi gebruiker aan met de gewenste instellingen, en selecteer deze hieronder. Deze instellingen/rechten worden opgeslagen en toegepast voor nieuwe Ombi gebruikers die jfa-go aanmaakt als dit profiel is geselecteerd.",
|
||||
"userProfiles": "Gebruikersprofielen",
|
||||
"userProfilesDescription": "Profielen worden toegepast op gebruikers wanneer ze een account aanmaken. Een profiel bevat rechten voor bibliotheken en indeling van de startpagina.",
|
||||
"userProfilesIsDefault": "Standaard",
|
||||
@@ -67,14 +67,14 @@
|
||||
"settingsRestarting": "Aan het herstarten…",
|
||||
"announce": "Aankondiging",
|
||||
"markdownSupported": "Markdown wordt ondersteund.",
|
||||
"subject": "E-mailonderwerp",
|
||||
"subject": "Onderwerp",
|
||||
"message": "Bericht",
|
||||
"variables": "Variabelen",
|
||||
"customizeEmailsDescription": "Als je de e-mailsjablonen van jfa-go niet wilt gebruiken, kun je met gebruik van Markdown je eigen aanmaken.",
|
||||
"customizeMessagesDescription": "Als je de e-mailsjablonen van jfa-go niet wilt gebruiken, kun je met gebruik van Markdown je eigen aanmaken.",
|
||||
"preview": "Voorbeeld",
|
||||
"reset": "Resetten",
|
||||
"reset": "Reset",
|
||||
"edit": "Bewerken",
|
||||
"customizeEmails": "E-mails aanpassen",
|
||||
"customizeMessages": "E-mails aanpassen",
|
||||
"inviteDuration": "Geldigheidsduur uitnodiging",
|
||||
"userExpiryDescription": "Een bepaalde tijd na elke aanmelding, wordt de account verwijderd/uitgeschakeld door jfa-go. Dit kan aangepast worden in de instellingen.",
|
||||
"enabled": "Ingeschakeld",
|
||||
@@ -91,7 +91,27 @@
|
||||
"inviteMonths": "Maanden",
|
||||
"reEnable": "Opnieuw inschakelen",
|
||||
"disable": "Uitschakelen",
|
||||
"conditionals": "Voorwaarden"
|
||||
"conditionals": "Voorwaarden",
|
||||
"donate": "Doneer",
|
||||
"contactThrough": "Stuur bericht via:",
|
||||
"sendPIN": "Vraag de gebruiker om onderstaande pincode naar de bot te sturen.",
|
||||
"add": "Voeg toe",
|
||||
"searchDiscordUser": "Begin de Discord gebruikersnaam te typen om de gebruiker te vinden.",
|
||||
"linkMatrixDescription": "Vul de gebruikersnaam en wachtwoord in van de gebruiker om als bot te gebruiken. De app start zodra ze zijn verstuurd.",
|
||||
"select": "Selecteer",
|
||||
"findDiscordUser": "Zoek Discord gebruiker",
|
||||
"matrixHomeServer": "Adres home server",
|
||||
"templates": "Sjablonen",
|
||||
"templateEnterName": "Voer een naam in om dit sjabloon op te slaan.",
|
||||
"saveAsTemplate": "Sla op als sjabloon",
|
||||
"deleteTemplate": "Verwijder sjabloon",
|
||||
"setExpiry": "Stel verloop in",
|
||||
"sendPWRManual": "Gebruiker {n} heeft geen contactmogelijkheden, druk op kopiëren om een link te krijgen die je kunt sturen.",
|
||||
"sendPWRSuccessManual": "Als de gebruiker hem niet heeft ontvangen, druk dan op kopiëren om een link te krijgen die je handmatig kunt sturen.",
|
||||
"sendPWR": "Verstuur wachtwoordreset",
|
||||
"sendPWRSuccess": "Wachtwoordreset-link verstuurd.",
|
||||
"sendPWRValidFor": "De link is 30m geldig.",
|
||||
"ombiProfile": "Ombi gebruikersprofiel"
|
||||
},
|
||||
"notifications": {
|
||||
"changedEmailAddress": "E-mailadres van {n} gewijzigd.",
|
||||
@@ -119,7 +139,7 @@
|
||||
"errorChangedEmailAddress": "Wijzigen van e-mailadres van {n} mislukt.",
|
||||
"errorFailureCheckLogs": "Mislukt (controleer console/logbestanden)",
|
||||
"errorPartialFailureCheckLogs": "Gedeeltelijke fout (controleer console/logbestanden)",
|
||||
"errorSendWelcomeEmail": "Versturen van welkomste-mail is mislukt (zie console/logs)",
|
||||
"errorSendWelcomeEmail": "Versturen van welkomstbericht is mislukt (zie console/logs)",
|
||||
"errorUserCreated": "Aanmaken van gebruiker {n} is mislukt.",
|
||||
"sentAnnouncement": "Aankondiging verzonden.",
|
||||
"saveEmail": "E-mail opgeslagen.",
|
||||
@@ -128,7 +148,13 @@
|
||||
"errorApplyUpdate": "Installatie van update mislukt, probeer handmatig.",
|
||||
"errorCheckUpdate": "Controleren op update mislukt.",
|
||||
"updateAvailable": "Er is een nieuwe update beschikbaar, kijk bij instellingen.",
|
||||
"noUpdatesAvailable": "Geen nieuwe updates beschikbaar."
|
||||
"noUpdatesAvailable": "Geen nieuwe updates beschikbaar.",
|
||||
"telegramVerified": "Telegram-account goedgekeurd.",
|
||||
"updateAppliedRefresh": "Update toegepast, ververs alsjeblieft.",
|
||||
"accountConnected": "Account gekoppeld.",
|
||||
"savedAnnouncement": "Aankondiging opgeslagen.",
|
||||
"setOmbiProfile": "Opgeslagen ombi-profiel.",
|
||||
"errorSetOmbiProfile": "Opslaan van ombi-profiel mislukt."
|
||||
},
|
||||
"quantityStrings": {
|
||||
"modifySettingsFor": {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"modifySettings": "Modificar configurações",
|
||||
"modifySettingsDescription": "Aplique as configurações de um perfil existente ou obtenha-as diretamente de um usuário.",
|
||||
"applyHomescreenLayout": "Aplicar layout na tela inicial",
|
||||
"sendDeleteNotificationEmail": "Enviar email de notificação",
|
||||
"sendDeleteNotificationEmail": "Enviar mensagem de notificação",
|
||||
"sendDeleteNotifiationExample": "Sua conta foi deletada.",
|
||||
"settingsRestartRequired": "Necessário reiniciar",
|
||||
"settingsRestartRequiredDescription": "É necessário reiniciar para aplicar algumas configurações alteradas. Deseja reiniciar agora ou mais tarde?",
|
||||
@@ -66,15 +66,15 @@
|
||||
"settingsRestart": "Reiniciar",
|
||||
"settingsRestarting": "Reiniciando…",
|
||||
"announce": "Anunciar",
|
||||
"subject": "Assunto do email",
|
||||
"subject": "Assunto",
|
||||
"message": "Mensagem",
|
||||
"markdownSupported": "Suporte a Markdown.",
|
||||
"customizeEmailsDescription": "Se não quiser usar os modelos de email do jfa-go, você pode criar o seu próprio usando o Markdown.",
|
||||
"customizeMessagesDescription": "Se não quiser usar os modelos de email do jfa-go, você pode criar o seu próprio usando o Markdown.",
|
||||
"variables": "Variáveis",
|
||||
"preview": "Pre-visualizar",
|
||||
"reset": "Reiniciar",
|
||||
"edit": "Editar",
|
||||
"customizeEmails": "Customizar Emails",
|
||||
"customizeMessages": "Customizar Emails",
|
||||
"disabled": "Desativado",
|
||||
"userExpiryDescription": "Após um determinado período de tempo de cada inscrição, o jfa-go apagará/desabilitará a conta. Você pode alterar essa opção nas configurações.",
|
||||
"inviteDuration": "Duração do Convite",
|
||||
@@ -91,7 +91,20 @@
|
||||
"inviteMonths": "Meses",
|
||||
"reEnable": "Reativar",
|
||||
"disable": "Desativar",
|
||||
"conditionals": "Condicionais"
|
||||
"conditionals": "Condicionais",
|
||||
"donate": "Doar",
|
||||
"contactThrough": "Contato através:",
|
||||
"sendPIN": "Peça ao usuário para enviar o PIN abaixo para o bot.",
|
||||
"searchDiscordUser": "Digite o nome de usuário do Discord.",
|
||||
"findDiscordUser": "Encontrar usuário Discord",
|
||||
"add": "Adicionar",
|
||||
"linkMatrixDescription": "Digite o nome de usuário e a senha para usar como bot. Depois de enviado, o aplicativo será reiniciado.",
|
||||
"select": "Selecionar",
|
||||
"templates": "Modelos",
|
||||
"matrixHomeServer": "Endereço do servidor local",
|
||||
"saveAsTemplate": "Salvar o modelo",
|
||||
"deleteTemplate": "Deletar modelo",
|
||||
"templateEnterName": "Digite um nome para salvar este modelo."
|
||||
},
|
||||
"notifications": {
|
||||
"changedEmailAddress": "Endereço de e-mail alterado de {n}.",
|
||||
@@ -120,7 +133,7 @@
|
||||
"errorFailureCheckLogs": "Falha (verificar console/logs)",
|
||||
"errorPartialFailureCheckLogs": "Falha parcial (verificar console/logs)",
|
||||
"errorUserCreated": "Falha ao criar o usuário {n}.",
|
||||
"errorSendWelcomeEmail": "Falha ao enviar e-mail de boas-vindas (verifique console/logs)",
|
||||
"errorSendWelcomeEmail": "Falha ao enviar mensagem de boas-vindas (verifique console/logs)",
|
||||
"sentAnnouncement": "Comunicado enviado.",
|
||||
"saveEmail": "Email salvo.",
|
||||
"errorSaveEmail": "Falha ao salvar o email.",
|
||||
@@ -128,7 +141,11 @@
|
||||
"errorApplyUpdate": "Falha ao aplicar a atualização, tente manualmente.",
|
||||
"updateAvailable": "Uma nova atualização está disponível, verifique as configurações.",
|
||||
"errorCheckUpdate": "Falha ao verificar atualizações.",
|
||||
"noUpdatesAvailable": "Nenhuma atualização disponível."
|
||||
"noUpdatesAvailable": "Nenhuma atualização disponível.",
|
||||
"telegramVerified": "Conta do Telegram verificada.",
|
||||
"updateAppliedRefresh": "Atualização instalada, atualize.",
|
||||
"accountConnected": "Conta conectada.",
|
||||
"savedAnnouncement": "Anúncio salvo."
|
||||
},
|
||||
"quantityStrings": {
|
||||
"modifySettingsFor": {
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
"preview": "Förhandsvisning",
|
||||
"reset": "Återställ",
|
||||
"edit": "Redigera",
|
||||
"customizeEmails": "Anpassa e-post",
|
||||
"customizeEmailsDescription": "Om du inte vill använda jfa-go's e-postmallar, så kan du skapa dina egna med Markdown.",
|
||||
"customizeMessages": "Anpassa e-post",
|
||||
"customizeMessagesDescription": "Om du inte vill använda jfa-go's e-postmallar, så kan du skapa dina egna med Markdown.",
|
||||
"markdownSupported": "Markdown stöds.",
|
||||
"modifySettings": "Ändra inställningar",
|
||||
"modifySettingsDescription": "Tillämpa inställningar från en befintlig profil eller kopiera dem direkt från en användare.",
|
||||
|
||||
210
lang/admin/vi-vn.json
Normal file
@@ -0,0 +1,210 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "Vietnamese (VN)"
|
||||
},
|
||||
"strings": {
|
||||
"invites": "Lời mời",
|
||||
"accounts": "Tài khoản",
|
||||
"settings": "Cài đặt",
|
||||
"inviteMonths": "Tháng",
|
||||
"inviteDays": "Ngày",
|
||||
"inviteHours": "Giờ",
|
||||
"inviteMinutes": "Phút",
|
||||
"inviteNumberOfUses": "Số lần sử dụng",
|
||||
"inviteDuration": "Thời hạn hiệu lực",
|
||||
"warning": "Cảnh báo",
|
||||
"inviteInfiniteUsesWarning": "các lời mời không giới hạn số lần sử dụng có thể bị lạm dụng",
|
||||
"inviteSendToEmail": "Gửi tới",
|
||||
"login": "Đăng nhập",
|
||||
"logout": "Đăng xuất",
|
||||
"create": "Tạo mới",
|
||||
"apply": "Áp dụng",
|
||||
"delete": "Xóa",
|
||||
"add": "Thêm",
|
||||
"select": "Chọn",
|
||||
"name": "Tên",
|
||||
"date": "Ngày",
|
||||
"enabled": "Mở",
|
||||
"disabled": "Tắt",
|
||||
"reEnable": "Mở lại",
|
||||
"setExpiry": "Đặt hết hạn",
|
||||
"disable": "Tắt",
|
||||
"admin": "Admin",
|
||||
"updates": "Cập nhật",
|
||||
"update": "Cập nhật",
|
||||
"download": "Tải về",
|
||||
"search": "Tìm kiếm",
|
||||
"advancedSettings": "Cài đặt Cấp cao",
|
||||
"lastActiveTime": "Lần cuối Hoạt động",
|
||||
"from": "Từ",
|
||||
"user": "Người dùng",
|
||||
"expiry": "Hết hạn",
|
||||
"userExpiry": "Hết hạn Người dùng",
|
||||
"userExpiryDescription": "Sau một khoảng thời gian nhất định sau khi mỗi đăng ký, jfa-go sẽ xóa/vô hiệu hóa tài khoản. Bạn có thể chỉnh sửa chế độ này trong cài đặt.",
|
||||
"aboutProgram": "Thông tin",
|
||||
"version": "Phiên bản",
|
||||
"commitNoun": "Gửi",
|
||||
"newUser": "Người dùng mới",
|
||||
"profile": "Hồ sơ",
|
||||
"unknown": "Không xác định",
|
||||
"label": "Nhãn",
|
||||
"announce": "Thông báo",
|
||||
"templates": "Mẫu",
|
||||
"subject": "Chủ đề",
|
||||
"message": "Tin nhắn",
|
||||
"variables": "Biến",
|
||||
"conditionals": "Điều kiện",
|
||||
"preview": "Xem trước",
|
||||
"reset": "Đặt lại",
|
||||
"edit": "Chỉnh sửa",
|
||||
"donate": "Đóng góp",
|
||||
"sendPWR": "Gửi Đặt lại Mật khẩu",
|
||||
"contactThrough": "Liên lạc qua:",
|
||||
"extendExpiry": "Gia hạn",
|
||||
"sendPWRManual": "Người dùng {n} không có phương thức liên lạc, nhấn chép để lấy đường link để gửi cho họ.",
|
||||
"sendPWRSuccess": "Link đặt lại mật khẩu đã được gửi đi.",
|
||||
"sendPWRSuccessManual": "Nếu người dùng chưa nhận được, nhấn chép để lấy đường link có thể gửi đến họ.",
|
||||
"sendPWRValidFor": "Link có hiệu lực trong vòng 30 phút.",
|
||||
"customizeMessages": "Tùy chỉnh Tin nhắn",
|
||||
"customizeMessagesDescription": "Nếu bạn không muốn sử dụng mẫu tin nhắn của jfa-go, bạn có thể tự tạo mẫu của mình bằng Markdown.",
|
||||
"markdownSupported": "Có hỗ trợ Markdown.",
|
||||
"modifySettings": "Chỉnh sửa Cài đặt",
|
||||
"modifySettingsDescription": "Áp dụng các cài đặt từ một mẫu có sẵn, hoặc lấy trực tiếp từ một người dùng.",
|
||||
"applyHomescreenLayout": "Áp dụng bố cục trang chủ",
|
||||
"sendDeleteNotificationEmail": "Gửi tin nhắn thông báo",
|
||||
"sendDeleteNotifiationExample": "Tài khoản của bạn đã bị xóa.",
|
||||
"settingsRestart": "Khởi động lại",
|
||||
"settingsRestarting": "Đang khởi động lại…",
|
||||
"settingsRestartRequired": "Cần khởi động lại",
|
||||
"settingsRestartRequiredDescription": "Một số cài đặt bạn đã thay đổi cần phải khởi động lại để có hiệu lực. Khởi động lại ngay hay để sau?",
|
||||
"settingsApplyRestartLater": "Áp dụng, khởi động lại sau",
|
||||
"settingsApplyRestartNow": "Áp dụng & khởi động lại",
|
||||
"settingsApplied": "Cài đặt đã được áp dụng.",
|
||||
"settingsRefreshPage": "Làm mới trang trong một vài giây nữa.",
|
||||
"settingsRequiredOrRestartMessage": "Lưu ý: {n} là những cài đặt cần thiết, {n} là những cài đặt cần khởi động lại nếu chúng được thay đổi.",
|
||||
"settingsSave": "Lưu",
|
||||
"ombiProfile": "Tài khoản người dùng Ombi",
|
||||
"ombiUserDefaultsDescription": "Tạo và cài đặt một tài khoản người dùng Ombi, sau đó chọn nó bên dưới. Các cài đặt/quyền sẽ được lưu lại và được áp dụng cho các tài khoản Ombi được tạo bởi jfa-go khi tài khoản mẫy này được chọn.",
|
||||
"userProfiles": "Thông tin Người dùng",
|
||||
"userProfilesDescription": "Mẫu tài khoản được áp dụng cho người dùng khi họ tạo tài khoản. Mẫu tài khoản bao gồm các quyền truy cập thư viện và bốc cục trang chủ.",
|
||||
"userProfilesIsDefault": "Mặc định",
|
||||
"userProfilesLibraries": "Thư viện",
|
||||
"addProfile": "Thêm Tài khoản Mẫu",
|
||||
"addProfileDescription": "Tạo một tài khoản Jellyfin và cấu hình nó, rồi chọn nó bên dưới. Khi tài khoản mẫu này được áp dụng trong lời mời, các người dùng mới sẽ được tạo với các cài đặt tương tự.",
|
||||
"addProfileNameOf": "Tên Tài khoản mẫu",
|
||||
"addProfileStoreHomescreenLayout": "Lưu bố cục trang chủ",
|
||||
"inviteNoUsersCreated": "Chưa có!",
|
||||
"inviteUsersCreated": "Người dùng đã tạo",
|
||||
"inviteNoProfile": "Không có Tài khoản mẫu",
|
||||
"inviteDateCreated": "Tạo",
|
||||
"inviteRemainingUses": "Số lần sử dụng còn lại",
|
||||
"inviteNoInvites": "Không có",
|
||||
"inviteExpiresInTime": "Hết hạn trong {n}",
|
||||
"notifyEvent": "Thông báo khi:",
|
||||
"notifyInviteExpiry": "Khi hết hạn",
|
||||
"notifyUserCreation": "Khi có người dùng được tạo",
|
||||
"sendPIN": "Yêu cầu người dùng gửi mã PIN bên dưới cho bot.",
|
||||
"searchDiscordUser": "Gõ tên người dùng Discord để tìm người dùng.",
|
||||
"findDiscordUser": "Tìm người dùng Discord",
|
||||
"linkMatrixDescription": "Nhập tên đăng nhập và mật khẩu của người dùng được sử dụng làm bot. Khi hoàn thành, ứng dụng sẽ khởi động lại.",
|
||||
"matrixHomeServer": "Địa chỉ máy chủ",
|
||||
"saveAsTemplate": "Lưu thành mẫu",
|
||||
"deleteTemplate": "Xóa mẫu",
|
||||
"templateEnterName": "Nhập tên mẫu để lưu mẫu này."
|
||||
},
|
||||
"notifications": {
|
||||
"changedEmailAddress": "Đã đổi địa chỉ email của {n}.",
|
||||
"userCreated": "Người dùng {n} đã được tạo.",
|
||||
"createProfile": "Đã tạo tài khoản mẫu {n}.",
|
||||
"saveSettings": "Cài đặt đã được lưu",
|
||||
"saveEmail": "Email đã được lưu.",
|
||||
"sentAnnouncement": "Thông báo đã được gửi.",
|
||||
"savedAnnouncement": "Thông báo đã được lưu.",
|
||||
"setOmbiProfile": "Mẫu tài khoản Ombi đã được lưu trữ.",
|
||||
"updateApplied": "Cập nhật mới đã được áp dụng, vui lòng khởi động lại.",
|
||||
"updateAppliedRefresh": "Cập nhật mới đã được áp dụng, vui lòng làm mới lại trang.",
|
||||
"telegramVerified": "Tài khoản Telegram đã được xác thực.",
|
||||
"accountConnected": "Tài khoản đã được kết nối.",
|
||||
"errorConnection": "Không thể kết nối với jfa-go.",
|
||||
"error401Unauthorized": "Không được phép. Hãy thử làm mới trang.",
|
||||
"errorSettingsAppliedNoHomescreenLayout": "Cài đặt đã được áp dụng, nhưng việc áp dụng bố cục màn hình chính có thể không thành công.",
|
||||
"errorHomescreenAppliedNoSettings": "",
|
||||
"errorSettingsFailed": "",
|
||||
"errorLoginBlank": "",
|
||||
"errorUnknown": "",
|
||||
"errorSaveEmail": "",
|
||||
"errorBlankFields": "",
|
||||
"errorDeleteProfile": "",
|
||||
"errorLoadProfiles": "",
|
||||
"errorCreateProfile": "",
|
||||
"errorSetDefaultProfile": "",
|
||||
"errorLoadUsers": "",
|
||||
"errorSaveSettings": "",
|
||||
"errorLoadSettings": "",
|
||||
"errorSetOmbiProfile": "",
|
||||
"errorLoadOmbiUsers": "",
|
||||
"errorChangedEmailAddress": "",
|
||||
"errorFailureCheckLogs": "",
|
||||
"errorPartialFailureCheckLogs": "",
|
||||
"errorUserCreated": "",
|
||||
"errorSendWelcomeEmail": "",
|
||||
"errorApplyUpdate": "",
|
||||
"errorCheckUpdate": "",
|
||||
"updateAvailable": "",
|
||||
"noUpdatesAvailable": ""
|
||||
},
|
||||
"quantityStrings": {
|
||||
"modifySettingsFor": {
|
||||
"singular": "",
|
||||
"plural": ""
|
||||
},
|
||||
"deleteNUsers": {
|
||||
"singular": "",
|
||||
"plural": ""
|
||||
},
|
||||
"disableUsers": {
|
||||
"singular": "",
|
||||
"plural": ""
|
||||
},
|
||||
"reEnableUsers": {
|
||||
"singular": "",
|
||||
"plural": ""
|
||||
},
|
||||
"addUser": {
|
||||
"singular": "",
|
||||
"plural": ""
|
||||
},
|
||||
"deleteUser": {
|
||||
"singular": "",
|
||||
"plural": ""
|
||||
},
|
||||
"deletedUser": {
|
||||
"singular": "",
|
||||
"plural": ""
|
||||
},
|
||||
"disabledUser": {
|
||||
"singular": "",
|
||||
"plural": ""
|
||||
},
|
||||
"enabledUser": {
|
||||
"singular": "",
|
||||
"plural": ""
|
||||
},
|
||||
"announceTo": {
|
||||
"singular": "",
|
||||
"plural": ""
|
||||
},
|
||||
"appliedSettings": {
|
||||
"singular": "",
|
||||
"plural": ""
|
||||
},
|
||||
"extendExpiry": {
|
||||
"singular": "",
|
||||
"plural": ""
|
||||
},
|
||||
"extendedExpiry": {
|
||||
"singular": "",
|
||||
"plural": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
204
lang/admin/zh-hans.json
Normal file
@@ -0,0 +1,204 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "简体中文(CN)"
|
||||
},
|
||||
"strings": {
|
||||
"invites": "邀请",
|
||||
"accounts": "用户",
|
||||
"settings": "设置",
|
||||
"inviteMonths": "月",
|
||||
"inviteDays": "日",
|
||||
"inviteHours": "小时",
|
||||
"inviteMinutes": "分钟",
|
||||
"inviteNumberOfUses": "使用次数",
|
||||
"inviteDuration": "邀请时长",
|
||||
"warning": "警告",
|
||||
"inviteInfiniteUsesWarning": "无限使用次数的邀请码可能被滥用",
|
||||
"inviteSendToEmail": "发送到",
|
||||
"login": "登录",
|
||||
"logout": "登出",
|
||||
"create": "创建",
|
||||
"apply": "申请",
|
||||
"delete": "删除",
|
||||
"add": "添加",
|
||||
"select": "选择",
|
||||
"name": "名称",
|
||||
"date": "日期",
|
||||
"enabled": "已启用",
|
||||
"disabled": "已禁用",
|
||||
"reEnable": "重新启用",
|
||||
"disable": "禁用",
|
||||
"admin": "管理员",
|
||||
"updates": "更新",
|
||||
"update": "更新",
|
||||
"download": "下载",
|
||||
"search": "搜索",
|
||||
"advancedSettings": "高级设置",
|
||||
"lastActiveTime": "上次活动",
|
||||
"from": "从",
|
||||
"user": "用户",
|
||||
"expiry": "到期",
|
||||
"userExpiry": "用户到期",
|
||||
"userExpiryDescription": "每次注册后的指定时间,jfa-go 将删除/禁用该帐户。您可以在设置中更改此行为。",
|
||||
"aboutProgram": "关于",
|
||||
"version": "版本",
|
||||
"commitNoun": "提交",
|
||||
"newUser": "新用户",
|
||||
"profile": "个人资料",
|
||||
"unknown": "未知",
|
||||
"label": "标签",
|
||||
"announce": "宣布",
|
||||
"templates": "模板",
|
||||
"subject": "主题",
|
||||
"message": "信息",
|
||||
"variables": "变量",
|
||||
"conditionals": "条件性条款",
|
||||
"preview": "预览",
|
||||
"reset": "重设",
|
||||
"edit": "编辑",
|
||||
"donate": "捐助",
|
||||
"contactThrough": "联系方式:",
|
||||
"extendExpiry": "延长有效期",
|
||||
"customizeMessages": "自定义消息",
|
||||
"customizeMessagesDescription": "如果不想使用 jfa-go 的消息模板,可以使用 Markdown 创建自己的消息模板。",
|
||||
"markdownSupported": "已支持Markdown。",
|
||||
"modifySettings": "修改设置",
|
||||
"modifySettingsDescription": "应用现有配置文件中的设置,或直接从用户处获取设置。",
|
||||
"applyHomescreenLayout": "应用主屏幕布局",
|
||||
"sendDeleteNotificationEmail": "发送通知消息",
|
||||
"sendDeleteNotifiationExample": "您的帐户已被删除。",
|
||||
"settingsRestart": "重启",
|
||||
"settingsRestarting": "正在重启……",
|
||||
"settingsRestartRequired": "需要重启",
|
||||
"settingsRestartRequiredDescription": "需要重新启动才能应用您更改的某些设置。现在重启还是稍后重启?",
|
||||
"settingsApplyRestartLater": "应用,稍后重启",
|
||||
"settingsApplyRestartNow": "应用并重启",
|
||||
"settingsApplied": "已应用设置。",
|
||||
"settingsRefreshPage": "几秒钟后刷新页面。",
|
||||
"settingsRequiredOrRestartMessage": "注意:{n} 表示必填字段,{n} 表示更改需要重新启动。",
|
||||
"settingsSave": "保存",
|
||||
"ombiUserDefaults": "Ombi 用户默认值",
|
||||
"ombiUserDefaultsDescription": "创建并配置 Ombi 用户,然后在下面选择它。它的设置/权限将被存储并应用于由 jfa-go 创建的新 Ombi 用户",
|
||||
"userProfiles": "用户档案",
|
||||
"userProfilesDescription": "配置文件在用户创建帐户时应用于用户。配置文件包括库访问权限和主屏幕布局。",
|
||||
"userProfilesIsDefault": "默认",
|
||||
"userProfilesLibraries": "库",
|
||||
"addProfile": "添加档案",
|
||||
"addProfileDescription": "创建一个 Jellyfin 用户并配置它,然后在下面选择它。将此配置文件应用于邀请时,将使用这些设置创建新用户。",
|
||||
"addProfileNameOf": "配置文件名称",
|
||||
"addProfileStoreHomescreenLayout": "保存主屏幕布局",
|
||||
"inviteNoUsersCreated": "暂时不!",
|
||||
"inviteUsersCreated": "已创建的用户",
|
||||
"inviteNoProfile": "没有个人资料",
|
||||
"inviteDateCreated": "已创建",
|
||||
"inviteRemainingUses": "剩余使用次数",
|
||||
"inviteNoInvites": "无",
|
||||
"inviteExpiresInTime": "在 {n} 到期",
|
||||
"notifyEvent": "通知:",
|
||||
"notifyInviteExpiry": "在到期时",
|
||||
"notifyUserCreation": "在创建用户时",
|
||||
"sendPIN": "要求用户将下面的 PIN 发送给机器人。",
|
||||
"searchDiscordUser": "开始输入 Discord 用户名以查找用户。",
|
||||
"findDiscordUser": "查找 Discord 用户",
|
||||
"linkMatrixDescription": "输入要用作机器人的用户的用户名和密码。一旦提交,应用程序将重新启动。",
|
||||
"matrixHomeServer": "主服务器地址",
|
||||
"saveAsTemplate": "保存为模板",
|
||||
"deleteTemplate": "删除模板",
|
||||
"templateEnterName": "输入名称以保存此模板。"
|
||||
},
|
||||
"notifications": {
|
||||
"changedEmailAddress": "更改了 {n} 的电子邮件地址。",
|
||||
"userCreated": "用户 {n} 已创建。",
|
||||
"createProfile": "创建了配置文件{n}。",
|
||||
"saveSettings": "设置已保存",
|
||||
"saveEmail": "电子邮件已保存。",
|
||||
"sentAnnouncement": "公告已发出。",
|
||||
"savedAnnouncement": "公告已保存。",
|
||||
"setOmbiDefaults": "存储的ombi默认值。",
|
||||
"updateApplied": "已应用更新,请重新启动。",
|
||||
"updateAppliedRefresh": "已应用更新,请刷新。",
|
||||
"telegramVerified": "Telegram账户已验证。",
|
||||
"accountConnected": "帐户已连接。",
|
||||
"errorConnection": "无法连接到 jfa-go。",
|
||||
"error401Unauthorized": "无授权。尝试刷新页面。",
|
||||
"errorSettingsAppliedNoHomescreenLayout": "已应用设置,但应用主屏幕布局可能失败。",
|
||||
"errorHomescreenAppliedNoSettings": "已应用主屏幕布局,但应用设置可能失败。",
|
||||
"errorSettingsFailed": "应用失败。",
|
||||
"errorLoginBlank": "用户名/密码留空。",
|
||||
"errorUnknown": "未知错误。",
|
||||
"errorSaveEmail": "电子邮箱保存失败。",
|
||||
"errorBlankFields": "字段留空",
|
||||
"errorDeleteProfile": "删除配置文件{n}失败",
|
||||
"errorLoadProfiles": "加载配置文件失败。",
|
||||
"errorCreateProfile": "创建配置文件{n}失败",
|
||||
"errorSetDefaultProfile": "设置默认配置文件失败。",
|
||||
"errorLoadUsers": "加载用户列表失败。",
|
||||
"errorSaveSettings": "无法保存设置。",
|
||||
"errorLoadSettings": "加载配置列表失败。",
|
||||
"errorSetOmbiDefaults": "存储Ombi默认值失败。",
|
||||
"errorLoadOmbiUsers": "加载ombi用户列表失败。",
|
||||
"errorChangedEmailAddress": "无法更改 {n} 的电子邮件地址。",
|
||||
"errorFailureCheckLogs": "失败(检查控制台/日志)",
|
||||
"errorPartialFailureCheckLogs": "部分失败(检查控制台/日志)",
|
||||
"errorUserCreated": "创建用户{n}失败。",
|
||||
"errorSendWelcomeEmail": "发送欢迎消息失败(检查控制台/日志)",
|
||||
"errorApplyUpdate": "无法应用更新,请手动尝试。",
|
||||
"errorCheckUpdate": "检查更新失败。",
|
||||
"updateAvailable": "有新更新可用,请检查设置。",
|
||||
"noUpdatesAvailable": "没有可用的更新。"
|
||||
},
|
||||
"quantityStrings": {
|
||||
"modifySettingsFor": {
|
||||
"singular": "修改{n}用户的设置",
|
||||
"plural": "修改{n}用户列表的设置"
|
||||
},
|
||||
"deleteNUsers": {
|
||||
"singular": "删除 {n} 个用户",
|
||||
"plural": "删除 {n} 个用户"
|
||||
},
|
||||
"disableUsers": {
|
||||
"singular": "禁用 {n} 个用户",
|
||||
"plural": "禁用 {n} 个用户"
|
||||
},
|
||||
"reEnableUsers": {
|
||||
"singular": "重新启用 {n} 个用户",
|
||||
"plural": "重新启用 {n} 个用户"
|
||||
},
|
||||
"addUser": {
|
||||
"singular": "添加用户",
|
||||
"plural": "添加用户"
|
||||
},
|
||||
"deleteUser": {
|
||||
"singular": "删除用户",
|
||||
"plural": "删除用户"
|
||||
},
|
||||
"deletedUser": {
|
||||
"singular": "删除了 {n} 个用户。",
|
||||
"plural": "删除了 {n} 个用户。"
|
||||
},
|
||||
"disabledUser": {
|
||||
"singular": "禁用 了{n} 个用户。",
|
||||
"plural": "禁用 了{n} 个用户。"
|
||||
},
|
||||
"enabledUser": {
|
||||
"singular": "启用了{n} 个用户。",
|
||||
"plural": "启用了{n} 个用户。"
|
||||
},
|
||||
"announceTo": {
|
||||
"singular": "通知 {n} 位用户",
|
||||
"plural": "通知 {n} 位用户"
|
||||
},
|
||||
"appliedSettings": {
|
||||
"singular": "将设置应用到 {n} 个用户。",
|
||||
"plural": "将设置应用到 {n} 个用户。"
|
||||
},
|
||||
"extendExpiry": {
|
||||
"singular": "延长 {n} 个用户的有效期",
|
||||
"plural": "延长 {n} 个用户的有效期"
|
||||
},
|
||||
"extendedExpiry": {
|
||||
"singular": "延长了 {n} 个用户的有效期。",
|
||||
"plural": "延长了 {n} 个用户的有效期。"
|
||||
}
|
||||
}
|
||||
}
|
||||
27
lang/common/da-dk.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "Dansk"
|
||||
},
|
||||
"strings": {
|
||||
"username": "Brugernavn",
|
||||
"password": "Adgangskode",
|
||||
"emailAddress": "E-mail Adresse",
|
||||
"name": "Navn",
|
||||
"submit": "Indsend",
|
||||
"send": "Send",
|
||||
"success": "Succes",
|
||||
"continue": "Fortsæt",
|
||||
"error": "Fejl",
|
||||
"copy": "Kopiér",
|
||||
"copied": "Kopiret",
|
||||
"time24h": "24 timers tid",
|
||||
"time12h": "12 timers tid",
|
||||
"linkTelegram": "Link Telegram",
|
||||
"contactEmail": "Kontakt gennem E-mail",
|
||||
"contactTelegram": "Kontakt gennem Telegram",
|
||||
"linkDiscord": "Link Discord",
|
||||
"linkMatrix": "Link Matrix",
|
||||
"contactDiscord": "Kontakt gennem Discord",
|
||||
"theme": "Tema"
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,19 @@
|
||||
"emailAddress": "E-Mail-Adresse",
|
||||
"submit": "Absenden",
|
||||
"success": "Erfolgreich",
|
||||
"continue": "Weiter",
|
||||
"error": "Fehler",
|
||||
"copy": "Kopieren",
|
||||
"theme": "Thema",
|
||||
"time24h": "24h-Format",
|
||||
"time12h": "12h-Format",
|
||||
"copied": "Kopiert"
|
||||
"copied": "Kopiert",
|
||||
"linkTelegram": "Link Telegram",
|
||||
"contactEmail": "Kontakt über E-Mail",
|
||||
"contactTelegram": "Kontakt über Telegram",
|
||||
"linkDiscord": "Link Discord",
|
||||
"linkMatrix": "Link Matrix",
|
||||
"send": "Senden",
|
||||
"contactDiscord": "Kontakt über Discord"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"name": "Όνομα",
|
||||
"submit": "Καταχώρηση",
|
||||
"success": "Επιτυχία",
|
||||
"continue": "Συνέχεια",
|
||||
"error": "Σφάλμα",
|
||||
"copy": "Αντιγραφή",
|
||||
"theme": "Θέμα",
|
||||
|
||||
@@ -8,12 +8,20 @@
|
||||
"emailAddress": "Email Address",
|
||||
"name": "Name",
|
||||
"submit": "Submit",
|
||||
"send": "Send",
|
||||
"success": "Success",
|
||||
"continue": "Continue",
|
||||
"error": "Error",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"time24h": "24h Time",
|
||||
"time12h": "12h Time",
|
||||
"linkTelegram": "Link Telegram",
|
||||
"contactEmail": "Contact through Email",
|
||||
"contactTelegram": "Contact through Telegram",
|
||||
"linkDiscord": "Link Discord",
|
||||
"linkMatrix": "Link Matrix",
|
||||
"contactDiscord": "Contact through Discord",
|
||||
"theme": "Theme"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "Español(ES)"
|
||||
"name": "Español (ES)"
|
||||
},
|
||||
"strings": {
|
||||
"username": "Nombre de usuario",
|
||||
@@ -9,11 +9,19 @@
|
||||
"name": "Nombre",
|
||||
"submit": "Enviar",
|
||||
"success": "Éxito",
|
||||
"continue": "Continuar",
|
||||
"error": "Error",
|
||||
"copy": "Copiar",
|
||||
"copied": "Copiado",
|
||||
"time24h": "24 horas",
|
||||
"time12h": "24 horas",
|
||||
"theme": "Tema"
|
||||
"time24h": "Formato de 24 horas",
|
||||
"time12h": "Formato de 12 horas",
|
||||
"theme": "Tema",
|
||||
"send": "Enviar",
|
||||
"contactDiscord": "Contactar por Discord",
|
||||
"contactEmail": "Contactar por correo electrónico",
|
||||
"contactTelegram": "Contactar por Telegram",
|
||||
"linkMatrix": "Enlace Matrix",
|
||||
"linkDiscord": "Enlace Discord",
|
||||
"linkTelegram": "Enlace Telegram"
|
||||
}
|
||||
}
|
||||
|
||||
27
lang/common/fa-ir.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "انگلیسی"
|
||||
},
|
||||
"strings": {
|
||||
"username": "نام کاربری",
|
||||
"password": "کلمه عبور",
|
||||
"emailAddress": "پست الکترونیک",
|
||||
"name": "نام",
|
||||
"submit": "تایید",
|
||||
"send": "ارسال",
|
||||
"success": "موفقیت",
|
||||
"continue": "ادامه دادن",
|
||||
"error": "خطا",
|
||||
"copy": "کپی",
|
||||
"copied": "کپی شد",
|
||||
"time24h": "ساعت 24 ساعت",
|
||||
"time12h": "ساعت 12 ساعت",
|
||||
"linkTelegram": "لینک تلگرام",
|
||||
"contactEmail": "تماس از طریق ایمیل",
|
||||
"contactTelegram": "تماس از طریق تلگرام",
|
||||
"linkDiscord": "پیوند دیسکورد",
|
||||
"linkMatrix": "پیوند ماتریکس",
|
||||
"contactDiscord": "از طریق دیسکورد تماس بگیرید",
|
||||
"theme": "موضوع"
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,19 @@
|
||||
"emailAddress": "Addresse Email",
|
||||
"submit": "Soumettre",
|
||||
"success": "Succès",
|
||||
"continue": "Continuer",
|
||||
"error": "Erreur",
|
||||
"copy": "Copier",
|
||||
"time24h": "Temps 24h",
|
||||
"time12h": "Temps 12h",
|
||||
"theme": "Thème"
|
||||
"theme": "Thème",
|
||||
"copied": "Copié",
|
||||
"linkTelegram": "Lien Telegram",
|
||||
"contactEmail": "Contact par e-mail",
|
||||
"contactTelegram": "Contact par Telegram",
|
||||
"linkDiscord": "Lier Discord",
|
||||
"linkMatrix": "Lier Matrix",
|
||||
"send": "Envoyer",
|
||||
"contactDiscord": "Contacter par Discord"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"name": "Nama",
|
||||
"submit": "Submit",
|
||||
"success": "Sukses",
|
||||
"continue": "Lanjut",
|
||||
"error": "Error",
|
||||
"copy": "Salin",
|
||||
"time24h": "Waktu 24 jam",
|
||||
|
||||
@@ -8,12 +8,20 @@
|
||||
"password": "Wachtwoord",
|
||||
"emailAddress": "E-mailadres",
|
||||
"submit": "Verstuur",
|
||||
"success": "Success",
|
||||
"success": "Succes",
|
||||
"continue": "Doorgaan",
|
||||
"error": "Fout",
|
||||
"copy": "Kopiëer",
|
||||
"theme": "Thema",
|
||||
"time24h": "24u-formaat",
|
||||
"time12h": "12u-formaat",
|
||||
"copied": "Gekopieerd"
|
||||
"copied": "Gekopieerd",
|
||||
"linkTelegram": "Koppel Telegram",
|
||||
"contactEmail": "Stuur e-mailbericht",
|
||||
"contactTelegram": "Stuur Telegram-bericht",
|
||||
"send": "Verstuur",
|
||||
"linkDiscord": "Koppel Discord",
|
||||
"linkMatrix": "Koppel Matrix",
|
||||
"contactDiscord": "Stuur Discord bericht"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,19 @@
|
||||
"emailAddress": "Endereço de Email",
|
||||
"submit": "Enviar",
|
||||
"success": "Sucesso",
|
||||
"continue": "Continuar",
|
||||
"error": "Erro",
|
||||
"copy": "Copiar",
|
||||
"theme": "Tema",
|
||||
"time24h": "Horário 24h",
|
||||
"time12h": "Horário 12h",
|
||||
"copied": "Copiado"
|
||||
"copied": "Copiado",
|
||||
"linkTelegram": "Link do Telegram",
|
||||
"contactEmail": "Contato por Email",
|
||||
"contactTelegram": "Contato pelo Telegram",
|
||||
"send": "Enviar",
|
||||
"linkDiscord": "Link do Discord",
|
||||
"linkMatrix": "Link do Matrix",
|
||||
"contactDiscord": "Contato através do Discord"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"name": "Namn",
|
||||
"submit": "Skicka",
|
||||
"success": "Lyckades",
|
||||
"continue": "Fortsätt",
|
||||
"error": "Fel",
|
||||
"copy": "Kopiera",
|
||||
"time24h": "24 timmarsklocka",
|
||||
|
||||
27
lang/common/zh-hans.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "简体中文(CN)"
|
||||
},
|
||||
"strings": {
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"emailAddress": "电子邮箱",
|
||||
"name": "名称",
|
||||
"submit": "提交",
|
||||
"send": "发送",
|
||||
"success": "成功",
|
||||
"continue": "继续",
|
||||
"error": "错误",
|
||||
"copy": "复制",
|
||||
"copied": "已复制",
|
||||
"time24h": "24小时制",
|
||||
"time12h": "12小时制",
|
||||
"linkTelegram": "关联Telegram",
|
||||
"contactEmail": "通过邮件联系",
|
||||
"contactTelegram": "通过Telegram联系",
|
||||
"linkDiscord": "关联Discord",
|
||||
"linkMatrix": "关联Matrix",
|
||||
"contactDiscord": "通过Discord联系",
|
||||
"theme": "主题"
|
||||
}
|
||||
}
|
||||
77
lang/email/da-dk.json
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "Dansk"
|
||||
},
|
||||
"strings": {
|
||||
"ifItWasNotYou": "Ignorer venligst hvis dette ikke var dig.",
|
||||
"helloUser": "Hej {username},",
|
||||
"reason": "Grund"
|
||||
},
|
||||
"userCreated": {
|
||||
"name": "Bruger oprettet",
|
||||
"title": "Meddelelse: Bruger oprettet",
|
||||
"aUserWasCreated": "En bruger blev oprettet med koden {code}.",
|
||||
"time": "Tid",
|
||||
"notificationNotice": "Meddelelse: Notifikations e-mails kan blive ændret på admin-siden."
|
||||
},
|
||||
"inviteExpiry": {
|
||||
"name": "Invitationens udløb",
|
||||
"title": "Meddelelse: Invitation udløbet",
|
||||
"inviteExpired": "Invitation udløbet.",
|
||||
"expiredAt": "Koden {code} udløber om {time}.",
|
||||
"notificationNotice": "Meddelelse: Notifikations e-mails kan blive ændret på admin-siden."
|
||||
},
|
||||
"passwordReset": {
|
||||
"name": "Nulstil Adgangskode",
|
||||
"title": "Nulstilling af adgangskode anmodet - Jellyfin",
|
||||
"someoneHasRequestedReset": "Nogen har for nylig anmodet om nulstilling af din adgangskode på Jellyfin.",
|
||||
"ifItWasYou": "Hvis dette var dig, så indtast venligst pinkoden nedenunder ind i prompten.",
|
||||
"ifItWasYouLink": "Hvis dette var dig, så tryk på linket nedenunder.",
|
||||
"codeExpiry": "Koden udløber den {date}, klokken {time} UTC, hvilket er om {expiresInMinutes}.",
|
||||
"pin": "PINKODE"
|
||||
},
|
||||
"userDeleted": {
|
||||
"name": "Sletning af bruger",
|
||||
"title": "Din konto blev slettet - Jellyfin",
|
||||
"yourAccountWasDeleted": "Din Jellyfin konto blev slettet."
|
||||
},
|
||||
"userDisabled": {
|
||||
"name": "Bruger deaktiveret",
|
||||
"title": "Din konto er blevet deaktiveret - Jellyfin",
|
||||
"yourAccountWasDisabled": "Din konto blev deaktiveret."
|
||||
},
|
||||
"userEnabled": {
|
||||
"name": "Bruger aktiveret",
|
||||
"title": "Din konto er blevet genaktiveret - Jellyfin",
|
||||
"yourAccountWasEnabled": "Din konto blev genaktiveret."
|
||||
},
|
||||
"inviteEmail": {
|
||||
"name": "Invitations e-mail",
|
||||
"title": "Invitation - Jellyfin",
|
||||
"hello": "Hej",
|
||||
"youHaveBeenInvited": "Du er blevet inviteret til Jellyfin.",
|
||||
"toJoin": "Tilmeld dig med linket nedenfor.",
|
||||
"inviteExpiry": "Invitationen vil udløbe den {date} kl. {time}, hvilket er om {expiresInMinutes}, så skynd dig.",
|
||||
"linkButton": "Opsæt din konto"
|
||||
},
|
||||
"welcomeEmail": {
|
||||
"name": "Velkommen",
|
||||
"title": "Velkommen til Jellyfin",
|
||||
"welcome": "Velkommen til Jellyfin!",
|
||||
"youCanLoginWith": "Du kan logge ind med nedenstående oplysninger",
|
||||
"yourAccountWillExpire": "Din konto udløber den {date}.",
|
||||
"jellyfinURL": "URL"
|
||||
},
|
||||
"emailConfirmation": {
|
||||
"name": "Bekræftelses e-mail",
|
||||
"title": "Bekræft din e-mail - Jellyfin",
|
||||
"clickBelow": "Klik på linket nedenunder for at bekræfte din e-mail adresse og start med at bruge Jellyfin.",
|
||||
"confirmEmail": "Bekræft E-mail"
|
||||
},
|
||||
"userExpired": {
|
||||
"name": "Brugerens udløb",
|
||||
"title": "Din konto er udløbet - Jellyfin",
|
||||
"yourAccountHasExpired": "Din konto er udløbet.",
|
||||
"contactTheAdmin": "Kontakt administratoren for mere information."
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Deutsch (DE)"
|
||||
},
|
||||
"strings": {
|
||||
"ifItWasNotYou": "Wenn du das nicht warst, ignoriere bitte diese E-Mail.",
|
||||
"ifItWasNotYou": "Wenn du das nicht warst, ignoriere bitte dies.",
|
||||
"reason": "Grund",
|
||||
"helloUser": "Hallo {username},"
|
||||
},
|
||||
@@ -27,7 +27,8 @@
|
||||
"ifItWasYou": "Wenn du das warst, gib die PIN unten in die Eingabeaufforderung ein.",
|
||||
"codeExpiry": "Der Code wird am {date}, um {time} UTC ablaufen, was in {expiresInMinutes} ist.",
|
||||
"pin": "PIN",
|
||||
"name": "Passwortzurücksetzung"
|
||||
"name": "Passwortzurücksetzung",
|
||||
"ifItWasYouLink": "Wenn du das warst, klick auf den Link unten."
|
||||
},
|
||||
"userDeleted": {
|
||||
"title": "Dein Konto wurde gelöscht - Jellyfin",
|
||||
@@ -48,7 +49,8 @@
|
||||
"welcome": "Willkommen bei Jellyfin!",
|
||||
"youCanLoginWith": "Du kannst dich mit den mit den untenstehenden Zugangsdaten anmelden",
|
||||
"jellyfinURL": "URL",
|
||||
"name": "Willkommens-E-Mail"
|
||||
"name": "Willkommen",
|
||||
"yourAccountWillExpire": "Dein Konto läuft am {date} ab."
|
||||
},
|
||||
"emailConfirmation": {
|
||||
"title": "Bestätige deine E-Mail - Jellyfin",
|
||||
@@ -61,5 +63,15 @@
|
||||
"title": "Dein Konto ist abgelaufen - Jellyfin",
|
||||
"yourAccountHasExpired": "Dein Konto ist abgelaufen.",
|
||||
"contactTheAdmin": "Kontaktiere den Administrator für weitere Informationen."
|
||||
},
|
||||
"userDisabled": {
|
||||
"name": "Benutzer deaktiviert",
|
||||
"title": "Dein Konto wurde deaktiviert - Jellyfin",
|
||||
"yourAccountWasDisabled": "Dein Konto wurde deaktiviert."
|
||||
},
|
||||
"userEnabled": {
|
||||
"name": "Benutzer aktiviert",
|
||||
"title": "Dein Konto wurde wieder freigeschaltet - Jellyfin",
|
||||
"yourAccountWasEnabled": "Dein Konto wurde wieder aktiviert."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "English (US)"
|
||||
},
|
||||
"strings": {
|
||||
"ifItWasNotYou": "If this wasn't you, please ignore this email.",
|
||||
"ifItWasNotYou": "If this wasn't you, please ignore this.",
|
||||
"helloUser": "Hi {username},",
|
||||
"reason": "Reason"
|
||||
},
|
||||
@@ -12,14 +12,14 @@
|
||||
"title": "Notice: User created",
|
||||
"aUserWasCreated": "A user was created using code {code}.",
|
||||
"time": "Time",
|
||||
"notificationNotice": "Note: Notification emails can be toggled on the admin dashboard."
|
||||
"notificationNotice": "Note: Notification messages can be toggled on the admin dashboard."
|
||||
},
|
||||
"inviteExpiry": {
|
||||
"name": "Invite expiry",
|
||||
"title": "Notice: Invite expired",
|
||||
"inviteExpired": "Invite expired.",
|
||||
"expiredAt": "Code {code} expired at {time}.",
|
||||
"notificationNotice": "Note: Notification emails can be toggled on the admin dashboard."
|
||||
"notificationNotice": "Note: Notification messages can be toggled on the admin dashboard."
|
||||
},
|
||||
"passwordReset": {
|
||||
"name": "Password reset",
|
||||
@@ -55,7 +55,7 @@
|
||||
"linkButton": "Setup your account"
|
||||
},
|
||||
"welcomeEmail": {
|
||||
"name": "Welcome email",
|
||||
"name": "Welcome",
|
||||
"title": "Welcome to Jellyfin",
|
||||
"welcome": "Welcome to Jellyfin!",
|
||||
"youCanLoginWith": "You can login with the details below",
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "Español(ES)"
|
||||
"name": "Español (ES)"
|
||||
},
|
||||
"strings": {
|
||||
"ifItWasNotYou": "Si no fue usted, ignore este correo electrónico.",
|
||||
"ifItWasNotYou": "Si no fue usted, ignórelo.",
|
||||
"helloUser": "Hola {username},",
|
||||
"reason": "Razón"
|
||||
},
|
||||
"userCreated": {
|
||||
"name": "Creación de usuarios",
|
||||
"title": "Noticia: Usuario creado",
|
||||
"title": "Aviso: Usuario creado",
|
||||
"aUserWasCreated": "Se creó un usuario con el código {code}.",
|
||||
"time": "Hora",
|
||||
"notificationNotice": "Nota: los correos electrónicos de notificación se pueden alternar en el panel de administración."
|
||||
"notificationNotice": "Nota: Los mensajes de notificación se pueden alternar en el panel de administración."
|
||||
},
|
||||
"inviteExpiry": {
|
||||
"name": "Vencimiento de la invitación",
|
||||
"title": "Aviso: Invitación caducada",
|
||||
"inviteExpired": "Invitación caducada.",
|
||||
"expiredAt": "El código {code} venció a las {time}.",
|
||||
"notificationNotice": "Nota: Los correos electrónicos de notificación se pueden alternar en el panel de administración."
|
||||
"notificationNotice": "Nota: Los mensajes de notificación se pueden alternar en el panel de administración."
|
||||
},
|
||||
"passwordReset": {
|
||||
"name": "Restablecimiento de contraseña",
|
||||
"title": "Solicitud de restablecimiento de contraseña - Jellyfin",
|
||||
"someoneHasRequestedReset": "Alguien ha solicitado recientemente un restablecimiento de contraseña en Jellyfin.",
|
||||
"ifItWasYou": "Si era usted, ingrese el pin a continuación en el mensaje.",
|
||||
"ifItWasYou": "Si era usted, ingrese el PIN a continuación en el mensaje.",
|
||||
"ifItWasYouLink": "Si fue usted, haga clic en el enlace de abajo.",
|
||||
"codeExpiry": "El código vencerá el {date}, a las {time} UTC, que está en {expiresInMinutes}.",
|
||||
"codeExpiry": "El código vencerá el {date}, a las {time} UTC, que es en {expiresInMinutes}.",
|
||||
"pin": "PIN"
|
||||
},
|
||||
"userDeleted": {
|
||||
@@ -36,26 +36,26 @@
|
||||
"yourAccountWasDeleted": "Su cuenta de Jellyfin fue eliminada."
|
||||
},
|
||||
"userDisabled": {
|
||||
"name": "Usuario deshabilitado",
|
||||
"title": "Su cuenta ha sido deshabilitada - Jellyfin",
|
||||
"yourAccountWasDisabled": "Su cuenta fue inhabilitada."
|
||||
"name": "Usuario desactivado",
|
||||
"title": "Su cuenta ha sido desactivada - Jellyfin",
|
||||
"yourAccountWasDisabled": "Su cuenta fue desactivada."
|
||||
},
|
||||
"userEnabled": {
|
||||
"name": "Usuario habilitado",
|
||||
"name": "Usuario activado",
|
||||
"title": "Su cuenta ha sido reactivada - Jellyfin",
|
||||
"yourAccountWasEnabled": "Su cuenta se volvió a habilitar."
|
||||
"yourAccountWasEnabled": "Su cuenta fue reactivada."
|
||||
},
|
||||
"inviteEmail": {
|
||||
"name": "Correo electrónico",
|
||||
"title": "Invitar - Jellyfin",
|
||||
"name": "Invitar por correo electrónico",
|
||||
"title": "Invitación - Jellyfin",
|
||||
"hello": "Hola",
|
||||
"youHaveBeenInvited": "Has sido invitado a Jellyfin.",
|
||||
"toJoin": "Para unirse, siga el enlace a continuación.",
|
||||
"inviteExpiry": "Esta invitación vencerá el {date} a las {time}, que está en {expiresInMinutes}, así que regístrese cuanto antes.",
|
||||
"linkButton": "Configurar tu cuenta"
|
||||
"inviteExpiry": "Esta invitación vencerá el {date} a las {time}, que es en {expiresInMinutes}, así que regístrese cuanto antes.",
|
||||
"linkButton": "Configura tu cuenta"
|
||||
},
|
||||
"welcomeEmail": {
|
||||
"name": "Correo de bienvenida",
|
||||
"name": "Bienvenido",
|
||||
"title": "Bienvenido a Jellyfin",
|
||||
"welcome": "¡Bienvenido a Jellyfin!",
|
||||
"youCanLoginWith": "Puede iniciar sesión con los detalles a continuación",
|
||||
@@ -66,12 +66,12 @@
|
||||
"name": "Email de confirmación",
|
||||
"title": "Confirma tu correo electrónico - Jellyfin",
|
||||
"clickBelow": "Haga clic en el enlace de abajo para confirmar su dirección de correo electrónico y comenzar a usar Jellyfin.",
|
||||
"confirmEmail": "Confirmar correo electrónico"
|
||||
"confirmEmail": "Confirma el correo electrónico"
|
||||
},
|
||||
"userExpired": {
|
||||
"name": "Caducidad del usuario",
|
||||
"title": "Tu cuenta ha caducado - Jellyfin",
|
||||
"yourAccountHasExpired": "Tu cuenta ha expirado.",
|
||||
"contactTheAdmin": "Comuníquese con el administrador para obtener más información."
|
||||
"contactTheAdmin": "Póngase en contacto con el administrador para obtener más información."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"author": "https://github.com/Cornichon420"
|
||||
},
|
||||
"strings": {
|
||||
"ifItWasNotYou": "Si ce n'était pas toi, tu peux ignorer ce mail.",
|
||||
"ifItWasNotYou": "Si ce n'était pas toi, tu peux ignorer ceci.",
|
||||
"reason": "Motif",
|
||||
"helloUser": "Salut {username},"
|
||||
},
|
||||
@@ -12,14 +12,14 @@
|
||||
"title": "Notification : Utilisateur créé",
|
||||
"aUserWasCreated": "Un utilisateur a été créé avec ce code {code}.",
|
||||
"time": "Date",
|
||||
"notificationNotice": "Note : Les emails de notification peuvent être activés sur le tableau de bord administrateur.",
|
||||
"notificationNotice": "Note : Les messages de notification peuvent être activés sur le tableau de bord administrateur.",
|
||||
"name": "Création d'utilisateur"
|
||||
},
|
||||
"inviteExpiry": {
|
||||
"title": "Notification : Invitation expirée",
|
||||
"inviteExpired": "Invitation expirée.",
|
||||
"expiredAt": "Le code {code} a expiré à {time}.",
|
||||
"notificationNotice": "Note : Les emails de notification peuvent être activés sur le tableau de bord administrateur.",
|
||||
"notificationNotice": "Note : Les messages de notification peuvent être activés sur le tableau de bord administrateur.",
|
||||
"name": "Expiration de l'invitation"
|
||||
},
|
||||
"passwordReset": {
|
||||
@@ -50,7 +50,7 @@
|
||||
"title": "Bienvenue sur Jellyfin",
|
||||
"welcome": "Bienvenue sur Jellyfin !",
|
||||
"jellyfinURL": "URL",
|
||||
"name": "Courriel de bienvenue",
|
||||
"name": "Bienvenue",
|
||||
"yourAccountWillExpire": "Ton compte expirera le {date}."
|
||||
},
|
||||
"emailConfirmation": {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Nederlands (NL)"
|
||||
},
|
||||
"strings": {
|
||||
"ifItWasNotYou": "Als jij dit niet was, negeer dan alsjeblieft deze email.",
|
||||
"ifItWasNotYou": "Als jij dit niet was, negeer dit dan alsjeblieft.",
|
||||
"reason": "Reden",
|
||||
"helloUser": "Hoi {username},"
|
||||
},
|
||||
@@ -11,14 +11,14 @@
|
||||
"title": "Melding: Gebruiker aangemaakt",
|
||||
"aUserWasCreated": "Er is een gebruiker aangemaakt door gebruik te maken van code {code}.",
|
||||
"time": "Tijdstip",
|
||||
"notificationNotice": "Opmerking: Meldingse-mails kunnen worden aan- of uitgezet via het beheerdersdashboard.",
|
||||
"notificationNotice": "Opmerking: Meldingen kunnen worden aan- of uitgezet via het beheerders-dashboard.",
|
||||
"name": "Gebruiker aangemaakt"
|
||||
},
|
||||
"inviteExpiry": {
|
||||
"title": "Melding: Uitnodiging verlopen",
|
||||
"inviteExpired": "Uitnodiging verlopen.",
|
||||
"expiredAt": "Code {code} is verlopen op {time}.",
|
||||
"notificationNotice": "Opmerking: Meldingse-mails kunnen worden aan- of uitgezet via het beheerdersdashboard.",
|
||||
"notificationNotice": "Opmerking: Meldingen kunnen worden aan- of uitgezet via het beheerders-dashboard.",
|
||||
"name": "Uitnodiging verlopen"
|
||||
},
|
||||
"passwordReset": {
|
||||
@@ -49,7 +49,7 @@
|
||||
"welcome": "Welkom bij Jellyfin!",
|
||||
"youCanLoginWith": "Je kunt inloggen met onderstaande gegevens",
|
||||
"jellyfinURL": "URL",
|
||||
"name": "Welkomste-mail",
|
||||
"name": "Welkom",
|
||||
"yourAccountWillExpire": "Je account verloopt op {date}."
|
||||
},
|
||||
"emailConfirmation": {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Português (BR)"
|
||||
},
|
||||
"strings": {
|
||||
"ifItWasNotYou": "Se não foi você, ignore este e-mail.",
|
||||
"ifItWasNotYou": "Se não foi você, ignore.",
|
||||
"reason": "Razão",
|
||||
"helloUser": "Ola {username},"
|
||||
},
|
||||
@@ -49,7 +49,7 @@
|
||||
"welcome": "Bem vindo ao Jellyfin!",
|
||||
"youCanLoginWith": "Abaixo está os detalhes para fazer o login",
|
||||
"jellyfinURL": "URL",
|
||||
"name": "Email de Boas vindas",
|
||||
"name": "Bem-vindo",
|
||||
"yourAccountWillExpire": "Sua conta irá expirar em {date}."
|
||||
},
|
||||
"emailConfirmation": {
|
||||
|
||||
77
lang/email/zh-hans.json
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "简体中文(CN)"
|
||||
},
|
||||
"strings": {
|
||||
"ifItWasNotYou": "如果这不是您,请忽略本邮件。",
|
||||
"helloUser": "你好{username},",
|
||||
"reason": "原因"
|
||||
},
|
||||
"userCreated": {
|
||||
"name": "创建用户",
|
||||
"title": "注意:用户已创建",
|
||||
"aUserWasCreated": "使用代码{code}创建了一个用户。",
|
||||
"time": "时间",
|
||||
"notificationNotice": "注意:通知邮件可以在管理仪表板上进行切换。"
|
||||
},
|
||||
"inviteExpiry": {
|
||||
"name": "邀请到期",
|
||||
"title": "注意:邀请已过期",
|
||||
"inviteExpired": "邀请已过期。",
|
||||
"expiredAt": "代码 {code} 已于 {time} 过期。",
|
||||
"notificationNotice": "注意:通知邮件可以在管理仪表板上进行切换。"
|
||||
},
|
||||
"passwordReset": {
|
||||
"name": "重设密码",
|
||||
"title": "要求重设密码 - Jellyfin",
|
||||
"someoneHasRequestedReset": "最近在Jellyfin上有一个重设密码的请求。",
|
||||
"ifItWasYou": "如果这是您,请在提示中输入下面的PIN。",
|
||||
"ifItWasYouLink": "如果这是您,请单击下面的链接。",
|
||||
"codeExpiry": "该代码将在UTC时间{日期} {时间}到期,也就是{expires In Minutes}。",
|
||||
"pin": "PIN"
|
||||
},
|
||||
"userDeleted": {
|
||||
"name": "删除用户",
|
||||
"title": "您的账户已被删除 - Jellyfin",
|
||||
"yourAccountWasDeleted": "您的 Jellyfin 帐户已被删除。"
|
||||
},
|
||||
"userDisabled": {
|
||||
"name": "用户禁用",
|
||||
"title": "您的帐户已被禁用 - Jellyfin",
|
||||
"yourAccountWasDisabled": "您的帐户已被禁用。"
|
||||
},
|
||||
"userEnabled": {
|
||||
"name": "用户已启用",
|
||||
"title": "您的帐户已重新启用 - Jellyfin",
|
||||
"yourAccountWasEnabled": "您的帐户已重新启用。"
|
||||
},
|
||||
"inviteEmail": {
|
||||
"name": "邀请邮件",
|
||||
"title": "邀请 - Jellyfin",
|
||||
"hello": "您好",
|
||||
"youHaveBeenInvited": "您已受邀注册 Jellyfin。",
|
||||
"toJoin": "若要加入,请点击以下链接。",
|
||||
"inviteExpiry": "此邀请将于 {date} 的 {time} 到期,即 {expires In Minutes},因此请迅速行动。",
|
||||
"linkButton": "设置您的帐户"
|
||||
},
|
||||
"welcomeEmail": {
|
||||
"name": "欢迎",
|
||||
"title": "欢迎使用Jellyfin",
|
||||
"welcome": "欢迎使用Jellyfin!",
|
||||
"youCanLoginWith": "您可以使用以下信息登录",
|
||||
"yourAccountWillExpire": "您的帐户将于 {date}到期。",
|
||||
"jellyfinURL": "链接"
|
||||
},
|
||||
"emailConfirmation": {
|
||||
"name": "确认邮件",
|
||||
"title": "确认您的电子邮件 - Jellyfin",
|
||||
"clickBelow": "单击下面的链接以确认您的电子邮件地址并开始使用 Jellyfin。",
|
||||
"confirmEmail": "确认电子邮件"
|
||||
},
|
||||
"userExpired": {
|
||||
"name": "用户到期",
|
||||
"title": "您的帐户已过期 - Jellyfin",
|
||||
"yourAccountHasExpired": "您的帐户已过期。",
|
||||
"contactTheAdmin": "请联系管理员了解更多信息。"
|
||||
}
|
||||
}
|
||||
56
lang/form/da-dk.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "Dansk"
|
||||
},
|
||||
"strings": {
|
||||
"pageTitle": "Opret Jellyfin Konto",
|
||||
"createAccountHeader": "Opret Konto",
|
||||
"accountDetails": "Detaljer",
|
||||
"emailAddress": "E-mail",
|
||||
"username": "Brugernavn",
|
||||
"password": "Adgangskode",
|
||||
"reEnterPassword": "Genindtast Adgangskode",
|
||||
"reEnterPasswordInvalid": "Adgangskoderne er ikke ens.",
|
||||
"createAccountButton": "Opret Konto",
|
||||
"passwordRequirementsHeader": "Adgangskodekrav",
|
||||
"successHeader": "Succes!",
|
||||
"confirmationRequired": "E-mail bekræftelse er påkrævet",
|
||||
"confirmationRequiredMessage": "Tjek venligst din e-mail indbakke for at verificere din adresse.",
|
||||
"yourAccountIsValidUntil": "Din konto er gyldig indtil {date}.",
|
||||
"sendPIN": "Send nedenstående pinkode til botten, og kom derefter tilbage her for at linke din konto.",
|
||||
"sendPINDiscord": "Skriv {command} i {server_channel} på Discord, og send PIN-koden nedenfor via. DM til boten.",
|
||||
"matrixEnterUser": "Skriv dit Bruger ID, tryk Indsend, og en PIN-kode vil blive sendt til dig. Skriv den her efter for at fortsætte."
|
||||
},
|
||||
"notifications": {
|
||||
"errorUserExists": "Brugeren eksistere allerede.",
|
||||
"errorInvalidCode": "Ugyldig invitations kode.",
|
||||
"errorTelegramVerification": "Telegram verifikation påkrævet.",
|
||||
"errorDiscordVerification": "Discord verifikation påkrævet.",
|
||||
"errorMatrixVerification": "Matrix verifikation påkrævet.",
|
||||
"errorInvalidPIN": "PIN-koden er ugyldig.",
|
||||
"errorUnknown": "Ukendt fejl.",
|
||||
"verified": "konto verificeret."
|
||||
},
|
||||
"validationStrings": {
|
||||
"length": {
|
||||
"singular": "Skal mindst have {n} tegn",
|
||||
"plural": "Skal mindst have {n} tegn"
|
||||
},
|
||||
"uppercase": {
|
||||
"singular": "Skal mindst have {n} store bogstaver",
|
||||
"plural": "Skal mindst have {n} store bogstaver"
|
||||
},
|
||||
"lowercase": {
|
||||
"singular": "Skal mindst have {n} små bogstaver",
|
||||
"plural": "Skal mindst have {n} små bogstaver"
|
||||
},
|
||||
"number": {
|
||||
"singular": "Skal mindst have {n} tal",
|
||||
"plural": "Skal mindst have {n} tal"
|
||||
},
|
||||
"special": {
|
||||
"singular": "Skal mindst have {n} specialtegn",
|
||||
"plural": "Skal mindst have {n} specialtegn"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,12 @@
|
||||
"createAccountButton": "Konto erstellen",
|
||||
"passwordRequirementsHeader": "Passwortanforderungen",
|
||||
"successHeader": "Erfolgreich!",
|
||||
"successContinueButton": "Weiter",
|
||||
"confirmationRequired": "E-Mail-Bestätigung erforderlich",
|
||||
"confirmationRequiredMessage": "Bitte überprüfe dein Posteingang und bestätige deine E-Mail-Adresse.",
|
||||
"yourAccountIsValidUntil": "Dein Konto wird bis zum {date} gültig sein."
|
||||
"yourAccountIsValidUntil": "Dein Konto wird bis zum {date} gültig sein.",
|
||||
"sendPIN": "Sende die untenstehende PIN an den Bot und komm dann hierher zurück, um dein Konto zu verbinden.",
|
||||
"sendPINDiscord": "Gib auf Discord {command} in {server_channel} ein und sende die untenstehende PIN als DM an den Bot.",
|
||||
"matrixEnterUser": "Gib deine Benutzer-ID ein und drücke auf Absenden. Anschließend erhälst du ein PIN, die hier eingegeben wird um fortzufahren."
|
||||
},
|
||||
"validationStrings": {
|
||||
"length": {
|
||||
@@ -43,6 +45,13 @@
|
||||
},
|
||||
"notifications": {
|
||||
"errorUserExists": "Benutzer existiert bereits.",
|
||||
"errorInvalidCode": "Ungültiger Invite-Code."
|
||||
"errorInvalidCode": "Ungültiger Invite-Code.",
|
||||
"telegramVerified": "Telegram-Konto verifiziert.",
|
||||
"errorTelegramVerification": "Verifizierung von Telegram erforderlich.",
|
||||
"errorInvalidPIN": "PIN ist ungültig.",
|
||||
"errorDiscordVerification": "Discord-Verifizierung erforderlich.",
|
||||
"errorMatrixVerification": "Matrix-Verifizierung erforderlich.",
|
||||
"errorUnknown": "Unbekannter Fehler.",
|
||||
"verified": "Konto verifiziert."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
"createAccountButton": "Δημιουργία Λογαρισμού",
|
||||
"passwordRequirementsHeader": "Απαιτήσεις Κωδικού",
|
||||
"successHeader": "Επιτυχία!",
|
||||
"successContinueButton": "Συνέχεια",
|
||||
"confirmationRequired": "Απαιτείται επιβεβαίωση Email",
|
||||
"confirmationRequiredMessage": "Παρακαλώ ελέγξτε το email σας για να επιβεβαιώσετε την διεύθυνση σας .",
|
||||
"yourAccountIsValidUntil": "Ο λογαριασμός σου θα ισχύει μέχρι {date}."
|
||||
|
||||
@@ -14,14 +14,23 @@
|
||||
"createAccountButton": "Create Account",
|
||||
"passwordRequirementsHeader": "Password Requirements",
|
||||
"successHeader": "Success!",
|
||||
"successContinueButton": "Continue",
|
||||
"confirmationRequired": "Email confirmation required",
|
||||
"confirmationRequiredMessage": "Please check your email inbox to verify your address.",
|
||||
"yourAccountIsValidUntil": "Your account will be valid until {date}."
|
||||
"yourAccountIsValidUntil": "Your account will be valid until {date}.",
|
||||
"sendPIN": "Send the PIN below to the bot, then come back here to link your account.",
|
||||
"sendPINDiscord": "Type {command} in {server_channel} on Discord, then send the PIN below via DM to the bot.",
|
||||
"matrixEnterUser": "Enter your User ID, press submit, and a PIN will be sent to you. Enter it here to continue."
|
||||
},
|
||||
"notifications": {
|
||||
"errorUserExists": "User already exists.",
|
||||
"errorInvalidCode": "Invalid invite code."
|
||||
"errorInvalidCode": "Invalid invite code.",
|
||||
"errorTelegramVerification": "Telegram verification required.",
|
||||
"errorDiscordVerification": "Discord verification required.",
|
||||
"errorMatrixVerification": "Matrix verification required.",
|
||||
"errorInvalidPIN": "PIN is invalid.",
|
||||
"errorUnknown": "Unknown error.",
|
||||
"errorNoEmail": "Email required.",
|
||||
"verified": "Account verified."
|
||||
},
|
||||
"validationStrings": {
|
||||
"length": {
|
||||
|
||||
@@ -9,19 +9,28 @@
|
||||
"emailAddress": "Correo electrónico",
|
||||
"username": "Nombre de usuario",
|
||||
"password": "Contraseña",
|
||||
"reEnterPassword": "Rescriba su contraseña",
|
||||
"reEnterPasswordInvalid": "Las contraseñas no son coincidentes.",
|
||||
"reEnterPassword": "Confirme la contraseña",
|
||||
"reEnterPasswordInvalid": "Las contraseñas no son similares.",
|
||||
"createAccountButton": "Crear una cuenta",
|
||||
"passwordRequirementsHeader": "Requisitos de contraseña",
|
||||
"successHeader": "¡Éxito!",
|
||||
"successContinueButton": "Continuar",
|
||||
"confirmationRequired": "Se requiere confirmación por correo electrónico",
|
||||
"confirmationRequiredMessage": "Revise la bandeja de entrada de su correo electrónico para verificar su dirección.",
|
||||
"yourAccountIsValidUntil": "Su cuenta será válida hasta el {date}."
|
||||
"confirmationRequiredMessage": "Por favor, revise la bandeja de entrada de su correo electrónico para verificar su dirección.",
|
||||
"yourAccountIsValidUntil": "Su cuenta será válida hasta el {date}.",
|
||||
"sendPINDiscord": "Escribe {command} en {server_channel} en Discord, luego envía el PIN a continuación a través de DM al bot.",
|
||||
"sendPIN": "Envíe el PIN a continuación al bot, luego regrese aquí para vincular su cuenta.",
|
||||
"matrixEnterUser": "Ingrese su ID de usuario, presione enviar y se le enviará un PIN. Ingrese aquí para continuar."
|
||||
},
|
||||
"notifications": {
|
||||
"errorUserExists": "El usuario ya existe.",
|
||||
"errorInvalidCode": "Código de invitación no es válido."
|
||||
"errorInvalidCode": "Código de invitación no válido.",
|
||||
"errorDiscordVerification": "Se requiere verificación de Discord.",
|
||||
"errorUnknown": "Error desconocido.",
|
||||
"verified": "Cuenta verificada.",
|
||||
"errorTelegramVerification": "Se requiere verificación de Telegram.",
|
||||
"errorInvalidPIN": "PIN inválido.",
|
||||
"errorMatrixVerification": "Se requiere verificación de Matrix.",
|
||||
"errorNoEmail": "Correo electrónico requerido."
|
||||
},
|
||||
"validationStrings": {
|
||||
"length": {
|
||||
@@ -29,11 +38,11 @@
|
||||
"plural": "Debe tener al menos {n} caracteres"
|
||||
},
|
||||
"uppercase": {
|
||||
"singular": "Debe tener al menos {n} caracteres en mayúscula",
|
||||
"singular": "Debe tener al menos {n} carácter en mayúscula",
|
||||
"plural": "Debe tener al menos {n} caracteres en mayúscula"
|
||||
},
|
||||
"lowercase": {
|
||||
"singular": "Debe tener al menos {n} caracteres en minúscula",
|
||||
"singular": "Debe tener al menos {n} carácter en minúscula",
|
||||
"plural": "Debe tener al menos {n} caracteres en minúscula"
|
||||
},
|
||||
"number": {
|
||||
|
||||
56
lang/form/fa-ir.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "انگلیسی"
|
||||
},
|
||||
"strings": {
|
||||
"pageTitle": "ساختن حساب کاربری",
|
||||
"createAccountHeader": "ساخت اکانت",
|
||||
"accountDetails": "جزئیات",
|
||||
"emailAddress": "پست الکترونیک",
|
||||
"username": "نام کاربری",
|
||||
"password": "کلمه عبور",
|
||||
"reEnterPassword": "کلمه عبور را دوباره وارد کنید",
|
||||
"reEnterPasswordInvalid": "کلمه های عبور یکسان نیستند.",
|
||||
"createAccountButton": "ساخت حساب کاربری",
|
||||
"passwordRequirementsHeader": "کلمه عبور لازم است",
|
||||
"successHeader": "موفقیت!",
|
||||
"confirmationRequired": "تایید ایمیل لازم است",
|
||||
"confirmationRequiredMessage": "لطفاً برای تأیید آدرس خود ، صندوق پستی ایمیل خود را بررسی کنید.",
|
||||
"yourAccountIsValidUntil": "حساب شما تا {date} معتبر خواهد بود.",
|
||||
"sendPIN": "پین زیر را به ربات ارسال کنید ، سپس برای پیوند حساب خود به اینجا برگردید.",
|
||||
"sendPINDiscord": "در Discord {command} را در {server_channel} تایپ کنید ، سپس پین زیر را از طریق DM به ربات ارسال کنید.",
|
||||
"matrixEnterUser": "شناسه کاربری خود را وارد کرده ، submit را فشار دهید و یک پین برای شما ارسال می شود. برای ادامه آن را اینجا وارد کنید."
|
||||
},
|
||||
"notifications": {
|
||||
"errorUserExists": "کاربر قبلاً وجود دارد.",
|
||||
"errorInvalidCode": "کد دعوت نامعتبر است.",
|
||||
"errorTelegramVerification": "تایید تلگرام الزامی است.",
|
||||
"errorDiscordVerification": "تأیید دیسکورد مورد نیاز است.",
|
||||
"errorMatrixVerification": "تأیید ماتریکس مورد نیاز است.",
|
||||
"errorInvalidPIN": "پین نامعتبر است.",
|
||||
"errorUnknown": "خطای ناشناخته.",
|
||||
"verified": "حساب تأیید شد."
|
||||
},
|
||||
"validationStrings": {
|
||||
"length": {
|
||||
"singular": "باید حداقل {n} نویسه داشته باشد",
|
||||
"plural": "باید حداقل {n} نویسه داشته باشد"
|
||||
},
|
||||
"uppercase": {
|
||||
"singular": "باید حداقل {n} نویسه بزرگ داشته باشد",
|
||||
"plural": "باید حداقل {n} نویسه بزرگ داشته باشد"
|
||||
},
|
||||
"lowercase": {
|
||||
"singular": "باید حداقل {n} نویسه کوچک داشته باشد",
|
||||
"plural": "باید حداقل {n} نویسه کوچک داشته باشد"
|
||||
},
|
||||
"number": {
|
||||
"singular": "باید حداقل {n} شماره داشته باشد",
|
||||
"plural": "باید حداقل {n} شماره داشته باشد"
|
||||
},
|
||||
"special": {
|
||||
"singular": "باید حداقل {n} نویسه خاص داشته باشد",
|
||||
"plural": "باید حداقل {n} نویسه خاص داشته باشد"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,17 +7,20 @@
|
||||
"pageTitle": "Créer un compte Jellyfin",
|
||||
"createAccountHeader": "Création du compte",
|
||||
"accountDetails": "Détails",
|
||||
"emailAddress": "Email",
|
||||
"emailAddress": "E-mail",
|
||||
"username": "Nom d'utilisateur",
|
||||
"password": "Mot de passe",
|
||||
"reEnterPassword": "Confirmez mot de passe",
|
||||
"reEnterPasswordInvalid": "Les mots de passe ne correspondent pas.",
|
||||
"createAccountButton": "Créer le compte",
|
||||
"passwordRequirementsHeader": "Mot de passe requis",
|
||||
"successHeader": "Succes!",
|
||||
"successContinueButton": "Continuer",
|
||||
"successHeader": "Succès !",
|
||||
"confirmationRequired": "Confirmation de l'adresse e-mail requise",
|
||||
"confirmationRequiredMessage": "Veuillez vérifier votre boite de réception pour confirmer votre adresse e-mail."
|
||||
"confirmationRequiredMessage": "Veuillez vérifier votre boite de réception pour confirmer votre adresse e-mail.",
|
||||
"yourAccountIsValidUntil": "Votre compte sera valide jusqu'au {date}.",
|
||||
"sendPIN": "Envoyez le code PIN ci-dessous au bot, puis revenez ici pour lier votre compte.",
|
||||
"sendPINDiscord": "Écrivez {command} dans le salon {server_channel} sur Discord puis envoyez le PIN en message privé au bot.",
|
||||
"matrixEnterUser": "Entrez votre nom d'utilisateur, appuyez sur soumettre et un code PIN vous sera envoyé. Cliquez ici pour continuez."
|
||||
},
|
||||
"validationStrings": {
|
||||
"length": {
|
||||
@@ -43,6 +46,13 @@
|
||||
},
|
||||
"notifications": {
|
||||
"errorUserExists": "Utilisateur déjà existant.",
|
||||
"errorInvalidCode": "Code d’invitation non valide."
|
||||
"errorInvalidCode": "Code d’invitation non valide.",
|
||||
"errorTelegramVerification": "Vérification Telegram requise.",
|
||||
"errorInvalidPIN": "PIN Telegram invalide.",
|
||||
"telegramVerified": "Compte Telegram vérifié.",
|
||||
"errorDiscordVerification": "Vérification Discord requise.",
|
||||
"errorMatrixVerification": "Vérification Matrix requise.",
|
||||
"errorUnknown": "Erreur inconnue.",
|
||||
"verified": "Compte vérifié."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
"createAccountButton": "Buat Akun",
|
||||
"passwordRequirementsHeader": "Persyaratan Kata Sandi",
|
||||
"successHeader": "Sukses!",
|
||||
"successContinueButton": "Lanjut",
|
||||
"confirmationRequired": "Konfirmasi email diperlukan",
|
||||
"confirmationRequiredMessage": "Silakan periksa kotak masuk email Anda untuk memverifikasi alamat Anda."
|
||||
},
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
"createAccountButton": "Crea Un Account",
|
||||
"passwordRequirementsHeader": "Requisiti Password",
|
||||
"successHeader": "Successo!",
|
||||
"successContinueButton": "Continua",
|
||||
"confirmationRequired": "Richiesta la conferma Email",
|
||||
"confirmationRequiredMessage": "Controlla la tua casella email per verificare il tuo indirizzo."
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Nederlands (NL)"
|
||||
},
|
||||
"strings": {
|
||||
"pageTitle": "Maak Jellyfin account aan",
|
||||
"pageTitle": "Maak Jellyfin-account aan",
|
||||
"createAccountHeader": "Account aanmaken",
|
||||
"accountDetails": "Details",
|
||||
"emailAddress": "E-mail",
|
||||
@@ -14,10 +14,12 @@
|
||||
"createAccountButton": "Maak account aan",
|
||||
"passwordRequirementsHeader": "Wachtwoordvereisten",
|
||||
"successHeader": "Succes!",
|
||||
"successContinueButton": "Doorgaan",
|
||||
"confirmationRequired": "Bevestiging van e-mailadres verplicht",
|
||||
"confirmationRequiredMessage": "Controleer je e-mail inbox om je adres te bevestigen.",
|
||||
"yourAccountIsValidUntil": "Je account zal geldig zijn tot {date}."
|
||||
"yourAccountIsValidUntil": "Je account zal geldig zijn tot {date}.",
|
||||
"sendPIN": "Stuur onderstaande pincode naar de bot, en kom daarna hier terug om je account te koppelen.",
|
||||
"matrixEnterUser": "Voer je gebruikers ID in, druk op versturen, en er wordt je een pincode toegestuurd. Vul die hier in om door te gaan.",
|
||||
"sendPINDiscord": "Typ {command} in {server_channel} op Discord, stuur daarna onderstaande pincode via DM naar de bot."
|
||||
},
|
||||
"validationStrings": {
|
||||
"length": {
|
||||
@@ -43,6 +45,14 @@
|
||||
},
|
||||
"notifications": {
|
||||
"errorUserExists": "Gebruiker bestaat al.",
|
||||
"errorInvalidCode": "Ongeldige uitnodigingscode."
|
||||
"errorInvalidCode": "Ongeldige uitnodigingscode.",
|
||||
"telegramVerified": "Telegram-account goedgekeurd.",
|
||||
"errorTelegramVerification": "Telegram-verificatie nodig.",
|
||||
"errorInvalidPIN": "Pincode is ongeldig.",
|
||||
"errorDiscordVerification": "Discord-verificatie vereist.",
|
||||
"errorUnknown": "Onbekende fout.",
|
||||
"errorMatrixVerification": "Matrix-verificatie vereist.",
|
||||
"verified": "Account geverifieerd.",
|
||||
"errorNoEmail": "E-mail vereist."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,15 +13,24 @@
|
||||
"reEnterPasswordInvalid": "Senha não Coincidem. Tentar novamente.",
|
||||
"createAccountButton": "Criar Conta",
|
||||
"passwordRequirementsHeader": "Requisitos da Senha",
|
||||
"successHeader": "Sucesso!",
|
||||
"successContinueButton": "Continuar",
|
||||
"confirmationRequired": "Necessária confirmação de e-mail",
|
||||
"successHeader": "Concluído!",
|
||||
"confirmationRequired": "Confirmação por e-mail",
|
||||
"confirmationRequiredMessage": "Verifique sua caixa de email para finalizar o cadastro.",
|
||||
"yourAccountIsValidUntil": "Sua conta é válida até {date}."
|
||||
"yourAccountIsValidUntil": "Sua conta é válida até {date}.",
|
||||
"sendPIN": "Envie o PIN abaixo para o bot e volte aqui para vincular sua conta.",
|
||||
"sendPINDiscord": "Digite {command} em {server_channel} no Discord e envie o PIN abaixo via DM para o bot.",
|
||||
"matrixEnterUser": "Digite sua ID de usuário, pressione enviar e um PIN será enviado. E digite aqui para continuar."
|
||||
},
|
||||
"notifications": {
|
||||
"errorUserExists": "Esse usuário já existe.",
|
||||
"errorInvalidCode": "Código do convite invalido."
|
||||
"errorInvalidCode": "Código do convite invalido.",
|
||||
"telegramVerified": "Conta do Telegram verificada.",
|
||||
"errorInvalidPIN": "PIN inválido.",
|
||||
"errorTelegramVerification": "Requer a verificação do telegram.",
|
||||
"errorDiscordVerification": "Necessária verificação pelo Discord.",
|
||||
"errorMatrixVerification": "Necessária verificação Matrix.",
|
||||
"errorUnknown": "Erro desconhecido.",
|
||||
"verified": "Conta verificada."
|
||||
},
|
||||
"validationStrings": {
|
||||
"length": {
|
||||
|
||||
@@ -14,14 +14,17 @@
|
||||
"createAccountButton": "Skapa konto",
|
||||
"passwordRequirementsHeader": "Lösenordskrav",
|
||||
"successHeader": "Lyckades!",
|
||||
"successContinueButton": "Fortsätt",
|
||||
"confirmationRequired": "E-postbekräftelse krävs",
|
||||
"confirmationRequiredMessage": "Kontrollera din e-postkorg för att verifiera din adress.",
|
||||
"yourAccountIsValidUntil": "Ditt konto är giltigt fram tills {date}."
|
||||
"yourAccountIsValidUntil": "Ditt konto är giltigt fram tills {date}.",
|
||||
"sendPIN": "Skicka PIN-koden nedan till botten, återvänd sedan till den här sidan för att länka ditt konto."
|
||||
},
|
||||
"notifications": {
|
||||
"errorUserExists": "Användare finns redan.",
|
||||
"errorInvalidCode": "Ogiltig inbjudningskod."
|
||||
"errorInvalidCode": "Ogiltig inbjudningskod.",
|
||||
"errorInvalidPIN": "Telegram-PIN är ogiltig.",
|
||||
"errorTelegramVerification": "Telegram-verifiering krävs.",
|
||||
"telegramVerified": "Telegram-konto verifierades."
|
||||
},
|
||||
"validationStrings": {
|
||||
"length": {
|
||||
|
||||
56
lang/form/zh-hans.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "简体中文(CN)"
|
||||
},
|
||||
"strings": {
|
||||
"pageTitle": "创建Jellyfin账户",
|
||||
"createAccountHeader": "创建账户",
|
||||
"accountDetails": "账户详情",
|
||||
"emailAddress": "电子邮箱",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"reEnterPassword": "再次输入密码",
|
||||
"reEnterPasswordInvalid": "两次输入的密码不相同。",
|
||||
"createAccountButton": "创建账户",
|
||||
"passwordRequirementsHeader": "密码格式要求",
|
||||
"successHeader": "成功!",
|
||||
"confirmationRequired": "需要邮件确认",
|
||||
"confirmationRequiredMessage": "请登录您的邮箱收件箱来验证您的地址。",
|
||||
"yourAccountIsValidUntil": "您的账户将在 {date} 之前有效。",
|
||||
"sendPIN": "把PIN码发送给下面的bot,然后回到这里来关联您的账户。",
|
||||
"sendPINDiscord": "在 Discord 上,在 {server_channel} 中输入 {command} 并将下面的 PIN 码作为 DM 发送给机器人。",
|
||||
"matrixEnterUser": "输入您的用户ID并点击提交,将发送给您的PIN码输入到这里并继续。"
|
||||
},
|
||||
"notifications": {
|
||||
"errorUserExists": "用户已经存在。",
|
||||
"errorInvalidCode": "无效的邀请码",
|
||||
"errorTelegramVerification": "需要Telegram验证",
|
||||
"errorDiscordVerification": "需要Discord验证。",
|
||||
"errorMatrixVerification": "需要Matrix验证。",
|
||||
"errorInvalidPIN": "无效的PIN码",
|
||||
"errorUnknown": "未知错误。",
|
||||
"verified": "账户已验证。"
|
||||
},
|
||||
"validationStrings": {
|
||||
"length": {
|
||||
"singular": "必须至少包含 {n} 个字符",
|
||||
"plural": "必须至少包含 {n} 个字符"
|
||||
},
|
||||
"uppercase": {
|
||||
"singular": "必须至少包含 {n} 个大写字母",
|
||||
"plural": "必须至少包含 {n} 个大写字母"
|
||||
},
|
||||
"lowercase": {
|
||||
"singular": "必须至少包含 {n} 个小写字母",
|
||||
"plural": "必须至少包含 {n} 个小写字母"
|
||||
},
|
||||
"number": {
|
||||
"singular": "必须至少包含 {n} 个数字",
|
||||
"plural": "必须至少包含 {n} 个数字"
|
||||
},
|
||||
"special": {
|
||||
"singular": "必须至少包含 {n} 个特殊字符",
|
||||
"plural": "必须至少包含 {n} 个特殊字符"
|
||||
}
|
||||
}
|
||||
}
|
||||
15
lang/pwreset/da-dk.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "Dansk"
|
||||
},
|
||||
"strings": {
|
||||
"passwordReset": "Nulstil adgangskode",
|
||||
"reset": "Nulstil",
|
||||
"resetFailed": "Nulstilling af adgangskode fejlede",
|
||||
"tryAgain": "Prøv venligst igen.",
|
||||
"youCanLogin": "Du kan nu logge ind med koden nedenfor som din adgangskode.",
|
||||
"youCanLoginOmbi": "Du kan nu logge ind på Jellyfin & Ombi med koden nedenfor som din adgangskode.",
|
||||
"changeYourPassword": "Sørg for at ændre din adgangskode, når du har logget ind.",
|
||||
"enterYourPassword": "Indtast din nye adgangskode nedenfor."
|
||||
}
|
||||
}
|
||||