mirror of
https://github.com/hrfee/jfa-go.git
synced 2026-01-18 16:47:42 +01:00
config: start adding path parameters
to change the urls of the admin page, the my account page and of invites. Seems to work, but need to check all the code over and test.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
25
config.go
25
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 == "" {
|
||||
|
||||
@@ -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
|
||||
|
||||
4
email.go
4
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"),
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="{{ .cssClass }}">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="css/{{ .cssVersion }}bundle.css">
|
||||
{{ template "header.html" . }}
|
||||
<title>404 - jfa-go</title>
|
||||
{{ template "header.html" . }}
|
||||
</head>
|
||||
<body class="section">
|
||||
<div class="page-container m-2 lg:my-20 lg:mx-64">
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="{{ .cssClass }}">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="{{ .urlBase }}/css/{{ .cssVersion }}bundle.css">
|
||||
<script>
|
||||
window.URLBase = "{{ .urlBase }}";
|
||||
window.notificationsEnabled = {{ .notifications }};
|
||||
window.emailEnabled = {{ .emailEnabled }};
|
||||
window.telegramEnabled = {{ .telegramEnabled }};
|
||||
window.discordEnabled = {{ .discordEnabled }};
|
||||
window.matrixEnabled = {{ .matrixEnabled }};
|
||||
window.ombiEnabled = {{ .ombiEnabled }};
|
||||
window.jellyseerrEnabled = {{ .jellyseerrEnabled }};
|
||||
window.usernameEnabled = {{ .username }};
|
||||
window.langFile = JSON.parse({{ .language }});
|
||||
window.linkResetEnabled = {{ .linkResetEnabled }};
|
||||
@@ -18,7 +9,6 @@
|
||||
window.jellyfinLogin = {{ .jellyfinLogin }};
|
||||
window.jfAdminOnly = {{ .jfAdminOnly }};
|
||||
window.jfAllowAll = {{ .jfAllowAll }};
|
||||
window.referralsEnabled = {{ .referralsEnabled }};
|
||||
window.loginAppearance = "{{ .loginAppearance }}";
|
||||
</script>
|
||||
<title>Admin - jfa-go</title>
|
||||
@@ -47,7 +37,7 @@
|
||||
</div>
|
||||
<div id="modal-about" class="modal">
|
||||
<div class="relative mx-auto my-[10%] w-11/12 sm:w-4/5 lg:w-1/2 content card">
|
||||
<img src="{{ .urlBase }}/banner.svg" class="banner header" alt="jfa-go banner">
|
||||
<img src="{{ .pages.Base }}/banner.svg" class="banner header" alt="jfa-go banner">
|
||||
<span class="heading"><span class="modal-close">×</span></span>
|
||||
<p>{{ .strings.version }} <span class="text-black dark:text-white font-mono bg-inherit">{{ .version }}</span></p>
|
||||
<p>{{ .strings.commitNoun }} <span class="text-black dark:text-white font-mono bg-inherit">{{ .commit }}</span></p>
|
||||
@@ -551,7 +541,7 @@
|
||||
<span class="button ~critical @low unfocused" id="logout-button">{{ .strings.logout }}</span>
|
||||
{{ if .userPageEnabled }}
|
||||
<div class="">
|
||||
<a class="button ~info" href="{{ .urlBase }}/my/account"><i class="ri-account-circle-fill mr-2"></i>{{ .strings.myAccount }}</a>
|
||||
<a class="button ~info" href="{{ .pages.Base }}{{ .pages.MyAccount }}"><i class="ri-account-circle-fill mr-2"></i>{{ .strings.myAccount }}</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
@@ -925,6 +915,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="{{ .urlBase }}/js/admin.js" type="module"></script>
|
||||
<script src="{{ .pages.Base }}/js/admin.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!--- This CSS is inlined so we should keep this here! -->
|
||||
<link inline rel="stylesheet" type="text/css" href="web/css/v3bundle.css">
|
||||
{{ template "header.html" . }}
|
||||
<title>Crash report</title>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="{{ .cssClass }}">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="{{ .urlBase }}/css/{{ .cssVersion }}bundle.css">
|
||||
{{ template "header.html" . }}
|
||||
<title>{{ .strings.successHeader }} - jfa-go</title>
|
||||
</head>
|
||||
|
||||
@@ -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 }};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="{{ .cssClass }}">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="css/{{ .cssVersion }}bundle.css">
|
||||
{{ template "header.html" . }}
|
||||
{{ if .passwordReset }}
|
||||
<title>{{ .strings.passwordReset }}</title>
|
||||
|
||||
@@ -1,13 +1,32 @@
|
||||
<link rel="stylesheet" type="text/css" href="{{ .pages.Base }}/css/{{ .cssVersion }}bundle.css">
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="Description" content="jfa-go, a better way to manage Jellyfin users.">
|
||||
<meta name="color-scheme" content="dark light">
|
||||
<meta name="robots" content="noindex">
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ .urlBase }}/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{{ .urlBase }}/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{{ .urlBase }}/favicon-16x16.png">
|
||||
<link rel="manifest" href="{{ .urlBase }}/site.webmanifest">
|
||||
<link rel="mask-icon" href="{{ .urlBase }}/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ .pages.Base }}/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{{ .pages.Base }}/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{{ .pages.Base }}/favicon-16x16.png">
|
||||
<link rel="manifest" href="{{ .pages.Base }}/site.webmanifest">
|
||||
<link rel="mask-icon" href="{{ .pages.Base }}/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="msapplication-TileColor" content="#603cba">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<script>
|
||||
window.pages = {
|
||||
"Base": "{{ .pages.Base }}",
|
||||
"Current": "{{ .pages.Current }}",
|
||||
"Admin": "{{ .pages.Admin }}",
|
||||
"MyAccount": "{{ .pages.MyAccount }}",
|
||||
"Form": "{{ .pages.Form }}"
|
||||
};
|
||||
window.emailEnabled = {{ .emailEnabled }};
|
||||
window.discordEnabled = {{ .discordEnabled }};
|
||||
window.telegramEnabled = {{ .telegramEnabled }};
|
||||
window.matrixEnabled = {{ .matrixEnabled }};
|
||||
window.notificationsEnabled = {{ .notifications }};
|
||||
window.ombiEnabled = {{ .ombiEnabled }};
|
||||
window.jellyseerrEnabled = {{ .jellyseerrEnabled }};
|
||||
window.referralsEnabled = {{ .referralsEnabled }};
|
||||
window.pwrEnabled = {{ .pwrEnabled }};
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="{{ .cssClass }}">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="{{ .urlBase }}/css/{{ .cssVersion }}bundle.css">
|
||||
{{ template "header.html" . }}
|
||||
<title>Invalid Code - jfa-go</title>
|
||||
</head>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
{{ $hasTwoCards = 1 }}
|
||||
<div class="card mx-2 flex-initial w-full lg:w-[35%] mb-4 lg:mb-0 dark:~d_neutral @low content">
|
||||
<span class="heading row">{{ .strings.loginNotAdmin }}</span>
|
||||
<a class="button ~info h-12 w-full" href="{{ .urlBase }}/my/account"><i class="ri-account-circle-fill mr-2"></i>{{ .strings.myAccount }}</a>
|
||||
<a class="button ~info h-12 w-full" href="{{ .pages.Base }}{{ .pages.MyAccount }}"><i class="ri-account-circle-fill mr-2"></i>{{ .strings.myAccount }}</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="{{ .cssClass }}">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="css/{{ .cssVersion }}bundle.css">
|
||||
{{ template "header.html" . }}
|
||||
<title>{{ .strings.passwordReset }} - jfa-go</title>
|
||||
</head>
|
||||
@@ -40,6 +39,6 @@
|
||||
</div>
|
||||
<i class="content">{{ .contactMessage }}</i>
|
||||
</div>
|
||||
<script src="{{ .urlBase }}/js/pwr-pin.js" type="module"></script>
|
||||
<script src="{{ .pages.Base }}/js/pwr-pin.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="light">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="css/{{ .cssVersion }}bundle.css">
|
||||
{{ template "header.html" . }}
|
||||
<title>{{ .lang.Strings.pageTitle }}</title>
|
||||
</head>
|
||||
|
||||
@@ -1,31 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="light">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="{{ .urlBase }}/css/{{ .cssVersion }}bundle.css">
|
||||
<script>
|
||||
window.URLBase = "{{ .urlBase }}";
|
||||
window.notificationsEnabled = {{ .notifications }};
|
||||
window.ombiEnabled = {{ .ombiEnabled }};
|
||||
window.langFile = JSON.parse({{ .language }});
|
||||
window.pwrEnabled = {{ .pwrEnabled }};
|
||||
window.linkResetEnabled = {{ .linkResetEnabled }};
|
||||
window.language = "{{ .langName }}";
|
||||
window.telegramEnabled = {{ .telegramEnabled }};
|
||||
window.telegramRequired = {{ .telegramRequired }};
|
||||
window.telegramUsername = {{ .telegramUsername }};
|
||||
window.telegramURL = {{ .telegramURL }};
|
||||
window.emailEnabled = {{ .emailEnabled }};
|
||||
window.emailRequired = {{ .emailRequired }};
|
||||
window.discordEnabled = {{ .discordEnabled }};
|
||||
window.discordRequired = {{ .discordRequired }};
|
||||
window.discordServerName = "{{ .discordServerName }}";
|
||||
window.discordInviteLink = {{ .discordInviteLink }};
|
||||
window.discordSendPINMessage = "{{ .discordSendPINMessage }}";
|
||||
window.matrixEnabled = {{ .matrixEnabled }};
|
||||
window.matrixRequired = {{ .matrixRequired }};
|
||||
window.matrixUserID = "{{ .matrixUser }}";
|
||||
window.validationStrings = JSON.parse({{ .validationStrings }});
|
||||
window.referralsEnabled = {{ .referralsEnabled }};
|
||||
</script>
|
||||
{{ template "header.html" . }}
|
||||
<title>{{ .strings.myAccount }}</title>
|
||||
@@ -156,7 +146,7 @@
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
<script src="{{ .urlBase }}/js/user.js" type="module"></script>
|
||||
<script src="{{ .pages.Base }}/js/user.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
64
main.go
64
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,
|
||||
|
||||
15
models.go
15
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"`
|
||||
}
|
||||
|
||||
34
router.go
34
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)
|
||||
|
||||
2
setup.go
2
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],
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
12
ts/form.ts
12
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"],
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-user" data-id="${this._act.user_id}" href="${window.URLBase}/accounts?user=${this._act.user_id}">${this._genUserText()}</a>`;
|
||||
return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-user" data-id="${this._act.user_id}" href="${window.pages.Base}${window.pages.Admin}/accounts?user=${this._act.user_id}">${this._genUserText()}</a>`;
|
||||
}
|
||||
|
||||
_genSrcUserLink = (): string => {
|
||||
return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-user" data-id="${this._act.user_id}" href="${window.URLBase}/accounts?user=${this._act.source}">${this._genSrcUserText()}</a>`;
|
||||
return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-user" data-id="${this._act.user_id}" href="${window.pages.Base}${window.pages.Admin}/accounts?user=${this._act.source}">${this._genSrcUserText()}</a>`;
|
||||
}
|
||||
|
||||
private _renderInvText = (): string => { return `<span class="font-medium font-mono">${this.value || this.invite_code || "???"}</span>`; }
|
||||
|
||||
private _genInvLink = (): string => {
|
||||
return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-invite" data-id="${this.invite_code}" href="${window.URLBase}/?invite=${this.invite_code}">${this._renderInvText()}</a>`;
|
||||
return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-invite" data-id="${this.invite_code}" href="${window.pages.Base}${window.pages.Admin}/?invite=${this.invite_code}">${this._renderInvText()}</a>`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import {addLoader, removeLoader, _get} from "../modules/common.js";
|
||||
|
||||
declare var window: GlobalWindow;
|
||||
|
||||
export interface DiscordUser {
|
||||
name: string;
|
||||
avatar_url: string;
|
||||
|
||||
@@ -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, '-');
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
declare var window: Window;
|
||||
declare var window: GlobalWindow;
|
||||
|
||||
export class Modal implements Modal {
|
||||
modal: HTMLElement;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const dateParser = require("any-date-parser");
|
||||
|
||||
declare var window: GlobalWindow;
|
||||
|
||||
export interface QueryType {
|
||||
name: string;
|
||||
description?: string;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { toClipboard, notificationBox } from "./modules/common.js";
|
||||
|
||||
declare var window: GlobalWindow;
|
||||
|
||||
const pin = document.getElementById("pin") as HTMLSpanElement;
|
||||
|
||||
if (pin) {
|
||||
|
||||
@@ -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"));
|
||||
|
||||
|
||||
@@ -12,8 +12,19 @@ interface ArrayConstructor {
|
||||
from(arrayLike: any, mapFn?, thisArg?): Array<any>;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
13
ts/user.ts
13
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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
227
views.go
227
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(),
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user