@capgo/cli 4.13.2 → 4.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/.github/FUNDING.yml +0 -1
- package/.github/workflows/autofix.yml +0 -25
- package/.github/workflows/build.yml +0 -46
- package/.github/workflows/bump_version.yml +0 -56
- package/.github/workflows/check_posix_paths.yml +0 -229
- package/.github/workflows/test.yml +0 -30
- package/.prettierignore +0 -6
- package/.vscode/launch.json +0 -23
- package/.vscode/settings.json +0 -46
- package/.vscode/tasks.json +0 -42
- package/CHANGELOG.md +0 -3392
- package/build.mjs +0 -21
- package/bun.lockb +0 -0
- package/bunfig.toml +0 -2
- package/capacitor.config.ts +0 -33
- package/crypto_explained.png +0 -0
- package/eslint.config.js +0 -10
- package/renovate.json +0 -23
- package/src/api/app.ts +0 -55
- package/src/api/channels.ts +0 -163
- package/src/api/crypto.ts +0 -116
- package/src/api/devices_override.ts +0 -41
- package/src/api/update.ts +0 -13
- package/src/api/versions.ts +0 -101
- package/src/app/add.ts +0 -157
- package/src/app/debug.ts +0 -258
- package/src/app/delete.ts +0 -110
- package/src/app/info.ts +0 -99
- package/src/app/list.ts +0 -67
- package/src/app/set.ts +0 -96
- package/src/bundle/check.ts +0 -42
- package/src/bundle/cleanup.ts +0 -123
- package/src/bundle/compatibility.ts +0 -70
- package/src/bundle/decrypt.ts +0 -54
- package/src/bundle/delete.ts +0 -52
- package/src/bundle/encrypt.ts +0 -60
- package/src/bundle/list.ts +0 -42
- package/src/bundle/unlink.ts +0 -88
- package/src/bundle/upload.ts +0 -552
- package/src/bundle/zip.ts +0 -145
- package/src/channel/add.ts +0 -80
- package/src/channel/currentBundle.ts +0 -72
- package/src/channel/delete.ts +0 -57
- package/src/channel/list.ts +0 -49
- package/src/channel/set.ts +0 -179
- package/src/config/index.ts +0 -156
- package/src/index.ts +0 -310
- package/src/init.ts +0 -495
- package/src/key.ts +0 -135
- package/src/login.ts +0 -70
- package/src/types/capacitor__cli.d.ts +0 -6
- package/src/types/supabase.types.ts +0 -2123
- package/src/user/account.ts +0 -11
- package/src/utils.ts +0 -1076
- package/test/VerifyZip.java +0 -83
- package/test/check-posix-paths.js +0 -21
- package/test/chunk_convert.ts +0 -28
- package/test/data.ts +0 -18769
- package/test/test_headers_rls.ts +0 -24
- package/test/test_semver.ts +0 -13
- package/test/test_upload/app.js +0 -3
- package/test/test_upload/assets/check-posix-paths.js +0 -21
- package/test/test_upload/index.html +0 -0
- package/test/test_zip_swift/Package.resolved +0 -24
- package/test/test_zip_swift/Package.swift +0 -29
- package/test/test_zip_swift/Sources/main.swift +0 -80
- package/tsconfig.json +0 -39
package/src/app/add.ts
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
-
import { exit } from 'node:process'
|
|
3
|
-
import mime from 'mime'
|
|
4
|
-
import { program } from 'commander'
|
|
5
|
-
import { intro, log, outro } from '@clack/prompts'
|
|
6
|
-
import { checkLatest } from '../api/update'
|
|
7
|
-
import type { Options } from '../api/app'
|
|
8
|
-
import { checkAppExists, newIconPath } from '../api/app'
|
|
9
|
-
import type {
|
|
10
|
-
Organization,
|
|
11
|
-
} from '../utils'
|
|
12
|
-
import {
|
|
13
|
-
checkPlanValid,
|
|
14
|
-
createSupabaseClient,
|
|
15
|
-
findSavedKey,
|
|
16
|
-
formatError,
|
|
17
|
-
getConfig,
|
|
18
|
-
getOrganization,
|
|
19
|
-
verifyUser,
|
|
20
|
-
} from '../utils'
|
|
21
|
-
|
|
22
|
-
export async function addApp(appId: string, options: Options, throwErr = true) {
|
|
23
|
-
await addAppInternal(appId, options, undefined, throwErr)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export async function addAppInternal(appId: string, options: Options, organization?: Organization, throwErr = true) {
|
|
27
|
-
if (throwErr)
|
|
28
|
-
intro(`Adding`)
|
|
29
|
-
|
|
30
|
-
await checkLatest()
|
|
31
|
-
options.apikey = options.apikey || findSavedKey()
|
|
32
|
-
const extConfig = await getConfig()
|
|
33
|
-
appId = appId || extConfig?.config?.appId
|
|
34
|
-
|
|
35
|
-
if (!options.apikey) {
|
|
36
|
-
log.error(`Missing API key, you need to provide a API key to upload your bundle`)
|
|
37
|
-
program.error('')
|
|
38
|
-
}
|
|
39
|
-
if (!appId) {
|
|
40
|
-
log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
|
|
41
|
-
program.error('')
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (appId.includes('--')) {
|
|
45
|
-
log.error('The app id includes illegal symbols. You cannot use "--" in the app id')
|
|
46
|
-
program.error('')
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const supabase = await createSupabaseClient(options.apikey)
|
|
50
|
-
|
|
51
|
-
await verifyUser(supabase, options.apikey, ['write', 'all'])
|
|
52
|
-
|
|
53
|
-
// Check we have app access to this appId
|
|
54
|
-
const appExist = await checkAppExists(supabase, appId)
|
|
55
|
-
if (appExist) {
|
|
56
|
-
log.error(`App ${appId} already exist`)
|
|
57
|
-
program.error('')
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (!organization)
|
|
61
|
-
organization = await getOrganization(supabase, ['admin', 'super_admin'])
|
|
62
|
-
|
|
63
|
-
const organizationUid = organization.gid
|
|
64
|
-
|
|
65
|
-
await checkPlanValid(supabase, organizationUid, options.apikey, undefined, false)
|
|
66
|
-
|
|
67
|
-
let { name, icon } = options
|
|
68
|
-
name = name || extConfig.config?.appName || 'Unknown'
|
|
69
|
-
icon = icon || 'resources/icon.png' // default path for capacitor app
|
|
70
|
-
if (!icon || !name) {
|
|
71
|
-
log.error('Missing argument, you need to provide a appId and a name, or be in a capacitor project')
|
|
72
|
-
program.error('')
|
|
73
|
-
}
|
|
74
|
-
if (throwErr)
|
|
75
|
-
log.info(`Adding ${appId} to Capgo`)
|
|
76
|
-
|
|
77
|
-
let iconBuff
|
|
78
|
-
let iconType
|
|
79
|
-
|
|
80
|
-
if (icon && existsSync(icon)) {
|
|
81
|
-
iconBuff = readFileSync(icon)
|
|
82
|
-
const contentType = mime.getType(icon)
|
|
83
|
-
iconType = contentType || 'image/png'
|
|
84
|
-
log.warn(`Found app icon ${icon}`)
|
|
85
|
-
}
|
|
86
|
-
else if (existsSync(newIconPath)) {
|
|
87
|
-
iconBuff = readFileSync(newIconPath)
|
|
88
|
-
const contentType = mime.getType(newIconPath)
|
|
89
|
-
iconType = contentType || 'image/png'
|
|
90
|
-
log.warn(`Found app icon ${newIconPath}`)
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
log.warn(`Cannot find app icon in any of the following locations: ${icon}, ${newIconPath}`)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const fileName = `icon`
|
|
97
|
-
let signedURL = 'https://xvwzpoazmxkqosrdewyv.supabase.co/storage/v1/object/public/images/capgo.png'
|
|
98
|
-
|
|
99
|
-
// upload image if available
|
|
100
|
-
if (iconBuff && iconType) {
|
|
101
|
-
const { error } = await supabase.storage
|
|
102
|
-
.from(`images/org/${organizationUid}/${appId}`)
|
|
103
|
-
.upload(fileName, iconBuff, {
|
|
104
|
-
contentType: iconType,
|
|
105
|
-
})
|
|
106
|
-
if (error) {
|
|
107
|
-
console.error(error)
|
|
108
|
-
log.error(`Could not add app ${formatError(error)}`)
|
|
109
|
-
program.error('')
|
|
110
|
-
}
|
|
111
|
-
const { data: signedURLData } = await supabase
|
|
112
|
-
.storage
|
|
113
|
-
.from(`images/org/${organizationUid}/${appId}`)
|
|
114
|
-
.getPublicUrl(fileName)
|
|
115
|
-
signedURL = signedURLData?.publicUrl || signedURL
|
|
116
|
-
}
|
|
117
|
-
// add app to db
|
|
118
|
-
const { error: dbError } = await supabase
|
|
119
|
-
.from('apps')
|
|
120
|
-
.insert({
|
|
121
|
-
icon_url: signedURL,
|
|
122
|
-
owner_org: organizationUid,
|
|
123
|
-
name,
|
|
124
|
-
app_id: appId,
|
|
125
|
-
})
|
|
126
|
-
if (dbError) {
|
|
127
|
-
log.error(`Could not add app ${formatError(dbError)}`)
|
|
128
|
-
program.error('')
|
|
129
|
-
}
|
|
130
|
-
const { error: dbVersionError } = await supabase
|
|
131
|
-
.from('app_versions')
|
|
132
|
-
.insert([{
|
|
133
|
-
owner_org: organizationUid,
|
|
134
|
-
deleted: true,
|
|
135
|
-
name: 'unknown',
|
|
136
|
-
app_id: appId,
|
|
137
|
-
}, {
|
|
138
|
-
owner_org: organizationUid,
|
|
139
|
-
deleted: true,
|
|
140
|
-
name: 'builtin',
|
|
141
|
-
app_id: appId,
|
|
142
|
-
}])
|
|
143
|
-
if (dbVersionError) {
|
|
144
|
-
log.error(`Could not add app ${formatError(dbVersionError)}`)
|
|
145
|
-
program.error('')
|
|
146
|
-
}
|
|
147
|
-
log.success(`App ${appId} added to Capgo. ${throwErr ? 'You can upload a bundle now' : ''}`)
|
|
148
|
-
if (throwErr) {
|
|
149
|
-
outro(`Done ✅`)
|
|
150
|
-
exit()
|
|
151
|
-
}
|
|
152
|
-
return true
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export async function addCommand(apikey: string, options: Options) {
|
|
156
|
-
addApp(apikey, options, true)
|
|
157
|
-
}
|
package/src/app/debug.ts
DELETED
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
import { exit } from 'node:process'
|
|
2
|
-
import ky from 'ky'
|
|
3
|
-
import { program } from 'commander'
|
|
4
|
-
import type LogSnag from 'logsnag'
|
|
5
|
-
import { confirm as confirmC, intro, isCancel, log, outro, spinner } from '@clack/prompts'
|
|
6
|
-
import type { Database } from '../types/supabase.types'
|
|
7
|
-
import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
|
|
8
|
-
import { checkLatest } from '../api/update'
|
|
9
|
-
import { OrganizationPerm, convertAppName, createSupabaseClient, defaultApiHost, findSavedKey, formatError, getConfig, getLocalConfig, getOrganizationId, useLogSnag, verifyUser } from '../utils'
|
|
10
|
-
|
|
11
|
-
function wait(ms: number) {
|
|
12
|
-
return new Promise((resolve) => {
|
|
13
|
-
setTimeout(resolve, ms)
|
|
14
|
-
})
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface OptionsBaseDebug {
|
|
18
|
-
apikey: string
|
|
19
|
-
device?: string
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export async function markSnag(channel: string, orgId: string, snag: LogSnag, event: string, icon = '✅') {
|
|
23
|
-
await snag.track({
|
|
24
|
-
channel,
|
|
25
|
-
event,
|
|
26
|
-
icon,
|
|
27
|
-
user_id: orgId,
|
|
28
|
-
notify: false,
|
|
29
|
-
}).catch()
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function cancelCommand(channel: string, command: boolean | symbol, orgId: string, snag: LogSnag) {
|
|
33
|
-
if (isCancel(command)) {
|
|
34
|
-
await markSnag(channel, orgId, snag, 'canceled', '🤷')
|
|
35
|
-
exit()
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface Order {
|
|
40
|
-
key: string
|
|
41
|
-
sortable?: 'asc' | 'desc'
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
interface QueryStats {
|
|
45
|
-
appId: string
|
|
46
|
-
devicesId?: string[]
|
|
47
|
-
search?: string
|
|
48
|
-
order?: Order[]
|
|
49
|
-
rangeStart?: string
|
|
50
|
-
rangeEnd?: string
|
|
51
|
-
limit?: number
|
|
52
|
-
}
|
|
53
|
-
interface LogData {
|
|
54
|
-
app_id: string
|
|
55
|
-
device_id: string
|
|
56
|
-
action: Database['public']['Enums']['stats_action']
|
|
57
|
-
version_id: number
|
|
58
|
-
version?: number
|
|
59
|
-
created_at: string
|
|
60
|
-
}
|
|
61
|
-
export async function getStats(apikey: string, query: QueryStats, after: string | null): Promise<LogData[]> {
|
|
62
|
-
try {
|
|
63
|
-
const dataD = await ky
|
|
64
|
-
.post(`${defaultApiHost}/private/stats`, {
|
|
65
|
-
headers: {
|
|
66
|
-
'Content-Type': 'application/json',
|
|
67
|
-
'capgkey': apikey,
|
|
68
|
-
},
|
|
69
|
-
body: JSON.stringify(query),
|
|
70
|
-
})
|
|
71
|
-
.then(res => res.json<LogData[]>())
|
|
72
|
-
.catch((err) => {
|
|
73
|
-
console.error('Cannot get devices', err)
|
|
74
|
-
return [] as LogData[]
|
|
75
|
-
})
|
|
76
|
-
if (dataD?.length > 0 && (after === null || after !== dataD[0].created_at))
|
|
77
|
-
return dataD
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
log.error(`Cannot get stats ${formatError(error)}`)
|
|
81
|
-
}
|
|
82
|
-
return []
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async function displayError(data: LogData, channel: string, orgId: string, snag: LogSnag, baseUrl: string) {
|
|
86
|
-
log.info(`Log from Device: ${data.device_id}`)
|
|
87
|
-
if (data.action === 'get') {
|
|
88
|
-
log.info('Update Sent your your device, wait until event download complete')
|
|
89
|
-
await markSnag(channel, orgId, snag, 'done')
|
|
90
|
-
}
|
|
91
|
-
else if (data.action.startsWith('download_')) {
|
|
92
|
-
const action = data.action.split('_')[1]
|
|
93
|
-
if (action === 'complete') {
|
|
94
|
-
log.info('Your bundle has been downloaded on your device, background the app now and open it again to see the update')
|
|
95
|
-
await markSnag(channel, orgId, snag, 'downloaded')
|
|
96
|
-
}
|
|
97
|
-
else if (action === 'fail') {
|
|
98
|
-
log.error('Your bundle has failed to download on your device.')
|
|
99
|
-
log.error('Please check if you have network connection and try again')
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
log.info(`Your bundle is downloading ${action}% ...`)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
else if (data.action === 'set') {
|
|
106
|
-
log.info('Your bundle has been set on your device ❤️')
|
|
107
|
-
await markSnag(channel, orgId, snag, 'set')
|
|
108
|
-
return false
|
|
109
|
-
}
|
|
110
|
-
else if (data.action === 'NoChannelOrOverride') {
|
|
111
|
-
log.error(`No default channel or override (channel/device) found, please create it here ${baseUrl}`)
|
|
112
|
-
}
|
|
113
|
-
else if (data.action === 'needPlanUpgrade') {
|
|
114
|
-
log.error('Your are out of quota, please upgrade your plan here https://web.capgo.app/dashboard/settings/plans')
|
|
115
|
-
}
|
|
116
|
-
else if (data.action === 'missingBundle') {
|
|
117
|
-
log.error('Your bundle is missing, please check how you build your app')
|
|
118
|
-
}
|
|
119
|
-
else if (data.action === 'noNew') {
|
|
120
|
-
log.error(`The version number you uploaded to your default channel in Capgo, is the same as the present in the device ${data.device_id}.`)
|
|
121
|
-
log.error(`To fix it, ensure the variable:
|
|
122
|
-
- iOS: keyCFBundleShortVersionString or MARKETING_VERSION
|
|
123
|
-
- Android: versionName
|
|
124
|
-
Are lower than the version number you uploaded to Capgo.`)
|
|
125
|
-
log.error('More info here: https://capgo.app/blog/how-version-work-in-capgo/#versioning-system')
|
|
126
|
-
}
|
|
127
|
-
else if (data.action === 'disablePlatformIos') {
|
|
128
|
-
log.error(`iOS is disabled in the default channel and your device ${data.device_id} is an iOS device ${baseUrl}`)
|
|
129
|
-
}
|
|
130
|
-
else if (data.action === 'disablePlatformAndroid') {
|
|
131
|
-
log.error(`Android is disabled in the default channel and your device ${data.device_id} is an Android device ${baseUrl}`)
|
|
132
|
-
}
|
|
133
|
-
else if (data.action === 'disableAutoUpdateToMajor') {
|
|
134
|
-
log.error(`The version number you uploaded to your default channel in Capgo, is a major version higher (ex: 1.0.0 in device to 2.0.0 in Capgo) than the present in the device ${data.device_id}.`)
|
|
135
|
-
log.error('Capgo is set by default to protect you from this, and avoid sending breaking changes incompatible with the native code present in the device.')
|
|
136
|
-
log.error(`To fix it, ensure the variable:
|
|
137
|
-
- iOS: keyCFBundleShortVersionString or MARKETING_VERSION
|
|
138
|
-
- Android: versionName
|
|
139
|
-
Are lower than the version number you uploaded to Capgo.`)
|
|
140
|
-
log.error('More info here: https://capgo.app/blog/how-version-work-in-capgo/#versioning-system')
|
|
141
|
-
}
|
|
142
|
-
else if (data.action === 'disableAutoUpdateUnderNative') {
|
|
143
|
-
log.error(`The version number you uploaded to your default channel in Capgo, is lower than the present in the device ${data.device_id}.`)
|
|
144
|
-
log.error(`To fix it, ensure the variable:
|
|
145
|
-
- iOS: keyCFBundleShortVersionString or MARKETING_VERSION
|
|
146
|
-
- Android: versionName
|
|
147
|
-
Are lower than the version number you uploaded to Capgo.`)
|
|
148
|
-
log.error('More info here: https://capgo.app/blog/how-version-work-in-capgo/#versioning-system')
|
|
149
|
-
}
|
|
150
|
-
else if (data.action === 'disableDevBuild') {
|
|
151
|
-
log.error(`Dev build is disabled in the default channel. ${baseUrl}`)
|
|
152
|
-
log.error('Set your channel to allow it if you wanna test your app')
|
|
153
|
-
}
|
|
154
|
-
else if (data.action === 'disableEmulator') {
|
|
155
|
-
log.error(`Emulator is disabled in the default channel. ${baseUrl}`)
|
|
156
|
-
log.error('Set your channel to allow it if you wanna test your app')
|
|
157
|
-
}
|
|
158
|
-
else if (data.action === 'cannotGetBundle') {
|
|
159
|
-
log.error(`We cannot get your bundle from the default channel. ${baseUrl}`)
|
|
160
|
-
log.error('Are you sure your default channel has a bundle set?')
|
|
161
|
-
}
|
|
162
|
-
else if (data.action === 'set_fail') {
|
|
163
|
-
log.error(`Your bundle seems to be corrupted, try to download from ${baseUrl} to identify the issue`)
|
|
164
|
-
}
|
|
165
|
-
else if (data.action === 'reset') {
|
|
166
|
-
log.error('Your device has been reset to the builtin bundle, did notifyAppReady() is present in the code builded and uploaded to Capgo ?')
|
|
167
|
-
}
|
|
168
|
-
else if (data.action === 'update_fail') {
|
|
169
|
-
log.error('Your bundle has been installed but failed to call notifyAppReady()')
|
|
170
|
-
log.error('Please check if you have network connection and try again')
|
|
171
|
-
}
|
|
172
|
-
else if (data.action === 'checksum_fail') {
|
|
173
|
-
log.error('Your bundle has failed to validate checksum, please check your code and send it again to Capgo')
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
log.error(`Log from Capgo ${data.action}`)
|
|
177
|
-
}
|
|
178
|
-
return true
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export async function waitLog(channel: string, apikey: string, appId: string, snag: LogSnag, orgId: string, deviceId?: string) {
|
|
182
|
-
let loop = true
|
|
183
|
-
const appIdUrl = convertAppName(appId)
|
|
184
|
-
const config = await getLocalConfig()
|
|
185
|
-
const baseUrl = `${config.hostWeb}/app/p/${appIdUrl}`
|
|
186
|
-
await markSnag(channel, orgId, snag, 'Use waitlog')
|
|
187
|
-
const query: QueryStats = {
|
|
188
|
-
appId,
|
|
189
|
-
devicesId: deviceId ? [deviceId] : undefined,
|
|
190
|
-
order: [{
|
|
191
|
-
key: 'created_at',
|
|
192
|
-
sortable: 'desc',
|
|
193
|
-
}],
|
|
194
|
-
rangeStart: new Date().toISOString(),
|
|
195
|
-
}
|
|
196
|
-
let after: string | null = null
|
|
197
|
-
const s = spinner()
|
|
198
|
-
s.start(`Waiting for logs (Expect delay of 30 sec)`)
|
|
199
|
-
while (loop) {
|
|
200
|
-
await wait(5000)
|
|
201
|
-
const data = await getStats(apikey, query, after)
|
|
202
|
-
if (data.length > 0) {
|
|
203
|
-
after = data[0].created_at
|
|
204
|
-
for (const d of data) {
|
|
205
|
-
loop = await displayError(d, channel, orgId, snag, baseUrl)
|
|
206
|
-
if (!loop)
|
|
207
|
-
break
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
s.stop(`Stop watching logs`)
|
|
212
|
-
return Promise.resolve()
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export async function debugApp(appId: string, options: OptionsBaseDebug) {
|
|
216
|
-
intro(`Debug Live update in Capgo`)
|
|
217
|
-
|
|
218
|
-
await checkLatest()
|
|
219
|
-
options.apikey = options.apikey || findSavedKey()
|
|
220
|
-
const extConfig = await getConfig()
|
|
221
|
-
appId = appId || extConfig?.config?.appId
|
|
222
|
-
const deviceId = options.device
|
|
223
|
-
if (!options.apikey) {
|
|
224
|
-
log.error(`Missing API key, you need to provide an API key to delete your app`)
|
|
225
|
-
program.error('')
|
|
226
|
-
}
|
|
227
|
-
if (!appId) {
|
|
228
|
-
log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
|
|
229
|
-
program.error('')
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const supabase = await createSupabaseClient(options.apikey)
|
|
233
|
-
const snag = useLogSnag()
|
|
234
|
-
|
|
235
|
-
const userId = await verifyUser(supabase, options.apikey)
|
|
236
|
-
|
|
237
|
-
log.info(`Getting active bundle in Capgo`)
|
|
238
|
-
|
|
239
|
-
// Check we have app access to this appId
|
|
240
|
-
await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.admin)
|
|
241
|
-
|
|
242
|
-
const orgId = await getOrganizationId(supabase, appId)
|
|
243
|
-
|
|
244
|
-
const doRun = await confirmC({ message: `Automatic check if update working in device ?` })
|
|
245
|
-
await cancelCommand('debug', doRun, userId, snag)
|
|
246
|
-
if (doRun) {
|
|
247
|
-
log.info(`Wait logs sent to Capgo from ${appId} device, Please open your app 💪`)
|
|
248
|
-
await waitLog('debug', options.apikey, appId, snag, orgId, deviceId)
|
|
249
|
-
outro(`Done ✅`)
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
// const appIdUrl = convertAppName(appId)
|
|
253
|
-
// log.info(`Check logs in https://web.capgo.app/app/p/${appIdUrl}/logs to see if update works.`)
|
|
254
|
-
outro(`Canceled ❌`)
|
|
255
|
-
}
|
|
256
|
-
outro(`Done ✅`)
|
|
257
|
-
exit()
|
|
258
|
-
}
|
package/src/app/delete.ts
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { exit } from 'node:process'
|
|
2
|
-
import { program } from 'commander'
|
|
3
|
-
import { intro, isCancel, log, outro, select } from '@clack/prompts'
|
|
4
|
-
import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
|
|
5
|
-
import type { OptionsBase } from '../utils'
|
|
6
|
-
import { OrganizationPerm, createSupabaseClient, findSavedKey, formatError, getConfig, getOrganizationId, useLogSnag, verifyUser } from '../utils'
|
|
7
|
-
|
|
8
|
-
export async function deleteApp(appId: string, options: OptionsBase) {
|
|
9
|
-
intro(`Deleting`)
|
|
10
|
-
options.apikey = options.apikey || findSavedKey()
|
|
11
|
-
const extConfig = await getConfig()
|
|
12
|
-
appId = appId || extConfig?.config?.appId
|
|
13
|
-
const snag = useLogSnag()
|
|
14
|
-
|
|
15
|
-
if (!options.apikey) {
|
|
16
|
-
log.error('Missing API key, you need to provide a API key to upload your bundle')
|
|
17
|
-
program.error('')
|
|
18
|
-
}
|
|
19
|
-
if (!appId) {
|
|
20
|
-
log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
|
|
21
|
-
program.error('')
|
|
22
|
-
}
|
|
23
|
-
const supabase = await createSupabaseClient(options.apikey)
|
|
24
|
-
|
|
25
|
-
const userId = await verifyUser(supabase, options.apikey, ['write', 'all'])
|
|
26
|
-
// Check we have app access to this appId
|
|
27
|
-
await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.super_admin)
|
|
28
|
-
|
|
29
|
-
const { data: appOwnerRaw, error: appOwnerError } = await supabase.from('apps')
|
|
30
|
-
.select(`owner_org ( created_by, id )`)
|
|
31
|
-
.eq('app_id', appId)
|
|
32
|
-
.single()
|
|
33
|
-
|
|
34
|
-
const appOwner = appOwnerRaw as { owner_org: { created_by: string, id: string } } | null
|
|
35
|
-
|
|
36
|
-
if (!appOwnerError && (appOwner?.owner_org.created_by ?? '') !== userId) {
|
|
37
|
-
// We are dealing with a member user that is not the owner
|
|
38
|
-
// Deleting the app is not recomended at this stage
|
|
39
|
-
|
|
40
|
-
log.warn('Deleting the app is not recomended for users that are not the organization owner')
|
|
41
|
-
log.warn('You are invited as a super_admin but your are not the owner')
|
|
42
|
-
log.warn('It\'s strongly recomended that you do not continue!')
|
|
43
|
-
|
|
44
|
-
const shouldContinue = await select({
|
|
45
|
-
message: 'Do you want to continue?',
|
|
46
|
-
options: [
|
|
47
|
-
{
|
|
48
|
-
label: 'Yes',
|
|
49
|
-
value: 'yes',
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
label: 'No',
|
|
53
|
-
value: 'no',
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
if (isCancel(shouldContinue) || shouldContinue === 'no') {
|
|
59
|
-
log.error('Canceled deleting the app, exiting')
|
|
60
|
-
program.error('')
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
else if (appOwnerError) {
|
|
64
|
-
log.warn(`Cannot get the app owner ${formatError(appOwnerError)}`)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const { error } = await supabase
|
|
68
|
-
.storage
|
|
69
|
-
.from(`images`)
|
|
70
|
-
.remove([`org/${appOwner?.owner_org.id}/${appId}/icon`])
|
|
71
|
-
if (error) {
|
|
72
|
-
console.error(error, `images/org/${appOwner?.owner_org.id}/${appId}`)
|
|
73
|
-
log.error('Could not delete app logo')
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// TODO: make the version delete in R2 too
|
|
77
|
-
const { error: delError } = await supabase
|
|
78
|
-
.storage
|
|
79
|
-
.from(`apps/${appId}/${userId}`)
|
|
80
|
-
.remove(['versions'])
|
|
81
|
-
if (delError)
|
|
82
|
-
log.error('Could not delete app version')
|
|
83
|
-
// We should not care too much, most is in r2 anyways :/
|
|
84
|
-
// program.error('')
|
|
85
|
-
|
|
86
|
-
const { error: dbError } = await supabase
|
|
87
|
-
.from('apps')
|
|
88
|
-
.delete()
|
|
89
|
-
.eq('app_id', appId)
|
|
90
|
-
// .eq('user_id', userId)
|
|
91
|
-
|
|
92
|
-
if (dbError) {
|
|
93
|
-
log.error('Could not delete app')
|
|
94
|
-
program.error('')
|
|
95
|
-
}
|
|
96
|
-
const orgId = await getOrganizationId(supabase, appId)
|
|
97
|
-
await snag.track({
|
|
98
|
-
channel: 'app',
|
|
99
|
-
event: 'App Deleted',
|
|
100
|
-
icon: '🗑️',
|
|
101
|
-
user_id: orgId,
|
|
102
|
-
tags: {
|
|
103
|
-
'app-id': appId,
|
|
104
|
-
},
|
|
105
|
-
notify: false,
|
|
106
|
-
}).catch()
|
|
107
|
-
log.success(`App deleted in Capgo`)
|
|
108
|
-
outro('Done ✅')
|
|
109
|
-
exit()
|
|
110
|
-
}
|
package/src/app/info.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { exit, version as nodeVersion } from 'node:process'
|
|
2
|
-
import { platform, version } from 'node:os'
|
|
3
|
-
import getLatest from 'get-latest-version'
|
|
4
|
-
import { log, spinner } from '@clack/prompts'
|
|
5
|
-
import { getConfig, readPackageJson } from '../utils'
|
|
6
|
-
import pack from '../../package.json'
|
|
7
|
-
|
|
8
|
-
async function getLatestDependencies(installedDependencies: { [key: string]: string }) {
|
|
9
|
-
const latestDependencies: { [key: string]: string } = {}
|
|
10
|
-
const all = []
|
|
11
|
-
for (const dependency in installedDependencies) {
|
|
12
|
-
if (Object.prototype.hasOwnProperty.call(installedDependencies, dependency)) {
|
|
13
|
-
// get in npm the last version of the dependency
|
|
14
|
-
all.push(getLatest(dependency))
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
await Promise.all(all)
|
|
18
|
-
.then((values) => {
|
|
19
|
-
const keys = Object.keys(installedDependencies)
|
|
20
|
-
for (let i = 0; i < values.length; i += 1) {
|
|
21
|
-
const v = values[i]
|
|
22
|
-
if (v)
|
|
23
|
-
latestDependencies[keys[i]] = v
|
|
24
|
-
}
|
|
25
|
-
})
|
|
26
|
-
return latestDependencies
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async function getInstalledDependencies() {
|
|
30
|
-
const { dependencies } = await readPackageJson()
|
|
31
|
-
const installedDependencies: { [key: string]: string } = {
|
|
32
|
-
'@capgo/cli': pack.version,
|
|
33
|
-
}
|
|
34
|
-
for (const dependency in dependencies) {
|
|
35
|
-
if (Object.prototype.hasOwnProperty.call(dependencies, dependency)
|
|
36
|
-
&& dependency.startsWith('@capgo/')
|
|
37
|
-
&& dependency.startsWith('@capawesome/')
|
|
38
|
-
&& dependency.startsWith('capacitor')) {
|
|
39
|
-
// remove ^ or ~ from version
|
|
40
|
-
const version = dependencies[dependency].replace('^', '').replace('~', '')
|
|
41
|
-
installedDependencies[dependency] = version
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return installedDependencies
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export async function getInfo() {
|
|
48
|
-
log.warn(' 💊 Capgo Doctor 💊')
|
|
49
|
-
// app name
|
|
50
|
-
const extConfig = await getConfig()
|
|
51
|
-
const pkg = await readPackageJson()
|
|
52
|
-
// create bundle name format : 1.0.0-beta.x where x is a uuid
|
|
53
|
-
const appVersion = extConfig?.config?.plugins?.CapacitorUpdater?.version
|
|
54
|
-
|| pkg?.version
|
|
55
|
-
const appName = extConfig?.config?.appName || ''
|
|
56
|
-
log.info(` App Name: ${appName}`)
|
|
57
|
-
// app id
|
|
58
|
-
const appId = extConfig?.config?.appId || ''
|
|
59
|
-
log.info(` App ID: ${appId}`)
|
|
60
|
-
// app version
|
|
61
|
-
log.info(` App Version: ${appVersion}`)
|
|
62
|
-
// webdir
|
|
63
|
-
const webDir = extConfig?.config?.webDir || ''
|
|
64
|
-
log.info(` Web Dir: ${webDir}`)
|
|
65
|
-
// os
|
|
66
|
-
log.info(` OS: ${platform()} ${version()}`)
|
|
67
|
-
log.info(` Node: ${nodeVersion}`)
|
|
68
|
-
log.info(' Installed Dependencies:')
|
|
69
|
-
const installedDependencies = await getInstalledDependencies()
|
|
70
|
-
if (Object.keys(installedDependencies).length === 0) {
|
|
71
|
-
// display in red color in shell with console log
|
|
72
|
-
log.warning('\x1B[31m%s\x1B[0m 🚨 No dependencies found')
|
|
73
|
-
exit(1)
|
|
74
|
-
}
|
|
75
|
-
for (const dependency in installedDependencies) {
|
|
76
|
-
if (Object.prototype.hasOwnProperty.call(installedDependencies, dependency)) {
|
|
77
|
-
const installedVersion = (installedDependencies as any)[dependency]
|
|
78
|
-
log.info(` ${dependency}: ${installedVersion}`)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
const s = spinner()
|
|
82
|
-
s.start(`Running: Loading latest dependencies`)
|
|
83
|
-
const latestDependencies = await getLatestDependencies(installedDependencies)
|
|
84
|
-
s.stop(`Latest Dependencies:`)
|
|
85
|
-
for (const dependency in latestDependencies) {
|
|
86
|
-
if (Object.prototype.hasOwnProperty.call(latestDependencies, dependency)) {
|
|
87
|
-
const latestVersion = (latestDependencies as any)[dependency]
|
|
88
|
-
log.info(` ${dependency}: ${latestVersion}`)
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (JSON.stringify(installedDependencies) !== JSON.stringify(latestDependencies)) {
|
|
92
|
-
// display in red color in shell with console log
|
|
93
|
-
log.warn('\x1B[31m🚨 Some dependencies are not up to date\x1B[0m')
|
|
94
|
-
exit(1)
|
|
95
|
-
}
|
|
96
|
-
// display in green color in shell with console log
|
|
97
|
-
log.success('\x1B[32m✅ All dependencies are up to date\x1B[0m')
|
|
98
|
-
exit()
|
|
99
|
-
}
|
package/src/app/list.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { exit } from 'node:process'
|
|
2
|
-
import { program } from 'commander'
|
|
3
|
-
import { Table } from 'console-table-printer'
|
|
4
|
-
import type { SupabaseClient } from '@supabase/supabase-js'
|
|
5
|
-
import { intro, log, outro } from '@clack/prompts'
|
|
6
|
-
import type { Database } from '../types/supabase.types'
|
|
7
|
-
import type { OptionsBase } from '../utils'
|
|
8
|
-
import { createSupabaseClient, findSavedKey, getHumanDate, verifyUser } from '../utils'
|
|
9
|
-
import { checkLatest } from '../api/update'
|
|
10
|
-
|
|
11
|
-
function displayApp(data: Database['public']['Tables']['apps']['Row'][]) {
|
|
12
|
-
if (!data.length) {
|
|
13
|
-
log.error('No apps found')
|
|
14
|
-
exit(1)
|
|
15
|
-
}
|
|
16
|
-
const t = new Table({
|
|
17
|
-
title: 'Apps',
|
|
18
|
-
charLength: { '❌': 2, '✅': 2 },
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
// add rows with color
|
|
22
|
-
data.reverse().forEach((row) => {
|
|
23
|
-
t.addRow({
|
|
24
|
-
Name: row.name,
|
|
25
|
-
id: row.app_id,
|
|
26
|
-
Created: getHumanDate(row.created_at),
|
|
27
|
-
})
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
log.success(t.render())
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export async function getActiveApps(supabase: SupabaseClient<Database>) {
|
|
34
|
-
const { data, error: vError } = await supabase
|
|
35
|
-
.from('apps')
|
|
36
|
-
.select()
|
|
37
|
-
// .eq('user_id', userId)
|
|
38
|
-
.order('created_at', { ascending: false })
|
|
39
|
-
|
|
40
|
-
if (vError) {
|
|
41
|
-
log.error('Apps not found')
|
|
42
|
-
program.error('')
|
|
43
|
-
}
|
|
44
|
-
return data
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export async function listApp(options: OptionsBase) {
|
|
48
|
-
intro(`List apps in Capgo`)
|
|
49
|
-
|
|
50
|
-
await checkLatest()
|
|
51
|
-
options.apikey = options.apikey || findSavedKey()
|
|
52
|
-
|
|
53
|
-
const supabase = await createSupabaseClient(options.apikey)
|
|
54
|
-
|
|
55
|
-
await verifyUser(supabase, options.apikey, ['write', 'all', 'read', 'upload'])
|
|
56
|
-
|
|
57
|
-
log.info(`Getting active bundle in Capgo`)
|
|
58
|
-
|
|
59
|
-
// Get all active app versions we might possibly be able to cleanup
|
|
60
|
-
const allApps = await getActiveApps(supabase)
|
|
61
|
-
|
|
62
|
-
log.info(`Active app in Capgo: ${allApps?.length}`)
|
|
63
|
-
|
|
64
|
-
displayApp(allApps)
|
|
65
|
-
outro(`Done ✅`)
|
|
66
|
-
exit()
|
|
67
|
-
}
|