Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 2 additions & 67 deletions cli/src/api/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { SupabaseClient } from '@supabase/supabase-js'
import type { Database } from '../types/supabase.types'
import { confirm as confirmC, intro, log, outro, spinner } from '@clack/prompts'
import { Table } from '@sauber/table'
import { formatError, getOrganizationId } from '../utils'
import { formatError } from '../utils'

interface CheckVersionOptions {
silent?: boolean
Expand Down Expand Up @@ -66,14 +66,9 @@ export async function checkVersionNotUsedInChannel(
const s = silent ? null : spinner()
s?.start(`Unlinking channel ${channel.name}`)

const unknownVersion = await findUnknownVersion(supabase, appid, { silent })
if (!unknownVersion) {
s?.stop(`Cannot find unknown version for ${appid}`)
throw new Error(`Cannot find unknown version for ${appid}`)
}
const { error: errorChannelUpdate } = await supabase
.from('channels')
.update({ version: unknownVersion.id })
.update({ version: null })
.eq('id', channel.id)

if (errorChannelUpdate) {
Expand All @@ -88,66 +83,6 @@ export async function checkVersionNotUsedInChannel(
outro(`Version unlinked from ${channelFound.length} channel${channelFound.length > 1 ? 's' : ''}`)
}

interface FindUnknownOptions {
silent?: boolean
}

export async function findUnknownVersion(
supabase: SupabaseClient<Database>,
appId: string,
options: FindUnknownOptions = {},
) {
const { silent = false } = options

// Try to find existing unknown version
const { data, error: findError } = await supabase
.from('app_versions')
.select('id')
.eq('app_id', appId)
.eq('name', 'unknown')
.maybeSingle()

if (findError) {
if (!silent)
log.error(`Cannot find unknown version for ${appId}: ${formatError(findError)}`)
throw new Error(`Cannot find unknown version for app ${appId}: ${formatError(findError)}`)
}

if (data) {
return data
}

// Not found - create or reuse the synthetic placeholder version safely.
try {
const orgId = await getOrganizationId(supabase, appId)
const { data: newVersion, error: createError } = await supabase
.from('app_versions')
.upsert({
owner_org: orgId,
deleted: true,
name: 'unknown',
app_id: appId,
})
.select('id')
.eq('app_id', appId)
.eq('name', 'unknown')
.single()

if (createError) {
if (!silent)
log.error(`Cannot create unknown version for ${appId}: ${formatError(createError)}`)
throw new Error(`Cannot find or create unknown version for app ${appId}: ${formatError(createError)}`)
}

return newVersion
}
catch (createErr) {
if (!silent)
log.error(`Cannot create unknown version for ${appId}: ${formatError(createErr)}`)
throw new Error(`Cannot retrieve or create unknown version for app ${appId}: ${formatError(createErr)}`)
}
}

export function createChannel(
supabase: SupabaseClient<Database>,
update: Database['public']['Tables']['channels']['Insert'],
Expand Down
11 changes: 2 additions & 9 deletions cli/src/channel/add.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ChannelAddOptions } from '../schemas/channel'
import { intro, log, outro } from '@clack/prompts'
import { check2FAComplianceForApp, checkAppExistsAndHasPermissionOrgErr } from '../api/app'
import { createChannel, findUnknownVersion } from '../api/channels'
import { createChannel } from '../api/channels'
import {
createSupabaseClient,
findSavedKey,
Expand Down Expand Up @@ -42,20 +42,13 @@ export async function addChannelInternal(channelId: string, appId: string, optio
if (!silent)
log.info(`Creating channel ${appId}#${channelId} to Capgo`)

const data = await findUnknownVersion(supabase, appId, { silent })
if (!data) {
if (!silent)
log.error('Cannot find default version for channel creation, please contact Capgo support 🤨')
throw new Error('Cannot find default version for channel creation')
}

const orgId = await getOrganizationId(supabase, appId)
const userId = await resolveUserIdFromApiKey(supabase, options.apikey)

const res = await createChannel(supabase, {
name: channelId,
app_id: appId,
version: data.id,
version: null,
created_by: userId,
owner_org: orgId,
allow_device_self_set: options.selfAssign ?? false,
Expand Down
8 changes: 4 additions & 4 deletions cli/src/types/supabase.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ export type Database = {
public: boolean
rbac_id: string
updated_at: string
version: number
version: number | null
}
Insert: {
allow_dev?: boolean
Expand All @@ -737,7 +737,7 @@ export type Database = {
public?: boolean
rbac_id?: string
updated_at?: string
version: number
version?: number | null
}
Update: {
allow_dev?: boolean
Expand All @@ -759,7 +759,7 @@ export type Database = {
public?: boolean
rbac_id?: string
updated_at?: string
version?: number
version?: number | null
}
Relationships: [
{
Expand Down Expand Up @@ -2993,7 +2993,7 @@ export type Database = {
}
check_revert_to_builtin_version: {
Args: { appid: string }
Returns: number
Returns: number | null
}
cleanup_expired_apikeys: { Args: never; Returns: undefined }
cleanup_expired_demo_apps: { Args: never; Returns: undefined }
Expand Down
2 changes: 1 addition & 1 deletion cli/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1998,7 +1998,7 @@ export async function getRemoteDependencies(supabase: SupabaseClient<Database>,
log.error(`Error fetching native packages: ${error.message}`)
throw new Error(`Error fetching native packages: ${error.message}`)
}
return convertNativePackages((remoteNativePackages.version.native_packages as any) ?? [])
return convertNativePackages(((remoteNativePackages.version as any)?.native_packages as any) ?? [])
}

export async function checkChecksum(supabase: SupabaseClient<Database>, appId: string, channel: string, currentChecksum: string) {
Expand Down
1 change: 1 addition & 0 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@
"channel": "Channel",
"channel-actions": "channel actions",
"channel-allow-device-self-set": "Allow Device Self-Assignment",
"channel-builtin": "builtin",
"channel-bundle-linked": "This bundle is linked to one or more channels. ({channels}) Would you like to unlink it from those channels?",
"channel-create": "Create channel",
"channel-created": "Channel Created",
Expand Down
2 changes: 1 addition & 1 deletion scripts/fix_app_stats_day_1.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// list all apps in supabase and create version unknown for each
// Backfill first-day app statistics from stored app versions.
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = 'https://sb.capgo.app'
Expand Down
75 changes: 0 additions & 75 deletions scripts/fix_app_versions.mjs

This file was deleted.

2 changes: 1 addition & 1 deletion scripts/fix_app_versions_meta.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// list all apps in supabase and create version unknown for each
// Backfill metadata size for deleted app versions.
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = 'https://***.supabase.co'
Expand Down
2 changes: 1 addition & 1 deletion scripts/fix_app_versions_trigger.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// list all apps in supabase and create version unknown for each
// Backfill app_stats device counts for recently active apps.
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = 'https://***.supabase.co'
Expand Down
25 changes: 3 additions & 22 deletions src/components/tables/BundleTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -295,32 +295,13 @@ async function refreshData() {
}
}

async function unlinkChannels(app_id: string, unlink: LinkedChannel[]) {
async function unlinkChannels(_appId: string, unlink: LinkedChannel[]) {
if (unlink.length === 0) {
return
}
const { data: unknownVersion, error: unknownError } = await supabase
.from('app_versions')
.select()
.eq('app_id', app_id)
.eq('name', 'unknown')
.single()

if (unknownError) {
toast.error(t('cannot-find-unknown-version'))
console.error('Cannot find unknown', JSON.stringify(unknownError))
return Promise.reject(new Error('Cannot find unknown'))
}

if (!unknownVersion?.id || typeof unknownVersion.id !== 'number') {
toast.error(t('error-invalid-version'))
console.error('Invalid unknown version ID:', unknownVersion)
return Promise.reject(new Error('Invalid unknown version ID'))
}

const { error: updateError } = await supabase
.from('channels')
.update({ version: unknownVersion.id })
.update({ version: null })
.in('id', unlink.map(c => c.id))

if (updateError) {
Expand Down Expand Up @@ -535,7 +516,7 @@ async function massDelete() {
}

const channelsList = linkedChannelsList
.map(val => val.rawChannel?.map((ch: any) => `${ch.name} (${ch.version.name})`).join(', '))
.map(val => val.rawChannel?.map((ch: any) => `${ch.name} (${ch.version?.name ?? t('channel-builtin')})`).join(', '))
.join(', ')
const message = t('channel-bundle-linked', { channels: channelsList })
const shouldUnlink = await showUnlinkDialog(message)
Expand Down
28 changes: 9 additions & 19 deletions src/components/tables/ChannelTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ interface Channel {
name: string
created_at: string
min_update_version: string | null
}
} | null
misconfigured: boolean | undefined
}
type Element = Database['public']['Tables']['channels']['Row'] & Channel
Expand All @@ -47,7 +47,7 @@ const search = ref('')
const elements = ref<(Element)[]>([])
const isLoading = ref(true)
const currentPage = ref(1)
const versionId = ref<number>()
const versionId = ref<number | null>(null)
const filters = ref()
const newChannelName = ref('')
const canPromoteChannel = ref<Record<number, boolean>>({})
Expand All @@ -70,19 +70,8 @@ const currentVersionsNumber = computed(() => {
})
const { currentOrganization } = storeToRefs(organizationStore)

function findUnknownVersion() {
return supabase
.from('app_versions')
.select('id')
.eq('app_id', props.appId)
.eq('name', 'unknown')
.throwOnError()
.single()
.then(({ data }) => data?.id)
}

async function addChannel(name: string) {
if (!name || !versionId.value || !main.user)
if (!name || !main.user)
return
try {
console.log('addChannel', name, versionId.value, main.user)
Expand All @@ -96,7 +85,7 @@ async function addChannel(name: string) {
{
name,
app_id: props.appId,
version: versionId.value as number,
version: versionId.value,
owner_org: currentGid as string,
created_by: main.user?.id,
},
Expand Down Expand Up @@ -164,15 +153,15 @@ async function getData() {
.map(e => e as any as Element)

for (const channel of channels) {
if (channel.version.min_update_version === null) {
if (channel.version && channel.version.min_update_version === null) {
channel.misconfigured = true
anyMisconfigured = true
}
}

// Inform the parent component if there are any misconfigured channels
emit('misconfigured', anyMisconfigured)
versionId.value = await findUnknownVersion()
versionId.value = null
await loadChannelPermissions(elements.value)
}
catch (error) {
Expand Down Expand Up @@ -309,7 +298,7 @@ columns.value = [
key: 'version',
mobile: true,
sortable: true,
displayFunction: (elem: Element) => elem.version.name,
displayFunction: (elem: Element) => elem.version?.name ?? t('channel-builtin'),
onClick: (elem: Element) => openOneVersion(elem),
},
{
Expand Down Expand Up @@ -382,7 +371,8 @@ async function showAddModal() {
}

async function openOneVersion(one: Element) {
router.push(`/app/${props.appId}/bundle/${one.version?.id}`)
if (one.version?.id)
router.push(`/app/${props.appId}/bundle/${one.version.id}`)
}

async function openOne(one: Element) {
Expand Down
Loading
Loading