Ctrl K
ComunidadGuías…

src/services/onboardingAppCreate.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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 }
}