diff --git a/api-messages.go b/api-messages.go index cb4f67f..e3297b5 100644 --- a/api-messages.go +++ b/api-messages.go @@ -414,7 +414,7 @@ func (app *appContext) TelegramVerified(gc *gin.Context) { respondBool(200, ok, gc) } -// @Summary Returns true/false on whether or not a telegram PIN was verified. Requires invite code. +// @Summary Returns true/false on whether or not a telegram PIN was verified. Requires invite code. NOTE: "/invite" might have been changed in Settings > URL Paths. // @Produce json // @Success 200 {object} boolResponse // @Success 401 {object} boolResponse @@ -438,7 +438,7 @@ func (app *appContext) TelegramVerifiedInvite(gc *gin.Context) { respondBool(200, ok, gc) } -// @Summary Returns true/false on whether or not a discord PIN was verified. Requires invite code. +// @Summary Returns true/false on whether or not a discord PIN was verified. Requires invite code. NOTE: "/invite" might have been changed in Settings > URL Paths. // @Produce json // @Success 200 {object} boolResponse // @Failure 401 {object} boolResponse @@ -462,7 +462,7 @@ func (app *appContext) DiscordVerifiedInvite(gc *gin.Context) { respondBool(200, ok, gc) } -// @Summary Returns a 10-minute, one-use Discord server invite +// @Summary Returns a 10-minute, one-use Discord server invite. NOTE: "/invite" might have been changed in Settings > URL Paths. // @Produce json // @Success 200 {object} DiscordInviteDTO // @Failure 400 {object} boolResponse @@ -489,7 +489,7 @@ func (app *appContext) DiscordServerInvite(gc *gin.Context) { gc.JSON(200, DiscordInviteDTO{invURL, iconURL}) } -// @Summary Generate and send a new PIN to a specified Matrix user. +// @Summary Generate and send a new PIN to a specified Matrix user. NOTE: "/invite" might have been changed in Settings > URL Paths. // @Produce json // @Success 200 {object} boolResponse // @Failure 400 {object} stringResponse @@ -528,7 +528,7 @@ func (app *appContext) MatrixSendPIN(gc *gin.Context) { respondBool(200, true, gc) } -// @Summary Check whether a matrix PIN is valid, and mark the token as verified if so. Requires invite code. +// @Summary Check whether a matrix PIN is valid, and mark the token as verified if so. Requires invite code. NOTE: "/invite" might have been changed in Settings > URL Paths. // @Produce json // @Success 200 {object} boolResponse // @Failure 401 {object} boolResponse diff --git a/api-userpage.go b/api-userpage.go index a7d9b77..f608fbb 100644 --- a/api-userpage.go +++ b/api-userpage.go @@ -164,9 +164,7 @@ func (app *appContext) confirmMyAction(gc *gin.Context, key string) { var target ConfirmationTarget var id string fail := func() { - gcHTML(gc, 404, "404.html", gin.H{ - "cssClass": app.cssClass, - "cssVersion": cssVersion, + app.gcHTML(gc, 404, "404.html", OtherPage, gin.H{ "contactMessage": app.config.Section("ui").Key("contact_message").String(), }) } @@ -201,7 +199,7 @@ func (app *appContext) confirmMyAction(gc *gin.Context, key string) { // Perform an Action if target == NoOp { - gc.Redirect(http.StatusSeeOther, "/my/account") + gc.Redirect(http.StatusSeeOther, PAGES.MyAccount) return } else if target == UserEmailChange { app.modifyEmail(id, claims["email"].(string)) @@ -216,7 +214,7 @@ func (app *appContext) confirmMyAction(gc *gin.Context, key string) { }, gc, true) app.info.Printf(lm.UserEmailAdjusted, gc.GetString("jfId")) - gc.Redirect(http.StatusSeeOther, "/my/account") + gc.Redirect(http.StatusSeeOther, PAGES.MyAccount) return } } diff --git a/config.go b/config.go index ff8a29e..4cb2bb7 100644 --- a/config.go +++ b/config.go @@ -22,6 +22,9 @@ var telegramEnabled = false var discordEnabled = false var matrixEnabled = false +// URL subpaths. Ignore the "Current" field. +var PAGES = PagePaths{} + func (app *appContext) GetPath(sect, key string) (fs.FS, string) { val := app.config.Section(sect).Key(key).MustString("") if strings.HasPrefix(val, "jfa-go:") { @@ -35,6 +38,13 @@ func (app *appContext) MustSetValue(section, key, val string) { app.config.Section(section).Key(key).SetValue(app.config.Section(section).Key(key).MustString(val)) } +func (app *appContext) MustSetURLPath(section, key, val string) { + if !strings.HasPrefix(val, "/") { + val = "/" + val + } + app.MustSetValue(section, key, val) +} + func (app *appContext) loadConfig() error { var err error app.config, err = ini.ShadowLoad(app.configPath) @@ -42,6 +52,13 @@ func (app *appContext) loadConfig() error { return err } + app.MustSetURLPath("url_paths", "admin", "/") + app.MustSetURLPath("url_paths", "user_page", "/my/account") + app.MustSetURLPath("url_paths", "form", "/invite") + PAGES.Admin = app.config.Section("url_paths").Key("admin").MustString("/") + PAGES.MyAccount = app.config.Section("url_paths").Key("user_page").MustString("/my/account") + PAGES.Form = app.config.Section("url_paths").Key("form").MustString("/invite") + 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()) @@ -58,12 +75,12 @@ func (app *appContext) loadConfig() error { 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(""), "/") - if app.URLBase == "/invite" || app.URLBase == "/accounts" || app.URLBase == "/settings" || app.URLBase == "/activity" { - app.err.Printf(lm.BadURLBase, app.URLBase) + PAGES.Base = strings.TrimSuffix(app.config.Section("ui").Key("url_base").MustString(""), "/") + if PAGES.Base == "/invite" || PAGES.Base == "/accounts" || PAGES.Base == "/settings" || PAGES.Base == "/activity" { + app.err.Printf(lm.BadURLBase, PAGES.Base) } app.ExternalURI = strings.TrimSuffix(strings.TrimSuffix(app.config.Section("ui").Key("jfa_url").MustString(""), "/invite"), "/") - if !strings.HasSuffix(app.ExternalURI, app.URLBase) { + if !strings.HasSuffix(app.ExternalURI, PAGES.Base) { app.err.Println(lm.NoURLSuffix) } if app.ExternalURI == "" { diff --git a/config/config-base.yaml b/config/config-base.yaml index d429010..2a17342 100644 --- a/config/config-base.yaml +++ b/config/config-base.yaml @@ -232,6 +232,33 @@ sections: - ["opaque", "Opaque"] value: clear description: Appearance of the Admin login screen. +- section: url_paths + meta: + name: URL Paths + description: Settings for changing where different pages are accessed. + advanced: true + settings: + - setting: admin + name: Admin page subpath + type: text + required: true + requires_restart: true + value: "/" + description: URL subpath the admin page should be at. + - setting: user_page + name: "\"My Account\" subpath" + type: text + required: true + requires_restart: true + value: "/my/account" + description: URL subpath the "My Account" page should be at. + - setting: form + name: Invite subpath + type: text + required: true + requires_restart: true + value: "/invite" + description: URL subpath invites should be on. - section: advanced meta: name: Advanced diff --git a/email.go b/email.go index 82da3f2..5201a33 100644 --- a/email.go +++ b/email.go @@ -329,7 +329,7 @@ func (emailer *Emailer) confirmationValues(code, username, key string, app *appC if code == "" { // Personal email change inviteLink = fmt.Sprintf("%s/my/confirm/%s", inviteLink, url.PathEscape(key)) } else { // Invite email confirmation - inviteLink = fmt.Sprintf("%s/invite/%s?key=%s", inviteLink, code, url.PathEscape(key)) + inviteLink = fmt.Sprintf("%s%s/%s?key=%s", inviteLink, PAGES.Form, code, url.PathEscape(key)) } template["helloUser"] = emailer.lang.Strings.template("helloUser", tmpl{"username": username}) template["confirmationURL"] = inviteLink @@ -393,7 +393,7 @@ func (emailer *Emailer) inviteValues(code string, invite Invite, app *appContext expiry := invite.ValidTill d, t, expiresIn := emailer.formatExpiry(expiry, false, app.datePattern, app.timePattern) message := app.config.Section("messages").Key("message").String() - inviteLink := fmt.Sprintf("%s/invite/%s", app.ExternalURI, code) + inviteLink := fmt.Sprintf("%s%s/%s", app.ExternalURI, PAGES.Form, code) template := map[string]interface{}{ "hello": emailer.lang.InviteEmail.get("hello"), "youHaveBeenInvited": emailer.lang.InviteEmail.get("youHaveBeenInvited"), diff --git a/html/404.html b/html/404.html index 22b0cbb..429ecba 100644 --- a/html/404.html +++ b/html/404.html @@ -1,9 +1,8 @@ - - {{ template "header.html" . }} 404 - jfa-go + {{ template "header.html" . }}
diff --git a/html/admin.html b/html/admin.html index bb10c4c..25630bd 100644 --- a/html/admin.html +++ b/html/admin.html @@ -1,16 +1,7 @@ - Admin - jfa-go @@ -47,7 +37,7 @@
- + diff --git a/html/crash.html b/html/crash.html index 44e4b43..e01329b 100644 --- a/html/crash.html +++ b/html/crash.html @@ -1,6 +1,7 @@ + {{ template "header.html" . }} Crash report diff --git a/html/create-success.html b/html/create-success.html index 19b7660..105424f 100644 --- a/html/create-success.html +++ b/html/create-success.html @@ -1,7 +1,6 @@ - {{ template "header.html" . }} {{ .strings.successHeader }} - jfa-go diff --git a/html/form-base.html b/html/form-base.html index f25bb2e..77e6a36 100644 --- a/html/form-base.html +++ b/html/form-base.html @@ -3,7 +3,6 @@ window.usernameEnabled = {{ .username }}; window.validationStrings = JSON.parse({{ .validationStrings }}); window.invalidPassword = "{{ .strings.reEnterPasswordInvalid }}"; - window.URLBase = "{{ .urlBase }}"; window.code = "{{ .code }}"; window.language = "{{ .langName }}"; window.messages = JSON.parse({{ .notifications }}); @@ -14,16 +13,13 @@ 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 }}"; window.captcha = {{ .captcha }}; diff --git a/html/form.html b/html/form.html index 5199cad..336d21d 100644 --- a/html/form.html +++ b/html/form.html @@ -1,7 +1,6 @@ - {{ template "header.html" . }} {{ if .passwordReset }} {{ .strings.passwordReset }} diff --git a/html/header.html b/html/header.html index 6af2da8..b857990 100644 --- a/html/header.html +++ b/html/header.html @@ -1,13 +1,32 @@ + - - - - - + + + + + + diff --git a/html/invalidCode.html b/html/invalidCode.html index bc8de0f..611577b 100644 --- a/html/invalidCode.html +++ b/html/invalidCode.html @@ -1,7 +1,6 @@ - {{ template "header.html" . }} Invalid Code - jfa-go diff --git a/html/login-modal.html b/html/login-modal.html index 408e5c7..dde3998 100644 --- a/html/login-modal.html +++ b/html/login-modal.html @@ -15,7 +15,7 @@ {{ $hasTwoCards = 1 }}
{{ .strings.loginNotAdmin }} - {{ .strings.myAccount }} + {{ .strings.myAccount }}
{{ end }} {{ end }} diff --git a/html/password-reset.html b/html/password-reset.html index ebd75fd..4ffd558 100644 --- a/html/password-reset.html +++ b/html/password-reset.html @@ -1,7 +1,6 @@ - {{ template "header.html" . }} {{ .strings.passwordReset }} - jfa-go @@ -40,6 +39,6 @@ {{ .contactMessage }} - + diff --git a/html/setup.html b/html/setup.html index 0d53716..ffed526 100644 --- a/html/setup.html +++ b/html/setup.html @@ -1,7 +1,6 @@ - {{ template "header.html" . }} {{ .lang.Strings.pageTitle }} diff --git a/html/user.html b/html/user.html index 7e7484b..8de812b 100644 --- a/html/user.html +++ b/html/user.html @@ -1,31 +1,21 @@ - {{ template "header.html" . }} {{ .strings.myAccount }} @@ -156,7 +146,7 @@ {{ end }} - + diff --git a/main.go b/main.go index d2f881c..7d1de5a 100644 --- a/main.go +++ b/main.go @@ -103,37 +103,37 @@ type appContext struct { adminUsers []User invalidTokens []string // Keeping jf name because I can't think of a better one - jf *mediabrowser.MediaBrowser - authJf *mediabrowser.MediaBrowser - ombi *OmbiWrapper - js *JellyseerrWrapper - thirdPartyServices []ThirdPartyService - datePattern string - timePattern string - storage Storage - validator Validator - email *Emailer - telegram *TelegramDaemon - discord *DiscordDaemon - matrix *MatrixDaemon - contactMethods []ContactMethodLinker - info, debug, err *logger.Logger - host string - port int - version string - URLBase, ExternalURI, ExternalDomain string - updater *Updater - webhooks *WebhookSender - newUpdate bool // Whether whatever's in update is new. - tag Tag - update Update - proxyEnabled bool - proxyTransport *http.Transport - proxyConfig easyproxy.ProxyConfig - internalPWRs map[string]InternalPWR - pwrCaptchas map[string]Captcha - ConfirmationKeys map[string]map[string]ConfirmationKey // Map of invite code to jwt to request - confirmationKeysLock sync.Mutex + jf *mediabrowser.MediaBrowser + authJf *mediabrowser.MediaBrowser + ombi *OmbiWrapper + js *JellyseerrWrapper + thirdPartyServices []ThirdPartyService + datePattern string + timePattern string + storage Storage + validator Validator + email *Emailer + telegram *TelegramDaemon + discord *DiscordDaemon + matrix *MatrixDaemon + contactMethods []ContactMethodLinker + info, debug, err *logger.Logger + host string + port int + version string + ExternalURI, ExternalDomain string + updater *Updater + webhooks *WebhookSender + newUpdate bool // Whether whatever's in update is new. + tag Tag + update Update + proxyEnabled bool + proxyTransport *http.Transport + proxyConfig easyproxy.ProxyConfig + internalPWRs map[string]InternalPWR + pwrCaptchas map[string]Captcha + ConfirmationKeys map[string]map[string]ConfirmationKey // Map of invite code to jwt to request + confirmationKeysLock sync.Mutex } func generateSecret(length int) (string, error) { @@ -147,7 +147,7 @@ func generateSecret(length int) (string, error) { func test(app *appContext) { fmt.Printf("\n\n----\n\n") - settings := map[string]interface{}{ + settings := map[string]any{ "server": app.jf.Server, "server version": app.jf.ServerInfo.Version, "server name": app.jf.ServerInfo.Name, diff --git a/models.go b/models.go index 97e669b..686effd 100644 --- a/models.go +++ b/models.go @@ -467,3 +467,18 @@ type ContactMethodKey struct { PIN string User ContactMethodUser } + +type PagePaths struct { + // The base subfolder the app is hosted on. + Base string `json:"Base"` + // Those for other pages + Admin string `json:"Admin"` + MyAccount string `json:"MyAccount"` + Form string `json:"Form"` +} + +type PagePathsDTO struct { + PagePaths + // The subdirectory this bit of the app is hosted on (e.g. admin is usually on "/", myacc is usually on "/my/account") + Current string `json:"Current"` +} diff --git a/router.go b/router.go index 9edc32c..e88f49f 100644 --- a/router.go +++ b/router.go @@ -108,8 +108,8 @@ func (app *appContext) loadRouter(address string, debug bool) *gin.Engine { } func (app *appContext) loadRoutes(router *gin.Engine) { - routePrefixes := []string{app.URLBase} - if app.URLBase != "" { + routePrefixes := []string{PAGES.Base} + if PAGES.Base != "" { routePrefixes = append(routePrefixes, "") } @@ -118,7 +118,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) { for _, p := range routePrefixes { router.GET(p+"/lang/:page", app.GetLanguages) router.Use(static.Serve(p+"/", app.webFS)) - router.GET(p+"/", app.AdminPage) + router.GET(p+PAGES.Admin, app.AdminPage) if app.config.Section("password_resets").Key("link_reset").MustBool(false) { router.GET(p+"/reset", app.ResetPassword) @@ -127,39 +127,39 @@ func (app *appContext) loadRoutes(router *gin.Engine) { } } - router.GET(p+"/accounts", app.AdminPage) - router.GET(p+"/settings", app.AdminPage) - router.GET(p+"/activity", app.AdminPage) - router.GET(p+"/accounts/user/:userID", app.AdminPage) - router.GET(p+"/invites/:code", app.AdminPage) + router.GET(p+PAGES.Admin+"/accounts", app.AdminPage) + router.GET(p+PAGES.Admin+"/settings", app.AdminPage) + router.GET(p+PAGES.Admin+"/activity", app.AdminPage) + router.GET(p+PAGES.Admin+"/accounts/user/:userID", app.AdminPage) + router.GET(p+PAGES.Admin+"/invites/:code", app.AdminPage) router.GET(p+"/lang/:page/:file", app.ServeLang) router.GET(p+"/token/login", app.getTokenLogin) router.GET(p+"/token/refresh", app.getTokenRefresh) router.POST(p+"/user/invite", app.NewUserFromInvite) - router.Use(static.Serve(p+"/invite/", app.webFS)) - router.GET(p+"/invite/:invCode", app.InviteProxy) + router.Use(static.Serve(p+PAGES.Form, app.webFS)) + router.GET(p+PAGES.Form+"/:invCode", app.InviteProxy) if app.config.Section("captcha").Key("enabled").MustBool(false) { router.GET(p+"/captcha/gen/:invCode", app.GenCaptcha) router.GET(p+"/captcha/img/:invCode/:captchaID", app.GetCaptcha) router.POST(p+"/captcha/verify/:invCode/:captchaID/:text", app.VerifyCaptcha) } if telegramEnabled { - router.GET(p+"/invite/:invCode/telegram/verified/:pin", app.TelegramVerifiedInvite) + router.GET(p+PAGES.Form+"/:invCode/telegram/verified/:pin", app.TelegramVerifiedInvite) } if discordEnabled { - router.GET(p+"/invite/:invCode/discord/verified/:pin", app.DiscordVerifiedInvite) + router.GET(p+PAGES.Form+"/:invCode/discord/verified/:pin", app.DiscordVerifiedInvite) if app.config.Section("discord").Key("provide_invite").MustBool(false) { - router.GET(p+"/invite/:invCode/discord/invite", app.DiscordServerInvite) + router.GET(p+PAGES.Form+"/:invCode/discord/invite", app.DiscordServerInvite) } } if matrixEnabled { - router.GET(p+"/invite/:invCode/matrix/verified/:userID/:pin", app.MatrixCheckPIN) - router.POST(p+"/invite/:invCode/matrix/user", app.MatrixSendPIN) + router.GET(p+PAGES.Form+"/:invCode/matrix/verified/:userID/:pin", app.MatrixCheckPIN) + router.POST(p+PAGES.Form+"/:invCode/matrix/user", app.MatrixSendPIN) router.POST(p+"/users/matrix", app.MatrixConnect) } if userPageEnabled { - router.GET(p+"/my/account", app.MyUserPage) - router.GET(p+"/my/account/password/reset", app.MyUserPage) + router.GET(p+PAGES.MyAccount, app.MyUserPage) + router.GET(p+PAGES.MyAccount+"/password/reset", app.MyUserPage) router.GET(p+"/my/token/login", app.getUserTokenLogin) router.GET(p+"/my/token/refresh", app.getUserTokenRefresh) router.GET(p+"/my/confirm/:jwt", app.ConfirmMyAction) diff --git a/setup.go b/setup.go index bebba74..5d2f1ab 100644 --- a/setup.go +++ b/setup.go @@ -39,8 +39,10 @@ func (app *appContext) ServeSetup(gc *gin.Context) { respond(500, "Failed to fetch default values", gc) return } + pages := PagePathsDTO{PagePaths: PAGES} gc.HTML(200, "setup.html", gin.H{ "cssVersion": cssVersion, + "pages": pages, "lang": app.storage.lang.Setup[lang], "strings": app.storage.lang.Setup[lang].Strings, "emailLang": app.storage.lang.Email[emailLang], diff --git a/ts/admin.ts b/ts/admin.ts index 814fa37..090a8e4 100644 --- a/ts/admin.ts +++ b/ts/admin.ts @@ -11,6 +11,8 @@ import { _get, _post, notificationBox, whichAnimationEvent, bindManualDropdowns import { Updater } from "./modules/update.js"; import { Login } from "./modules/login.js"; +declare var window: GlobalWindow; + const theme = new ThemeManager(document.getElementById("button-theme")); window.lang = new lang(window.langFile as LangFile); @@ -165,12 +167,12 @@ const defaultTab = tabs[0]; window.tabs = new Tabs(); for (let tab of tabs) { - window.tabs.addTab(tab.id, tab.url, null, tab.reloader); + window.tabs.addTab(tab.id, window.pages.Admin + "/" + tab.url, null, tab.reloader); } let matchedTab = false -for (let tab of tabs) { - if (window.location.pathname.startsWith(window.URLBase + "/" + tab.url)) { +for (const tab of tabs) { + if (window.location.pathname.startsWith(window.pages.Base + window.pages.Current + "/" + tab.url)) { window.tabs.switch(tab.url, true); matchedTab = true; } diff --git a/ts/form.ts b/ts/form.ts index 6552040..7ec413c 100644 --- a/ts/form.ts +++ b/ts/form.ts @@ -6,7 +6,7 @@ import { Validator, ValidatorConf, ValidatorRespDTO } from "./modules/validator. import { Discord, Telegram, Matrix, ServiceConfiguration, MatrixConfiguration } from "./modules/account-linking.js"; import { Captcha, GreCAPTCHA } from "./modules/captcha.js"; -interface formWindow extends Window { +interface formWindow extends GlobalWindow { invalidPassword: string; successModal: Modal; telegramModal: Modal; @@ -59,7 +59,7 @@ if (window.telegramEnabled) { modal: window.telegramModal as Modal, pin: window.telegramPIN, pinURL: "", - verifiedURL: "/invite/" + window.code + "/telegram/verified/", + verifiedURL: window.pages.Form + "/" + window.code + "/telegram/verified/", invalidCodeError: window.messages["errorInvalidPIN"], accountLinkedError: window.messages["errorAccountLinked"], successError: window.messages["verified"], @@ -89,9 +89,9 @@ if (window.discordEnabled) { const discordConf: ServiceConfiguration = { modal: window.discordModal as Modal, pin: window.discordPIN, - inviteURL: window.discordInviteLink ? ("/invite/" + window.code + "/discord/invite") : "", + inviteURL: window.discordInviteLink ? (window.pages.Form + "/" + window.code + "/discord/invite") : "", pinURL: "", - verifiedURL: "/invite/" + window.code + "/discord/verified/", + verifiedURL: window.pages.Form + "/" + window.code + "/discord/verified/", invalidCodeError: window.messages["errorInvalidPIN"], accountLinkedError: window.messages["errorAccountLinked"], successError: window.messages["verified"], @@ -121,8 +121,8 @@ if (window.matrixEnabled) { const matrixConf: MatrixConfiguration = { modal: window.matrixModal as Modal, - sendMessageURL: "/invite/" + window.code + "/matrix/user", - verifiedURL: "/invite/" + window.code + "/matrix/verified/", + sendMessageURL: window.pages.Form + "/" + window.code + "/matrix/user", + verifiedURL: window.pages.Form + "/" + window.code + "/matrix/verified/", invalidCodeError: window.messages["errorInvalidPIN"], accountLinkedError: window.messages["errorAccountLinked"], unknownError: window.messages["errorUnknown"], diff --git a/ts/modules/account-linking.ts b/ts/modules/account-linking.ts index 301c3cb..effc511 100644 --- a/ts/modules/account-linking.ts +++ b/ts/modules/account-linking.ts @@ -1,7 +1,7 @@ import { Modal } from "../modules/modal.js"; import { _get, _post, toggleLoader, addLoader, removeLoader } from "../modules/common.js"; -interface formWindow extends Window { +interface formWindow extends GlobalWindow { invalidPassword: string; successModal: Modal; telegramModal: Modal; diff --git a/ts/modules/accounts.ts b/ts/modules/accounts.ts index ab88af0..c338bfc 100644 --- a/ts/modules/accounts.ts +++ b/ts/modules/accounts.ts @@ -6,6 +6,8 @@ import { DiscordUser, newDiscordSearch } from "../modules/discord.js"; import { Search, SearchConfiguration, QueryType, SearchableItem } from "../modules/search.js"; import { HiddenInputField } from "./ui.js"; +declare var window: GlobalWindow; + const dateParser = require("any-date-parser"); interface User { diff --git a/ts/modules/activity.ts b/ts/modules/activity.ts index d54972f..1049390 100644 --- a/ts/modules/activity.ts +++ b/ts/modules/activity.ts @@ -3,6 +3,8 @@ import { Search, SearchConfiguration, QueryType, SearchableItem } from "../modul import { accountURLEvent } from "../modules/accounts.js"; import { inviteURLEvent } from "../modules/invites.js"; +declare var window: GlobalWindow; + export interface activity { id: string; type: string; @@ -52,8 +54,8 @@ export class Activity implements activity, SearchableItem { link = link.split(split)[0]; } if (link.slice(-1) != "/") { link += "/"; } - // FIXME: I should probably just be using window.URLBase, but incase thats not right, i'll put this warning here - if (link != window.URLBase) console.error(`URL Bases don't match: "${link}" != "${window.URLBase}"`); + // FIXME: I should probably just be using window.pages.Base, but incase thats not right, i'll put this warning here + if (link != window.pages.Base) console.error(`URL Bases don't match: "${link}" != "${window.pages.Base}"`); return link; })(); */ @@ -66,17 +68,17 @@ export class Activity implements activity, SearchableItem { } _genUserLink = (): string => { - return `${this._genUserText()}`; + return `${this._genUserText()}`; } _genSrcUserLink = (): string => { - return `${this._genSrcUserText()}`; + return `${this._genSrcUserText()}`; } private _renderInvText = (): string => { return `${this.value || this.invite_code || "???"}`; } private _genInvLink = (): string => { - return `${this._renderInvText()}`; + return `${this._renderInvText()}`; } diff --git a/ts/modules/common.ts b/ts/modules/common.ts index d87498a..40b5ddd 100644 --- a/ts/modules/common.ts +++ b/ts/modules/common.ts @@ -1,4 +1,4 @@ -declare var window: Window; +declare var window: GlobalWindow; export function toDateString(date: Date): string { const locale = window.language || (window as any).navigator.userLanguage || window.navigator.language; @@ -23,7 +23,7 @@ export function toDateString(date: Date): string { export const _get = (url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, noConnectionError: boolean = false): void => { let req = new XMLHttpRequest(); - if (window.URLBase) { url = window.URLBase + url; } + if (window.pages) { url = window.pages.Base + url; } req.open("GET", url, true); req.responseType = 'json'; req.setRequestHeader("Authorization", "Bearer " + window.token); @@ -42,7 +42,7 @@ export const _get = (url: string, data: Object, onreadystatechange: (req: XMLHtt export const _download = (url: string, fname: string): void => { let req = new XMLHttpRequest(); - if (window.URLBase) { url = window.URLBase + url; } + if (window.pages) { url = window.pages.Base + url; } req.open("GET", url, true); req.responseType = 'blob'; req.setRequestHeader("Authorization", "Bearer " + window.token); @@ -58,7 +58,7 @@ export const _download = (url: string, fname: string): void => { export const _upload = (url: string, formData: FormData): void => { let req = new XMLHttpRequest(); - if (window.URLBase) { url = window.URLBase + url; } + if (window.pages) { url = window.pages.Base + url; } req.open("POST", url, true); req.setRequestHeader("Authorization", "Bearer " + window.token); // req.setRequestHeader('Content-Type', 'multipart/form-data'); @@ -67,7 +67,8 @@ export const _upload = (url: string, formData: FormData): void => { export const _post = (url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, response?: boolean, statusHandler?: (req: XMLHttpRequest) => void, noConnectionError: boolean = false): void => { let req = new XMLHttpRequest(); - req.open("POST", window.URLBase + url, true); + if (window.pages) { url = window.pages.Base + url; } + req.open("POST", url, true); if (response) { req.responseType = 'json'; } @@ -88,7 +89,8 @@ export const _post = (url: string, data: Object, onreadystatechange: (req: XMLHt export function _delete(url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, noConnectionError: boolean = false): void { let req = new XMLHttpRequest(); - req.open("DELETE", window.URLBase + url, true); + if (window.pages) { url = window.pages.Base + url; } + req.open("DELETE", url, true); req.setRequestHeader("Authorization", "Bearer " + window.token); req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); req.onreadystatechange = () => { diff --git a/ts/modules/discord.ts b/ts/modules/discord.ts index 89e8a8c..f773ccf 100644 --- a/ts/modules/discord.ts +++ b/ts/modules/discord.ts @@ -1,5 +1,7 @@ import {addLoader, removeLoader, _get} from "../modules/common.js"; +declare var window: GlobalWindow; + export interface DiscordUser { name: string; avatar_url: string; diff --git a/ts/modules/invites.ts b/ts/modules/invites.ts index c9b759c..4c34a90 100644 --- a/ts/modules/invites.ts +++ b/ts/modules/invites.ts @@ -2,6 +2,8 @@ import { _get, _post, _delete, toClipboard, toggleLoader, toDateString } from ". import { DiscordUser, newDiscordSearch } from "../modules/discord.js"; import { reloadProfileNames } from "../modules/profiles.js"; +declare var window: GlobalWindow; + class DOMInvite implements Invite { updateNotify = (checkbox: HTMLInputElement) => { let state: { [code: string]: { [type: string]: boolean } } = {}; @@ -66,7 +68,7 @@ class DOMInvite implements Invite { codeLink = codeLink.split(split)[0]; } if (codeLink.slice(-1) != "/") { codeLink += "/"; } - this._codeLink = codeLink + "invite/" + code; + this._codeLink = codeLink + window.pages.Form + "/" + code; const linkEl = this._codeArea.querySelector("a") as HTMLAnchorElement; if (this.label == "") { linkEl.textContent = code.replace(/-/g, '-'); diff --git a/ts/modules/login.ts b/ts/modules/login.ts index 71cd590..11d09bd 100644 --- a/ts/modules/login.ts +++ b/ts/modules/login.ts @@ -1,6 +1,8 @@ import { Modal } from "../modules/modal.js"; import { toggleLoader, _post, unicodeB64Encode } from "../modules/common.js"; +declare var window: GlobalWindow; + export class Login { loggedIn: boolean = false; private _modal: Modal; @@ -14,7 +16,7 @@ export class Login { constructor(modal: Modal, endpoint: string, appearance: string) { this._endpoint = endpoint; - this._url = window.URLBase + endpoint; + this._url = window.pages.Base + endpoint; if (this._url[this._url.length-1] != '/') this._url += "/"; this._modal = modal; diff --git a/ts/modules/modal.ts b/ts/modules/modal.ts index 77cbb4e..92e3435 100644 --- a/ts/modules/modal.ts +++ b/ts/modules/modal.ts @@ -1,4 +1,4 @@ -declare var window: Window; +declare var window: GlobalWindow; export class Modal implements Modal { modal: HTMLElement; diff --git a/ts/modules/pages.ts b/ts/modules/pages.ts index 686f771..edf21f3 100644 --- a/ts/modules/pages.ts +++ b/ts/modules/pages.ts @@ -73,6 +73,7 @@ export class PageManager { } loadPage (p: Page) { + console.log("loading page with", p.name || this.defaultName, p.title, p.url + window.location.search); window.history.pushState(p.name || this.defaultName, p.title, p.url + window.location.search); } diff --git a/ts/modules/profiles.ts b/ts/modules/profiles.ts index e58fb27..51d6eb3 100644 --- a/ts/modules/profiles.ts +++ b/ts/modules/profiles.ts @@ -1,5 +1,7 @@ import { _get, _post, _delete, toggleLoader } from "../modules/common.js"; +declare var window: GlobalWindow; + export const profileLoadEvent = new CustomEvent("profileLoadEvent"); export const reloadProfileNames = (then?: () => void) => _get("/profiles/names", null, (req: XMLHttpRequest) => { if (req.readyState != 4) return; diff --git a/ts/modules/search.ts b/ts/modules/search.ts index dd01dbe..fa8d1e1 100644 --- a/ts/modules/search.ts +++ b/ts/modules/search.ts @@ -1,5 +1,7 @@ const dateParser = require("any-date-parser"); +declare var window: GlobalWindow; + export interface QueryType { name: string; description?: string; diff --git a/ts/modules/settings.ts b/ts/modules/settings.ts index c9d1459..a1a43f8 100644 --- a/ts/modules/settings.ts +++ b/ts/modules/settings.ts @@ -2,6 +2,8 @@ import { _get, _post, _delete, _download, _upload, toggleLoader, addLoader, remo import { Marked } from "@ts-stack/markdown"; import { stripMarkdown } from "../modules/stripmd.js"; +declare var window: GlobalWindow; + const toBool = (s: string): boolean => { let b = Boolean(s); if (s == "false") b = false; diff --git a/ts/modules/tabs.ts b/ts/modules/tabs.ts index 628ed7e..3f7d78b 100644 --- a/ts/modules/tabs.ts +++ b/ts/modules/tabs.ts @@ -1,5 +1,7 @@ import { PageManager, Page } from "../modules/pages.js"; +declare var window: GlobalWindow; + export interface Tab { page: Page; tabEl: HTMLDivElement; @@ -38,7 +40,7 @@ export class Tabs implements Tabs { tab.page = { name: tabID, title: document.title, /*FIXME: Get actual names from translations*/ - url: window.URLBase + "/" + url, + url: url, show: () => { tab.buttonEl.classList.add("active", "~urge"); tab.tabEl.classList.remove("unfocused"); diff --git a/ts/modules/update.ts b/ts/modules/update.ts index ba2c946..c39fc63 100644 --- a/ts/modules/update.ts +++ b/ts/modules/update.ts @@ -1,6 +1,8 @@ import { _get, _post, toggleLoader, toDateString } from "../modules/common.js"; import { Marked, Renderer } from "@ts-stack/markdown"; +declare var window: GlobalWindow; + interface updateDTO { new: boolean; update: Update; diff --git a/ts/pwr-pin.ts b/ts/pwr-pin.ts index 8774967..a274a58 100644 --- a/ts/pwr-pin.ts +++ b/ts/pwr-pin.ts @@ -1,5 +1,7 @@ import { toClipboard, notificationBox } from "./modules/common.js"; +declare var window: GlobalWindow; + const pin = document.getElementById("pin") as HTMLSpanElement; if (pin) { diff --git a/ts/setup.ts b/ts/setup.ts index 747ed83..d987060 100644 --- a/ts/setup.ts +++ b/ts/setup.ts @@ -3,12 +3,11 @@ import { lang, LangFile, loadLangSelector } from "./modules/lang.js"; import { ThemeManager } from "./modules/theme.js"; import { PageManager } from "./modules/pages.js"; -interface sWindow extends Window { +interface sWindow extends GlobalWindow { messages: {}; } declare var window: sWindow; -window.URLBase = ""; const theme = new ThemeManager(document.getElementById("button-theme")); diff --git a/ts/typings/d.ts b/ts/typings/d.ts index a53a3fb..b19a1c7 100644 --- a/ts/typings/d.ts +++ b/ts/typings/d.ts @@ -12,8 +12,19 @@ interface ArrayConstructor { from(arrayLike: any, mapFn?, thisArg?): Array; } -declare interface Window { - URLBase: string; +declare interface PagePaths { + // The base subfolder the app is hosted on. + Base: string; + // The subdirectory this bit of the app is hosted on (e.g. admin is usually on "/", myacc is usually on "/my/account") + Current: string; + // Those for other pages + Admin: string; + MyAccount: string; + Form: string; +} + +declare interface GlobalWindow extends Window { + pages: PagePaths; modals: Modals; cssFile: string; availableProfiles: string[]; @@ -25,6 +36,7 @@ declare interface Window { matrixEnabled: boolean; ombiEnabled: boolean; jellyseerrEnabled: boolean; + pwrEnabled: boolean; usernameEnabled: boolean; linkResetEnabled: boolean; token: string; diff --git a/ts/user.ts b/ts/user.ts index b72b22b..cceb19e 100644 --- a/ts/user.ts +++ b/ts/user.ts @@ -7,7 +7,7 @@ import { Discord, Telegram, Matrix, ServiceConfiguration, MatrixConfiguration } import { Validator, ValidatorConf, ValidatorRespDTO } from "./modules/validator.js"; import { PageManager } from "./modules/pages.js"; -interface userWindow extends Window { +interface userWindow extends GlobalWindow { jellyfinID: string; username: string; emailRequired: boolean; @@ -18,14 +18,13 @@ interface userWindow extends Window { discordInviteLink: boolean; matrixUserID: string; discordSendPINMessage: string; - pwrEnabled: string; referralsEnabled: boolean; } -const basePath = window.location.pathname.replace("/password/reset", ""); - declare var window: userWindow; +const basePath = window.location.pathname.replace("/password/reset", ""); + const theme = new ThemeManager(document.getElementById("button-theme")); window.lang = new lang(window.langFile as LangFile); @@ -38,7 +37,7 @@ window.token = ""; window.modals = {} as Modals; -let pages = new PageManager({ +const pages = new PageManager({ hideOthersOnPageShow: true, defaultName: "", defaultTitle: document.title, @@ -311,7 +310,7 @@ class ReferralCard { path = path.split(split)[0]; } if (path.slice(-1) != "/") { path += "/"; } - path = path + "invite/" + this._code; + path = path + window.pages.Form + "/" + this._code; u.pathname = path; u.hash = ""; @@ -661,7 +660,7 @@ document.addEventListener("details-reload", () => { expiryCard.expiry = details.expiry; const adminBackButton = document.getElementById("admin-back-button") as HTMLAnchorElement; - adminBackButton.href = window.location.href.replace("my/account", ""); + adminBackButton.href = window.location.href.replace(window.pages.MyAccount, window.pages.Admin); let messageCard = document.getElementById("card-message"); if (details.accounts_admin) { diff --git a/user-auth.go b/user-auth.go index 5af2320..c80e213 100644 --- a/user-auth.go +++ b/user-auth.go @@ -67,7 +67,8 @@ func (app *appContext) getUserTokenLogin(gc *gin.Context) { // host := gc.Request.URL.Hostname() host := app.ExternalDomain uri := "/my" - if strings.HasPrefix(gc.Request.RequestURI, app.URLBase) { + // FIXME: This seems like a bad idea? I think it's to deal with people having Reverse proxy subfolder/URL base set to /accounts. + if strings.HasPrefix(gc.Request.RequestURI, PAGES.Base) { uri = "/accounts/my" } gc.SetCookie("user-refresh", refresh, REFRESH_TOKEN_VALIDITY_SEC, uri, host, true, true) diff --git a/views.go b/views.go index 7542606..be574b4 100644 --- a/views.go +++ b/views.go @@ -32,7 +32,7 @@ func (app *appContext) loadCSSHeader() string { l := len(css) h := "" for i, f := range css { - h += "<" + app.URLBase + "/css/" + f + ">; rel=preload; as=style" + h += "<" + PAGES.Base + "/css/" + f + ">; rel=preload; as=style" if l > 1 && i != (l-1) { h += ", " } @@ -41,18 +41,19 @@ func (app *appContext) loadCSSHeader() string { } func (app *appContext) getURLBase(gc *gin.Context) string { - if strings.HasPrefix(gc.Request.URL.String(), app.URLBase) { + if strings.HasPrefix(gc.Request.URL.String(), PAGES.Base) { // Hack to fix the common URL base /accounts - if app.URLBase == "/accounts" && strings.HasPrefix(gc.Request.URL.String(), "/accounts/user/") { + if PAGES.Base == "/accounts" && strings.HasPrefix(gc.Request.URL.String(), "/accounts/user/") { return "" } - return app.URLBase + return PAGES.Base } return "" } -func gcHTML(gc *gin.Context, code int, file string, templ gin.H) { +func (app *appContext) gcHTML(gc *gin.Context, code int, file string, page Page, templ gin.H) { gc.Header("Cache-Control", "no-cache") + app.BasePageTemplateValues(gc, page, templ) gc.HTML(code, file, templ) } @@ -61,16 +62,14 @@ func (app *appContext) pushResources(gc *gin.Context, page Page) { switch page { case AdminPage: toPush = []string{"/js/admin.js", "/js/theme.js", "/js/lang.js", "/js/modal.js", "/js/tabs.js", "/js/invites.js", "/js/accounts.js", "/js/settings.js", "/js/profiles.js", "/js/common.js"} - break case UserPage: toPush = []string{"/js/user.js", "/js/theme.js", "/js/lang.js", "/js/modal.js", "/js/common.js"} - break default: toPush = []string{} } if pusher := gc.Writer.Pusher(); pusher != nil { for _, f := range toPush { - if err := pusher.Push(app.URLBase+f, nil); err != nil { + if err := pusher.Push(PAGES.Base+f, nil); err != nil { app.debug.Printf(lm.FailedServerPush, err) } } @@ -78,6 +77,48 @@ func (app *appContext) pushResources(gc *gin.Context, page Page) { gc.Header("Link", cssHeader) } +// Returns a gin.H with general values (url base, css version, etc.) +func (app *appContext) BasePageTemplateValues(gc *gin.Context, page Page, base gin.H) { + set := func(k string, v any) { + if _, ok := base[k]; !ok { + base[k] = v + } + } + + pages := PagePathsDTO{ + PagePaths: PAGES, + } + pages.Base = app.getURLBase(gc) + switch page { + case AdminPage: + pages.Current = PAGES.Admin + case FormPage: + pages.Current = PAGES.Form + case UserPage: + pages.Current = PAGES.MyAccount + default: + pages.Current = "/" + } + set("pages", pages) + ombiEnabled := app.config.Section("ombi").Key("enabled").MustBool(false) + jellyseerrEnabled := app.config.Section("jellyseerr").Key("enabled").MustBool(false) + notificationsEnabled, _ := app.config.Section("notifications").Key("enabled").Bool() + set("notifications", notificationsEnabled) + set("cssClass", app.cssClass) + set("cssVersion", cssVersion) + set("emailEnabled", emailEnabled) + set("telegramEnabled", telegramEnabled) + set("discordEnabled", discordEnabled) + set("matrixEnabled", matrixEnabled) + set("ombiEnabled", ombiEnabled) + set("jellyseerrEnabled", jellyseerrEnabled) + // QUIRK: The login modal html template uses this' existence to check if the modal is for the admin or user page. + if page != AdminPage { + set("pwrEnabled", app.config.Section("password_resets").Key("enabled").MustBool(false)) + } + set("referralsEnabled", app.config.Section("user_page").Key("enabled").MustBool(false) && app.config.Section("user_page").Key("referrals").MustBool(false)) +} + type Page int const ( @@ -132,10 +173,6 @@ func (app *appContext) getLang(gc *gin.Context, page Page, chosen string) string func (app *appContext) AdminPage(gc *gin.Context) { app.pushResources(gc, AdminPage) lang := app.getLang(gc, AdminPage, app.storage.lang.chosenAdminLang) - emailEnabled, _ := app.config.Section("invite_emails").Key("enabled").Bool() - notificationsEnabled, _ := app.config.Section("notifications").Key("enabled").Bool() - ombiEnabled := app.config.Section("ombi").Key("enabled").MustBool(false) - jellyseerrEnabled := app.config.Section("jellyseerr").Key("enabled").MustBool(false) jfAdminOnly := app.config.Section("ui").Key("admin_only").MustBool(true) jfAllowAll := app.config.Section("ui").Key("allow_all").MustBool(false) var license string @@ -157,62 +194,36 @@ func (app *appContext) AdminPage(gc *gin.Context) { builtBy = "???" } - gcHTML(gc, http.StatusOK, "admin.html", gin.H{ - "urlBase": app.getURLBase(gc), - "cssClass": app.cssClass, - "cssVersion": cssVersion, - "contactMessage": "", - "emailEnabled": emailEnabled, - "telegramEnabled": telegramEnabled, - "discordEnabled": discordEnabled, - "matrixEnabled": matrixEnabled, - "ombiEnabled": ombiEnabled, - "jellyseerrEnabled": jellyseerrEnabled, - "linkResetEnabled": app.config.Section("password_resets").Key("link_reset").MustBool(false), - "notifications": notificationsEnabled, - "version": version, - "commit": commit, - "buildTime": buildTime, - "builtBy": builtBy, - "buildTags": buildTags, - "username": !app.config.Section("email").Key("no_username").MustBool(false), - "strings": app.storage.lang.Admin[lang].Strings, - "quantityStrings": app.storage.lang.Admin[lang].QuantityStrings, - "language": app.storage.lang.Admin[lang].JSON, - "langName": lang, - "license": license, - "jellyfinLogin": app.jellyfinLogin, - "jfAdminOnly": jfAdminOnly, - "jfAllowAll": jfAllowAll, - "userPageEnabled": app.config.Section("user_page").Key("enabled").MustBool(false), - "showUserPageLink": app.config.Section("user_page").Key("show_link").MustBool(true), - "referralsEnabled": app.config.Section("user_page").Key("enabled").MustBool(false) && app.config.Section("user_page").Key("referrals").MustBool(false), - "loginAppearance": app.config.Section("ui").Key("login_appearance").MustString("clear"), + app.gcHTML(gc, http.StatusOK, "admin.html", AdminPage, gin.H{ + "contactMessage": "", + "linkResetEnabled": app.config.Section("password_resets").Key("link_reset").MustBool(false), + "version": version, + "commit": commit, + "buildTime": buildTime, + "builtBy": builtBy, + "buildTags": buildTags, + "username": !app.config.Section("email").Key("no_username").MustBool(false), + "strings": app.storage.lang.Admin[lang].Strings, + "quantityStrings": app.storage.lang.Admin[lang].QuantityStrings, + "language": app.storage.lang.Admin[lang].JSON, + "langName": lang, + "license": license, + "jellyfinLogin": app.jellyfinLogin, + "jfAdminOnly": jfAdminOnly, + "jfAllowAll": jfAllowAll, + "userPageEnabled": app.config.Section("user_page").Key("enabled").MustBool(false), + "showUserPageLink": app.config.Section("user_page").Key("show_link").MustBool(true), + "loginAppearance": app.config.Section("ui").Key("login_appearance").MustString("clear"), }) } func (app *appContext) MyUserPage(gc *gin.Context) { app.pushResources(gc, UserPage) lang := app.getLang(gc, UserPage, app.storage.lang.chosenUserLang) - emailEnabled, _ := app.config.Section("invite_emails").Key("enabled").Bool() - notificationsEnabled, _ := app.config.Section("notifications").Key("enabled").Bool() - ombiEnabled := app.config.Section("ombi").Key("enabled").MustBool(false) - jellyseerrEnabled := app.config.Section("jellyseerr").Key("enabled").MustBool(false) data := gin.H{ - "urlBase": app.getURLBase(gc), - "cssClass": app.cssClass, - "cssVersion": cssVersion, "contactMessage": app.config.Section("ui").Key("contact_message").String(), - "emailEnabled": emailEnabled, "emailRequired": app.config.Section("email").Key("required").MustBool(false), - "telegramEnabled": telegramEnabled, - "discordEnabled": discordEnabled, - "matrixEnabled": matrixEnabled, - "ombiEnabled": ombiEnabled, - "jellyseerrEnabled": jellyseerrEnabled, - "pwrEnabled": app.config.Section("password_resets").Key("enabled").MustBool(false), "linkResetEnabled": app.config.Section("password_resets").Key("link_reset").MustBool(false), - "notifications": notificationsEnabled, "username": !app.config.Section("email").Key("no_username").MustBool(false), "strings": app.storage.lang.User[lang].Strings, "validationStrings": app.storage.lang.User[lang].validationStringsJSON, @@ -220,7 +231,6 @@ func (app *appContext) MyUserPage(gc *gin.Context) { "langName": lang, "jfLink": app.config.Section("ui").Key("redirect_url").String(), "requirements": app.validator.getCriteria(), - "referralsEnabled": app.config.Section("user_page").Key("enabled").MustBool(false) && app.config.Section("user_page").Key("referrals").MustBool(false), } if telegramEnabled { data["telegramUsername"] = app.telegram.username @@ -264,7 +274,7 @@ func (app *appContext) MyUserPage(gc *gin.Context) { data[name+"MessageContent"] = template.HTML(markdown.ToHTML([]byte(msg.Content), nil, markdownRenderer)) } - gcHTML(gc, http.StatusOK, "user.html", data) + app.gcHTML(gc, http.StatusOK, "user.html", UserPage, data) } func (app *appContext) ResetPassword(gc *gin.Context) { @@ -278,14 +288,9 @@ func (app *appContext) ResetPassword(gc *gin.Context) { app.pushResources(gc, PWRPage) lang := app.getLang(gc, PWRPage, app.storage.lang.chosenPWRLang) data := gin.H{ - "urlBase": app.getURLBase(gc), - "cssClass": app.cssClass, - "cssVersion": cssVersion, "contactMessage": app.config.Section("ui").Key("contact_message").String(), "strings": app.storage.lang.PasswordReset[lang].Strings, "success": false, - "ombiEnabled": app.config.Section("ombi").Key("enabled").MustBool(false), - "jellyseerrEnabled": app.config.Section("jellyseerr").Key("enabled").MustBool(false), "customSuccessCard": false, } pwr, isInternal := app.internalPWRs[pin] @@ -299,6 +304,7 @@ func (app *appContext) ResetPassword(gc *gin.Context) { data["requirements"] = app.validator.getCriteria() data["strings"] = app.storage.lang.PasswordReset[lang].Strings data["validationStrings"] = app.storage.lang.User[lang].validationStringsJSON + // ewwwww, reusing an existing field, FIXME! data["notifications"] = app.storage.lang.User[lang].notificationsJSON data["langName"] = lang data["passwordReset"] = true @@ -309,10 +315,10 @@ func (app *appContext) ResetPassword(gc *gin.Context) { data["reCAPTCHA"] = app.config.Section("captcha").Key("recaptcha").MustBool(false) data["reCAPTCHASiteKey"] = app.config.Section("captcha").Key("recaptcha_site_key").MustString("") data["pwrPIN"] = pin - gcHTML(gc, http.StatusOK, "form-loader.html", data) + app.gcHTML(gc, http.StatusOK, "form-loader.html", PWRPage, data) return } - defer gcHTML(gc, http.StatusOK, "password-reset.html", data) + defer app.gcHTML(gc, http.StatusOK, "password-reset.html", PWRPage, data) // If it's a bot, pretend to be a success so the preview is nice. if isBot { app.debug.Println(lm.IgnoreBotPWR) @@ -413,10 +419,7 @@ func (app *appContext) GetCaptcha(gc *gin.Context) { if !isPWR { inv, ok = app.storage.GetInvitesKey(code) if !ok { - gcHTML(gc, 404, "invalidCode.html", gin.H{ - "urlBase": app.getURLBase(gc), - "cssClass": app.cssClass, - "cssVersion": cssVersion, + app.gcHTML(gc, 404, "invalidCode.html", OtherPage, gin.H{ "contactMessage": app.config.Section("ui").Key("contact_message").String(), }) } @@ -453,10 +456,7 @@ func (app *appContext) GenCaptcha(gc *gin.Context) { } if !ok { - gcHTML(gc, 404, "invalidCode.html", gin.H{ - "urlBase": app.getURLBase(gc), - "cssClass": app.cssClass, - "cssVersion": cssVersion, + app.gcHTML(gc, 404, "invalidCode.html", OtherPage, gin.H{ "contactMessage": app.config.Section("ui").Key("contact_message").String(), }) } @@ -587,10 +587,7 @@ func (app *appContext) VerifyCaptcha(gc *gin.Context) { if !isPWR { inv, ok = app.storage.GetInvitesKey(code) if !ok { - gcHTML(gc, 404, "invalidCode.html", gin.H{ - "urlBase": app.getURLBase(gc), - "cssClass": app.cssClass, - "cssVersion": cssVersion, + app.gcHTML(gc, 404, "invalidCode.html", OtherPage, gin.H{ "contactMessage": app.config.Section("ui").Key("contact_message").String(), }) return @@ -617,10 +614,7 @@ func (app *appContext) VerifyCaptcha(gc *gin.Context) { func (app *appContext) NewUserFromConfirmationKey(invite Invite, key string, lang string, gc *gin.Context) { fail := func() { - gcHTML(gc, 404, "404.html", gin.H{ - "urlBase": app.getURLBase(gc), - "cssClass": app.cssClass, - "cssVersion": cssVersion, + app.gcHTML(gc, 404, "404.html", OtherPage, gin.H{ "contactMessage": app.config.Section("ui").Key("contact_message").String(), }) } @@ -691,10 +685,7 @@ func (app *appContext) NewUserFromConfirmationKey(invite Invite, key string, lan if app.config.Section("ui").Key("auto_redirect").MustBool(false) { gc.Redirect(301, jfLink) } else { - gcHTML(gc, http.StatusOK, "create-success.html", gin.H{ - "urlBase": app.getURLBase(gc), - "cssClass": app.cssClass, - "cssVersion": cssVersion, + app.gcHTML(gc, http.StatusOK, "create-success.html", OtherPage, gin.H{ "strings": app.storage.lang.User[lang].Strings, "successMessage": app.config.Section("ui").Key("success_message").String(), "contactMessage": app.config.Section("ui").Key("contact_message").String(), @@ -725,10 +716,7 @@ func (app *appContext) InviteProxy(gc *gin.Context) { // if app.checkInvite(code, false, "") { invite, ok := app.storage.GetInvitesKey(gc.Param("invCode")) if !ok { - gcHTML(gc, 404, "invalidCode.html", gin.H{ - "urlBase": app.getURLBase(gc), - "cssClass": app.cssClass, - "cssVersion": cssVersion, + app.gcHTML(gc, 404, "invalidCode.html", FormPage, gin.H{ "contactMessage": app.config.Section("ui").Key("contact_message").String(), }) return @@ -747,7 +735,7 @@ func (app *appContext) InviteProxy(gc *gin.Context) { discord := discordEnabled && app.config.Section("discord").Key("show_on_reg").MustBool(true) matrix := matrixEnabled && app.config.Section("matrix").Key("show_on_reg").MustBool(true) - userPageAddress := fmt.Sprintf("%s/my/account", app.ExternalURI) + userPageAddress := app.ExternalURI + PAGES.MyAccount fromUser := "" if invite.ReferrerJellyfinID != "" { @@ -758,9 +746,6 @@ func (app *appContext) InviteProxy(gc *gin.Context) { } data := gin.H{ - "urlBase": app.getURLBase(gc), - "cssClass": app.cssClass, - "cssVersion": cssVersion, "contactMessage": app.config.Section("ui").Key("contact_message").String(), "helpMessage": app.config.Section("ui").Key("help_message").String(), "successMessage": app.config.Section("ui").Key("success_message").String(), @@ -772,28 +757,29 @@ func (app *appContext) InviteProxy(gc *gin.Context) { "username": !app.config.Section("email").Key("no_username").MustBool(false), "strings": app.storage.lang.User[lang].Strings, "validationStrings": app.storage.lang.User[lang].validationStringsJSON, - "notifications": app.storage.lang.User[lang].notificationsJSON, - "code": invite.Code, - "confirmation": app.config.Section("email_confirmation").Key("enabled").MustBool(false), - "userExpiry": invite.UserExpiry, - "userExpiryMonths": invite.UserMonths, - "userExpiryDays": invite.UserDays, - "userExpiryHours": invite.UserHours, - "userExpiryMinutes": invite.UserMinutes, - "userExpiryMessage": app.storage.lang.User[lang].Strings.get("yourAccountIsValidUntil"), - "langName": lang, - "passwordReset": false, - "customSuccessCard": false, - "telegramEnabled": telegram, - "discordEnabled": discord, - "matrixEnabled": matrix, - "emailRequired": app.config.Section("email").Key("required").MustBool(false), - "captcha": app.config.Section("captcha").Key("enabled").MustBool(false), - "reCAPTCHA": app.config.Section("captcha").Key("recaptcha").MustBool(false), - "reCAPTCHASiteKey": app.config.Section("captcha").Key("recaptcha_site_key").MustString(""), - "userPageEnabled": app.config.Section("user_page").Key("enabled").MustBool(false), - "userPageAddress": userPageAddress, - "fromUser": fromUser, + // ewwwww, reusing an existing field, FIXME! + "notifications": app.storage.lang.User[lang].notificationsJSON, + "code": invite.Code, + "confirmation": app.config.Section("email_confirmation").Key("enabled").MustBool(false), + "userExpiry": invite.UserExpiry, + "userExpiryMonths": invite.UserMonths, + "userExpiryDays": invite.UserDays, + "userExpiryHours": invite.UserHours, + "userExpiryMinutes": invite.UserMinutes, + "userExpiryMessage": app.storage.lang.User[lang].Strings.get("yourAccountIsValidUntil"), + "langName": lang, + "passwordReset": false, + "customSuccessCard": false, + "telegramEnabled": telegram, + "discordEnabled": discord, + "matrixEnabled": matrix, + "emailRequired": app.config.Section("email").Key("required").MustBool(false), + "captcha": app.config.Section("captcha").Key("enabled").MustBool(false), + "reCAPTCHA": app.config.Section("captcha").Key("recaptcha").MustBool(false), + "reCAPTCHASiteKey": app.config.Section("captcha").Key("recaptcha_site_key").MustString(""), + "userPageEnabled": app.config.Section("user_page").Key("enabled").MustBool(false), + "userPageAddress": userPageAddress, + "fromUser": fromUser, } if telegram { data["telegramPIN"] = app.telegram.NewAuthToken() @@ -837,15 +823,12 @@ func (app *appContext) InviteProxy(gc *gin.Context) { // pin := "" // for _, token := range app.discord.tokens { // if - gcHTML(gc, http.StatusOK, "form-loader.html", data) + app.gcHTML(gc, http.StatusOK, "form-loader.html", OtherPage, data) } func (app *appContext) NoRouteHandler(gc *gin.Context) { app.pushResources(gc, OtherPage) - gcHTML(gc, 404, "404.html", gin.H{ - "urlBase": app.getURLBase(gc), - "cssClass": app.cssClass, - "cssVersion": cssVersion, + app.gcHTML(gc, 404, "404.html", OtherPage, gin.H{ "contactMessage": app.config.Section("ui").Key("contact_message").String(), }) }