mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 06:25:41 +00:00
feat(servers/settings): magic string invites, Navidrome admin share, and add-user validation (#258)
* feat(servers): magic string invites and duplicate-aware labels Add psysonic1- share payloads (URL, Subsonic credentials, optional server name). Login and add-server forms decode on input and keep magic field below standard fields. Navidrome User Management generates strings after PUT password updates where required. Resolve duplicate display names as username@host in chrome, settings, and login. * feat(servers): tighten magic-string import and admin share flow After a decoded magic string, username is read-only and the password field shows a fixed-length mask so length is not inferred. Password reveal stays disabled until saved-server quick connect (login) or reopening the add- server form. Navidrome admin share UI persists the password via API before copy and adds a plaintext-handling disclaimer. Locales updated. * feat(settings): save new Navidrome user and copy magic string Add a non-admin "Save and get magic string" flow: create the user, assign libraries when needed, then encode credentials to the clipboard. Reuse the plaintext-handling disclaimer without the password-update hint meant for edits. Strings added for all locales. * fix(settings): tighten Navidrome add-user validation and submit feedback Require username, display name, and a non-empty password (trimmed) for new users; gate Save and “save and get magic string” on explicit checks with toast feedback. Show red borders only after a failed submit, and clear them when the user fills the fields. When editing, keep an empty password as “unchanged” and validate identity fields with a dedicated message. Strings: userMgmtValidationMissingIdentity across locales.
This commit is contained in:
@@ -351,6 +351,9 @@ export const deTranslation = {
|
||||
urlRequired: 'Bitte Server-URL eingeben.',
|
||||
savedServers: 'Gespeicherte Server',
|
||||
addNew: 'Oder neuen Server hinzufügen',
|
||||
orMagicString: 'Oder Magic-String',
|
||||
magicStringPlaceholder: 'Freigabe-String einfügen (psysonic1-…)',
|
||||
magicStringInvalid: 'Ungültiger oder nicht lesbarer Magic-String.',
|
||||
},
|
||||
connection: {
|
||||
connected: 'Verbunden',
|
||||
@@ -509,6 +512,19 @@ export const deTranslation = {
|
||||
userMgmtUpdated: 'Benutzer aktualisiert.',
|
||||
userMgmtDeleted: 'Benutzer gelöscht.',
|
||||
userMgmtValidationMissing: 'Benutzername, Anzeigename und Passwort sind erforderlich.',
|
||||
userMgmtValidationMissingIdentity: 'Benutzername und Anzeigename sind erforderlich.',
|
||||
userMgmtMagicStringGenerate: 'Magic-String erzeugen',
|
||||
userMgmtSaveAndMagicString: 'Speichern und Magic-String kopieren',
|
||||
userMgmtMagicStringPasswordNavHint:
|
||||
'Navidrome speichert dieses Passwort für den Benutzer. Weicht es vom aktuellen ab, wird das Anmeldepasswort auf dem Server aktualisiert.',
|
||||
userMgmtMagicStringPlaintextWarning:
|
||||
'Teilen Sie den Magic-String vorsichtig: Er enthält das Passwort im Klartext (Kodierung ist keine Verschlüsselung). Wer die vollständige Zeichenkette hat, kann sich als dieser Benutzer anmelden.',
|
||||
userMgmtMagicStringCopied: 'Magic-String in die Zwischenablage kopiert.',
|
||||
userMgmtMagicStringCopyFailed: 'Kopieren in die Zwischenablage fehlgeschlagen.',
|
||||
userMgmtMagicStringLoginFailed: 'Passwortprüfung fehlgeschlagen — Anmeldedaten konnten nicht verifiziert werden.',
|
||||
userMgmtMagicStringModalTitle: 'Magic-String erzeugen',
|
||||
userMgmtMagicStringModalDesc: 'Subsonic-Passwort für „{{username}}" eingeben — es wird in den kopierten Magic-String übernommen.',
|
||||
userMgmtMagicStringModalConfirm: 'String kopieren',
|
||||
audiomuseTitle: 'AudioMuse-AI (Navidrome)',
|
||||
audiomuseDesc:
|
||||
'Aktivieren, wenn dieser Server das <pluginLink>AudioMuse-AI-Navidrome-Plugin</pluginLink> nutzt. Schaltet Instant Mix pro Titel frei und nutzt ähnliche Künstler vom Server statt Last.fm auf Künstlerseiten.',
|
||||
|
||||
@@ -352,6 +352,9 @@ export const enTranslation = {
|
||||
urlRequired: 'Please enter a server URL.',
|
||||
savedServers: 'Saved Servers',
|
||||
addNew: 'Or add a new server',
|
||||
orMagicString: 'Or magic string',
|
||||
magicStringPlaceholder: 'Paste a share string (psysonic1-…)',
|
||||
magicStringInvalid: 'Invalid or unreadable magic string.',
|
||||
},
|
||||
connection: {
|
||||
connected: 'Connected',
|
||||
@@ -511,6 +514,19 @@ export const enTranslation = {
|
||||
userMgmtUpdated: 'User updated.',
|
||||
userMgmtDeleted: 'User deleted.',
|
||||
userMgmtValidationMissing: 'Username, display name and password are required.',
|
||||
userMgmtValidationMissingIdentity: 'Username and display name are required.',
|
||||
userMgmtMagicStringGenerate: 'Generate magic string',
|
||||
userMgmtSaveAndMagicString: 'Save and get magic string',
|
||||
userMgmtMagicStringPasswordNavHint:
|
||||
'Navidrome will save this password for the user. If it differs from the current one, the server will update the login password.',
|
||||
userMgmtMagicStringPlaintextWarning:
|
||||
'Share the magic string carefully: it contains an unencrypted password (encoding is not encryption). Anyone with the full string can sign in as this user.',
|
||||
userMgmtMagicStringCopied: 'Magic string copied to clipboard.',
|
||||
userMgmtMagicStringCopyFailed: 'Could not copy to clipboard.',
|
||||
userMgmtMagicStringLoginFailed: 'Password check failed — credentials could not be verified.',
|
||||
userMgmtMagicStringModalTitle: 'Generate magic string',
|
||||
userMgmtMagicStringModalDesc: 'Enter the Subsonic password for "{{username}}". It is included in the copied magic string.',
|
||||
userMgmtMagicStringModalConfirm: 'Copy string',
|
||||
audiomuseTitle: 'AudioMuse-AI (Navidrome)',
|
||||
audiomuseDesc:
|
||||
'Turn on if this server has the <pluginLink>AudioMuse-AI Navidrome plugin</pluginLink> configured. Enables Instant Mix from tracks and uses server-side similar artists instead of Last.fm on artist pages.',
|
||||
|
||||
@@ -353,6 +353,9 @@ export const esTranslation = {
|
||||
urlRequired: 'Por favor ingresa una URL de servidor.',
|
||||
savedServers: 'Servidores Guardados',
|
||||
addNew: 'O agregar un nuevo servidor',
|
||||
orMagicString: 'O cadena mágica',
|
||||
magicStringPlaceholder: 'Pega una cadena de invitación (psysonic1-…)',
|
||||
magicStringInvalid: 'Cadena mágica no válida o ilegible.',
|
||||
},
|
||||
connection: {
|
||||
connected: 'Conectado',
|
||||
@@ -502,6 +505,19 @@ export const esTranslation = {
|
||||
userMgmtUpdated: 'Usuario actualizado.',
|
||||
userMgmtDeleted: 'Usuario eliminado.',
|
||||
userMgmtValidationMissing: 'Se requieren nombre de usuario, nombre visible y contraseña.',
|
||||
userMgmtValidationMissingIdentity: 'Se requieren nombre de usuario y nombre visible.',
|
||||
userMgmtMagicStringGenerate: 'Generar cadena mágica',
|
||||
userMgmtSaveAndMagicString: 'Guardar y obtener cadena mágica',
|
||||
userMgmtMagicStringPasswordNavHint:
|
||||
'Navidrome guardará esta contraseña para el usuario. Si difiere de la actual, se actualizará la contraseña de acceso en el servidor.',
|
||||
userMgmtMagicStringPlaintextWarning:
|
||||
'Comparte la cadena mágica con cuidado: contiene la contraseña sin cifrar (la codificación no es cifrado). Quien tenga la cadena completa puede iniciar sesión como este usuario.',
|
||||
userMgmtMagicStringCopied: 'Cadena mágica copiada al portapapeles.',
|
||||
userMgmtMagicStringCopyFailed: 'No se pudo copiar al portapapeles.',
|
||||
userMgmtMagicStringLoginFailed: 'Falló la comprobación de la contraseña; no se pudieron verificar las credenciales.',
|
||||
userMgmtMagicStringModalTitle: 'Generar cadena mágica',
|
||||
userMgmtMagicStringModalDesc: 'Introduce la contraseña Subsonic de «{{username}}». Se incluirá en la cadena mágica copiada.',
|
||||
userMgmtMagicStringModalConfirm: 'Copiar cadena',
|
||||
audiomuseTitle: 'AudioMuse-AI (Navidrome)',
|
||||
audiomuseDesc:
|
||||
'Activa si este servidor tiene el plugin <pluginLink>AudioMuse-AI Navidrome</pluginLink> configurado. Habilita Mezcla Instantánea desde pistas y usa artistas similares del servidor en lugar de Last.fm en páginas de artistas.',
|
||||
|
||||
@@ -351,6 +351,9 @@ export const frTranslation = {
|
||||
urlRequired: 'Veuillez saisir une URL de serveur.',
|
||||
savedServers: 'Serveurs enregistrés',
|
||||
addNew: 'Ou ajouter un nouveau serveur',
|
||||
orMagicString: 'Ou chaîne magique',
|
||||
magicStringPlaceholder: 'Collez une chaîne de partage (psysonic1-…)',
|
||||
magicStringInvalid: 'Chaîne magique invalide ou illisible.',
|
||||
},
|
||||
connection: {
|
||||
connected: 'Connecté',
|
||||
@@ -499,6 +502,19 @@ export const frTranslation = {
|
||||
userMgmtUpdated: 'Utilisateur mis à jour.',
|
||||
userMgmtDeleted: 'Utilisateur supprimé.',
|
||||
userMgmtValidationMissing: 'Nom d’utilisateur, nom affiché et mot de passe sont requis.',
|
||||
userMgmtValidationMissingIdentity: 'Le nom d’utilisateur et le nom affiché sont requis.',
|
||||
userMgmtMagicStringGenerate: 'Générer la chaîne magique',
|
||||
userMgmtSaveAndMagicString: 'Enregistrer et obtenir la chaîne magique',
|
||||
userMgmtMagicStringPasswordNavHint:
|
||||
'Navidrome enregistrera ce mot de passe pour l’utilisateur. S’il diffère de l’actuel, le mot de passe de connexion sur le serveur sera mis à jour.',
|
||||
userMgmtMagicStringPlaintextWarning:
|
||||
'Partagez la chaîne magique avec prudence : elle contient un mot de passe non chiffré (l’encodage n’est pas du chiffrement). Quiconque possède la chaîne complète peut se connecter en tant que cet utilisateur.',
|
||||
userMgmtMagicStringCopied: 'Chaîne magique copiée dans le presse-papiers.',
|
||||
userMgmtMagicStringCopyFailed: 'Impossible de copier dans le presse-papiers.',
|
||||
userMgmtMagicStringLoginFailed: 'Échec de la vérification du mot de passe — identifiants non confirmés.',
|
||||
userMgmtMagicStringModalTitle: 'Générer la chaîne magique',
|
||||
userMgmtMagicStringModalDesc: 'Saisissez le mot de passe Subsonic de « {{username}} ». Il est inclus dans la chaîne magique copiée.',
|
||||
userMgmtMagicStringModalConfirm: 'Copier la chaîne',
|
||||
audiomuseTitle: 'AudioMuse-AI (Navidrome)',
|
||||
audiomuseDesc:
|
||||
'Activez si ce serveur utilise le <pluginLink>plugin Navidrome AudioMuse-AI</pluginLink>. Active le mix instantané depuis un morceau et affiche les artistes similaires côté serveur au lieu de Last.fm sur les pages artiste.',
|
||||
|
||||
@@ -351,6 +351,9 @@ export const nbTranslation = {
|
||||
urlRequired: 'Vennligst skriv inn en tjeneer-URL.',
|
||||
savedServers: 'Lagrede servere',
|
||||
addNew: 'Eller legg til en ny tjener',
|
||||
orMagicString: 'Eller magic string',
|
||||
magicStringPlaceholder: 'Lim inn en delingsstreng (psysonic1-…)',
|
||||
magicStringInvalid: 'Ugyldig eller uleselig magic string.',
|
||||
},
|
||||
connection: {
|
||||
connected: 'Tilkoblet',
|
||||
@@ -499,6 +502,19 @@ export const nbTranslation = {
|
||||
userMgmtUpdated: 'Bruker oppdatert.',
|
||||
userMgmtDeleted: 'Bruker slettet.',
|
||||
userMgmtValidationMissing: 'Brukernavn, visningsnavn og passord er påkrevd.',
|
||||
userMgmtValidationMissingIdentity: 'Brukernavn og visningsnavn er påkrevd.',
|
||||
userMgmtMagicStringGenerate: 'Generer magic string',
|
||||
userMgmtSaveAndMagicString: 'Lagre og hent magic string',
|
||||
userMgmtMagicStringPasswordNavHint:
|
||||
'Navidrome lagrer dette passordet for brukeren. Hvis det avviker fra det nåværende, oppdateres innloggingspassordet på serveren.',
|
||||
userMgmtMagicStringPlaintextWarning:
|
||||
'Del magic string med varsomhet: den inneholder passordet i klartekst (koding er ikke kryptering). Den som har hele strengen, kan logge inn som denne brukeren.',
|
||||
userMgmtMagicStringCopied: 'Magic string er kopiert til utklippstavlen.',
|
||||
userMgmtMagicStringCopyFailed: 'Klarte ikke å kopiere til utklippstavlen.',
|
||||
userMgmtMagicStringLoginFailed: 'Passordsjekk mislyktes — påloggingsdetaljene kunne ikke verifiseres.',
|
||||
userMgmtMagicStringModalTitle: 'Generer magic string',
|
||||
userMgmtMagicStringModalDesc: 'Skriv inn Subsonic-passordet for «{{username}}». Det tas med i den kopierte magic string-en.',
|
||||
userMgmtMagicStringModalConfirm: 'Kopier streng',
|
||||
audiomuseTitle: 'AudioMuse-AI (Navidrome)',
|
||||
audiomuseDesc:
|
||||
'Slå på hvis denne serveren bruker <pluginLink>AudioMuse-AI Navidrome-plugin</pluginLink>. Aktiverer Instant Mix fra spor og henter lignende artister fra serveren i stedet for Last.fm på artistsider.',
|
||||
|
||||
@@ -350,6 +350,9 @@ export const nlTranslation = {
|
||||
urlRequired: 'Voer een server-URL in.',
|
||||
savedServers: 'Opgeslagen servers',
|
||||
addNew: 'Of een nieuwe server toevoegen',
|
||||
orMagicString: 'Of magic string',
|
||||
magicStringPlaceholder: 'Plak een deelstring (psysonic1-…)',
|
||||
magicStringInvalid: 'Ongeldige of onleesbare magic string.',
|
||||
},
|
||||
connection: {
|
||||
connected: 'Verbonden',
|
||||
@@ -498,6 +501,19 @@ export const nlTranslation = {
|
||||
userMgmtUpdated: 'Gebruiker bijgewerkt.',
|
||||
userMgmtDeleted: 'Gebruiker verwijderd.',
|
||||
userMgmtValidationMissing: 'Gebruikersnaam, weergavenaam en wachtwoord zijn vereist.',
|
||||
userMgmtValidationMissingIdentity: 'Gebruikersnaam en weergavenaam zijn vereist.',
|
||||
userMgmtMagicStringGenerate: 'Magic string genereren',
|
||||
userMgmtSaveAndMagicString: 'Opslaan en magic string kopiëren',
|
||||
userMgmtMagicStringPasswordNavHint:
|
||||
'Navidrome slaat dit wachtwoord voor de gebruiker op. Als het afwijkt van het huidige wachtwoord, wordt het inlogwachtwoord op de server bijgewerkt.',
|
||||
userMgmtMagicStringPlaintextWarning:
|
||||
'Wees voorzichtig met delen: de magic string bevat een onversleuteld wachtwoord (codering is geen encryptie). Wie de volledige string heeft, kan als deze gebruiker inloggen.',
|
||||
userMgmtMagicStringCopied: 'Magic string naar het klembord gekopieerd.',
|
||||
userMgmtMagicStringCopyFailed: 'Kopiëren naar het klembord is mislukt.',
|
||||
userMgmtMagicStringLoginFailed: 'Wachtwoordcontrole mislukt — aanmeldgegevens konden niet worden geverifieerd.',
|
||||
userMgmtMagicStringModalTitle: 'Magic string genereren',
|
||||
userMgmtMagicStringModalDesc: 'Voer het Subsonic-wachtwoord voor „{{username}}" in. Het wordt opgenomen in de gekopieerde magic string.',
|
||||
userMgmtMagicStringModalConfirm: 'String kopiëren',
|
||||
audiomuseTitle: 'AudioMuse-AI (Navidrome)',
|
||||
audiomuseDesc:
|
||||
'Zet aan als deze server de <pluginLink>AudioMuse-AI Navidrome-plugin</pluginLink> gebruikt. Schakelt Instant Mix per nummer in en toont vergelijkbare artiesten van de server i.p.v. Last.fm op artiestpagina’s.',
|
||||
|
||||
@@ -370,6 +370,9 @@ export const ruTranslation = {
|
||||
urlRequired: 'Укажите адрес сервера.',
|
||||
savedServers: 'Сохранённые серверы',
|
||||
addNew: 'Или добавить новый сервер',
|
||||
orMagicString: 'Или magic string',
|
||||
magicStringPlaceholder: 'Вставьте строку приглашения (psysonic1-…)',
|
||||
magicStringInvalid: 'Неверная или нечитаемая magic string.',
|
||||
},
|
||||
connection: {
|
||||
connected: 'Подключено',
|
||||
@@ -522,6 +525,19 @@ export const ruTranslation = {
|
||||
userMgmtUpdated: 'Пользователь обновлён.',
|
||||
userMgmtDeleted: 'Пользователь удалён.',
|
||||
userMgmtValidationMissing: 'Требуются имя пользователя, отображаемое имя и пароль.',
|
||||
userMgmtValidationMissingIdentity: 'Укажите имя пользователя и отображаемое имя.',
|
||||
userMgmtMagicStringGenerate: 'Сгенерировать magic string',
|
||||
userMgmtSaveAndMagicString: 'Сохранить и получить magic string',
|
||||
userMgmtMagicStringPasswordNavHint:
|
||||
'Navidrome сохранит этот пароль для пользователя. Если он не совпадает с текущим, на сервере будет установлен новый пароль входа.',
|
||||
userMgmtMagicStringPlaintextWarning:
|
||||
'Передавайте magic string осторожно: в ней пароль в открытом виде (кодирование — не шифрование). У кого есть полная строка, тот может войти под этим пользователем.',
|
||||
userMgmtMagicStringCopied: 'Magic string скопирован в буфер обмена.',
|
||||
userMgmtMagicStringCopyFailed: 'Не удалось скопировать в буфер обмена.',
|
||||
userMgmtMagicStringLoginFailed: 'Пароль не подошёл — не удалось проверить учётные данные.',
|
||||
userMgmtMagicStringModalTitle: 'Сгенерировать magic string',
|
||||
userMgmtMagicStringModalDesc: 'Введите пароль Subsonic для «{{username}}» — он попадёт в копируемую magic string.',
|
||||
userMgmtMagicStringModalConfirm: 'Скопировать строку',
|
||||
audiomuseTitle: 'AudioMuse-AI (Navidrome)',
|
||||
audiomuseDesc:
|
||||
'Включите, если на этом сервере настроен <pluginLink>плагин AudioMuse-AI для Navidrome</pluginLink>. Появится Instant Mix для треков, а на странице исполнителя похожие будут браться с сервера вместо Last.fm.',
|
||||
|
||||
@@ -350,6 +350,9 @@ export const zhTranslation = {
|
||||
urlRequired: '请输入服务器地址。',
|
||||
savedServers: '已保存的服务器',
|
||||
addNew: '或添加新服务器',
|
||||
orMagicString: '或使用魔法字符串',
|
||||
magicStringPlaceholder: '粘贴分享字符串(psysonic1-…)',
|
||||
magicStringInvalid: '魔法字符串无效或无法解析。',
|
||||
},
|
||||
connection: {
|
||||
connected: '已连接',
|
||||
@@ -494,6 +497,19 @@ export const zhTranslation = {
|
||||
userMgmtUpdated: '用户已更新。',
|
||||
userMgmtDeleted: '用户已删除。',
|
||||
userMgmtValidationMissing: '用户名、显示名称和密码均为必填项。',
|
||||
userMgmtValidationMissingIdentity: '用户名和显示名称为必填项。',
|
||||
userMgmtMagicStringGenerate: '生成魔法字符串',
|
||||
userMgmtSaveAndMagicString: '保存并获取魔法字符串',
|
||||
userMgmtMagicStringPasswordNavHint:
|
||||
'Navidrome 会保存此密码。若与当前密码不同,服务器上的登录密码将被更新。',
|
||||
userMgmtMagicStringPlaintextWarning:
|
||||
'请谨慎分享魔法字符串:其中包含未加密的密码(编码不等于加密)。掌握完整字符串的人可以以该用户身份登录。',
|
||||
userMgmtMagicStringCopied: '魔法字符串已复制到剪贴板。',
|
||||
userMgmtMagicStringCopyFailed: '无法复制到剪贴板。',
|
||||
userMgmtMagicStringLoginFailed: '密码验证失败,无法确认凭据。',
|
||||
userMgmtMagicStringModalTitle: '生成魔法字符串',
|
||||
userMgmtMagicStringModalDesc: '请输入用户「{{username}}」的 Subsonic 密码,它会包含在复制的魔法字符串中。',
|
||||
userMgmtMagicStringModalConfirm: '复制字符串',
|
||||
audiomuseTitle: 'AudioMuse-AI(Navidrome)',
|
||||
audiomuseDesc:
|
||||
'若此服务器已配置 <pluginLink>AudioMuse-AI Navidrome 插件</pluginLink>请开启。可从曲目启动即时混音,并在艺人页使用服务器返回的相似艺人,而非 Last.fm。',
|
||||
|
||||
Reference in New Issue
Block a user