this repo has no description
1import { api, type Session, type CreateAccountParams, type CreateAccountResult, ApiError } from './api'
2
3const STORAGE_KEY = 'bspds_session'
4
5interface AuthState {
6 session: Session | null
7 loading: boolean
8 error: string | null
9}
10
11let state = $state<AuthState>({
12 session: null,
13 loading: true,
14 error: null,
15})
16
17function saveSession(session: Session | null) {
18 if (session) {
19 localStorage.setItem(STORAGE_KEY, JSON.stringify(session))
20 } else {
21 localStorage.removeItem(STORAGE_KEY)
22 }
23}
24
25function loadSession(): Session | null {
26 const stored = localStorage.getItem(STORAGE_KEY)
27 if (stored) {
28 try {
29 return JSON.parse(stored)
30 } catch {
31 return null
32 }
33 }
34 return null
35}
36
37export async function initAuth() {
38 state.loading = true
39 state.error = null
40
41 const stored = loadSession()
42 if (stored) {
43 try {
44 const session = await api.getSession(stored.accessJwt)
45 state.session = { ...session, accessJwt: stored.accessJwt, refreshJwt: stored.refreshJwt }
46 } catch (e) {
47 if (e instanceof ApiError && e.status === 401) {
48 try {
49 const refreshed = await api.refreshSession(stored.refreshJwt)
50 state.session = refreshed
51 saveSession(refreshed)
52 } catch {
53 saveSession(null)
54 state.session = null
55 }
56 } else {
57 saveSession(null)
58 state.session = null
59 }
60 }
61 }
62
63 state.loading = false
64}
65
66export async function login(identifier: string, password: string): Promise<void> {
67 state.loading = true
68 state.error = null
69
70 try {
71 const session = await api.createSession(identifier, password)
72 state.session = session
73 saveSession(session)
74 } catch (e) {
75 if (e instanceof ApiError) {
76 state.error = e.message
77 } else {
78 state.error = 'Login failed'
79 }
80 throw e
81 } finally {
82 state.loading = false
83 }
84}
85
86export async function register(params: CreateAccountParams): Promise<CreateAccountResult> {
87 try {
88 const result = await api.createAccount(params)
89 return result
90 } catch (e) {
91 if (e instanceof ApiError) {
92 state.error = e.message
93 } else {
94 state.error = 'Registration failed'
95 }
96 throw e
97 }
98}
99
100export async function confirmSignup(did: string, verificationCode: string): Promise<void> {
101 state.loading = true
102 state.error = null
103
104 try {
105 const result = await api.confirmSignup(did, verificationCode)
106 const session: Session = {
107 did: result.did,
108 handle: result.handle,
109 accessJwt: result.accessJwt,
110 refreshJwt: result.refreshJwt,
111 }
112 state.session = session
113 saveSession(session)
114 } catch (e) {
115 if (e instanceof ApiError) {
116 state.error = e.message
117 } else {
118 state.error = 'Verification failed'
119 }
120 throw e
121 } finally {
122 state.loading = false
123 }
124}
125
126export async function resendVerification(did: string): Promise<void> {
127 try {
128 await api.resendVerification(did)
129 } catch (e) {
130 if (e instanceof ApiError) {
131 throw e
132 }
133 throw new Error('Failed to resend verification code')
134 }
135}
136
137export async function logout(): Promise<void> {
138 if (state.session) {
139 try {
140 await api.deleteSession(state.session.accessJwt)
141 } catch {
142 // Ignore errors on logout
143 }
144 }
145 state.session = null
146 saveSession(null)
147}
148
149export function getAuthState() {
150 return state
151}
152
153export function getToken(): string | null {
154 return state.session?.accessJwt ?? null
155}
156
157export function isAuthenticated(): boolean {
158 return state.session !== null
159}
160
161export function _testSetState(newState: { session: Session | null; loading: boolean; error: string | null }) {
162 state.session = newState.session
163 state.loading = newState.loading
164 state.error = newState.error
165}
166
167export function _testReset() {
168 state.session = null
169 state.loading = true
170 state.error = null
171 localStorage.removeItem(STORAGE_KEY)
172}