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

Compare commits

..

No commits in common. "8e153cd92f8689dd398966997283515e00c0ea3b" and "68004e1d34945300ca906e2bd490785afa496733" have entirely different histories.

39 changed files with 116 additions and 308 deletions

View File

@ -10,7 +10,7 @@ import (
"github.com/golang-jwt/jwt" "github.com/golang-jwt/jwt"
) )
// @Summary Returns the logged-in user's Jellyfin ID & Username, and other details. // @Summary Returns the logged-in user's Jellyfin ID & Username.
// @Produce json // @Produce json
// @Success 200 {object} MyDetailsDTO // @Success 200 {object} MyDetailsDTO
// @Router /my/details [get] // @Router /my/details [get]
@ -379,103 +379,3 @@ func (app *appContext) MyTelegramVerifiedInvite(gc *gin.Context) {
app.storage.SetTelegramKey(gc.GetString("jfId"), tgUser) app.storage.SetTelegramKey(gc.GetString("jfId"), tgUser)
respondBool(200, true, gc) respondBool(200, true, gc)
} }
// @Summary Generate and send a new PIN to your given matrix user.
// @Produce json
// @Success 200 {object} boolResponse
// @Failure 400 {object} stringResponse
// @Failure 401 {object} boolResponse
// @Failure 500 {object} boolResponse
// @Param MatrixSendPINDTO body MatrixSendPINDTO true "User's Matrix ID."
// @Router /my/matrix/user [post]
// @tags User Page
func (app *appContext) MatrixSendMyPIN(gc *gin.Context) {
var req MatrixSendPINDTO
gc.BindJSON(&req)
if req.UserID == "" {
respond(400, "errorNoUserID", gc)
return
}
if app.config.Section("matrix").Key("require_unique").MustBool(false) {
for _, u := range app.storage.GetMatrix() {
if req.UserID == u.UserID {
respondBool(400, false, gc)
return
}
}
}
ok := app.matrix.SendStart(req.UserID)
if !ok {
respondBool(500, false, gc)
return
}
respondBool(200, true, gc)
}
// @Summary Check whether your matrix PIN is valid, and link the account to yours if so.
// @Produce json
// @Success 200 {object} boolResponse
// @Failure 401 {object} boolResponse
// @Param pin path string true "PIN code to check"
// @Param invCode path string true "invite Code"
// @Param userID path string true "Matrix User ID"
// @Router /my/matrix/verified/{userID}/{pin} [get]
// @tags User Page
func (app *appContext) MatrixCheckMyPIN(gc *gin.Context) {
userID := gc.Param("userID")
pin := gc.Param("pin")
user, ok := app.matrix.tokens[pin]
if !ok {
app.debug.Println("Matrix: PIN not found")
respondBool(200, false, gc)
return
}
if user.User.UserID != userID {
app.debug.Println("Matrix: User ID of PIN didn't match")
respondBool(200, false, gc)
return
}
mxUser := *user.User
mxUser.Contact = true
existingUser, ok := app.storage.GetMatrixKey(gc.GetString("jfId"))
if ok {
mxUser.Lang = existingUser.Lang
mxUser.Contact = existingUser.Contact
}
app.storage.SetMatrixKey(gc.GetString("jfId"), mxUser)
delete(app.matrix.tokens, pin)
respondBool(200, true, gc)
}
// @Summary unlink the Discord account from your Jellyfin user. Always succeeds.
// @Produce json
// @Success 200 {object} boolResponse
// @Router /my/discord [delete]
// @Tags Users
func (app *appContext) UnlinkMyDiscord(gc *gin.Context) {
app.storage.DeleteDiscordKey(gc.GetString("jfId"))
respondBool(200, true, gc)
}
// @Summary unlink the Telegram account from your Jellyfin user. Always succeeds.
// @Produce json
// @Success 200 {object} boolResponse
// @Router /my/telegram [delete]
// @Tags Users
func (app *appContext) UnlinkMyTelegram(gc *gin.Context) {
app.storage.DeleteTelegramKey(gc.GetString("jfId"))
respondBool(200, true, gc)
}
// @Summary unlink the Matrix account from your Jellyfin user. Always succeeds.
// @Produce json
// @Success 200 {object} boolResponse
// @Router /my/matrix [delete]
// @Tags Users
func (app *appContext) UnlinkMyMatrix(gc *gin.Context) {
app.storage.DeleteMatrixKey(gc.GetString("jfId"))
respondBool(200, true, gc)
}

View File

@ -17,6 +17,7 @@
"inviteSendToEmail": "Send til", "inviteSendToEmail": "Send til",
"create": "Opret", "create": "Opret",
"apply": "Anvend", "apply": "Anvend",
"delete": "Slet",
"select": "Vælg", "select": "Vælg",
"name": "Navn", "name": "Navn",
"date": "Dato", "date": "Dato",

View File

@ -15,6 +15,7 @@
"inviteSendToEmail": "Senden an", "inviteSendToEmail": "Senden an",
"create": "Erstellen", "create": "Erstellen",
"apply": "Anwenden", "apply": "Anwenden",
"delete": "Löschen",
"name": "Name", "name": "Name",
"date": "Datum", "date": "Datum",
"lastActiveTime": "Zuletzt aktiv", "lastActiveTime": "Zuletzt aktiv",

View File

@ -15,6 +15,7 @@
"inviteSendToEmail": "Αποστολή σε", "inviteSendToEmail": "Αποστολή σε",
"create": "Δημιουργία", "create": "Δημιουργία",
"apply": "Εφαρμογή", "apply": "Εφαρμογή",
"delete": "Διαγραφή",
"name": "Όνομα", "name": "Όνομα",
"date": "Ημερομηνία", "date": "Ημερομηνία",
"lastActiveTime": "Τελευταία Ενεργός", "lastActiveTime": "Τελευταία Ενεργός",

View File

@ -69,6 +69,7 @@
"inviteInfiniteUsesWarning": "invites with infinite uses can be used abusively", "inviteInfiniteUsesWarning": "invites with infinite uses can be used abusively",
"inviteSendToEmail": "Send to", "inviteSendToEmail": "Send to",
"apply": "Apply", "apply": "Apply",
"delete": "Delete",
"updates": "Updates", "updates": "Updates",
"variables": "Variables", "variables": "Variables",
"preview": "Preview", "preview": "Preview",

View File

@ -17,6 +17,7 @@
"inviteSendToEmail": "Send to", "inviteSendToEmail": "Send to",
"create": "Create", "create": "Create",
"apply": "Apply", "apply": "Apply",
"delete": "Delete",
"select": "Select", "select": "Select",
"name": "Name", "name": "Name",
"date": "Date", "date": "Date",

View File

@ -17,6 +17,7 @@
"inviteSendToEmail": "Enviar a", "inviteSendToEmail": "Enviar a",
"create": "Crear", "create": "Crear",
"apply": "Aplicar", "apply": "Aplicar",
"delete": "Eliminar",
"name": "Nombre", "name": "Nombre",
"date": "Fecha", "date": "Fecha",
"updates": "Actualizaciones", "updates": "Actualizaciones",

View File

@ -17,6 +17,7 @@
"inviteSendToEmail": "Envoyer à", "inviteSendToEmail": "Envoyer à",
"create": "Créer", "create": "Créer",
"apply": "Appliquer", "apply": "Appliquer",
"delete": "Effacer",
"name": "Nom", "name": "Nom",
"date": "Date", "date": "Date",
"lastActiveTime": "Dernière activité", "lastActiveTime": "Dernière activité",

View File

@ -17,6 +17,7 @@
"inviteSendToEmail": "Címzett", "inviteSendToEmail": "Címzett",
"create": "Létrehozás", "create": "Létrehozás",
"apply": "Alkalmaz", "apply": "Alkalmaz",
"delete": "Törlés",
"select": "Kiválasztás", "select": "Kiválasztás",
"name": "Név", "name": "Név",
"date": "Dátum", "date": "Dátum",

View File

@ -15,6 +15,7 @@
"inviteSendToEmail": "Dikirim kepada", "inviteSendToEmail": "Dikirim kepada",
"create": "Buat", "create": "Buat",
"apply": "Terapkan", "apply": "Terapkan",
"delete": "Hapus",
"name": "Nama", "name": "Nama",
"date": "Tanggal", "date": "Tanggal",
"lastActiveTime": "Terakhir Aktif", "lastActiveTime": "Terakhir Aktif",

View File

@ -15,6 +15,7 @@
"inviteSendToEmail": "Stuur naar", "inviteSendToEmail": "Stuur naar",
"create": "Aanmaken", "create": "Aanmaken",
"apply": "Toepassen", "apply": "Toepassen",
"delete": "Verwijderen",
"name": "Naam", "name": "Naam",
"date": "Datum", "date": "Datum",
"lastActiveTime": "Laatst actief", "lastActiveTime": "Laatst actief",

View File

@ -17,6 +17,7 @@
"inviteSendToEmail": "", "inviteSendToEmail": "",
"create": "", "create": "",
"apply": "", "apply": "",
"delete": "",
"select": "", "select": "",
"name": "Imię", "name": "Imię",
"date": "Data", "date": "Data",

View File

@ -15,6 +15,7 @@
"inviteSendToEmail": "Enviar para", "inviteSendToEmail": "Enviar para",
"create": "Criar", "create": "Criar",
"apply": "Aplicar", "apply": "Aplicar",
"delete": "Deletar",
"name": "Nome", "name": "Nome",
"date": "Data", "date": "Data",
"lastActiveTime": "Ativo pela última vez", "lastActiveTime": "Ativo pela última vez",

View File

@ -15,6 +15,7 @@
"inviteSendToEmail": "Skicka till", "inviteSendToEmail": "Skicka till",
"create": "Skapa", "create": "Skapa",
"apply": "Tillämpa", "apply": "Tillämpa",
"delete": "Radera",
"name": "Namn", "name": "Namn",
"date": "Datum", "date": "Datum",
"lastActiveTime": "Senast aktiv", "lastActiveTime": "Senast aktiv",

View File

@ -17,6 +17,7 @@
"inviteSendToEmail": "Gửi tới", "inviteSendToEmail": "Gửi tới",
"create": "Tạo mới", "create": "Tạo mới",
"apply": "Áp dụng", "apply": "Áp dụng",
"delete": "Xóa",
"select": "Chọn", "select": "Chọn",
"name": "Tên", "name": "Tên",
"date": "Ngày", "date": "Ngày",

View File

@ -17,6 +17,7 @@
"inviteSendToEmail": "发送到", "inviteSendToEmail": "发送到",
"create": "创建", "create": "创建",
"apply": "申请", "apply": "申请",
"delete": "删除",
"select": "选择", "select": "选择",
"name": "名称", "name": "名称",
"date": "日期", "date": "日期",

View File

@ -17,6 +17,7 @@
"inviteSendToEmail": "發送到", "inviteSendToEmail": "發送到",
"create": "創建", "create": "創建",
"apply": "應用", "apply": "應用",
"delete": "刪除",
"select": "選擇", "select": "選擇",
"name": "帳戶名稱", "name": "帳戶名稱",
"date": "日期", "date": "日期",

View File

@ -34,8 +34,7 @@
"disable": "Deaktiver", "disable": "Deaktiver",
"expiry": "Udløb", "expiry": "Udløb",
"add": "Tilføj", "add": "Tilføj",
"edit": "Rediger", "edit": "Rediger"
"delete": "Slet"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "Brugernavnet og/eller adgangskoden blev efterladt tomme.", "errorLoginBlank": "Brugernavnet og/eller adgangskoden blev efterladt tomme.",

View File

@ -34,8 +34,7 @@
"disable": "Deaktivieren", "disable": "Deaktivieren",
"expiry": "Ablaufdatum", "expiry": "Ablaufdatum",
"add": "Hinzufügen", "add": "Hinzufügen",
"edit": "Bearbeiten", "edit": "Bearbeiten"
"delete": "Löschen"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "Der Benutzername und/oder das Passwort wurden nicht ausgefüllt.", "errorLoginBlank": "Der Benutzername und/oder das Passwort wurden nicht ausgefüllt.",

View File

@ -24,8 +24,7 @@
"reEnable": "Επανα-ενεργοποίηση", "reEnable": "Επανα-ενεργοποίηση",
"disable": "Απενεργοποίηση", "disable": "Απενεργοποίηση",
"expiry": "Λήξη", "expiry": "Λήξη",
"edit": "Επεξεργασία", "edit": "Επεξεργασία"
"delete": "Διαγραφή"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "Το όνομα χρήστη και/ή ο κωδικός ήταν κενά.", "errorLoginBlank": "Το όνομα χρήστη και/ή ο κωδικός ήταν κενά.",

View File

@ -34,8 +34,7 @@
"disable": "Disable", "disable": "Disable",
"expiry": "Expiry", "expiry": "Expiry",
"add": "Add", "add": "Add",
"edit": "Edit", "edit": "Edit"
"delete": "Delete"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "The username and/or password was left blank.", "errorLoginBlank": "The username and/or password was left blank.",

View File

@ -33,12 +33,13 @@
"reEnable": "Re-enable", "reEnable": "Re-enable",
"disable": "Disable", "disable": "Disable",
"contactMethods": "Contact Methods", "contactMethods": "Contact Methods",
"addContactMethod": "Add Contact Method",
"editContactMethod": "Edit Contact Method",
"accountStatus": "Account Status", "accountStatus": "Account Status",
"notSet": "Not set", "notSet": "Not set",
"expiry": "Expiry", "expiry": "Expiry",
"add": "Add", "add": "Add",
"edit": "Edit", "edit": "Edit"
"delete": "Delete"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "The username and/or password were left blank.", "errorLoginBlank": "The username and/or password were left blank.",
@ -61,4 +62,4 @@
"plural": "{n} Days" "plural": "{n} Days"
} }
} }
} }

View File

@ -34,8 +34,7 @@
"disable": "Desactivar", "disable": "Desactivar",
"expiry": "Expiración", "expiry": "Expiración",
"add": "Agregar", "add": "Agregar",
"edit": "Editar", "edit": "Editar"
"delete": "Eliminar"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "El nombre de usuario y/o la contraseña se dejaron en blanco.", "errorLoginBlank": "El nombre de usuario y/o la contraseña se dejaron en blanco.",

View File

@ -34,8 +34,7 @@
"disable": "Désactivé", "disable": "Désactivé",
"expiry": "Expiration", "expiry": "Expiration",
"add": "Ajouter", "add": "Ajouter",
"edit": "Éditer", "edit": "Éditer"
"delete": "Effacer"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "Le nom d'utilisateur et/ou le mot de passe sont vides.", "errorLoginBlank": "Le nom d'utilisateur et/ou le mot de passe sont vides.",

View File

@ -12,8 +12,7 @@
"disable": "Letiltás", "disable": "Letiltás",
"expiry": "Lejárat", "expiry": "Lejárat",
"add": "Hozzáadás", "add": "Hozzáadás",
"edit": "Szerkesztés", "edit": "Szerkesztés"
"delete": "Törlés"
}, },
"notifications": {}, "notifications": {},
"quantityStrings": {} "quantityStrings": {}

View File

@ -18,8 +18,7 @@
"theme": "Tema", "theme": "Tema",
"login": "Masuk", "login": "Masuk",
"logout": "Keluar", "logout": "Keluar",
"edit": "Edit", "edit": "Edit"
"delete": "Hapus"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "Nama pengguna dan / atau sandi kosong.", "errorLoginBlank": "Nama pengguna dan / atau sandi kosong.",

View File

@ -34,8 +34,7 @@
"disable": "Uitschakelen", "disable": "Uitschakelen",
"expiry": "Verloop", "expiry": "Verloop",
"add": "Voeg toe", "add": "Voeg toe",
"edit": "Bewerken", "edit": "Bewerken"
"delete": "Verwijderen"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "De gebruikersnaam en/of wachtwoord is leeg.", "errorLoginBlank": "De gebruikersnaam en/of wachtwoord is leeg.",

View File

@ -34,8 +34,7 @@
"disable": "Desativar", "disable": "Desativar",
"expiry": "Expira", "expiry": "Expira",
"add": "Adicionar", "add": "Adicionar",
"edit": "Editar", "edit": "Editar"
"delete": "Deletar"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "O nome de usuário e/ou senha foram deixados em branco.", "errorLoginBlank": "O nome de usuário e/ou senha foram deixados em branco.",

View File

@ -21,8 +21,7 @@
"enabled": "Aktiverad", "enabled": "Aktiverad",
"disabled": "Inaktiverad", "disabled": "Inaktiverad",
"expiry": "Löper ut", "expiry": "Löper ut",
"edit": "Redigera", "edit": "Redigera"
"delete": "Radera"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "Användarnamnet och/eller lösenordet lämnades tomt.", "errorLoginBlank": "Användarnamnet och/eller lösenordet lämnades tomt.",

View File

@ -12,8 +12,7 @@
"disable": "Tắt", "disable": "Tắt",
"expiry": "Hết hạn", "expiry": "Hết hạn",
"add": "Thêm", "add": "Thêm",
"edit": "Chỉnh sửa", "edit": "Chỉnh sửa"
"delete": "Xóa"
}, },
"notifications": { "notifications": {
"errorConnection": "Không thể kết nối với jfa-go.", "errorConnection": "Không thể kết nối với jfa-go.",

View File

@ -34,8 +34,7 @@
"disable": "禁用", "disable": "禁用",
"expiry": "到期", "expiry": "到期",
"add": "添加", "add": "添加",
"edit": "编辑", "edit": "编辑"
"delete": "删除"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "用户名/密码留空。", "errorLoginBlank": "用户名/密码留空。",

View File

@ -34,8 +34,7 @@
"disable": "禁用", "disable": "禁用",
"expiry": "到期", "expiry": "到期",
"add": "添加", "add": "添加",
"edit": "編輯", "edit": "編輯"
"delete": "刪除"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "帳戶名稱和/或密碼留空。", "errorLoginBlank": "帳戶名稱和/或密碼留空。",

View File

@ -61,4 +61,4 @@
"plural": "Must have at least {n} special characters" "plural": "Must have at least {n} special characters"
} }
} }
} }

View File

@ -235,11 +235,6 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
user.GET(p+"/pin/:service", app.GetMyPIN) user.GET(p+"/pin/:service", app.GetMyPIN)
user.GET(p+"/discord/verified/:pin", app.MyDiscordVerifiedInvite) user.GET(p+"/discord/verified/:pin", app.MyDiscordVerifiedInvite)
user.GET(p+"/telegram/verified/:pin", app.MyTelegramVerifiedInvite) user.GET(p+"/telegram/verified/:pin", app.MyTelegramVerifiedInvite)
user.POST(p+"/matrix/user", app.MatrixSendMyPIN)
user.POST(p+"/matrix/verified/:userID/:pin", app.MatrixCheckMyPIN)
user.DELETE(p+"/discord", app.UnlinkMyDiscord)
user.DELETE(p+"/telegram", app.UnlinkMyTelegram)
user.DELETE(p+"/matrix", app.UnlinkMyMatrix)
} }
} }
} }

View File

@ -36,9 +36,8 @@
"accountStatus": "common", "accountStatus": "common",
"notSet": "common", "notSet": "common",
"expiry": "common", "expiry": "common",
"add": "common", "add": "admin",
"edit": "common", "edit": "admin"
"delete": "admin"
}, },
"notifications": { "notifications": {
"errorLoginBlank": "common", "errorLoginBlank": "common",

View File

@ -63,7 +63,6 @@ func (st *Storage) SetEmailsKey(k string, v EmailAddress) {
func (st *Storage) DeleteEmailsKey(k string) { func (st *Storage) DeleteEmailsKey(k string) {
st.emailsLock.Lock() st.emailsLock.Lock()
delete(st.emails, k) delete(st.emails, k)
st.storeEmails()
st.emailsLock.Unlock() st.emailsLock.Unlock()
} }
@ -90,7 +89,6 @@ func (st *Storage) SetDiscordKey(k string, v DiscordUser) {
func (st *Storage) DeleteDiscordKey(k string) { func (st *Storage) DeleteDiscordKey(k string) {
st.discordLock.Lock() st.discordLock.Lock()
delete(st.discord, k) delete(st.discord, k)
st.storeDiscordUsers()
st.discordLock.Unlock() st.discordLock.Unlock()
} }
@ -117,7 +115,6 @@ func (st *Storage) SetTelegramKey(k string, v TelegramUser) {
func (st *Storage) DeleteTelegramKey(k string) { func (st *Storage) DeleteTelegramKey(k string) {
st.telegramLock.Lock() st.telegramLock.Lock()
delete(st.telegram, k) delete(st.telegram, k)
st.storeTelegramUsers()
st.telegramLock.Unlock() st.telegramLock.Unlock()
} }
@ -144,7 +141,6 @@ func (st *Storage) SetMatrixKey(k string, v MatrixUser) {
func (st *Storage) DeleteMatrixKey(k string) { func (st *Storage) DeleteMatrixKey(k string) {
st.matrixLock.Lock() st.matrixLock.Lock()
delete(st.matrix, k) delete(st.matrix, k)
st.storeMatrixUsers()
st.matrixLock.Unlock() st.matrixLock.Unlock()
} }

View File

@ -3,7 +3,7 @@ import { notificationBox, whichAnimationEvent } from "./modules/common.js";
import { _get, _post, toggleLoader, addLoader, removeLoader, toDateString } from "./modules/common.js"; import { _get, _post, toggleLoader, addLoader, removeLoader, toDateString } from "./modules/common.js";
import { loadLangSelector } from "./modules/lang.js"; import { loadLangSelector } from "./modules/lang.js";
import { initValidator } from "./modules/validator.js"; import { initValidator } from "./modules/validator.js";
import { Discord, Telegram, Matrix, ServiceConfiguration, MatrixConfiguration } from "./modules/account-linking.js"; import { Discord, Telegram, ServiceConfiguration } from "./modules/account-linking.js";
interface formWindow extends Window { interface formWindow extends Window {
invalidPassword: string; invalidPassword: string;
@ -56,7 +56,7 @@ if (window.telegramEnabled) {
pin: window.telegramPIN, pin: window.telegramPIN,
pinURL: "", pinURL: "",
verifiedURL: "/invite/" + window.code + "/telegram/verified/", verifiedURL: "/invite/" + window.code + "/telegram/verified/",
invalidCodeError: window.messages["errorInvalidPIN"], invalidCodeError: window.messages["errorInvalidCode"],
accountLinkedError: window.messages["errorAccountLinked"], accountLinkedError: window.messages["errorAccountLinked"],
successError: window.messages["verified"], successError: window.messages["verified"],
successFunc: (modalClosed: boolean) => { successFunc: (modalClosed: boolean) => {
@ -88,7 +88,7 @@ if (window.discordEnabled) {
inviteURL: window.discordInviteLink ? ("/invite/" + window.code + "/discord/invite") : "", inviteURL: window.discordInviteLink ? ("/invite/" + window.code + "/discord/invite") : "",
pinURL: "", pinURL: "",
verifiedURL: "/invite/" + window.code + "/discord/verified/", verifiedURL: "/invite/" + window.code + "/discord/verified/",
invalidCodeError: window.messages["errorInvalidPIN"], invalidCodeError: window.messages["errorInvalidCode"],
accountLinkedError: window.messages["errorAccountLinked"], accountLinkedError: window.messages["errorAccountLinked"],
successError: window.messages["verified"], successError: window.messages["verified"],
successFunc: (modalClosed: boolean) => { successFunc: (modalClosed: boolean) => {
@ -114,31 +114,69 @@ var matrixPIN = "";
if (window.matrixEnabled) { if (window.matrixEnabled) {
window.matrixModal = new Modal(document.getElementById("modal-matrix"), window.matrixRequired); window.matrixModal = new Modal(document.getElementById("modal-matrix"), window.matrixRequired);
const matrixButton = document.getElementById("link-matrix") as HTMLSpanElement; const matrixButton = document.getElementById("link-matrix") as HTMLSpanElement;
matrixButton.onclick = window.matrixModal.show;
const matrixConf: MatrixConfiguration = { const submitButton = document.getElementById("matrix-send") as HTMLSpanElement;
modal: window.matrixModal as Modal, const input = document.getElementById("matrix-userid") as HTMLInputElement;
sendMessageURL: "/invite/" + window.code + "/matrix/user", let userID = "";
verifiedURL: "/invite/" + window.code + "/matrix/verified/", submitButton.onclick = () => {
invalidCodeError: window.messages["errorInvalidPIN"], addLoader(submitButton);
accountLinkedError: window.messages["errorAccountLinked"], if (userID == "") {
unknownError: window.messages["errorUnknown"], const send = {
successError: window.messages["verified"], user_id: input.value
successFunc: () => { };
matrixVerified = true; _post("/invite/" + window.code + "/matrix/user", send, (req: XMLHttpRequest) => {
matrixPIN = matrix.pin; if (req.readyState == 4) {
matrixButton.classList.add("unfocused"); if (req.status == 400 && req.response["error"] == "errorAccountLinked") {
document.getElementById("contact-via").classList.remove("unfocused"); window.matrixModal.close();
document.getElementById("contact-via-email").parentElement.classList.remove("unfocused"); window.notifications.customError("accountLinkedError", window.messages["errorAccountLinked"]);
const radio = document.getElementById("contact-via-matrix") as HTMLInputElement; }
radio.parentElement.classList.remove("unfocused"); removeLoader(submitButton);
radio.checked = true; userID = input.value;
validatorFunc(); if (req.status != 200) {
window.notifications.customError("errorUnknown", window.messages["errorUnknown"]);
window.matrixModal.close();
return;
}
submitButton.classList.add("~positive");
submitButton.classList.remove("~info");
setTimeout(() => {
submitButton.classList.add("~info");
submitButton.classList.remove("~positive");
}, 2000);
input.placeholder = "PIN";
input.value = "";
}
});
} else {
_get("/invite/" + window.code + "/matrix/verified/" + userID + "/" + input.value, null, (req: XMLHttpRequest) => {
if (req.readyState == 4) {
removeLoader(submitButton)
const valid = req.response["success"] as boolean;
if (valid) {
window.matrixModal.close();
window.notifications.customPositive("successVerified", "", window.messages["verified"]);
matrixVerified = true;
matrixPIN = input.value;
matrixButton.classList.add("unfocused");
document.getElementById("contact-via").classList.remove("unfocused");
document.getElementById("contact-via-email").parentElement.classList.remove("unfocused");
const radio = document.getElementById("contact-via-matrix") as HTMLInputElement;
radio.parentElement.classList.remove("unfocused");
radio.checked = true;
validatorFunc();
} else {
window.notifications.customError("errorInvalidPIN", window.messages["errorInvalidPIN"]);
submitButton.classList.add("~critical");
submitButton.classList.remove("~info");
setTimeout(() => {
submitButton.classList.add("~info");
submitButton.classList.remove("~critical");
}, 800);
}
}
},);
} }
}; };
const matrix = new Matrix(matrixConf);
matrixButton.onclick = () => { matrix.show(); };
} }
if (window.confirmation) { if (window.confirmation) {

View File

@ -1,5 +1,5 @@
import { Modal } from "../modules/modal.js"; import { Modal } from "../modules/modal.js";
import { _get, _post, toggleLoader, addLoader, removeLoader } from "../modules/common.js"; import { _get, _post, toggleLoader } from "../modules/common.js";
interface formWindow extends Window { interface formWindow extends Window {
invalidPassword: string; invalidPassword: string;
@ -172,95 +172,3 @@ export class Telegram extends ServiceLinker {
this._waiting = document.getElementById("telegram-waiting") as HTMLSpanElement; this._waiting = document.getElementById("telegram-waiting") as HTMLSpanElement;
} }
}; };
export interface MatrixConfiguration {
modal: Modal;
sendMessageURL: string;
verifiedURL: string;
invalidCodeError: string;
accountLinkedError: string;
unknownError: string;
successError: string;
successFunc: () => void;
}
export class Matrix {
private _conf: MatrixConfiguration;
private _verified = false;
private _name: string = "matrix";
private _userID: string = "";
private _pin: string = "";
private _input: HTMLInputElement;
private _submit: HTMLSpanElement;
get verified(): boolean { return this._verified; }
get pin(): string { return this._pin; }
constructor(conf: MatrixConfiguration) {
this._conf = conf;
this._input = document.getElementById("matrix-userid") as HTMLInputElement;
this._submit = document.getElementById("matrix-send") as HTMLSpanElement;
this._submit.onclick = () => { this._onclick(); };
}
private _onclick = () => {
addLoader(this._submit);
if (this._userID == "") {
this._sendMessage();
} else {
this._verifyCode();
}
};
show = () => {
this._input.value = "";
this._conf.modal.show();
}
private _sendMessage = () => _post(this._conf.sendMessageURL, { "user_id": this._input.value }, (req: XMLHttpRequest) => {
if (req.readyState != 4) return;
removeLoader(this._submit);
if (req.status == 400 && req.response["error"] == "errorAccountLinked") {
this._conf.modal.close();
window.notifications.customError("accountLinkedError", this._conf.accountLinkedError);
return;
} else if (req.status != 200) {
this._conf.modal.close();
window.notifications.customError("unknownError", this._conf.unknownError);
return;
}
this._userID = this._input.value;
this._submit.classList.add("~positive");
this._submit.classList.remove("~info");
setTimeout(() => {
this._submit.classList.add("~info");
this._submit.classList.remove("~positive");
}, 2000);
this._input.placeholder = "PIN";
this._input.value = "";
});
private _verifyCode = () => _post(this._conf.verifiedURL + this._userID + "/" + this._input.value, null, (req: XMLHttpRequest) => {
if (req.readyState != 4) return;
removeLoader(this._submit);
const valid = req.response["success"] as boolean;
if (valid) {
this._conf.modal.close();
window.notifications.customPositive(this._name + "Verified", "", this._conf.successError);
this._verified = true;
this._pin = this._input.value;
if (this._conf.successFunc) {
this._conf.successFunc();
}
} else {
window.notifications.customError("invalidCodeError", this._conf.invalidCodeError);
this._submit.classList.add("~critical");
this._submit.classList.remove("~info");
setTimeout(() => {
this._submit.classList.add("~info");
this._submit.classList.remove("~critical");
}, 800);
}
}, true);
}

View File

@ -1,9 +1,9 @@
import { ThemeManager } from "./modules/theme.js"; import { ThemeManager } from "./modules/theme.js";
import { lang, LangFile, loadLangSelector } from "./modules/lang.js"; import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
import { Modal } from "./modules/modal.js"; import { Modal } from "./modules/modal.js";
import { _get, _post, _delete, notificationBox, whichAnimationEvent, toDateString, toggleLoader } from "./modules/common.js"; import { _get, _post, notificationBox, whichAnimationEvent, toDateString, toggleLoader } from "./modules/common.js";
import { Login } from "./modules/login.js"; import { Login } from "./modules/login.js";
import { Discord, Telegram, Matrix, ServiceConfiguration, MatrixConfiguration } from "./modules/account-linking.js"; import { Discord, Telegram, ServiceConfiguration } from "./modules/account-linking.js";
interface userWindow extends Window { interface userWindow extends Window {
jellyfinID: string; jellyfinID: string;
@ -92,7 +92,7 @@ class ContactMethods {
this._buttons = {}; this._buttons = {};
} }
append = (name: string, details: MyDetailsContactMethod, icon: string, addEditFunc?: (add: boolean) => void, required?: boolean) => { append = (name: string, details: MyDetailsContactMethod, icon: string, addEditFunc?: (add: boolean) => void) => {
const row = document.createElement("div"); const row = document.createElement("div");
row.classList.add("row", "flex-expand", "my-2"); row.classList.add("row", "flex-expand", "my-2");
let innerHTML = ` let innerHTML = `
@ -118,15 +118,6 @@ class ContactMethods {
</button> </button>
`; `;
} }
if (!required && details.value != "") {
innerHTML += `
<button class="user-contact-delete button ~critical ml-2" alt="${window.lang.strings("delete")}" text="${window.lang.strings("delete")}">
&times;
</button>
`;
}
innerHTML += ` innerHTML += `
</div> </div>
`; `;
@ -168,14 +159,6 @@ class ContactMethods {
const addEditButton = row.querySelector(".user-contact-edit") as HTMLButtonElement; const addEditButton = row.querySelector(".user-contact-edit") as HTMLButtonElement;
addEditButton.onclick = () => addEditFunc(details.value == ""); addEditButton.onclick = () => addEditFunc(details.value == "");
} }
if (!required && details.value != "") {
const deleteButton = row.querySelector(".user-contact-delete") as HTMLButtonElement;
deleteButton.onclick = () => _delete("/my/" + name, null, (req: XMLHttpRequest) => {
if (req.readyState != 4) return;
window.location.reload();
});
}
this._content.appendChild(row); this._content.appendChild(row);
}; };
@ -265,6 +248,8 @@ class ExpiryCard {
this._interval = window.setInterval(this._drawCountdown, 60*1000); this._interval = window.setInterval(this._drawCountdown, 60*1000);
this._drawCountdown(); this._drawCountdown();
} }
} }
var expiryCard = new ExpiryCard(statusCard); var expiryCard = new ExpiryCard(statusCard);
@ -306,7 +291,7 @@ const discordConf: ServiceConfiguration = {
inviteURL: window.discordInviteLink ? "/my/discord/invite" : "", inviteURL: window.discordInviteLink ? "/my/discord/invite" : "",
pinURL: "/my/pin/discord", pinURL: "/my/pin/discord",
verifiedURL: "/my/discord/verified/", verifiedURL: "/my/discord/verified/",
invalidCodeError: window.lang.notif("errorInvalidPIN"), invalidCodeError: window.lang.notif("errorInvalidCode"),
accountLinkedError: window.lang.notif("errorAccountLinked"), accountLinkedError: window.lang.notif("errorAccountLinked"),
successError: window.lang.notif("verified"), successError: window.lang.notif("verified"),
successFunc: (modalClosed: boolean) => { successFunc: (modalClosed: boolean) => {
@ -321,7 +306,7 @@ const telegramConf: ServiceConfiguration = {
pin: "", pin: "",
pinURL: "/my/pin/telegram", pinURL: "/my/pin/telegram",
verifiedURL: "/my/telegram/verified/", verifiedURL: "/my/telegram/verified/",
invalidCodeError: window.lang.notif("errorInvalidPIN"), invalidCodeError: window.lang.notif("errorInvalidCode"),
accountLinkedError: window.lang.notif("errorAccountLinked"), accountLinkedError: window.lang.notif("errorAccountLinked"),
successError: window.lang.notif("verified"), successError: window.lang.notif("verified"),
successFunc: (modalClosed: boolean) => { successFunc: (modalClosed: boolean) => {
@ -331,22 +316,6 @@ const telegramConf: ServiceConfiguration = {
let telegram = new Telegram(telegramConf); let telegram = new Telegram(telegramConf);
const matrixConf: MatrixConfiguration = {
modal: window.modals.matrix as Modal,
sendMessageURL: "/my/matrix/user",
verifiedURL: "/my/matrix/verified/",
invalidCodeError: window.lang.notif("errorInvalidPIN"),
accountLinkedError: window.lang.notif("errorAccountLinked"),
unknownError: window.lang.notif("errorUnknown"),
successError: window.lang.notif("verified"),
successFunc: () => {
setTimeout(() => window.location.reload(), 1200);
}
};
let matrix = new Matrix(matrixConf);
document.addEventListener("details-reload", () => { document.addEventListener("details-reload", () => {
_get("/my/details", null, (req: XMLHttpRequest) => { _get("/my/details", null, (req: XMLHttpRequest) => {
if (req.readyState == 4) { if (req.readyState == 4) {
@ -374,16 +343,16 @@ document.addEventListener("details-reload", () => {
// Note the weird format of the functions for discord/telegram: // Note the weird format of the functions for discord/telegram:
// "this" was being redefined within the onclick() method, so // "this" was being redefined within the onclick() method, so
// they had to be wrapped in an anonymous function. // they had to be wrapped in an anonymous function.
const contactMethods: { name: string, icon: string, f: (add: boolean) => void, required: boolean }[] = [ const contactMethods: { name: string, icon: string, f: (add: boolean) => void }[] = [
{name: "email", icon: `<i class="ri-mail-fill ri-lg"></i>`, f: addEditEmail, required: true}, {name: "email", icon: `<i class="ri-mail-fill ri-lg"></i>`, f: addEditEmail},
{name: "discord", icon: `<i class="ri-discord-fill ri-lg"></i>`, f: (add: boolean) => { discord.onclick(); }, required: window.discordRequired}, {name: "discord", icon: `<i class="ri-discord-fill ri-lg"></i>`, f: (add: boolean) => { discord.onclick(); }},
{name: "telegram", icon: `<i class="ri-telegram-fill ri-lg"></i>`, f: (add: boolean) => { telegram.onclick() }, required: window.telegramRequired}, {name: "telegram", icon: `<i class="ri-telegram-fill ri-lg"></i>`, f: (add: boolean) => { telegram.onclick() }},
{name: "matrix", icon: `<span class="font-bold">[m]</span>`, f: (add: boolean) => { matrix.show(); }, required: window.matrixRequired} {name: "matrix", icon: `<span class="font-bold">[m]</span>`, f: null}
]; ];
for (let method of contactMethods) { for (let method of contactMethods) {
if (method.name in details) { if (method.name in details) {
contactMethodList.append(method.name, details[method.name], method.icon, method.f, method.required); contactMethodList.append(method.name, details[method.name], method.icon, method.f);
} }
} }