this repo has no description
1const API_BASE = '/xrpc'
2
3export class ApiError extends Error {
4 public did?: string
5 constructor(public status: number, public error: string, message: string, did?: string) {
6 super(message)
7 this.name = 'ApiError'
8 this.did = did
9 }
10}
11
12async function xrpc<T>(method: string, options?: {
13 method?: 'GET' | 'POST'
14 params?: Record<string, string>
15 body?: unknown
16 token?: string
17}): Promise<T> {
18 const { method: httpMethod = 'GET', params, body, token } = options ?? {}
19 let url = `${API_BASE}/${method}`
20 if (params) {
21 const searchParams = new URLSearchParams(params)
22 url += `?${searchParams}`
23 }
24 const headers: Record<string, string> = {}
25 if (token) {
26 headers['Authorization'] = `Bearer ${token}`
27 }
28 if (body) {
29 headers['Content-Type'] = 'application/json'
30 }
31 const res = await fetch(url, {
32 method: httpMethod,
33 headers,
34 body: body ? JSON.stringify(body) : undefined,
35 })
36 if (!res.ok) {
37 const err = await res.json().catch(() => ({ error: 'Unknown', message: res.statusText }))
38 throw new ApiError(res.status, err.error, err.message, err.did)
39 }
40 return res.json()
41}
42
43export interface Session {
44 did: string
45 handle: string
46 email?: string
47 emailConfirmed?: boolean
48 preferredChannel?: string
49 preferredChannelVerified?: boolean
50 isAdmin?: boolean
51 active?: boolean
52 status?: 'active' | 'deactivated'
53 accessJwt: string
54 refreshJwt: string
55}
56
57export interface AppPassword {
58 name: string
59 createdAt: string
60}
61
62export interface InviteCode {
63 code: string
64 available: number
65 disabled: boolean
66 forAccount: string
67 createdBy: string
68 createdAt: string
69 uses: { usedBy: string; usedAt: string }[]
70}
71
72export type VerificationChannel = 'email' | 'discord' | 'telegram' | 'signal'
73
74export interface CreateAccountParams {
75 handle: string
76 email: string
77 password: string
78 inviteCode?: string
79 verificationChannel?: VerificationChannel
80 discordId?: string
81 telegramUsername?: string
82 signalNumber?: string
83}
84
85export interface CreateAccountResult {
86 handle: string
87 did: string
88 verificationRequired: boolean
89 verificationChannel: string
90}
91
92export interface ConfirmSignupResult {
93 accessJwt: string
94 refreshJwt: string
95 handle: string
96 did: string
97 email?: string
98 emailConfirmed?: boolean
99 preferredChannel?: string
100 preferredChannelVerified?: boolean
101}
102
103export const api = {
104 async createAccount(params: CreateAccountParams): Promise<CreateAccountResult> {
105 return xrpc('com.atproto.server.createAccount', {
106 method: 'POST',
107 body: {
108 handle: params.handle,
109 email: params.email,
110 password: params.password,
111 inviteCode: params.inviteCode,
112 verificationChannel: params.verificationChannel,
113 discordId: params.discordId,
114 telegramUsername: params.telegramUsername,
115 signalNumber: params.signalNumber,
116 },
117 })
118 },
119
120 async confirmSignup(did: string, verificationCode: string): Promise<ConfirmSignupResult> {
121 return xrpc('com.atproto.server.confirmSignup', {
122 method: 'POST',
123 body: { did, verificationCode },
124 })
125 },
126
127 async resendVerification(did: string): Promise<{ success: boolean }> {
128 return xrpc('com.atproto.server.resendVerification', {
129 method: 'POST',
130 body: { did },
131 })
132 },
133
134 async createSession(identifier: string, password: string): Promise<Session> {
135 return xrpc('com.atproto.server.createSession', {
136 method: 'POST',
137 body: { identifier, password },
138 })
139 },
140
141 async getSession(token: string): Promise<Session> {
142 return xrpc('com.atproto.server.getSession', { token })
143 },
144
145 async refreshSession(refreshJwt: string): Promise<Session> {
146 return xrpc('com.atproto.server.refreshSession', {
147 method: 'POST',
148 token: refreshJwt,
149 })
150 },
151
152 async deleteSession(token: string): Promise<void> {
153 await xrpc('com.atproto.server.deleteSession', {
154 method: 'POST',
155 token,
156 })
157 },
158
159 async listAppPasswords(token: string): Promise<{ passwords: AppPassword[] }> {
160 return xrpc('com.atproto.server.listAppPasswords', { token })
161 },
162
163 async createAppPassword(token: string, name: string): Promise<{ name: string; password: string; createdAt: string }> {
164 return xrpc('com.atproto.server.createAppPassword', {
165 method: 'POST',
166 token,
167 body: { name },
168 })
169 },
170
171 async revokeAppPassword(token: string, name: string): Promise<void> {
172 await xrpc('com.atproto.server.revokeAppPassword', {
173 method: 'POST',
174 token,
175 body: { name },
176 })
177 },
178
179 async getAccountInviteCodes(token: string): Promise<{ codes: InviteCode[] }> {
180 return xrpc('com.atproto.server.getAccountInviteCodes', { token })
181 },
182
183 async createInviteCode(token: string, useCount: number = 1): Promise<{ code: string }> {
184 return xrpc('com.atproto.server.createInviteCode', {
185 method: 'POST',
186 token,
187 body: { useCount },
188 })
189 },
190
191 async requestPasswordReset(email: string): Promise<void> {
192 await xrpc('com.atproto.server.requestPasswordReset', {
193 method: 'POST',
194 body: { email },
195 })
196 },
197
198 async resetPassword(token: string, password: string): Promise<void> {
199 await xrpc('com.atproto.server.resetPassword', {
200 method: 'POST',
201 body: { token, password },
202 })
203 },
204
205 async requestEmailUpdate(token: string): Promise<{ tokenRequired: boolean }> {
206 return xrpc('com.atproto.server.requestEmailUpdate', {
207 method: 'POST',
208 token,
209 })
210 },
211
212 async updateEmail(token: string, email: string, emailToken?: string): Promise<void> {
213 await xrpc('com.atproto.server.updateEmail', {
214 method: 'POST',
215 token,
216 body: { email, token: emailToken },
217 })
218 },
219
220 async updateHandle(token: string, handle: string): Promise<void> {
221 await xrpc('com.atproto.identity.updateHandle', {
222 method: 'POST',
223 token,
224 body: { handle },
225 })
226 },
227
228 async requestAccountDelete(token: string): Promise<void> {
229 await xrpc('com.atproto.server.requestAccountDelete', {
230 method: 'POST',
231 token,
232 })
233 },
234
235 async deleteAccount(did: string, password: string, deleteToken: string): Promise<void> {
236 await xrpc('com.atproto.server.deleteAccount', {
237 method: 'POST',
238 body: { did, password, token: deleteToken },
239 })
240 },
241
242 async describeServer(): Promise<{
243 availableUserDomains: string[]
244 inviteCodeRequired: boolean
245 links?: { privacyPolicy?: string; termsOfService?: string }
246 }> {
247 return xrpc('com.atproto.server.describeServer')
248 },
249
250 async getNotificationPrefs(token: string): Promise<{
251 preferredChannel: string
252 email: string
253 discordId: string | null
254 discordVerified: boolean
255 telegramUsername: string | null
256 telegramVerified: boolean
257 signalNumber: string | null
258 signalVerified: boolean
259 }> {
260 return xrpc('com.tranquil.account.getNotificationPrefs', { token })
261 },
262
263 async updateNotificationPrefs(token: string, prefs: {
264 preferredChannel?: string
265 discordId?: string
266 telegramUsername?: string
267 signalNumber?: string
268 }): Promise<{ success: boolean }> {
269 return xrpc('com.tranquil.account.updateNotificationPrefs', {
270 method: 'POST',
271 token,
272 body: prefs,
273 })
274 },
275
276 async confirmChannelVerification(token: string, channel: string, code: string): Promise<{ success: boolean }> {
277 return xrpc('com.tranquil.account.confirmChannelVerification', {
278 method: 'POST',
279 token,
280 body: { channel, code },
281 })
282 },
283
284 async getNotificationHistory(token: string): Promise<{
285 notifications: Array<{
286 createdAt: string
287 channel: string
288 notificationType: string
289 status: string
290 subject: string | null
291 body: string
292 }>
293 }> {
294 return xrpc('com.tranquil.account.getNotificationHistory', { token })
295 },
296
297 async getServerStats(token: string): Promise<{
298 userCount: number
299 repoCount: number
300 recordCount: number
301 blobStorageBytes: number
302 }> {
303 return xrpc('com.tranquil.admin.getServerStats', { token })
304 },
305
306 async changePassword(token: string, currentPassword: string, newPassword: string): Promise<void> {
307 await xrpc('com.tranquil.account.changePassword', {
308 method: 'POST',
309 token,
310 body: { currentPassword, newPassword },
311 })
312 },
313
314 async listSessions(token: string): Promise<{
315 sessions: Array<{
316 id: string
317 createdAt: string
318 expiresAt: string
319 isCurrent: boolean
320 }>
321 }> {
322 return xrpc('com.tranquil.account.listSessions', { token })
323 },
324
325 async revokeSession(token: string, sessionId: string): Promise<void> {
326 await xrpc('com.tranquil.account.revokeSession', {
327 method: 'POST',
328 token,
329 body: { sessionId },
330 })
331 },
332
333 async searchAccounts(token: string, options?: {
334 handle?: string
335 cursor?: string
336 limit?: number
337 }): Promise<{
338 cursor?: string
339 accounts: Array<{
340 did: string
341 handle: string
342 email?: string
343 indexedAt: string
344 emailConfirmedAt?: string
345 deactivatedAt?: string
346 }>
347 }> {
348 const params: Record<string, string> = {}
349 if (options?.handle) params.handle = options.handle
350 if (options?.cursor) params.cursor = options.cursor
351 if (options?.limit) params.limit = String(options.limit)
352 return xrpc('com.atproto.admin.searchAccounts', { token, params })
353 },
354
355 async getInviteCodes(token: string, options?: {
356 sort?: 'recent' | 'usage'
357 cursor?: string
358 limit?: number
359 }): Promise<{
360 cursor?: string
361 codes: Array<{
362 code: string
363 available: number
364 disabled: boolean
365 forAccount: string
366 createdBy: string
367 createdAt: string
368 uses: Array<{ usedBy: string; usedAt: string }>
369 }>
370 }> {
371 const params: Record<string, string> = {}
372 if (options?.sort) params.sort = options.sort
373 if (options?.cursor) params.cursor = options.cursor
374 if (options?.limit) params.limit = String(options.limit)
375 return xrpc('com.atproto.admin.getInviteCodes', { token, params })
376 },
377
378 async disableInviteCodes(token: string, codes?: string[], accounts?: string[]): Promise<void> {
379 await xrpc('com.atproto.admin.disableInviteCodes', {
380 method: 'POST',
381 token,
382 body: { codes, accounts },
383 })
384 },
385
386 async getAccountInfo(token: string, did: string): Promise<{
387 did: string
388 handle: string
389 email?: string
390 indexedAt: string
391 emailConfirmedAt?: string
392 invitesDisabled?: boolean
393 deactivatedAt?: string
394 }> {
395 return xrpc('com.atproto.admin.getAccountInfo', { token, params: { did } })
396 },
397
398 async disableAccountInvites(token: string, account: string): Promise<void> {
399 await xrpc('com.atproto.admin.disableAccountInvites', {
400 method: 'POST',
401 token,
402 body: { account },
403 })
404 },
405
406 async enableAccountInvites(token: string, account: string): Promise<void> {
407 await xrpc('com.atproto.admin.enableAccountInvites', {
408 method: 'POST',
409 token,
410 body: { account },
411 })
412 },
413
414 async adminDeleteAccount(token: string, did: string): Promise<void> {
415 await xrpc('com.atproto.admin.deleteAccount', {
416 method: 'POST',
417 token,
418 body: { did },
419 })
420 },
421
422 async describeRepo(token: string, repo: string): Promise<{
423 handle: string
424 did: string
425 didDoc: unknown
426 collections: string[]
427 handleIsCorrect: boolean
428 }> {
429 return xrpc('com.atproto.repo.describeRepo', {
430 token,
431 params: { repo },
432 })
433 },
434
435 async listRecords(token: string, repo: string, collection: string, options?: {
436 limit?: number
437 cursor?: string
438 reverse?: boolean
439 }): Promise<{
440 records: Array<{ uri: string; cid: string; value: unknown }>
441 cursor?: string
442 }> {
443 const params: Record<string, string> = { repo, collection }
444 if (options?.limit) params.limit = String(options.limit)
445 if (options?.cursor) params.cursor = options.cursor
446 if (options?.reverse) params.reverse = 'true'
447 return xrpc('com.atproto.repo.listRecords', { token, params })
448 },
449
450 async getRecord(token: string, repo: string, collection: string, rkey: string): Promise<{
451 uri: string
452 cid: string
453 value: unknown
454 }> {
455 return xrpc('com.atproto.repo.getRecord', {
456 token,
457 params: { repo, collection, rkey },
458 })
459 },
460
461 async createRecord(token: string, repo: string, collection: string, record: unknown, rkey?: string): Promise<{
462 uri: string
463 cid: string
464 }> {
465 return xrpc('com.atproto.repo.createRecord', {
466 method: 'POST',
467 token,
468 body: { repo, collection, record, rkey },
469 })
470 },
471
472 async putRecord(token: string, repo: string, collection: string, rkey: string, record: unknown): Promise<{
473 uri: string
474 cid: string
475 }> {
476 return xrpc('com.atproto.repo.putRecord', {
477 method: 'POST',
478 token,
479 body: { repo, collection, rkey, record },
480 })
481 },
482
483 async deleteRecord(token: string, repo: string, collection: string, rkey: string): Promise<void> {
484 await xrpc('com.atproto.repo.deleteRecord', {
485 method: 'POST',
486 token,
487 body: { repo, collection, rkey },
488 })
489 },
490}