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:
@@ -0,0 +1,36 @@
|
||||
import type { ServerProfile } from '../store/authStore';
|
||||
|
||||
/** Host (+ port) from a server base URL, e.g. `https://music.one.com/foo` → `music.one.com`. */
|
||||
export function shortHostFromServerUrl(urlRaw: string): string {
|
||||
const t = urlRaw.trim();
|
||||
if (!t) return '';
|
||||
try {
|
||||
const u = new URL(t.includes('://') ? t : `https://${t}`);
|
||||
return u.host;
|
||||
} catch {
|
||||
return t
|
||||
.replace(/^https?:\/\//i, '')
|
||||
.split('/')[0]
|
||||
?.split('?')[0]
|
||||
?.trim() ?? t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Label for server lists and chrome: if several servers share the same effective name,
|
||||
* show `username@host` so entries stay distinguishable.
|
||||
*/
|
||||
export function serverListDisplayLabel(server: ServerProfile, all: ServerProfile[]): string {
|
||||
const nameTrim = (server.name || '').trim();
|
||||
const shortHost = shortHostFromServerUrl(server.url);
|
||||
const key = nameTrim || shortHost;
|
||||
const collisions = all.filter(s => {
|
||||
const nt = (s.name || '').trim();
|
||||
const sh = shortHostFromServerUrl(s.url);
|
||||
return (nt || sh) === key;
|
||||
});
|
||||
if (collisions.length < 2) {
|
||||
return nameTrim || shortHost || server.url.trim();
|
||||
}
|
||||
return `${server.username}@${shortHost}`;
|
||||
}
|
||||
Reference in New Issue
Block a user