auth.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import type { BootstrapResponse, CurrentUser, JsonValue, Profile } from '@shared/types'
  2. import { apiRequest, setApiCsrfToken, unwrapData } from './client'
  3. import { bool, normalizeProfile, normalizeUser, record, string, values } from '@/utils/normalize'
  4. import { getProfileIdentities } from './profiles'
  5. function normalizeBootstrap(payload: unknown): BootstrapResponse {
  6. const root = record(unwrapData(payload as never))
  7. const status = record(root.status)
  8. const authRequired = bool(root.authRequired ?? root.auth_required ?? status.authRequired ?? status.auth_required)
  9. const rawUser = root.user ?? root.identity
  10. const profiles = values(root.profiles).map(normalizeProfile)
  11. const csrfToken = string(root.csrfToken ?? root.csrf_token ?? root.csrf)
  12. const response: BootstrapResponse = {
  13. status: string(status.state ?? root.state) || undefined,
  14. authRequired,
  15. profiles,
  16. csrfToken,
  17. insecureLan: bool(root.insecureLan ?? root.insecure_lan),
  18. groupUploadsEnabled: bool(root.groupUploadsEnabled ?? root.group_uploads_enabled),
  19. upstreamReady: bool(root.upstreamReady ?? root.upstream_ready),
  20. upstreamError: string(root.upstreamError ?? root.upstream_error) || undefined,
  21. serverKind: string(root.serverKind ?? root.server_kind) || undefined,
  22. }
  23. if (rawUser) response.user = normalizeUser(rawUser)
  24. setApiCsrfToken(csrfToken)
  25. return response
  26. }
  27. export async function bootstrap(): Promise<BootstrapResponse> {
  28. return normalizeBootstrap(await apiRequest<unknown>('/api/app/bootstrap', { csrf: false }))
  29. }
  30. export interface LoginInput {
  31. username: string
  32. password: string
  33. provider?: string
  34. }
  35. export async function login(input: LoginInput): Promise<BootstrapResponse> {
  36. const payload = await apiRequest<unknown>('/api/app/login', {
  37. method: 'POST',
  38. body: { ...input, next: '/' } as JsonValue,
  39. notifyUnauthorized: false,
  40. })
  41. const response = normalizeBootstrap(payload)
  42. if (!response.user && response.authRequired) return bootstrap()
  43. return response
  44. }
  45. export async function logout(): Promise<void> {
  46. await apiRequest('/api/app/logout', { method: 'POST', body: {}, notifyUnauthorized: false })
  47. setApiCsrfToken('')
  48. }
  49. export async function fetchProfiles(): Promise<Profile[]> {
  50. const payload = record(unwrapData(await apiRequest<unknown>('/api/app/profiles')))
  51. return values(payload.profiles ?? payload.items ?? payload).map(normalizeProfile)
  52. }
  53. export async function fetchProfileIdentities(profiles: Profile[]): Promise<Record<string, { displayName: string; avatar: string }>> {
  54. return getProfileIdentities(profiles)
  55. }
  56. export async function fetchCurrentUser(): Promise<CurrentUser | undefined> {
  57. const response = await bootstrap()
  58. return response.user
  59. }
  60. export async function changeCredentials(input: {
  61. currentPassword: string
  62. newPassword: string
  63. username?: string
  64. }): Promise<CurrentUser> {
  65. const payload = record(unwrapData(await apiRequest<unknown>('/api/app/account/credentials', {
  66. method: 'PUT', body: input as unknown as JsonValue, notifyUnauthorized: false,
  67. })))
  68. return normalizeUser(payload.user ?? payload)
  69. }