src/services/onboardingAppCreate.ts
Rama main · réplica de lectura del repositorio original.
import type { ConsoleClient } from '~/services/consoleClient'
import type { Database } from '~/types/database.types'
import type { OnboardingAppDraft } from '~/utils/onboardingAppDraft'
import {
createOnboardingAppWithFallbackIds,
} from '~/utils/onboardingAppCreateHelpers'
import { isSafeImageFetchUrl } from '~/utils/urlSafety'
type AppRow = Database['public']['Tables']['apps']['Row']
interface CreateOnboardingAppResult {
app: AppRow
appIdFeedback?: string
}
async function uploadIconFromDraft(
capgo: ConsoleClient<Database>,
ownerOrgId: string,
appId: string,
draft: OnboardingAppDraft,
) {
const iconSource = draft.iconDataUrl || draft.storeIconDataUrl
if (!iconSource)
return
try {
let blob: Blob
if (iconSource.startsWith('data:')) {
const [header, payload = ''] = iconSource.split(',', 2)
const contentType = /^data:([^;]+)/.exec(header)?.[1] ?? 'image/png'
if (!payload)
return
const binary = atob(payload)
const bytes = Uint8Array.from(binary, char => char.codePointAt(0) ?? 0)
blob = new Blob([bytes], { type: contentType })
}
else {
if (!isSafeImageFetchUrl(iconSource))
return
const response = await fetch(iconSource)
const contentType = response.headers.get('content-type')?.split(';')[0]?.trim() ?? ''
if (!response.ok || !contentType.startsWith('image/'))
return
blob = await response.blob()
}
const iconPath = `org/${ownerOrgId}/${appId}/icon`
const { error: uploadError } = await capgo.storage
.from('images')
.upload(iconPath, blob, {
upsert: true,
contentType: blob.type || 'image/png',
})
if (uploadError) {
console.error('Cannot upload onboarding app icon', uploadError)
return
}
await capgo
.from('apps')
.update({ icon_url: iconPath })
.eq('app_id', appId)
}
catch (error) {
console.error('Cannot process onboarding app icon', error)
}
}
export async function createOnboardingAppFromDraft(
capgo: ConsoleClient<Database>,
ownerOrgId: string,
draft: OnboardingAppDraft,
orgName?: string,
): Promise<CreateOnboardingAppResult> {
const createResult = await createOnboardingAppWithFallbackIds(capgo, {
ownerOrgId,
baseAppId: draft.appId,
appName: draft.appName,
existingApp: draft.existingApp,
iosStoreUrl: draft.iosStoreUrl,
androidStoreUrl: draft.androidStoreUrl,
orgName,
})
if (createResult.ok === false) {
if (createResult.reason === 'all_conflicts')
throw new Error(`App ID ${createResult.originalAppId} is already used.`)
throw new Error(createResult.message)
}
const { app: responseData, usedAppId, originalAppId, wasRetried } = createResult
const appIdFeedback = wasRetried
? `App ID ${originalAppId} was already taken, so Capgo switched to ${usedAppId}.`
: undefined
await uploadIconFromDraft(capgo, ownerOrgId, responseData.app_id, draft)
const { data: refreshed } = await capgo
.from('apps')
.select()
.eq('app_id', responseData.app_id)
.single()
return { app: refreshed ?? responseData, appIdFeedback }
}