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

Compare commits

..

No commits in common. "3b3f37365a51b18fcf43fbfe638ba964824c954b" and "8113f794ab46e9899a386fd907623e282b4d590e" have entirely different histories.

17 changed files with 34 additions and 246 deletions

View File

@ -477,77 +477,3 @@ func (app *appContext) UnlinkMyMatrix(gc *gin.Context) {
app.storage.DeleteMatrixKey(gc.GetString("jfId")) app.storage.DeleteMatrixKey(gc.GetString("jfId"))
respondBool(200, true, gc) respondBool(200, true, gc)
} }
// @Summary Generate & send a password reset link if the given username/email/contact method exists. Doesn't give you any info about it's success.
// @Produce json
// @Param address path string true "address/contact method associated w/ your account."
// @Success 204 {object} boolResponse
// @Failure 400 {object} boolResponse
// @Failure 500 {object} boolResponse
// @Router /my/password/reset/{address} [post]
// @tags Users
func (app *appContext) ResetMyPassword(gc *gin.Context) {
// All requests should take 1 second, to make it harder to tell if a success occured or not.
timerWait := make(chan bool)
cancel := time.AfterFunc(1*time.Second, func() {
timerWait <- true
})
address := gc.Param("address")
if address == "" {
app.debug.Println("Ignoring empty request for PWR")
cancel.Stop()
respondBool(400, false, gc)
return
}
var pwr InternalPWR
var err error
jfID := app.ReverseUserSearch(address)
if jfID == "" {
app.debug.Printf("Ignoring PWR request: User not found")
for range timerWait {
respondBool(204, true, gc)
return
}
return
}
pwr, err = app.GenInternalReset(jfID)
if err != nil {
app.err.Printf("Failed to get user from Jellyfin: %v", err)
for range timerWait {
respondBool(204, true, gc)
return
}
return
}
if app.internalPWRs == nil {
app.internalPWRs = map[string]InternalPWR{}
}
app.internalPWRs[pwr.PIN] = pwr
// FIXME: Send to all contact methods
msg, err := app.email.constructReset(
PasswordReset{
Pin: pwr.PIN,
Username: pwr.Username,
Expiry: pwr.Expiry,
Internal: true,
}, app, false,
)
if err != nil {
app.err.Printf("Failed to construct password reset message for \"%s\": %v", pwr.Username, err)
for range timerWait {
respondBool(204, true, gc)
return
}
return
} else if err := app.sendByID(msg, jfID); err != nil {
app.err.Printf("Failed to send password reset message to \"%s\": %v", address, err)
} else {
app.info.Printf("Sent password reset message to \"%s\"", address)
}
for range timerWait {
respondBool(204, true, gc)
return
}
}

View File

@ -377,7 +377,7 @@
"order": [], "order": [],
"meta": { "meta": {
"name": "User Page", "name": "User Page",
"description": "The User Page (My Account) allows users to access and modify info directly, such as changing/adding contact methods, seeing their expiry date, or changing their password. Password resets can also be initiated from here, given a contact method or username. ", "description": "Settings for the user page, which provides useful info and tools to users directly.",
"depends_true": "ui|jellyfin_login" "depends_true": "ui|jellyfin_login"
}, },
"settings": { "settings": {

View File

@ -487,15 +487,6 @@ a:hover:not(.lang-link):not(.\~urge), a:active:not(.lang-link):not(.\~urge) {
color: var(--color-urge-200); color: var(--color-urge-200);
} }
a.button,
a.button:link,
a.button:visited,
a.button:focus,
a.buton:hover {
color: var(--color-content) !important;
}
.link-center { .link-center {
display: block; display: block;
text-align: center; text-align: center;

View File

@ -522,6 +522,18 @@ func (emailer *Emailer) constructCreated(code, username, address string, invite
return email, nil return email, nil
} }
// GenResetLink generates and returns a password reset link.
func (app *appContext) GenResetLink(pin string) (string, error) {
url := app.config.Section("password_resets").Key("url_base").String()
var pinLink string
if url == "" {
return pinLink, fmt.Errorf("disabled as no URL Base provided. Set in Settings > Password Resets.")
}
// Strip /invite from end of this URL, ik it's ugly.
pinLink = fmt.Sprintf("%s/reset?pin=%s", url, pin)
return pinLink, nil
}
func (emailer *Emailer) resetValues(pwr PasswordReset, app *appContext, noSub bool) map[string]interface{} { func (emailer *Emailer) resetValues(pwr PasswordReset, app *appContext, noSub bool) map[string]interface{} {
d, t, expiresIn := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern) d, t, expiresIn := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern)
message := app.config.Section("messages").Key("message").String() message := app.config.Section("messages").Key("message").String()
@ -823,37 +835,38 @@ func (emailer *Emailer) send(email *Message, address ...string) error {
return emailer.sender.Send(emailer.fromName, emailer.fromAddr, email, address...) return emailer.sender.Send(emailer.fromName, emailer.fromAddr, email, address...)
} }
func (app *appContext) sendByID(email *Message, ID ...string) (err error) { func (app *appContext) sendByID(email *Message, ID ...string) error {
for _, id := range ID { for _, id := range ID {
var err error
if tgChat, ok := app.storage.GetTelegramKey(id); ok && tgChat.Contact && telegramEnabled { if tgChat, ok := app.storage.GetTelegramKey(id); ok && tgChat.Contact && telegramEnabled {
err = app.telegram.Send(email, tgChat.ChatID) err = app.telegram.Send(email, tgChat.ChatID)
// if err != nil { if err != nil {
// return err return err
// } }
} }
if dcChat, ok := app.storage.GetDiscordKey(id); ok && dcChat.Contact && discordEnabled { if dcChat, ok := app.storage.GetDiscordKey(id); ok && dcChat.Contact && discordEnabled {
err = app.discord.Send(email, dcChat.ChannelID) err = app.discord.Send(email, dcChat.ChannelID)
// if err != nil { if err != nil {
// return err return err
// } }
} }
if mxChat, ok := app.storage.GetMatrixKey(id); ok && mxChat.Contact && matrixEnabled { if mxChat, ok := app.storage.GetMatrixKey(id); ok && mxChat.Contact && matrixEnabled {
err = app.matrix.Send(email, mxChat) err = app.matrix.Send(email, mxChat)
// if err != nil { if err != nil {
// return err return err
// } }
} }
if address, ok := app.storage.GetEmailsKey(id); ok && address.Contact && emailEnabled { if address, ok := app.storage.GetEmailsKey(id); ok && address.Contact && emailEnabled {
err = app.email.send(email, address.Addr) err = app.email.send(email, address.Addr)
// if err != nil { if err != nil {
// return err return err
// } }
}
if err != nil {
return err
} }
// if err != nil {
// return err
// }
} }
return return nil
} }
func (app *appContext) getAddressOrName(jfID string) string { func (app *appContext) getAddressOrName(jfID string) string {
@ -866,39 +879,5 @@ func (app *appContext) getAddressOrName(jfID string) string {
if addr, ok := app.storage.GetEmailsKey(jfID); ok { if addr, ok := app.storage.GetEmailsKey(jfID); ok {
return addr.Addr return addr.Addr
} }
if mxChat, ok := app.storage.GetMatrixKey(jfID); ok && mxChat.Contact && matrixEnabled {
return mxChat.UserID
}
return ""
}
// ReverseUserSearch returns the jellyfin ID of the user with the given username, email, or contact method username.
// returns "" if none found. returns only the first match, might be an issue if there are users with the same contact method usernames.
func (app *appContext) ReverseUserSearch(address string) string {
user, status, err := app.jf.UserByName(address, false)
if status == 200 && err == nil {
return user.ID
}
for id, email := range app.storage.GetEmails() {
if strings.ToLower(address) == strings.ToLower(email.Addr) {
return id
}
}
for id, dcUser := range app.storage.GetDiscord() {
if RenderDiscordUsername(dcUser) == strings.ToLower(address) {
return id
}
}
tgUsername := strings.TrimPrefix(address, "@")
for id, tgUser := range app.storage.GetTelegram() {
if tgUsername == tgUser.Username {
return id
}
}
for id, mxUser := range app.storage.GetMatrix() {
if address == mxUser.UserID {
return id
}
}
return "" return ""
} }

View File

@ -410,9 +410,6 @@
</span> </span>
<span class="button ~warning" alt="{{ .strings.theme }}" id="button-theme"><i class="ri-sun-line"></i></span> <span class="button ~warning" alt="{{ .strings.theme }}" id="button-theme"><i class="ri-sun-line"></i></span>
</div> </div>
<div class="top-4 right-4 absolute">
<a class="button ~info" href="/my/account"><i class="ri-account-circle-fill mr-2"></i>{{ .strings.myAccount }}</a>
</div>
<div class="page-container"> <div class="page-container">
<div class="mb-4"> <div class="mb-4">
<header class="flex flex-wrap items-center justify-between"> <header class="flex flex-wrap items-center justify-between">

View File

@ -29,7 +29,6 @@
window.captcha = {{ .captcha }}; window.captcha = {{ .captcha }};
window.reCAPTCHA = {{ .reCAPTCHA }}; window.reCAPTCHA = {{ .reCAPTCHA }};
window.reCAPTCHASiteKey = "{{ .reCAPTCHASiteKey }}"; window.reCAPTCHASiteKey = "{{ .reCAPTCHASiteKey }}";
window.userPageEnabled = {{ .userPageEnabled }};
</script> </script>
{{ if .passwordReset }} {{ if .passwordReset }}
<script src="js/pwr.js" type="module"></script> <script src="js/pwr.js" type="module"></script>

View File

@ -17,7 +17,6 @@
<div class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3"> <div class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3">
<span class="heading mb-4">{{ if .passwordReset }}{{ .strings.passwordReset }}{{ else }}{{ .strings.successHeader }}{{ end }}</span> <span class="heading mb-4">{{ if .passwordReset }}{{ .strings.passwordReset }}{{ else }}{{ .strings.successHeader }}{{ end }}</span>
<p class="content mb-4">{{ if .passwordReset }}{{ .strings.youCanLoginPassword }}{{ else }}{{ .successMessage }}{{ end }}</p> <p class="content mb-4">{{ if .passwordReset }}{{ .strings.youCanLoginPassword }}{{ else }}{{ .successMessage }}{{ end }}</p>
{{ if .userPageEnabled }}<p class="content mb-4" id="modal-success-user-page-area" my-account-term="{{ .strings.myAccount }}">{{ .strings.userPageSuccessMessage }}</p>{{ end }}
<a class="button ~urge @low full-width center supra submit" href="{{ .jfLink }}" id="create-success-button">{{ .strings.continue }}</a> <a class="button ~urge @low full-width center supra submit" href="{{ .jfLink }}" id="create-success-button">{{ .strings.continue }}</a>
</div> </div>
</div> </div>

View File

@ -14,11 +14,6 @@
<label> <label>
<input type="submit" class="unfocused"> <input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit">{{ .strings.login }}</span> <span class="button ~urge @low full-width center supra submit">{{ .strings.login }}</span>
{{ if index . "pwrEnabled" }}
{{ if .pwrEnabled }}
<span class="button ~info @low full-width center supra submit my-2" id="modal-login-pwr">{{ .strings.resetPassword }}</span>
{{ end }}
{{ end }}
</label> </label>
</form> </form>
</div> </div>

View File

@ -6,7 +6,6 @@
window.notificationsEnabled = {{ .notifications }}; window.notificationsEnabled = {{ .notifications }};
window.ombiEnabled = {{ .ombiEnabled }}; window.ombiEnabled = {{ .ombiEnabled }};
window.langFile = JSON.parse({{ .language }}); window.langFile = JSON.parse({{ .language }});
window.pwrEnabled = {{ .pwrEnabled }};
window.linkResetEnabled = {{ .linkResetEnabled }}; window.linkResetEnabled = {{ .linkResetEnabled }};
window.language = "{{ .langName }}"; window.language = "{{ .langName }}";
window.telegramEnabled = {{ .telegramEnabled }}; window.telegramEnabled = {{ .telegramEnabled }};
@ -25,7 +24,7 @@
window.matrixUserID = "{{ .matrixUser }}"; window.matrixUserID = "{{ .matrixUser }}";
</script> </script>
{{ template "header.html" . }} {{ template "header.html" . }}
<title>{{ .lang.Strings.myAccount }}</title> <title>{{ .lang.Strings.pageTitle }}</title>
</head> </head>
<body class="max-w-full overflow-x-hidden section"> <body class="max-w-full overflow-x-hidden section">
<div id="modal-email" class="modal"> <div id="modal-email" class="modal">
@ -44,30 +43,6 @@
</div> </div>
</div> </div>
</div> </div>
{{ if .pwrEnabled }}
<div id="modal-pwr" class="modal">
<div class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3 ~neutral @low">
<span class="heading">{{ .strings.resetPassword }}</span>
<p class="content my-2">
{{ if .linkResetEnabled }}
{{ .strings.resetPasswordThroughLink }}
{{ else }}
{{ .strings.resetPasswordThroughJellyfin }}
{{ end }}
</p>
<div class="row">
<input type="text" class="col sm field ~neutral @low input" id="pwr-address" placeholder="username | example@example.com | user#1234 | @user:host | @username">
</div>
{{ if .linkResetEnabled }}
<span class="button ~info @low full-width center mt-4" id="pwr-submit">
{{ .strings.submit }}
</span>
{{ else }}
<a class="button ~info @low full-width center mt-4" href="{{ .jfLink }}" target="_blank">{{ .strings.continue }}</a>
{{ end }}
</div>
</div>
{{ end }}
{{ template "login-modal.html" . }} {{ template "login-modal.html" . }}
{{ template "account-linking.html" . }} {{ template "account-linking.html" . }}
<div id="notification-box"></div> <div id="notification-box"></div>
@ -94,9 +69,6 @@
<span class="button ~warning" alt="{{ .strings.theme }}" id="button-theme"><i class="ri-sun-line"></i></span> <span class="button ~warning" alt="{{ .strings.theme }}" id="button-theme"><i class="ri-sun-line"></i></span>
<span class="button ~critical @low mb-4 unfocused" id="logout-button">{{ .strings.logout }}</span> <span class="button ~critical @low mb-4 unfocused" id="logout-button">{{ .strings.logout }}</span>
</div> </div>
<div class="top-4 right-4 absolute">
<a class="button ~info unfocused" href="/" id="admin-back-button"><i class="ri-arrow-left-fill mr-2"></i>{{ .strings.admin }}</a>
</div>
<div class="page-container unfocused"> <div class="page-container unfocused">
<div class="card @low dark:~d_neutral mb-4" id="card-user"> <div class="card @low dark:~d_neutral mb-4" id="card-user">
<span class="heading mb-2"></span> <span class="heading mb-2"></span>

View File

@ -38,8 +38,7 @@
"expiry": "Expiry", "expiry": "Expiry",
"add": "Add", "add": "Add",
"edit": "Edit", "edit": "Edit",
"delete": "Delete", "delete": "Delete"
"myAccount": "My Account"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "The username and/or password were left blank.", "errorLoginBlank": "The username and/or password were left blank.",

View File

@ -25,13 +25,7 @@
"editContactMethod": "Edit Contact Method", "editContactMethod": "Edit Contact Method",
"joinTheServer": "Join the server:", "joinTheServer": "Join the server:",
"customMessagePlaceholderHeader": "Customize this card", "customMessagePlaceholderHeader": "Customize this card",
"customMessagePlaceholderContent": "Click the user page edit button in settings to customize this card, or show one on the login screen, and don't worry, the user can't see this.", "customMessagePlaceholderContent": "Click the user page edit button in settings to customize this card, or show one on the login screen, and don't worry, the user can't see this."
"userPageSuccessMessage": "You can see and change details about your account later on the {myAccount} page.",
"resetPassword": "Reset Password",
"resetPasswordThroughJellyfin": "To reset your password, visit {jfLink} and press the \"Forgot Password\" button.",
"resetPasswordThroughLink": "To reset your password, enter your username, email address or a linked contact method username, and submit. A link will be sent to reset your password.",
"resetSent": "Reset Sent.",
"resetSentDescription": "If an account with the given username/contact method exists, a password reset link has been sent via all contact methods available. The code will expire in 30 minutes."
}, },
"notifications": { "notifications": {
"errorUserExists": "User already exists.", "errorUserExists": "User already exists.",

View File

@ -2,7 +2,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt"
"os" "os"
"strings" "strings"
"time" "time"
@ -26,18 +25,6 @@ func (app *appContext) GenInternalReset(userID string) (InternalPWR, error) {
return pwr, nil return pwr, nil
} }
// GenResetLink generates and returns a password reset link.
func (app *appContext) GenResetLink(pin string) (string, error) {
url := app.config.Section("password_resets").Key("url_base").String()
var pinLink string
if url == "" {
return pinLink, fmt.Errorf("disabled as no URL Base provided. Set in Settings > Password Resets.")
}
// Strip /invite from end of this URL, ik it's ugly.
pinLink = fmt.Sprintf("%s/reset?pin=%s", url, pin)
return pinLink, nil
}
func (app *appContext) StartPWR() { func (app *appContext) StartPWR() {
app.info.Println("Starting password reset daemon") app.info.Println("Starting password reset daemon")
path := app.config.Section("password_resets").Key("watch_directory").String() path := app.config.Section("password_resets").Key("watch_directory").String()

View File

@ -148,7 +148,6 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
router.GET(p+"/my/token/login", app.getUserTokenLogin) router.GET(p+"/my/token/login", app.getUserTokenLogin)
router.GET(p+"/my/token/refresh", app.getUserTokenRefresh) router.GET(p+"/my/token/refresh", app.getUserTokenRefresh)
router.GET(p+"/my/confirm/:jwt", app.ConfirmMyAction) router.GET(p+"/my/confirm/:jwt", app.ConfirmMyAction)
router.POST(p+"/my/password/reset/:address", app.ResetMyPassword)
} }
} }
if *SWAGGER { if *SWAGGER {

View File

@ -35,7 +35,6 @@ interface formWindow extends Window {
captcha: boolean; captcha: boolean;
reCAPTCHA: boolean; reCAPTCHA: boolean;
reCAPTCHASiteKey: string; reCAPTCHASiteKey: string;
userPageEnabled: boolean;
} }
loadLangSelector("form"); loadLangSelector("form");
@ -344,10 +343,6 @@ const create = (event: SubmitEvent) => {
const url = ((document.getElementById("modal-success") as HTMLDivElement).querySelector("a.submit") as HTMLAnchorElement).href; const url = ((document.getElementById("modal-success") as HTMLDivElement).querySelector("a.submit") as HTMLAnchorElement).href;
window.location.href = url; window.location.href = url;
} else { } else {
if (window.userPageEnabled) {
const userPageNoticeArea = document.getElementById("modal-success-user-page-area");
userPageNoticeArea.textContent = userPageNoticeArea.textContent.replace("{myAccount}", userPageNoticeArea.getAttribute("my-account-term"));
}
window.successModal.show(); window.successModal.show();
} }
} else { } else {

View File

@ -110,7 +110,6 @@ declare interface Modals {
discord: Modal; discord: Modal;
matrix: Modal; matrix: Modal;
sendPWR?: Modal; sendPWR?: Modal;
pwr?: Modal;
logs: Modal; logs: Modal;
email?: Modal; email?: Modal;
} }

View File

@ -16,7 +16,6 @@ interface userWindow extends Window {
discordInviteLink: boolean; discordInviteLink: boolean;
matrixUserID: string; matrixUserID: string;
discordSendPINMessage: string; discordSendPINMessage: string;
pwrEnabled: string;
} }
declare var window: userWindow; declare var window: userWindow;
@ -45,45 +44,10 @@ window.modals = {} as Modals;
if (window.matrixEnabled) { if (window.matrixEnabled) {
window.modals.matrix = new Modal(document.getElementById("modal-matrix"), false); window.modals.matrix = new Modal(document.getElementById("modal-matrix"), false);
} }
if (window.pwrEnabled) {
window.modals.pwr = new Modal(document.getElementById("modal-pwr"), false);
window.modals.pwr.onclose = () => {
window.modals.login.show();
};
const resetButton = document.getElementById("modal-login-pwr");
resetButton.onclick = () => {
const usernameInput = document.getElementById("login-user") as HTMLInputElement;
const input = document.getElementById("pwr-address") as HTMLInputElement;
input.value = usernameInput.value;
window.modals.login.close();
window.modals.pwr.show();
}
}
})(); })();
window.notifications = new notificationBox(document.getElementById('notification-box') as HTMLDivElement, 5); window.notifications = new notificationBox(document.getElementById('notification-box') as HTMLDivElement, 5);
if (window.pwrEnabled && window.linkResetEnabled) {
const submitButton = document.getElementById("pwr-submit");
const input = document.getElementById("pwr-address") as HTMLInputElement;
submitButton.onclick = () => {
toggleLoader(submitButton);
_post("/my/password/reset/" + input.value, null, (req: XMLHttpRequest) => {
if (req.readyState != 4) return;
toggleLoader(submitButton);
if (req.status != 204) {
window.notifications.customError("unkownError", window.lang.notif("errorUnknown"));;
window.modals.pwr.close();
return;
}
window.modals.pwr.modal.querySelector(".heading").textContent = window.lang.strings("resetSent");
window.modals.pwr.modal.querySelector(".content").textContent = window.lang.strings("resetSentDescription");
submitButton.classList.add("unfocused");
input.classList.add("unfocused");
});
};
}
const grid = document.querySelector(".grid"); const grid = document.querySelector(".grid");
var rootCard = document.getElementById("card-user"); var rootCard = document.getElementById("card-user");
var contactCard = document.getElementById("card-contact"); var contactCard = document.getElementById("card-contact");
@ -427,12 +391,9 @@ document.addEventListener("details-reload", () => {
expiryCard.expiry = details.expiry; expiryCard.expiry = details.expiry;
const adminBackButton = document.getElementById("admin-back-button") as HTMLAnchorElement;
adminBackButton.href = window.location.href.replace("my/account", "");
let messageCard = document.getElementById("card-message"); let messageCard = document.getElementById("card-message");
if (details.accounts_admin) { if (details.accounts_admin) {
adminBackButton.classList.remove("unfocused");
if (typeof(messageCard) == "undefined" || messageCard == null) { if (typeof(messageCard) == "undefined" || messageCard == null) {
messageCard = document.createElement("div"); messageCard = document.createElement("div");
messageCard.classList.add("card", "@low", "dark:~d_neutral", "content"); messageCard.classList.add("card", "@low", "dark:~d_neutral", "content");

View File

@ -157,7 +157,6 @@ func (app *appContext) AdminPage(gc *gin.Context) {
"jellyfinLogin": app.jellyfinLogin, "jellyfinLogin": app.jellyfinLogin,
"jfAdminOnly": jfAdminOnly, "jfAdminOnly": jfAdminOnly,
"jfAllowAll": jfAllowAll, "jfAllowAll": jfAllowAll,
"userPageEnabled": app.config.Section("user_page").Key("enabled").MustBool(false),
}) })
} }
@ -178,7 +177,6 @@ func (app *appContext) MyUserPage(gc *gin.Context) {
"discordEnabled": discordEnabled, "discordEnabled": discordEnabled,
"matrixEnabled": matrixEnabled, "matrixEnabled": matrixEnabled,
"ombiEnabled": ombiEnabled, "ombiEnabled": ombiEnabled,
"pwrEnabled": app.config.Section("password_resets").Key("enabled").MustBool(false),
"linkResetEnabled": app.config.Section("password_resets").Key("link_reset").MustBool(false), "linkResetEnabled": app.config.Section("password_resets").Key("link_reset").MustBool(false),
"notifications": notificationsEnabled, "notifications": notificationsEnabled,
"username": !app.config.Section("email").Key("no_username").MustBool(false), "username": !app.config.Section("email").Key("no_username").MustBool(false),
@ -186,7 +184,6 @@ func (app *appContext) MyUserPage(gc *gin.Context) {
"validationStrings": app.storage.lang.User[lang].ValidationStrings, "validationStrings": app.storage.lang.User[lang].ValidationStrings,
"language": app.storage.lang.User[lang].JSON, "language": app.storage.lang.User[lang].JSON,
"langName": lang, "langName": lang,
"jfLink": app.config.Section("ui").Key("redirect_url").String(),
} }
if telegramEnabled { if telegramEnabled {
data["telegramUsername"] = app.telegram.username data["telegramUsername"] = app.telegram.username
@ -628,7 +625,6 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
"captcha": app.config.Section("captcha").Key("enabled").MustBool(false), "captcha": app.config.Section("captcha").Key("enabled").MustBool(false),
"reCAPTCHA": app.config.Section("captcha").Key("recaptcha").MustBool(false), "reCAPTCHA": app.config.Section("captcha").Key("recaptcha").MustBool(false),
"reCAPTCHASiteKey": app.config.Section("captcha").Key("recaptcha_site_key").MustString(""), "reCAPTCHASiteKey": app.config.Section("captcha").Key("recaptcha_site_key").MustString(""),
"userPageEnabled": app.config.Section("user_page").Key("enabled").MustBool(false),
} }
if telegram { if telegram {
data["telegramPIN"] = app.telegram.NewAuthToken() data["telegramPIN"] = app.telegram.NewAuthToken()