72f46a885b
Lands four of the U3 frontend surfaces. Splits T5 because the dispatch covering everything in one shot crashed the runtime mid-execution; this is the salvageable half. - /settings gains three cards alongside Appearance: Profile (display name + email), Password (current + new + confirm with client-side mismatch check), API Token (display + copy + regenerate-with-double-click-confirm). Server error codes map to clear toasts. - /forgot-password — public; takes an email and always shows the success message regardless of whether the email is on file (mirrors the server's no-enumeration posture). - Login page gets a "Forgot password?" link below the existing register link. - API client functions for the four /me endpoints (changePassword, updateProfile, getAPIToken, regenerateAPIToken), the SMTP admin trio (getSMTPConfig, updateSMTPConfig, testSMTPConfig), and the forgot/reset auth pair (forgotPassword, resetPassword). Tests for these surfaces + /reset-password page + admin SMTP card land in T5b (next follow-up).
56 lines
1.7 KiB
TypeScript
56 lines
1.7 KiB
TypeScript
import { createQuery } from '@tanstack/svelte-query';
|
|
import { api } from './client';
|
|
import { qk } from './queries';
|
|
|
|
export type SystemPlaylistsStatus = {
|
|
in_flight: boolean;
|
|
last_run_at: string | null;
|
|
last_error: string | null;
|
|
};
|
|
|
|
export async function getSystemPlaylistsStatus(): Promise<SystemPlaylistsStatus> {
|
|
return api.get<SystemPlaylistsStatus>('/api/me/system-playlists-status');
|
|
}
|
|
|
|
// Refresh aggressively while a build is in_flight so the building → built
|
|
// transition appears live on the home page. Stale-time is short; manual
|
|
// invalidations on the server's POST endpoints are out of scope.
|
|
export function createSystemPlaylistsStatusQuery() {
|
|
return createQuery({
|
|
queryKey: qk.systemPlaylistsStatus(),
|
|
queryFn: getSystemPlaylistsStatus,
|
|
staleTime: 10_000
|
|
});
|
|
}
|
|
|
|
// Self-service profile / password / API-token endpoints (U3) ---------------
|
|
|
|
export type MyProfile = {
|
|
id: string;
|
|
username: string;
|
|
display_name: string | null;
|
|
email: string | null;
|
|
is_admin: boolean;
|
|
};
|
|
|
|
export type APITokenResponse = { api_token: string };
|
|
|
|
export async function changePassword(currentPassword: string, newPassword: string): Promise<void> {
|
|
await api.put('/api/me/password', {
|
|
current_password: currentPassword,
|
|
new_password: newPassword,
|
|
});
|
|
}
|
|
|
|
export async function updateProfile(input: { display_name?: string; email?: string }): Promise<MyProfile> {
|
|
return api.put<MyProfile>('/api/me/profile', input);
|
|
}
|
|
|
|
export async function getAPIToken(): Promise<APITokenResponse> {
|
|
return api.get<APITokenResponse>('/api/me/api-token');
|
|
}
|
|
|
|
export async function regenerateAPIToken(): Promise<APITokenResponse> {
|
|
return api.post<APITokenResponse>('/api/me/api-token', {});
|
|
}
|