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

Compare commits

..

10 Commits

Author SHA1 Message Date
5c87d109a3
use descriptive variable names in email translations
in preparation for an email editor.
2021-02-19 17:50:50 +00:00
3e020da66a
merge translation 2021-02-19 16:12:27 +00:00
78157f763f
use different color library, wrap logger functions with it 2021-02-19 16:12:14 +00:00
Richard de Boer
bcc0eeeb2f Translated using Weblate (Dutch)
Currently translated at 100.0% (95 of 95 strings)

Translation: jfa-go/Setup
Translate-URL: https://weblate.hrfee.dev/projects/jfa-go/setup/nl/
2021-02-19 15:58:27 +01:00
Richard de Boer
76b859f5bf Translated using Weblate (Dutch)
Currently translated at 100.0% (32 of 32 strings)

Translation: jfa-go/Emails
Translate-URL: https://weblate.hrfee.dev/projects/jfa-go/emails/nl/
2021-02-19 15:58:27 +01:00
Richard de Boer
676cf619d5 translation from Weblate (Dutch)
Currently translated at 100.0% (27 of 27 strings)

Translation: jfa-go/Account Creation Form
Translate-URL: https://weblate.hrfee.dev/projects/jfa-go/form/nl/
2021-02-19 15:58:27 +01:00
Richard de Boer
ce45bf2136 translation from Weblate (Dutch)
Currently translated at 100.0% (110 of 110 strings)

Translation: jfa-go/Admin Page
Translate-URL: https://weblate.hrfee.dev/projects/jfa-go/admin/nl/
2021-02-19 15:58:26 +01:00
b25f786018
use bulk email sending on account deletion 2021-02-19 14:51:36 +00:00
ca00796077
Merge pull request #61 from rigrig/main
use `apt-get` instead of `apt` in Dockerfile
2021-02-19 14:47:10 +00:00
Richard de Boer
a1bbf13d6a use apt-get instead of apt
Because `apt` is meant for humans, and complains when called in scripts.
(manpage: "While it tries not to break backward compatibility this is not guaranteed")
2021-02-19 15:26:11 +01:00
24 changed files with 179 additions and 106 deletions

View File

@ -2,10 +2,10 @@ FROM --platform=$BUILDPLATFORM golang:latest AS support
COPY . /opt/build
RUN apt update -y \
&& apt install build-essential python3-pip curl software-properties-common sed -y \
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 install nodejs \
&& apt-get install nodejs \
&& (cd /opt/build; make configuration npm email version typescript bundle-css swagger copy external-files 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

View File

@ -19,6 +19,7 @@ I chose to rewrite the python [jellyfin-accounts](https://github.com/hrfee/jelly
* Email addresses can optionally be used instead of usernames
* 🔑 Password resets: When user's 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.
* Notifications: Get notified when someone creates an account, or an invite expires.
* 📣 Announcements: Bulk email you 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
* 🌓 Customizable look
@ -37,15 +38,8 @@ I chose to rewrite the python [jellyfin-accounts](https://github.com/hrfee/jelly
#### Install
Available on the AUR as [jfa-go](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.
* For \*nix/macOS users, `chmod +x jfa-go` then place it somewhere in your PATH like `/usr/bin`.
Run the executable to start.
For [docker](https://hub.docker.com/repository/docker/hrfee/jfa-go), run:
```
The [Docker](https://hub.docker.com/repository/docker/hrfee/jfa-go) image is your best bet.
```sh
docker create \
--name "jfa-go" \ # Whatever you want to name it
-p 8056:8056 \
@ -55,6 +49,13 @@ 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/) 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.
* For \*nix/macOS users, `chmod +x jfa-go` then place it somewhere in your PATH like `/usr/bin`.
Run the executable to start.
#### Build from source
If you're using docker, a Dockerfile is provided that builds from source.

26
api.go
View File

@ -539,8 +539,9 @@ func (app *appContext) Announce(gc *gin.Context) {
// @Router /users [delete]
// @Security Bearer
// @tags Users
func (app *appContext) DeleteUser(gc *gin.Context) {
func (app *appContext) DeleteUsers(gc *gin.Context) {
var req deleteUserDTO
var addresses []string
gc.BindJSON(&req)
errors := map[string]string{}
ombiEnabled := app.config.Section("ombi").Key("enabled").MustBool(false)
@ -569,19 +570,22 @@ func (app *appContext) DeleteUser(gc *gin.Context) {
if emailEnabled && req.Notify {
addr, ok := app.storage.emails[userID]
if addr != nil && ok {
go func(userID, reason, address string) {
msg, err := app.email.constructDeleted(reason, app)
if err != nil {
app.err.Printf("%s: Failed to construct account deletion email: %s", userID, err)
} else if err := app.email.send(msg, address); err != nil {
app.err.Printf("%s: Failed to send to %s: %s", userID, address, err)
} else {
app.info.Printf("%s: Sent deletion email to %s", userID, address)
}
}(userID, req.Reason, addr.(string))
addresses = append(addresses, addr.(string))
}
}
}
if len(addresses) != 0 {
go func(reason string, addresses []string) {
msg, err := app.email.constructDeleted(reason, app)
if err != nil {
app.err.Printf("Failed to construct account deletion emails: %s", err)
} else if err := app.email.send(msg, addresses...); err != nil {
app.err.Printf("Failed to send account deletion emails: %s", err)
} else {
app.info.Println("Sent account deletion emails")
}
}(req.Reason, addresses)
}
app.jf.CacheExpiry = time.Now()
if len(errors) == len(req.Users) {
respondBool(500, false, gc)

View File

@ -148,7 +148,6 @@ func (app *appContext) getTokenLogin(gc *gin.Context) {
}
jfID = user.ID
if app.config.Section("ui").Key("admin_only").MustBool(true) {
fmt.Printf("%+v\n", user.Policy)
if !user.Policy.IsAdministrator {
app.debug.Printf("Auth denied: Users \"%s\" isn't admin", creds[0])
respond(401, "Unauthorized", gc)

View File

@ -221,7 +221,7 @@ func (emailer *Emailer) constructConfirmation(code, username, key string, app *a
inviteLink = fmt.Sprintf("%s/%s?key=%s", inviteLink, code, key)
var err error
email.html, email.text, err = emailer.construct(app, "email_confirmation", "email_", map[string]interface{}{
"helloUser": emailer.lang.Strings.format("helloUser", username),
"helloUser": emailer.lang.Strings.template("helloUser", tmpl{"username": username}),
"clickBelow": emailer.lang.EmailConfirmation.get("clickBelow"),
"ifItWasNotYou": emailer.lang.Strings.get("ifItWasNotYou"),
"urlVal": inviteLink,
@ -266,7 +266,7 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont
"hello": emailer.lang.InviteEmail.get("hello"),
"youHaveBeenInvited": emailer.lang.InviteEmail.get("youHaveBeenInvited"),
"toJoin": emailer.lang.InviteEmail.get("toJoin"),
"inviteExpiry": emailer.lang.InviteEmail.format("inviteExpiry", d, t, expiresIn),
"inviteExpiry": emailer.lang.InviteEmail.template("inviteExpiry", tmpl{"date": d, "time": t, "expiresInMinutes": expiresIn}),
"linkButton": emailer.lang.InviteEmail.get("linkButton"),
"invite_link": inviteLink,
"message": message,
@ -285,7 +285,7 @@ func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appCont
var err error
email.html, email.text, err = emailer.construct(app, "notifications", "expiry_", map[string]interface{}{
"inviteExpired": emailer.lang.InviteExpiry.get("inviteExpired"),
"expiredAt": emailer.lang.InviteExpiry.format("expiredAt", "\""+code+"\"", expiry),
"expiredAt": emailer.lang.InviteExpiry.template("expiredAt", tmpl{"code": "\"" + code + "\"", "time": expiry}),
"notificationNotice": emailer.lang.InviteExpiry.get("notificationNotice"),
})
if err != nil {
@ -307,7 +307,7 @@ func (emailer *Emailer) constructCreated(code, username, address string, invite
}
var err error
email.html, email.text, err = emailer.construct(app, "notifications", "created_", map[string]interface{}{
"aUserWasCreated": emailer.lang.UserCreated.format("aUserWasCreated", "\""+code+"\""),
"aUserWasCreated": emailer.lang.UserCreated.template("aUserWasCreated", tmpl{"code": "\"" + code + "\""}),
"name": emailer.lang.Strings.get("name"),
"address": emailer.lang.Strings.get("emailAddress"),
"time": emailer.lang.UserCreated.get("time"),
@ -330,10 +330,10 @@ func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext) (*Ema
message := app.config.Section("email").Key("message").String()
var err error
email.html, email.text, err = emailer.construct(app, "password_resets", "email_", map[string]interface{}{
"helloUser": emailer.lang.Strings.format("helloUser", pwr.Username),
"helloUser": emailer.lang.Strings.template("helloUser", tmpl{"username": pwr.Username}),
"someoneHasRequestedReset": emailer.lang.PasswordReset.get("someoneHasRequestedReset"),
"ifItWasYou": emailer.lang.PasswordReset.get("ifItWasYou"),
"codeExpiry": emailer.lang.PasswordReset.format("codeExpiry", d, t, expiresIn),
"codeExpiry": emailer.lang.PasswordReset.template("codeExpiry", tmpl{"date": d, "time": t, "expiresInMinutes": expiresIn}),
"ifItWasNotYou": emailer.lang.Strings.get("ifItWasNotYou"),
"pin": emailer.lang.PasswordReset.get("pin"),
"pinVal": pwr.Pin,

6
go.mod
View File

@ -12,6 +12,7 @@ replace github.com/hrfee/jfa-go/ombi => ./ombi
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/gin-contrib/pprof v1.3.0
github.com/gin-contrib/static v0.0.0-20200916080430-d45d9a37d28e
@ -20,7 +21,7 @@ require (
github.com/go-openapi/spec v0.20.3 // 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 // 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
@ -29,7 +30,6 @@ require (
github.com/jordan-wright/email v4.0.1-0.20200917010138-e1c00e156980+incompatible
github.com/knz/strtime v0.0.0-20200924090105-187c67f2bf5e
github.com/lithammer/shortuuid/v3 v3.0.4
github.com/logrusorgru/aurora/v3 v3.0.0
github.com/mailgun/mailgun-go/v4 v4.3.0
github.com/mailru/easyjson v0.7.7 // indirect
github.com/pkg/errors v0.9.1 // indirect
@ -38,7 +38,7 @@ require (
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/writeas/go-strip-markdown v2.0.1+incompatible // indirect
github.com/writeas/go-strip-markdown v2.0.1+incompatible
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 // indirect
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
golang.org/x/tools v0.1.0 // indirect

7
go.sum
View File

@ -36,6 +36,8 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojt
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/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
@ -151,8 +153,6 @@ 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/logrusorgru/aurora/v3 v3.0.0 h1:R6zcoZZbvVcGMvDCKo45A9U/lzYyzl5NfYIvznmDfE4=
github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc=
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/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@ -161,6 +161,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
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-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=
@ -280,6 +282,7 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w
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-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=

View File

@ -118,6 +118,15 @@ func (ls *setupLangs) getOptions() [][2]string {
}
type langSection map[string]string
type tmpl map[string]string
func (el langSection) template(field string, vals tmpl) string {
text := el.get(field)
for key, val := range vals {
text = strings.ReplaceAll(text, "{"+key+"}", val)
}
return text
}
func (el langSection) format(field string, vals ...string) string {
text := el.get(field)

View File

@ -65,7 +65,11 @@
"notifyUserCreation": "Bij aanmaken gebruiker",
"label": "Label",
"settingsRestart": "Herstart",
"settingsRestarting": "Aan het herstarten…"
"settingsRestarting": "Aan het herstarten…",
"announce": "Aankondiging",
"markdownSupported": "Markdown wordt ondersteund.",
"subject": "E-mailonderwerp",
"message": "Bericht"
},
"notifications": {
"changedEmailAddress": "E-mailadres van {n} gewijzigd.",
@ -94,7 +98,8 @@
"errorFailureCheckLogs": "Mislukt (controleer console/logbestanden)",
"errorPartialFailureCheckLogs": "Gedeeltelijke fout (controleer console/logbestanden)",
"errorSendWelcomeEmail": "Versturen van welkomste-mail is mislukt (zie console/logs)",
"errorUserCreated": "Aanmaken van gebruiker {n} is mislukt."
"errorUserCreated": "Aanmaken van gebruiker {n} is mislukt.",
"sentAnnouncement": "Aankondiging verzonden."
},
"quantityStrings": {
"modifySettingsFor": {
@ -120,6 +125,10 @@
"appliedSettings": {
"singular": "Instellingen toegepast op {n} gebruiker.",
"plural": "Instellingen toegepast op {n} gebruikers."
},
"announceTo": {
"singular": "Aankondigen aan {n} gebruikers",
"plural": "aankondigen aan {n} gebruikerss"
}
}
}

View File

@ -4,7 +4,7 @@
},
"strings": {
"ifItWasNotYou": "Wenn du das nicht warst, ignoriere bitte diese E-Mail.",
"helloUser": "Hallo {n},"
"helloUser": "Hallo {username},"
},
"userCreated": {
"title": "Mitteilung: Benutzer erstellt",
@ -15,14 +15,14 @@
"inviteExpiry": {
"title": "Mitteilung: Invite abgelaufen",
"inviteExpired": "Invite abgelaufen.",
"expiredAt": "Code {n} lief um {n} ab.",
"expiredAt": "Code {code} lief um {code} ab.",
"notificationNotice": "Hinweis: Benachrichtigungs-E-Mails können auf dem Administrator-Dashboard umgeschalten werden."
},
"passwordReset": {
"title": "Passwortzurücksetzung angefordert - Jellyfin",
"someoneHasRequestedReset": "Jemand hat vor kurzem eine Passwortzurücksetzung auf Jellyfin angefordert.",
"ifItWasYou": "Wenn du das warst, gib die PIN unten in die Eingabeaufforderung ein.",
"codeExpiry": "Der Code wird am {n}, um [n} UTC ablaufen, was in {n} ist.",
"codeExpiry": "Der Code wird am {time}, um [n} UTC ablaufen, was in {date} ist.",
"pin": "PIN"
},
"userDeleted": {
@ -35,7 +35,7 @@
"hello": "Hallo",
"youHaveBeenInvited": "Du wurdest zu Jellyfin eingeladen.",
"toJoin": "Um beizutreten, folge dem untenstehenden Link.",
"inviteExpiry": "Dieser Invite wird am {n}; um {n} ablaufen, was in {n} ist, also handle schnell.",
"inviteExpiry": "Dieser Invite wird am {time}; um {expiresInMinutes} ablaufen, was in {date} ist, also handle schnell.",
"linkButton": "Richte dein Konto ein"
},
"welcomeEmail": {

View File

@ -4,25 +4,25 @@
},
"strings": {
"ifItWasNotYou": "Αν δεν ήσασταν εσείς, παρακαλώ αγνοήστε αυτό το email.",
"helloUser": "Γεία σου {n},"
"helloUser": "Γεία σου {username},"
},
"userCreated": {
"title": "Σημείωση: Δημιουργήθηκε χρήστης",
"aUserWasCreated": "Δημιουργήθηκε ένας χρήστης χρησιμοποιώντας τον κωδικό {n}.",
"aUserWasCreated": "Δημιουργήθηκε ένας χρήστης χρησιμοποιώντας τον κωδικό {code}.",
"time": "Ώρα",
"notificationNotice": "Σημείωση: Τα email ειδοποιήσεων μπορούν να ενεργοποιηθούν στον πίνακα ελέγχου διαχειριστή."
},
"inviteExpiry": {
"title": "Σημείωση: Η πρόσκληση έληξε",
"inviteExpired": "Η πρόσκληση έληξε.",
"expiredAt": "Ο κωδικός {n} έληξε στις {n}.",
"expiredAt": "Ο κωδικός {code} έληξε στις {time}.",
"notificationNotice": "Σημείωση: Τα email ειδοποιήσεων μπορούν να ενεργοποιηθούν στον πίνακα ελέγχου διαχειριστή."
},
"passwordReset": {
"title": "Ζητήθηκε επαναφορά κωδικού πρόσβασης - Jellyfin",
"someoneHasRequestedReset": "Κάποιος ζήτησε πρόσφατα επαναφορά κωδικού πρόσβασης στο Jellyfin.",
"ifItWasYou": "Εάν ήσασταν εσείς, εισαγάγετε το πιν στο πεδίο.",
"codeExpiry": "Ο κωδικός θα λήξει στις {n}, στις {n} UTC, το οποίο είναι σε {n}.",
"codeExpiry": "Ο κωδικός θα λήξει στις {date}, στις {time} UTC, το οποίο είναι σε {expiresInMinutes}.",
"pin": "PIN"
},
"userDeleted": {
@ -35,7 +35,7 @@
"hello": "Γειά",
"youHaveBeenInvited": "Έχετε προσκληθεί στο Jellyfin.",
"toJoin": "Για να συμμετέχετε, ακολουθήστε τον παρακάτω σύνδεσμο.",
"inviteExpiry": "Αυτή η πρόσκληση θα λήξει στις {n} στις {n}, που είναι σε {n}, οπότε ενεργήστε γρήγορα.",
"inviteExpiry": "Αυτή η πρόσκληση θα λήξει στις {date} στις {time}, που είναι σε {expiresInMinutes}, οπότε ενεργήστε γρήγορα.",
"linkButton": "Ρυθμίστε τον λογαριασμό σας"
},
"welcomeEmail": {

View File

@ -4,25 +4,25 @@
},
"strings": {
"ifItWasNotYou": "If this wasn't you, please ignore this email.",
"helloUser": "Hi {n},"
"helloUser": "Hi {username},"
},
"userCreated": {
"title": "Notice: User created",
"aUserWasCreated": "A user was created using code {n}.",
"aUserWasCreated": "A user was created using code {code}.",
"time": "Time",
"notificationNotice": "Note: Notification emails can be toggled on the admin dashboard."
},
"inviteExpiry": {
"title": "Notice: Invite expired",
"inviteExpired": "Invite expired.",
"expiredAt": "Code {n} expired at {n}.",
"expiredAt": "Code {code} expired at {time}.",
"notificationNotice": "Note: Notification emails can be toggled on the admin dashboard."
},
"passwordReset": {
"title": "Password reset requested - Jellyfin",
"someoneHasRequestedReset": "Someone has recently requested a password reset on Jellyfin.",
"ifItWasYou": "If this was you, enter the pin below into the prompt.",
"codeExpiry": "The code will expire on {n}, at {n} UTC, which is in {n}.",
"codeExpiry": "The code will expire on {date}, at {time} UTC, which is in {expiresInMinutes}.",
"pin": "PIN"
},
"userDeleted": {
@ -35,7 +35,7 @@
"hello": "Hi",
"youHaveBeenInvited": "You've been invited to Jellyfin.",
"toJoin": "To join, follow the below link.",
"inviteExpiry": "This invite will expire on {n} at {n}, which is in {n}, so act quick.",
"inviteExpiry": "This invite will expire on {date} at {time}, which is in {expiresInMinutes}, so act quick.",
"linkButton": "Setup your account"
},
"welcomeEmail": {

View File

@ -5,25 +5,25 @@
},
"strings": {
"ifItWasNotYou": "Si ce n'était pas toi, tu peux ignorer ce mail.",
"helloUser": "Salut {n},"
"helloUser": "Salut {username},"
},
"userCreated": {
"title": "Notification : Utilisateur créé",
"aUserWasCreated": "Un utilisateur a été créé avec ce code {n}.",
"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."
},
"inviteExpiry": {
"title": "Notification : Invitation expirée",
"inviteExpired": "Invitation expirée.",
"expiredAt": "Le code {n} a expiré à {n}.",
"expiredAt": "Le code {code} a expiré à {time}.",
"notificationNotice": "Note : Les emails de notification peuvent être activés sur le tableau de bord administrateur."
},
"passwordReset": {
"title": "Réinitialisation de mot du passe demandée - Jellyfin",
"someoneHasRequestedReset": "Quelqu'un vient de demander une réinitialisation du mot de passe via Jellyfin.",
"ifItWasYou": "Si c'était bien toi, renseigne le code PIN en dessous.",
"codeExpiry": "Ce code expirera le {n}, à {n} UTC, soit dans {n}.",
"codeExpiry": "Ce code expirera le {date}, à {time} UTC, soit dans {expiresInMinutes}.",
"pin": "PIN"
},
"userDeleted": {
@ -36,7 +36,7 @@
"hello": "Salut",
"youHaveBeenInvited": "Tu as été invité à rejoindre Jellyfin.",
"toJoin": "Pour continuer, suis le lien en dessous.",
"inviteExpiry": "L'invitation expirera le {n}, à {n}, soit dans {n}, alors fais vite !",
"inviteExpiry": "L'invitation expirera le {date}, à {time}, soit dans {expiresInMinutes}, alors fais vite !",
"linkButton": "Lien"
},
"welcomeEmail": {

View File

@ -4,25 +4,25 @@
},
"strings": {
"ifItWasNotYou": "Jika ini bukan kamu, silahkan mengabaikan email ini.",
"helloUser": "Halo {n},"
"helloUser": "Halo {username},"
},
"userCreated": {
"title": "Perhatian: User telah dibuat",
"aUserWasCreated": "User telah dibuat menggunakan kode {n}.",
"aUserWasCreated": "User telah dibuat menggunakan kode {code}.",
"time": "Waktu",
"notificationNotice": "Catatan: Email notifikasi dapat diganti pada dasbor admin."
},
"inviteExpiry": {
"title": "Perhatian: Undangan telah kadaluarsa",
"inviteExpired": "Undangan telah kadaluarsa.",
"expiredAt": "Kode {n} kadaluarsa pada {n}.",
"expiredAt": "Kode {code} kadaluarsa pada {time}.",
"notificationNotice": "Catatan: Email notifikasi dapat diganti pada dasbor admin."
},
"passwordReset": {
"title": "Reset password telah di-request - Jellyfin",
"someoneHasRequestedReset": "Seseorang baru saja me-request reset password pada Jellyfin.",
"ifItWasYou": "Jika ini adalah benar anda, masukkan pin dibawah ke dalam tempat yang sudah disediakan.",
"codeExpiry": "Kode akan kadaluarsa pada {n}, pada waktu {n} UTC, yaitu dalam {n}.",
"codeExpiry": "Kode akan kadaluarsa pada {date}, pada waktu {time} UTC, yaitu dalam {expiresInMinutes}.",
"pin": "PIN"
},
"userDeleted": {
@ -35,7 +35,7 @@
"hello": "Halo",
"youHaveBeenInvited": "Anda telah diundang ke Jellyfin.",
"toJoin": "Untuk masuk, silahkan ikuti link dibawah ini.",
"inviteExpiry": "Undangan ini akan berakhir dalam {n} pada {n}, yaitu dalam {n}, jadi bergegaslah.",
"inviteExpiry": "Undangan ini akan berakhir dalam {date} pada {time}, yaitu dalam {expiresInMinutes}, jadi bergegaslah.",
"linkButton": "Persiapkan akunmu"
},
"welcomeEmail": {

View File

@ -4,25 +4,25 @@
},
"strings": {
"ifItWasNotYou": "Se non sei stato tu, puoi ignorare questa email.",
"helloUser": "Ciao {n},"
"helloUser": "Ciao {username},"
},
"userCreated": {
"title": "Nota: Utente creato",
"aUserWasCreated": "Un utente è stato creato usando il codice {n}.",
"aUserWasCreated": "Un utente è stato creato usando il codice {code}.",
"time": "Tempo",
"notificationNotice": "Nota: Le notifiche via email possono essere attivate nel pannello di admin."
},
"inviteExpiry": {
"title": "Nota: Invito scaduto",
"inviteExpired": "Invito scaduto.",
"expiredAt": "Il codice {n} è scaduto il {n}.",
"expiredAt": "Il codice {code} è scaduto il {time}.",
"notificationNotice": "Nota: le e-mail di notifica possono essere attivate dal pannello di admin."
},
"passwordReset": {
"title": "Richiesto un reset della password - Jellyfin",
"someoneHasRequestedReset": "Qualcuno ha recentemente richiesto un reset della password su Jellyfin.",
"ifItWasYou": "Se sei stato tu, scrivi il PIN sotto alla richiesta.",
"codeExpiry": "Il codice scadrà in {n}, alle {n} UTC, che è alle {n}.",
"codeExpiry": "Il codice scadrà in {date}, alle {time} UTC, che è alle {expiresInMinutes}.",
"pin": "PIN"
},
"userDeleted": {

View File

@ -4,25 +4,25 @@
},
"strings": {
"ifItWasNotYou": "Als jij dit niet was, negeer dan alsjeblieft deze email.",
"helloUser": "Hoi {n},"
"helloUser": "Hoi {username},"
},
"userCreated": {
"title": "Melding: Gebruiker aangemaakt",
"aUserWasCreated": "Er is een gebruiker aangemaakt door gebruik te maken van code {n}.",
"aUserWasCreated": "Er is een gebruiker aangemaakt door gebruik te maken van code {code}.",
"time": "Tijdstip",
"notificationNotice": "Opmerking: Meldingsemails kunnen worden aan- of uitgezet via het admin dashboard."
"notificationNotice": "Opmerking: Meldingse-mails kunnen worden aan- of uitgezet via het beheerdersdashboard."
},
"inviteExpiry": {
"title": "Melding: Uitnodiging verlopen",
"inviteExpired": "Uitnodiging verlopen.",
"expiredAt": "Code {n} is verlopen op {n}.",
"notificationNotice": "Opmerking: Meldingsemails kunnen worden aan- of uitgezet via het admin dashboard."
"expiredAt": "Code {code} is verlopen op {time}.",
"notificationNotice": "Opmerking: Meldingse-mails kunnen worden aan- of uitgezet via het beheerdersdashboard."
},
"passwordReset": {
"title": "Wachtwoordreset aangevraagd - Jellyfin",
"someoneHasRequestedReset": "Iemand heeft recentelijk een wachtwoordreset aangevraagd in Jellyfin.",
"ifItWasYou": "Als jij dit was, voor dan onderstaande PIN in.",
"codeExpiry": "De code verloopt op {n}, op {n} UTC, dat is over {n}.",
"codeExpiry": "De code verloopt op {date}, op {time} UTC, dat is over {expiresInMinutes}.",
"pin": "PIN"
},
"userDeleted": {
@ -35,7 +35,7 @@
"hello": "Hoi",
"youHaveBeenInvited": "Je bent uitgenodigd voor Jellyfin.",
"toJoin": "Volg onderstaande link om door te gaan.",
"inviteExpiry": "Deze uitnodiging verloopt op {n}, om {n}, dat is over {n}, dus wees er snel bij.",
"inviteExpiry": "Deze uitnodiging verloopt op {date}, om {time}, dat is over {expiresInMinutes}, dus wees er snel bij.",
"linkButton": "Maak account aan"
},
"welcomeEmail": {

View File

@ -4,25 +4,25 @@
},
"strings": {
"ifItWasNotYou": "Se não foi você, ignore este e-mail.",
"helloUser": "Ola {n},"
"helloUser": "Ola {username},"
},
"userCreated": {
"title": "Aviso: Usuário criado",
"aUserWasCreated": "Um usuário foi criado usando o código {n}.",
"aUserWasCreated": "Um usuário foi criado usando o código {code}.",
"time": "Tempo",
"notificationNotice": "Nota: Os emails de notificação podem ser alternados no painel do administrador."
},
"inviteExpiry": {
"title": "Aviso: Convite expirado",
"inviteExpired": "Convite expirado.",
"expiredAt": "O código {n} expirou em {n}.",
"expiredAt": "O código {code} expirou em {time}.",
"notificationNotice": "Nota: Os emails de notificação podem ser alternados no painel do administrador."
},
"passwordReset": {
"title": "Redefinir senha foi solicitada - Jellyfin",
"someoneHasRequestedReset": "Alguém recentemente solicitou uma redefinição de senha no Jellyfin.",
"ifItWasYou": "Se foi você, insira o PIN abaixo.",
"codeExpiry": "O código irá expirar em {n}, ás {n}, que está em {n}.",
"codeExpiry": "O código irá expirar em {date}, ás {time}, que está em {expiresInMinutes}.",
"pin": "PIN"
},
"userDeleted": {
@ -35,7 +35,7 @@
"hello": "Ola",
"youHaveBeenInvited": "Você recebeu um convite para o Jellyfin.",
"toJoin": "Para participar, clique no link abaixo.",
"inviteExpiry": "Este convite expira em {n} às {n}, que é em {n}, então seja rápido.",
"inviteExpiry": "Este convite expira em {date} às {time}, que é em {expiresInMinutes}, então seja rápido.",
"linkButton": "Crie sua conta"
},
"welcomeEmail": {

View File

@ -6,7 +6,7 @@
"pageTitle": "Maak Jellyfin account aan",
"createAccountHeader": "Account aanmaken",
"accountDetails": "Details",
"emailAddress": "Email",
"emailAddress": "E-mail",
"username": "Gebruikersnaam",
"password": "Wachtwoord",
"reEnterPassword": "Bevestig wachtwoord",

View File

@ -25,13 +25,13 @@
},
"endPage": {
"finished": "Klaar!",
"restartMessage": "Er staan meer instellingen op de adminpagina. Druk op de knop hieronder om opnieuw op te starten, en ververs daarna de pagina.",
"restartMessage": "Er staan meer instellingen op de beheerderspagina. Druk op de knop hieronder om opnieuw op te starten, en ververs daarna de pagina.",
"refreshPage": "Verversen"
},
"language": {
"title": "Taal",
"description": "Er zijn gemeenschapsvertalingen beschikbaar voor de meeste onderdelen van jfa-go. Je kunt hieronder de standaardtaal instellen, maar gebruikers kunnen die aanpassen indien gewenst. Als je wilt helpen met vertalen, meld je dan aan bij {n} en begin met vertalen!",
"defaultAdminLang": "Standaardtaal (admin)",
"defaultAdminLang": "Standaardtaal (beheer)",
"defaultFormLang": "Standaardtaal aanmaken nieuwe account",
"defaultEmailLang": "Standaardtaal (e-mails)"
},
@ -50,15 +50,15 @@
},
"login": {
"title": "Inloggen",
"description": "Om de admin-pagina te openen moet je inloggen met één van onderstaande methodes:",
"description": "Om de beheerderspagina te openen moet je inloggen met één van onderstaande methodes:",
"authorizeWithJellyfin": "Jellyfin/Emby authorisatie: Inloggegevens worden gedeeld met Jellyfin, meerdere gebruikers mogelijk.",
"authorizeManual": "Gebruikersnaam en wachtwoord: Stel handmatig een gebruikersnaam en wachtwoord in.",
"adminOnly": "Alleen admin-gebruikers (aanbevolen)",
"adminOnly": "Alleen beheerders (aanbevolen)",
"emailNotice": "Je e-mailadres kan gebruikt worden om meldingen te ontvangen."
},
"jellyfinEmby": {
"title": "Jellyfin/Emby",
"description": "Er is een admin-account nodig, omdat via de API geen gebruikers aangemaakt kunnen worden. Maak een aparte account aan en vink 'Deze gebruiker kan de server beheren' aan. Alle andere rechten kunnen uitgeschakeld worden. Vul daarna hier de inloggegevens in.",
"description": "Er is een beheerdersaccount nodig, omdat via de API geen gebruikers aangemaakt kunnen worden. Maak een aparte account aan en vink 'Deze gebruiker kan de server beheren' aan. Alle andere rechten kunnen uitgeschakeld worden. Vul daarna hier de inloggegevens in.",
"embyNotice": "Ondersteuning voor Emby is beperkt en ondersteunt geen wachtwoordresets.",
"internal": "Intern",
"external": "Extern",
@ -118,7 +118,7 @@
"title": "Uitleg",
"description": "Deze teksten worden getoond bij de account-aanmaakpagina en in sommige e-mails.",
"contactMessage": "Contacttekst",
"contactMessageNotice": "Getoond onderaan elke pagina behalve admin.",
"contactMessageNotice": "Getoond onderaan elke pagina behalve beheer.",
"helpMessage": "Helpbericht",
"helpMessageNotice": "Getoond op de account-aanmaakpagina.",
"successMessage": "Succesbericht",

45
logger.go Normal file
View File

@ -0,0 +1,45 @@
package main
import (
"io"
"log"
c "github.com/fatih/color"
)
type Logger interface {
Printf(format string, v ...interface{})
Print(v ...interface{})
Println(v ...interface{})
Fatal(v ...interface{})
Fatalf(format string, v ...interface{})
}
type logger struct {
logger *log.Logger
printer *c.Color
}
func NewLogger(out io.Writer, prefix string, flag int, color c.Attribute) (l logger) {
l.logger = log.New(out, prefix, flag)
l.printer = c.New(color)
return l
}
func (l logger) Printf(format string, v ...interface{}) {
l.logger.Print(l.printer.Sprintf(format, v...))
}
func (l logger) Print(v ...interface{}) { l.logger.Print(l.printer.Sprint(v...)) }
func (l logger) Println(v ...interface{}) { l.logger.Print(l.printer.Sprintln(v...)) }
func (l logger) Fatal(v ...interface{}) { l.logger.Fatal(l.printer.Sprint(v...)) }
func (l logger) Fatalf(format string, v ...interface{}) {
l.logger.Fatal(l.printer.Sprintf(format, v...))
}
type emptyLogger bool
func (l emptyLogger) Printf(format string, v ...interface{}) {}
func (l emptyLogger) Print(v ...interface{}) {}
func (l emptyLogger) Println(v ...interface{}) {}
func (l emptyLogger) Fatal(v ...interface{}) {}
func (l emptyLogger) Fatalf(format string, v ...interface{}) {}

27
main.go
View File

@ -7,7 +7,6 @@ import (
"encoding/json"
"flag"
"fmt"
"io"
"io/fs"
"log"
"mime"
@ -22,12 +21,12 @@ import (
"strings"
"time"
"github.com/fatih/color"
"github.com/hrfee/jfa-go/common"
_ "github.com/hrfee/jfa-go/docs"
"github.com/hrfee/jfa-go/mediabrowser"
"github.com/hrfee/jfa-go/ombi"
"github.com/lithammer/shortuuid/v3"
"github.com/logrusorgru/aurora/v3"
"gopkg.in/ini.v1"
)
@ -41,6 +40,10 @@ var (
DEBUG *bool
TEST bool
SWAGGER *bool
warning = color.New(color.FgYellow).SprintfFunc()
info = color.New(color.FgMagenta).SprintfFunc()
hiwhite = color.New(color.FgHiWhite).SprintfFunc()
white = color.New(color.FgWhite).SprintfFunc()
)
var serverTypes = map[string]string{
@ -79,7 +82,7 @@ type appContext struct {
storage Storage
validator Validator
email *Emailer
info, debug, err *log.Logger
info, debug, err Logger
host string
port int
version string
@ -146,8 +149,8 @@ func start(asDaemon, firstCall bool) {
fs: localFS,
}
app.info = log.New(os.Stdout, "[INFO] ", log.Ltime)
app.err = log.New(os.Stdout, "[ERROR] ", log.Ltime|log.Lshortfile)
app.info = NewLogger(os.Stdout, "[INFO] ", log.Ltime, color.FgHiWhite)
app.err = NewLogger(os.Stdout, "[ERROR] ", log.Ltime|log.Lshortfile, color.FgRed)
if firstCall {
DATA = flag.String("data", app.dataPath, "alternate path to data directory.")
@ -231,10 +234,10 @@ func start(asDaemon, firstCall bool) {
debugMode = true
}
if debugMode {
app.info.Print(aurora.Magenta("\n\nWARNING: Don't use debug mode in production, as it exposes pprof on the network.\n\n"))
app.debug = log.New(os.Stdout, "[DEBUG] ", log.Ltime|log.Lshortfile)
app.info.Print(warning("\n\nWARNING: Don't use debug mode in production, as it exposes pprof on the network.\n\n"))
app.debug = NewLogger(os.Stdout, "[DEBUG] ", log.Ltime|log.Lshortfile, color.FgYellow)
} else {
app.debug = log.New(io.Discard, "", 0)
app.debug = emptyLogger(false)
}
if asDaemon {
@ -404,7 +407,7 @@ func start(asDaemon, firstCall bool) {
serverType = mediabrowser.EmbyServer
timeoutHandler = common.NewTimeoutHandler("Emby", server, true)
app.info.Println("Using Emby server type")
fmt.Println(aurora.Yellow("WARNING: Emby compatibility is experimental, and support is limited.\nPassword resets are not available."))
fmt.Println(warning("WARNING: Emby compatibility is experimental, and support is limited.\nPassword resets are not available."))
} else {
app.info.Println("Using Jellyfin server type")
}
@ -453,11 +456,11 @@ func start(asDaemon, firstCall bool) {
var status int
var err error
if app.jf.Hyphens {
app.info.Println(aurora.Yellow("Your build of Jellyfin appears to hypenate user IDs. Your emails.json file will be modified to match."))
app.info.Println(info("Your build of Jellyfin appears to hypenate user IDs. Your emails.json file will be modified to match."))
time.Sleep(time.Second * time.Duration(3))
newEmails, status, err = app.hyphenateEmailStorage(app.storage.emails)
} else {
app.info.Println(aurora.Yellow("Your emails.json file uses hyphens, but the Jellyfin server no longer does. It will be modified."))
app.info.Println(info("Your emails.json file uses hyphens, but the Jellyfin server no longer does. It will be modified."))
time.Sleep(time.Second * time.Duration(3))
newEmails, status, err = app.deHyphenateEmailStorage(app.storage.emails)
}
@ -621,7 +624,7 @@ func flagPassed(name string) (found bool) {
// @tag.description Things that dont fit elsewhere.
func printVersion() {
fmt.Print(aurora.Sprintf(aurora.Magenta("jfa-go version: %s (%s)\n"), aurora.BrightWhite(VERSION), aurora.White(COMMIT)))
fmt.Println(info("jfa-go version: %s (%s)\n", hiwhite(VERSION), white(COMMIT)))
}
func main() {

6
package-lock.json generated
View File

@ -228,9 +228,9 @@
"integrity": "sha1-vfpzUplmTfr9NFKe1PhSKidf6lY="
},
"esbuild": {
"version": "0.8.48",
"resolved": "https://registry.npm.taobao.org/esbuild/download/esbuild-0.8.48.tgz",
"integrity": "sha1-pX5N3oTsVtocbsrv7pfp2mxbALU="
"version": "0.8.49",
"resolved": "https://registry.npm.taobao.org/esbuild/download/esbuild-0.8.49.tgz",
"integrity": "sha1-PTP3GzlmYR+CLPTIOBFfP70W3vI="
},
"escalade": {
"version": "3.1.1",

View File

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

View File

@ -1,17 +1,16 @@
package main
import (
"fmt"
"html/template"
"io/fs"
"net/http"
"os"
"path/filepath"
"github.com/fatih/color"
"github.com/gin-contrib/pprof"
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
"github.com/logrusorgru/aurora/v3"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
@ -44,9 +43,10 @@ func (app *appContext) loadHTML(router *gin.Engine) {
// sets gin logger.
func setGinLogger(router *gin.Engine, debugMode bool) {
sprintf := color.New(color.Faint).SprintfFunc()
if debugMode {
router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
return fmt.Sprintf("[GIN/DEBUG] %s: %s(%s) => %d in %s; %s\n",
return sprintf("[GIN/DEBUG] %s: %s(%s) => %d in %s; %s\n",
param.TimeStamp.Format("15:04:05"),
param.Method,
param.Path,
@ -62,7 +62,7 @@ func setGinLogger(router *gin.Engine, debugMode bool) {
}))
} else {
router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
return fmt.Sprintf("[GIN] %s(%s) => %d\n",
return sprintf("[GIN] %s(%s) => %d\n",
param.Method,
param.Path,
param.StatusCode,
@ -115,7 +115,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
router.GET(p+"/invite/:invCode", app.InviteProxy)
}
if *SWAGGER {
app.info.Print(aurora.Magenta("\n\nWARNING: Swagger should not be used on a public instance.\n\n"))
app.info.Print(warning("\n\nWARNING: Swagger should not be used on a public instance.\n\n"))
for _, p := range routePrefixes {
router.GET(p+"/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
@ -123,7 +123,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
api := router.Group("/", app.webAuth())
for _, p := range routePrefixes {
router.POST(p+"/logout", app.Logout)
api.DELETE(p+"/users", app.DeleteUser)
api.DELETE(p+"/users", app.DeleteUsers)
api.GET(p+"/users", app.GetUsers)
api.POST(p+"/users", app.NewUserAdmin)
api.POST(p+"/invites", app.GenerateInvite)