A website for the ATmosphereConf
at init 3.6 kB view raw
1import type { APIRoute } from 'astro' 2import { getOAuthClient } from '../../../lib/context' 3import { getSession } from '../../../lib/session' 4import { Agent, BlobRef } from '@atproto/api' 5import type { Main as ProfileRecord } from '../../../lexicon/types/org/atmosphereconf/profile' 6 7async function fileToBlob(agent: Agent, file: File): Promise<BlobRef> { 8 const arrayBuffer = await file.arrayBuffer() 9 const uint8Array = new Uint8Array(arrayBuffer) 10 11 const response = await agent.com.atproto.repo.uploadBlob(uint8Array, { 12 encoding: file.type, 13 }) 14 15 return response.data.blob 16} 17 18export const POST: APIRoute = async ({ request, cookies, redirect }) => { 19 try { 20 const session = getSession(cookies) 21 const oauthClient = getOAuthClient(cookies) 22 23 if (!session.did) { 24 return new Response('Unauthorized', { status: 401 }) 25 } 26 27 const oauthSession = await oauthClient.restore(session.did) 28 if (!oauthSession) { 29 return new Response('Session expired', { status: 401 }) 30 } 31 32 const agent = new Agent(oauthSession) 33 const formData = await request.formData() 34 35 // Extract form data 36 const displayName = formData.get('displayName') 37 const description = formData.get('description') 38 const avatarFile = formData.get('avatar') 39 const bannerFile = formData.get('banner') 40 41 if (!displayName || typeof displayName !== 'string') { 42 return new Response('Display name is required', { status: 400 }) 43 } 44 45 // Validate file sizes 46 if (avatarFile instanceof File && avatarFile.size > 0 && avatarFile.size > 1000000) { 47 return new Response('Avatar file size must be less than 1MB', { status: 400 }) 48 } 49 50 if (bannerFile instanceof File && bannerFile.size > 0 && bannerFile.size > 1000000) { 51 return new Response('Banner file size must be less than 1MB', { status: 400 }) 52 } 53 54 // Build the profile record 55 const record: Omit<ProfileRecord, '$type'> = { 56 displayName: displayName.slice(0, 64), 57 description: typeof description === 'string' ? description.slice(0, 256) : undefined, 58 createdAt: new Date().toISOString(), 59 } 60 61 // Upload avatar if provided 62 if (avatarFile instanceof File && avatarFile.size > 0) { 63 try { 64 record.avatar = await fileToBlob(agent, avatarFile) 65 } catch (err) { 66 console.error('Failed to upload avatar:', err) 67 return new Response('Failed to upload avatar', { status: 500 }) 68 } 69 } 70 71 // Upload banner if provided 72 if (bannerFile instanceof File && bannerFile.size > 0) { 73 try { 74 record.banner = await fileToBlob(agent, bannerFile) 75 } catch (err) { 76 console.error('Failed to upload banner:', err) 77 return new Response('Failed to upload banner', { status: 500 }) 78 } 79 } 80 81 // Create or update the profile record 82 try { 83 await agent.com.atproto.repo.putRecord({ 84 repo: agent.assertDid, 85 collection: 'org.atmosphereconf.profile', 86 rkey: 'self', 87 record: { 88 $type: 'org.atmosphereconf.profile', 89 ...record, 90 }, 91 }) 92 93 return redirect('/') 94 } catch (err) { 95 console.error('Failed to create profile:', err) 96 const error = err instanceof Error ? err.message : 'unexpected error' 97 return new Response(`Failed to create profile: ${error}`, { status: 500 }) 98 } 99 } catch (err) { 100 console.error('Profile creation failed:', err) 101 const error = err instanceof Error ? err.message : 'unexpected error' 102 return new Response(`Profile creation failed: ${error}`, { status: 500 }) 103 } 104}