@capgo/cli 4.0.12 → 4.0.13

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/src/utils.ts CHANGED
@@ -1,693 +1,682 @@
1
- import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
- import { homedir } from 'node:os';
3
- import { resolve } from 'node:path';
4
- import { loadConfig } from '@capacitor/cli/dist/config';
5
- import { program } from 'commander';
6
- import { createClient, SupabaseClient } from '@supabase/supabase-js';
7
- import prettyjson from 'prettyjson';
8
- import { LogSnag } from 'logsnag';
9
- import * as p from '@clack/prompts';
10
- import ky from 'ky';
1
+ import { existsSync, readFileSync, readdirSync } from 'node:fs'
2
+ import { homedir } from 'node:os'
3
+ import { resolve } from 'node:path'
4
+ import process from 'node:process'
5
+ import { loadConfig } from '@capacitor/cli/dist/config'
6
+ import { program } from 'commander'
7
+ import type { SupabaseClient } from '@supabase/supabase-js'
8
+ import { createClient } from '@supabase/supabase-js'
9
+ import prettyjson from 'prettyjson'
10
+ import { LogSnag } from 'logsnag'
11
+ import * as p from '@clack/prompts'
12
+ import ky from 'ky'
11
13
  import { promiseFiles } from 'node-dir'
12
- import { Database } from './types/supabase.types';
14
+ import type { Database } from './types/supabase.types'
13
15
 
14
- export const baseKey = '.capgo_key';
15
- export const baseKeyPub = `${baseKey}.pub`;
16
+ export const baseKey = '.capgo_key'
17
+ export const baseKeyPub = `${baseKey}.pub`
16
18
  export const defaultHost = 'https://capgo.app'
17
19
  export const defaultApiHost = 'https://api.capgo.app'
18
20
  export const defaultHostWeb = 'https://web.capgo.app'
19
- // eslint-disable-next-line max-len
21
+
20
22
  export const regexSemver = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
23
+ export const formatError = (error: any) => error ? `\n${prettyjson.render(error)}` : ''
21
24
 
22
25
  export interface OptionsBase {
23
- apikey: string;
26
+ apikey: string
24
27
  }
25
28
 
26
- export const getConfig = async () => {
27
- let config: Config;
28
- try {
29
- config = await loadConfig();
30
- } catch (err) {
31
- p.log.error('No capacitor config file found, run `cap init` first');
32
- program.error('')
33
- }
34
- return config;
29
+ export async function getConfig() {
30
+ let config: Config
31
+ try {
32
+ config = await loadConfig()
33
+ }
34
+ catch (err) {
35
+ p.log.error('No capacitor config file found, run `cap init` first')
36
+ program.error('')
37
+ }
38
+ return config
35
39
  }
36
40
 
37
- export const getLocalConfig = async () => {
38
- try {
39
- const config: Config = await getConfig();
40
- const capConfig: Partial<CapgoConfig> = {
41
- host: (config?.app?.extConfig?.plugins?.CapacitorUpdater?.localHost || defaultHost) as string,
42
- hostWeb: (config?.app?.extConfig?.plugins?.CapacitorUpdater?.localWebHost || defaultHostWeb) as string,
43
- }
44
- // eslint-disable-next-line max-len
45
- if (config?.app?.extConfig?.plugins?.CapacitorUpdater?.localSupa && config?.app?.extConfig?.plugins?.CapacitorUpdater?.localSupaAnon) {
46
- p.log.info('Using custom supabase instance from capacitor.config.json')
47
- capConfig.supaKey = config?.app?.extConfig?.plugins?.CapacitorUpdater?.localSupaAnon
48
- capConfig.supaHost = config?.app?.extConfig?.plugins?.CapacitorUpdater?.localSupa
49
- }
50
- return capConfig
51
- } catch (error) {
52
- return {
53
- host: defaultHost,
54
- hostWeb: defaultHostWeb,
55
- }
41
+ export async function getLocalConfig() {
42
+ try {
43
+ const config: Config = await getConfig()
44
+ const capConfig: Partial<CapgoConfig> = {
45
+ host: (config?.app?.extConfig?.plugins?.CapacitorUpdater?.localHost || defaultHost) as string,
46
+ hostWeb: (config?.app?.extConfig?.plugins?.CapacitorUpdater?.localWebHost || defaultHostWeb) as string,
56
47
  }
57
48
 
49
+ if (config?.app?.extConfig?.plugins?.CapacitorUpdater?.localSupa && config?.app?.extConfig?.plugins?.CapacitorUpdater?.localSupaAnon) {
50
+ p.log.info('Using custom supabase instance from capacitor.config.json')
51
+ capConfig.supaKey = config?.app?.extConfig?.plugins?.CapacitorUpdater?.localSupaAnon
52
+ capConfig.supaHost = config?.app?.extConfig?.plugins?.CapacitorUpdater?.localSupa
53
+ }
54
+ return capConfig
55
+ }
56
+ catch (error) {
57
+ return {
58
+ host: defaultHost,
59
+ hostWeb: defaultHostWeb,
60
+ }
61
+ }
58
62
  }
59
63
 
60
64
  const nativeFileRegex = /([A-Za-z0-9]+)\.(java|swift|kt|scala)$/
61
65
 
62
66
  interface CapgoConfig {
63
- supaHost: string
64
- supaKey: string
65
- host: string
66
- hostWeb: string
67
- signKey: string
67
+ supaHost: string
68
+ supaKey: string
69
+ host: string
70
+ hostWeb: string
71
+ signKey: string
68
72
  }
69
- export const getRemoteConfig = async () => {
70
- // call host + /api/get_config and parse the result as json using axios
71
- const localConfig = await getLocalConfig()
72
- return ky
73
- .get(`${defaultApiHost}/private/config`)
74
- .then((res) => res.json<CapgoConfig>())
75
- .then(data => ({ ...data, ...localConfig } as CapgoConfig))
76
- .catch(() => {
77
- console.log('Local config', localConfig);
78
- return localConfig
79
- })
73
+ export async function getRemoteConfig() {
74
+ // call host + /api/get_config and parse the result as json using axios
75
+ const localConfig = await getLocalConfig()
76
+ return ky
77
+ .get(`${defaultApiHost}/private/config`)
78
+ .then(res => res.json<CapgoConfig>())
79
+ .then(data => ({ ...data, ...localConfig } as CapgoConfig))
80
+ .catch(() => {
81
+ p.log.info(`Local config ${formatError(localConfig)}`)
82
+ return localConfig
83
+ })
80
84
  }
81
85
 
82
- export const createSupabaseClient = async (apikey: string) => {
83
- const config = await getRemoteConfig()
84
- if (!config.supaHost || !config.supaKey) {
85
- p.log.error('Cannot connect to server please try again later');
86
- program.error('');
87
- }
88
- return createClient<Database>(config.supaHost, config.supaKey, {
89
- auth: {
90
- persistSession: false,
91
- },
92
- global: {
93
- headers: {
94
- capgkey: apikey,
95
- }
96
- }
97
- })
86
+ export async function createSupabaseClient(apikey: string) {
87
+ const config = await getRemoteConfig()
88
+ if (!config.supaHost || !config.supaKey) {
89
+ p.log.error('Cannot connect to server please try again later')
90
+ program.error('')
91
+ }
92
+ return createClient<Database>(config.supaHost, config.supaKey, {
93
+ auth: {
94
+ persistSession: false,
95
+ },
96
+ global: {
97
+ headers: {
98
+ capgkey: apikey,
99
+ },
100
+ },
101
+ })
98
102
  }
99
103
 
100
- export const checkKey = async (supabase: SupabaseClient<Database>, apikey: string,
101
- keymode: Database['public']['Enums']['key_mode'][]) => {
102
- const { data: apiAccess } = await supabase
103
- .rpc('is_allowed_capgkey', { apikey, keymode })
104
- .single()
105
-
106
- if (!apiAccess) {
107
- p.log.error(`Invalid API key or insufficient permissions.`);
108
- // create a string from keymode array with comma and space and "or" for the last one
109
- const keymodeStr = keymode.map((k, i) => {
110
- if (i === keymode.length - 1) {
111
- return `or ${k}`
112
- }
113
- return `${k}, `
114
- }).join('')
115
- p.log.error(`Your key should be: ${keymodeStr} mode.`);
116
- program.error('')
117
- }
104
+ export async function checkKey(supabase: SupabaseClient<Database>, apikey: string, keymode: Database['public']['Enums']['key_mode'][]) {
105
+ const { data: apiAccess } = await supabase
106
+ .rpc('is_allowed_capgkey', { apikey, keymode })
107
+ .single()
108
+
109
+ if (!apiAccess) {
110
+ p.log.error(`Invalid API key or insufficient permissions.`)
111
+ // create a string from keymode array with comma and space and "or" for the last one
112
+ const keymodeStr = keymode.map((k, i) => {
113
+ if (i === keymode.length - 1)
114
+ return `or ${k}`
115
+
116
+ return `${k}, `
117
+ }).join('')
118
+ p.log.error(`Your key should be: ${keymodeStr} mode.`)
119
+ program.error('')
120
+ }
118
121
  }
119
122
 
120
- export const isGoodPlan = async (supabase: SupabaseClient<Database>, userId: string): Promise<boolean> => {
121
- const { data } = await supabase
122
- .rpc('is_good_plan_v5', { userid: userId })
123
- .single()
124
- return data || false
123
+ export async function isGoodPlan(supabase: SupabaseClient<Database>, userId: string): Promise<boolean> {
124
+ const { data } = await supabase
125
+ .rpc('is_good_plan_v5', { userid: userId })
126
+ .single()
127
+ return data || false
125
128
  }
126
129
 
127
- export const isPaying = async (supabase: SupabaseClient<Database>, userId: string): Promise<boolean> => {
128
- const { data } = await supabase
129
- .rpc('is_paying', { userid: userId })
130
- .single()
131
- return data || false
130
+ export async function isPaying(supabase: SupabaseClient<Database>, userId: string): Promise<boolean> {
131
+ const { data } = await supabase
132
+ .rpc('is_paying', { userid: userId })
133
+ .single()
134
+ return data || false
132
135
  }
133
136
 
134
- export const isTrial = async (supabase: SupabaseClient<Database>, userId: string): Promise<number> => {
135
- const { data } = await supabase
136
- .rpc('is_trial', { userid: userId })
137
- .single()
138
- return data || 0
137
+ export async function isTrial(supabase: SupabaseClient<Database>, userId: string): Promise<number> {
138
+ const { data } = await supabase
139
+ .rpc('is_trial', { userid: userId })
140
+ .single()
141
+ return data || 0
139
142
  }
140
143
 
141
- export const isAllowedAction = async (supabase: SupabaseClient<Database>, userId: string): Promise<boolean> => {
142
- const { data } = await supabase
143
- .rpc('is_allowed_action_user', { userid: userId })
144
- .single()
145
- return !!data
144
+ export async function isAllowedAction(supabase: SupabaseClient<Database>, userId: string): Promise<boolean> {
145
+ const { data } = await supabase
146
+ .rpc('is_allowed_action_user', { userid: userId })
147
+ .single()
148
+ return !!data
146
149
  }
147
150
 
148
- export const isAllowedActionAppIdApiKey = async (supabase: SupabaseClient<Database>, appId: string, apikey: string): Promise<boolean> => {
149
- const { data } = await supabase
150
- .rpc('is_allowed_action', { apikey, appid: appId })
151
- .single()
151
+ export async function isAllowedActionAppIdApiKey(supabase: SupabaseClient<Database>, appId: string, apikey: string): Promise<boolean> {
152
+ const { data } = await supabase
153
+ .rpc('is_allowed_action', { apikey, appid: appId })
154
+ .single()
152
155
 
153
- return !!data
156
+ return !!data
154
157
  }
155
158
 
156
- export const getAppOwner = async (supabase: SupabaseClient<Database>, appId: string): Promise<string> => {
157
- const { data, error } = await supabase
158
- .from('apps')
159
- .select('user_id')
160
- .eq('app_id', appId)
161
- .single()
162
-
163
- if (error) {
164
- p.log.error('Cannot get app owner, exiting')
165
- p.log.error('Please report the following error to capgo\'s staff')
166
- console.error(error)
167
- process.exit(1)
168
- }
169
-
170
- return data.user_id
159
+ export async function getAppOwner(supabase: SupabaseClient<Database>, appId: string): Promise<string> {
160
+ const { data, error } = await supabase
161
+ .from('apps')
162
+ .select('user_id')
163
+ .eq('app_id', appId)
164
+ .single()
165
+
166
+ if (error) {
167
+ p.log.error('Cannot get app owner, exiting')
168
+ p.log.error('Please report the following error to capgo\'s staff')
169
+ console.error(error)
170
+ process.exit(1)
171
+ }
172
+
173
+ return data.user_id
171
174
  }
172
175
 
173
- export const isAllowedApp = async (supabase: SupabaseClient<Database>, apikey: string, appId: string): Promise<boolean> => {
174
- const { data } = await supabase
175
- .rpc('is_app_owner', { apikey, appid: appId })
176
- .single()
177
- return !!data
176
+ export async function isAllowedApp(supabase: SupabaseClient<Database>, apikey: string, appId: string): Promise<boolean> {
177
+ const { data } = await supabase
178
+ .rpc('is_app_owner', { apikey, appid: appId })
179
+ .single()
180
+ return !!data
178
181
  }
179
182
 
180
183
  export enum OrganizationPerm {
181
- 'none' = 0,
182
- 'read' = 1,
183
- 'upload' = 2,
184
- 'write' = 3,
185
- 'admin' = 4,
186
- 'owner' = 5,
184
+ none = 0,
185
+ read = 1,
186
+ upload = 2,
187
+ write = 3,
188
+ admin = 4,
189
+ owner = 5,
187
190
  }
188
191
 
189
192
  export const hasOrganizationPerm = (perm: OrganizationPerm, required: OrganizationPerm): boolean => (perm as number) >= (required as number)
190
193
 
191
- export const isAllowedAppOrg = async (
192
- supabase: SupabaseClient<Database>,
193
- apikey: string,
194
- appId: string,
195
- ): Promise<{ okay: true, data: OrganizationPerm } | { okay: false, error: 'INVALID_APIKEY' | 'NO_APP' | 'NO_ORG' }> => {
196
- const { data, error } = await supabase
197
- .rpc('get_org_perm_for_apikey', { apikey, app_id: appId })
198
- .single()
199
-
200
- if (error) {
201
- p.log.error('Cannot get permissions for organization!')
202
- console.error(error)
203
- process.exit(1)
204
- }
205
-
206
- const ok = (data as string).includes('perm')
207
- if (ok) {
208
- let perm = null as (OrganizationPerm | null)
209
-
210
- switch (data as string) {
211
- case 'perm_none': {
212
- perm = OrganizationPerm.none
213
- break;
214
- }
215
- case 'perm_read': {
216
- perm = OrganizationPerm.read
217
- break;
218
- }
219
- case 'perm_upload': {
220
- perm = OrganizationPerm.upload
221
- break;
222
- }
223
- case 'perm_write': {
224
- perm = OrganizationPerm.write
225
- break;
226
- }
227
- case 'perm_admin': {
228
- perm = OrganizationPerm.admin
229
- break;
230
- }
231
- case 'perm_owner': {
232
- perm = OrganizationPerm.owner
233
- break;
234
- }
235
- default: {
236
- if ((data as string).includes('invite')) {
237
- p.log.info('Please accept/deny the organization invitation before trying to access the app')
238
- process.exit(1)
239
- }
240
-
241
- p.log.error(`Invalid output when fetching organization permission. Response: ${data}`)
242
- process.exit(1)
243
- }
244
- }
194
+ export async function isAllowedAppOrg(supabase: SupabaseClient<Database>, apikey: string, appId: string): Promise<{ okay: true, data: OrganizationPerm } | { okay: false, error: 'INVALID_APIKEY' | 'NO_APP' | 'NO_ORG' }> {
195
+ const { data, error } = await supabase
196
+ .rpc('get_org_perm_for_apikey', { apikey, app_id: appId })
197
+ .single()
245
198
 
246
- return {
247
- okay: true,
248
- data: perm
249
- }
250
- }
199
+ if (error) {
200
+ p.log.error('Cannot get permissions for organization!')
201
+ console.error(error)
202
+ process.exit(1)
203
+ }
251
204
 
252
- // This means that something went wrong here
253
- let functionError = null as 'INVALID_APIKEY' | 'NO_APP' | 'NO_ORG' | null
205
+ const ok = (data as string).includes('perm')
206
+ if (ok) {
207
+ let perm = null as (OrganizationPerm | null)
254
208
 
255
209
  switch (data as string) {
256
- case 'INVALID_APIKEY': {
257
- functionError = 'INVALID_APIKEY'
258
- break
259
- }
260
- case 'NO_APP': {
261
- functionError = 'NO_APP'
262
- break
263
- }
264
- case 'NO_ORG': {
265
- functionError = 'NO_ORG'
266
- break
267
- }
268
- default: {
269
- p.log.error(`Invalid error when fetching organization permission. Response: ${data}`)
270
- process.exit(1)
210
+ case 'perm_none': {
211
+ perm = OrganizationPerm.none
212
+ break
213
+ }
214
+ case 'perm_read': {
215
+ perm = OrganizationPerm.read
216
+ break
217
+ }
218
+ case 'perm_upload': {
219
+ perm = OrganizationPerm.upload
220
+ break
221
+ }
222
+ case 'perm_write': {
223
+ perm = OrganizationPerm.write
224
+ break
225
+ }
226
+ case 'perm_admin': {
227
+ perm = OrganizationPerm.admin
228
+ break
229
+ }
230
+ case 'perm_owner': {
231
+ perm = OrganizationPerm.owner
232
+ break
233
+ }
234
+ default: {
235
+ if ((data as string).includes('invite')) {
236
+ p.log.info('Please accept/deny the organization invitation before trying to access the app')
237
+ process.exit(1)
271
238
  }
239
+
240
+ p.log.error(`Invalid output when fetching organization permission. Response: ${data}`)
241
+ process.exit(1)
242
+ }
272
243
  }
273
244
 
274
245
  return {
275
- okay: false,
276
- error: functionError
246
+ okay: true,
247
+ data: perm,
277
248
  }
278
- }
249
+ }
279
250
 
280
- export const checkPlanValid = async (supabase: SupabaseClient<Database>, userId: string, appId: string, apikey: string, warning = true) => {
281
- const config = await getRemoteConfig()
282
- const validPlan = await isAllowedActionAppIdApiKey(supabase, appId, apikey)
283
- if (!validPlan) {
284
- p.log.error(`You need to upgrade your plan to continue to use capgo.\n Upgrade here: ${config.hostWeb}/dashboard/settings/plans\n`);
285
- setTimeout(() => {
286
- import('open')
287
- .then((module) => {
288
- module.default(`${config.hostWeb}/dashboard/settings/plans`);
289
- });
290
- program.error('')
291
- }, 1000)
292
- }
293
- const trialDays = await isTrial(supabase, userId)
294
- const ispaying = await isPaying(supabase, userId)
295
- if (trialDays > 0 && warning && !ispaying) {
296
- p.log.warn(`WARNING !!\nTrial expires in ${trialDays} days, upgrade here: ${config.hostWeb}/dashboard/settings/plans\n`);
297
- }
298
- }
251
+ // This means that something went wrong here
252
+ let functionError = null as 'INVALID_APIKEY' | 'NO_APP' | 'NO_ORG' | null
299
253
 
300
- export const findSavedKey = (quiet = false) => {
301
- // search for key in home dir
302
- const userHomeDir = homedir();
303
- let key
304
- let keyPath = `${userHomeDir}/.capgo`;
305
- if (existsSync(keyPath)) {
306
- if (!quiet)
307
- p.log.info(`Use global apy key ${keyPath}`)
308
- key = readFileSync(keyPath, 'utf8').trim();
254
+ switch (data as string) {
255
+ case 'INVALID_APIKEY': {
256
+ functionError = 'INVALID_APIKEY'
257
+ break
309
258
  }
310
- keyPath = `.capgo`;
311
- if (!key && existsSync(keyPath)) {
312
- if (!quiet)
313
- p.log.info(`Use local apy key ${keyPath}`)
314
- key = readFileSync(keyPath, 'utf8').trim();
259
+ case 'NO_APP': {
260
+ functionError = 'NO_APP'
261
+ break
315
262
  }
316
- if (!key) {
317
- p.log.error(`Cannot find API key in local folder or global, please login first with npx @capacitor/cli login`);
318
- program.error('')
263
+ case 'NO_ORG': {
264
+ functionError = 'NO_ORG'
265
+ break
319
266
  }
320
- return key
267
+ default: {
268
+ p.log.error(`Invalid error when fetching organization permission. Response: ${data}`)
269
+ process.exit(1)
270
+ }
271
+ }
272
+
273
+ return {
274
+ okay: false,
275
+ error: functionError,
276
+ }
277
+ }
278
+
279
+ export async function checkPlanValid(supabase: SupabaseClient<Database>, userId: string, appId: string, apikey: string, warning = true) {
280
+ const config = await getRemoteConfig()
281
+ const validPlan = await isAllowedActionAppIdApiKey(supabase, appId, apikey)
282
+ if (!validPlan) {
283
+ p.log.error(`You need to upgrade your plan to continue to use capgo.\n Upgrade here: ${config.hostWeb}/dashboard/settings/plans\n`)
284
+ setTimeout(() => {
285
+ import('open')
286
+ .then((module) => {
287
+ module.default(`${config.hostWeb}/dashboard/settings/plans`)
288
+ })
289
+ program.error('')
290
+ }, 1000)
291
+ }
292
+ const trialDays = await isTrial(supabase, userId)
293
+ const ispaying = await isPaying(supabase, userId)
294
+ if (trialDays > 0 && warning && !ispaying)
295
+ p.log.warn(`WARNING !!\nTrial expires in ${trialDays} days, upgrade here: ${config.hostWeb}/dashboard/settings/plans\n`)
296
+ }
297
+
298
+ export function findSavedKey(quiet = false) {
299
+ // search for key in home dir
300
+ const userHomeDir = homedir()
301
+ let key
302
+ let keyPath = `${userHomeDir}/.capgo`
303
+ if (existsSync(keyPath)) {
304
+ if (!quiet)
305
+ p.log.info(`Use global apy key ${keyPath}`)
306
+ key = readFileSync(keyPath, 'utf8').trim()
307
+ }
308
+ keyPath = `.capgo`
309
+ if (!key && existsSync(keyPath)) {
310
+ if (!quiet)
311
+ p.log.info(`Use local apy key ${keyPath}`)
312
+ key = readFileSync(keyPath, 'utf8').trim()
313
+ }
314
+ if (!key) {
315
+ p.log.error(`Cannot find API key in local folder or global, please login first with npx @capacitor/cli login`)
316
+ program.error('')
317
+ }
318
+ return key
321
319
  }
322
320
 
323
321
  async function* getFiles(dir: string): AsyncGenerator<string> {
324
- const dirents = await readdirSync(dir, { withFileTypes: true });
325
- for (const dirent of dirents) {
326
- const res = resolve(dir, dirent.name);
327
- if (dirent.isDirectory()
328
- && !dirent.name.startsWith('.')
329
- && !dirent.name.startsWith('node_modules')
330
- && !dirent.name.startsWith('dist')) {
331
- yield* getFiles(res);
332
- } else {
333
- yield res;
334
- }
335
- }
322
+ const dirents = await readdirSync(dir, { withFileTypes: true })
323
+ for (const dirent of dirents) {
324
+ const res = resolve(dir, dirent.name)
325
+ if (dirent.isDirectory()
326
+ && !dirent.name.startsWith('.')
327
+ && !dirent.name.startsWith('node_modules')
328
+ && !dirent.name.startsWith('dist'))
329
+ yield * getFiles(res)
330
+ else
331
+ yield res
332
+ }
336
333
  }
337
- export const findMainFile = async () => {
338
- const mainRegex = /(main|index)\.(ts|tsx|js|jsx)$/
339
- // search for main.ts or main.js in local dir and subdirs
340
- let mainFile = ''
341
- const pwd = process.cwd()
342
- const pwdL = pwd.split('/').length
343
- for await (const f of getFiles(pwd)) {
344
- // find number of folder in path after pwd
345
- const folders = f.split('/').length - pwdL
346
- if (folders <= 2 && mainRegex.test(f)) {
347
- mainFile = f
348
- p.log.info(`Found main file here ${f}`)
349
- break
350
- }
334
+ export async function findMainFile() {
335
+ const mainRegex = /(main|index)\.(ts|tsx|js|jsx)$/
336
+ // search for main.ts or main.js in local dir and subdirs
337
+ let mainFile = ''
338
+ const pwd = process.cwd()
339
+ const pwdL = pwd.split('/').length
340
+ for await (const f of getFiles(pwd)) {
341
+ // find number of folder in path after pwd
342
+ const folders = f.split('/').length - pwdL
343
+ if (folders <= 2 && mainRegex.test(f)) {
344
+ mainFile = f
345
+ p.log.info(`Found main file here ${f}`)
346
+ break
351
347
  }
352
- return mainFile
348
+ }
349
+ return mainFile
353
350
  }
354
351
 
355
- export const formatError = (error: any) => error ? `\n${prettyjson.render(error)}` : ''
356
-
357
352
  interface Config {
358
- app: {
359
- appId: string;
360
- appName: string;
361
- webDir: string;
362
- package: {
363
- version: string;
364
- };
365
- extConfigFilePath: string;
366
- extConfig: {
367
- extConfig: object;
368
- plugins: {
369
- extConfig: object;
370
- CapacitorUpdater: {
371
- autoUpdate?: boolean;
372
- localS3?: boolean;
373
- localHost?: string;
374
- localWebHost?: string;
375
- localSupa?: string;
376
- localSupaAnon?: string;
377
- statsUrl?: string;
378
- channelUrl?: string;
379
- updateUrl?: string;
380
- privateKey?: string;
381
- }
382
- }
383
- server: {
384
- cleartext: boolean
385
- url: string
386
- }
353
+ app: {
354
+ appId: string
355
+ appName: string
356
+ webDir: string
357
+ package: {
358
+ version: string
359
+ }
360
+ extConfigFilePath: string
361
+ extConfig: {
362
+ extConfig: object
363
+ plugins: {
364
+ extConfig: object
365
+ CapacitorUpdater: {
366
+ autoUpdate?: boolean
367
+ localS3?: boolean
368
+ localHost?: string
369
+ localWebHost?: string
370
+ localSupa?: string
371
+ localSupaAnon?: string
372
+ statsUrl?: string
373
+ channelUrl?: string
374
+ updateUrl?: string
375
+ privateKey?: string
387
376
  }
388
- };
377
+ }
378
+ server: {
379
+ cleartext: boolean
380
+ url: string
381
+ }
382
+ }
383
+ }
389
384
  }
390
385
 
391
- export const updateOrCreateVersion = async (supabase: SupabaseClient<Database>,
392
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
393
- update: Database['public']['Tables']['app_versions']['Insert'], apikey: string) =>
394
- // console.log('updateOrCreateVersion', update, apikey)
395
-
396
- supabase.from('app_versions')
397
- .upsert(update, { onConflict: 'name,app_id' })
398
- .eq('app_id', update.app_id)
399
- .eq('name', update.name)
400
-
386
+ export async function updateOrCreateVersion(supabase: SupabaseClient<Database>, update: Database['public']['Tables']['app_versions']['Insert']) {
387
+ return supabase.from('app_versions')
388
+ .upsert(update, { onConflict: 'name,app_id' })
389
+ .eq('app_id', update.app_id)
390
+ .eq('name', update.name)
391
+ }
401
392
 
402
393
  export async function uploadUrl(supabase: SupabaseClient<Database>, appId: string, bucketId: string): Promise<string> {
403
- const data = {
404
- app_id: appId,
405
- bucket_id: bucketId,
406
- }
407
- try {
408
- const pathUploadLink = 'private/upload_link'
409
- const res = await supabase.functions.invoke(pathUploadLink, { body: JSON.stringify(data) })
410
- return res.data.url
411
- } catch (error) {
412
- p.log.error(`Cannot get upload url ${JSON.stringify(error)}`);
413
- }
414
- return '';
394
+ const data = {
395
+ app_id: appId,
396
+ bucket_id: bucketId,
397
+ }
398
+ try {
399
+ const pathUploadLink = 'private/upload_link'
400
+ const res = await supabase.functions.invoke(pathUploadLink, { body: JSON.stringify(data) })
401
+ return res.data.url
402
+ }
403
+ catch (error) {
404
+ p.log.error(`Cannot get upload url ${formatError(error)}`)
405
+ }
406
+ return ''
415
407
  }
416
408
 
417
- export const updateOrCreateChannel = async (supabase: SupabaseClient<Database>,
418
- update: Database['public']['Tables']['channels']['Insert']) => {
419
- // console.log('updateOrCreateChannel', update)
420
- if (!update.app_id || !update.name || !update.created_by) {
421
- p.log.error('missing app_id, name, or created_by')
422
- return Promise.reject(new Error('missing app_id, name, or created_by'))
423
- }
424
- const { data, error } = await supabase
425
- .from('channels')
426
- .select('enable_progressive_deploy, secondaryVersionPercentage, secondVersion')
427
- .eq('app_id', update.app_id)
428
- .eq('name', update.name)
429
- // .eq('created_by', update.created_by)
430
- .single()
431
-
432
- if (data && !error) {
433
- if (data.enable_progressive_deploy) {
434
- p.log.info('Progressive deploy is enabled')
435
-
436
- if (data.secondaryVersionPercentage !== 1)
437
- p.log.warn('Latest progressive deploy has not finished')
438
-
439
- update.secondVersion = update.version
440
- if (!data.secondVersion) {
441
- p.log.error('missing secondVersion')
442
- return Promise.reject(new Error('missing secondVersion'))
443
- }
444
- update.version = data.secondVersion
445
- update.secondaryVersionPercentage = 0.1
446
- p.log.info('Started new progressive upload!')
447
-
448
- // update.version = undefined
449
- }
450
- return supabase
451
- .from('channels')
452
- .update(update)
453
- .eq('app_id', update.app_id)
454
- .eq('name', update.name)
455
- // .eq('created_by', update.created_by)
456
- .select()
457
- .single()
409
+ export async function updateOrCreateChannel(supabase: SupabaseClient<Database>, update: Database['public']['Tables']['channels']['Insert']) {
410
+ // console.log('updateOrCreateChannel', update)
411
+ if (!update.app_id || !update.name || !update.created_by) {
412
+ p.log.error('missing app_id, name, or created_by')
413
+ return Promise.reject(new Error('missing app_id, name, or created_by'))
414
+ }
415
+ const { data, error } = await supabase
416
+ .from('channels')
417
+ .select('enable_progressive_deploy, secondaryVersionPercentage, secondVersion')
418
+ .eq('app_id', update.app_id)
419
+ .eq('name', update.name)
420
+ // .eq('created_by', update.created_by)
421
+ .single()
422
+
423
+ if (data && !error) {
424
+ if (data.enable_progressive_deploy) {
425
+ p.log.info('Progressive deploy is enabled')
426
+
427
+ if (data.secondaryVersionPercentage !== 1)
428
+ p.log.warn('Latest progressive deploy has not finished')
429
+
430
+ update.secondVersion = update.version
431
+ if (!data.secondVersion) {
432
+ p.log.error('missing secondVersion')
433
+ return Promise.reject(new Error('missing secondVersion'))
434
+ }
435
+ update.version = data.secondVersion
436
+ update.secondaryVersionPercentage = 0.1
437
+ p.log.info('Started new progressive upload!')
438
+
439
+ // update.version = undefined
458
440
  }
459
-
460
441
  return supabase
461
- .from('channels')
462
- .insert(update)
463
- .select()
464
- .single()
442
+ .from('channels')
443
+ .update(update)
444
+ .eq('app_id', update.app_id)
445
+ .eq('name', update.name)
446
+ // .eq('created_by', update.created_by)
447
+ .select()
448
+ .single()
449
+ }
450
+
451
+ return supabase
452
+ .from('channels')
453
+ .insert(update)
454
+ .select()
455
+ .single()
465
456
  }
466
457
 
467
- export const useLogSnag = (): LogSnag => {
468
- const logsnag = new LogSnag({
469
- token: 'c124f5e9d0ce5bdd14bbb48f815d5583',
470
- project: 'capgo',
471
- })
472
- return logsnag
458
+ export function useLogSnag(): LogSnag {
459
+ const logsnag = new LogSnag({
460
+ token: 'c124f5e9d0ce5bdd14bbb48f815d5583',
461
+ project: 'capgo',
462
+ })
463
+ return logsnag
473
464
  }
474
465
 
475
466
  export const convertAppName = (appName: string) => appName.replace(/\./g, '--')
476
467
 
477
- export const verifyUser = async (supabase: SupabaseClient<Database>, apikey: string,
478
- keymod: Database['public']['Enums']['key_mode'][] = ['all']) => {
479
- await checkKey(supabase, apikey, keymod);
468
+ export async function verifyUser(supabase: SupabaseClient<Database>, apikey: string, keymod: Database['public']['Enums']['key_mode'][] = ['all']) {
469
+ await checkKey(supabase, apikey, keymod)
480
470
 
481
- const { data: dataUser, error: userIdError } = await supabase
482
- .rpc('get_user_id', { apikey })
483
- .single();
471
+ const { data: dataUser, error: userIdError } = await supabase
472
+ .rpc('get_user_id', { apikey })
473
+ .single()
484
474
 
485
- const userId = (dataUser || '').toString();
475
+ const userId = (dataUser || '').toString()
486
476
 
487
- if (!userId || userIdError) {
488
- p.log.error(`Cannot auth user with apikey`);
489
- program.error('')
490
- }
491
- return userId;
477
+ if (!userId || userIdError) {
478
+ p.log.error(`Cannot auth user with apikey`)
479
+ program.error('')
480
+ }
481
+ return userId
492
482
  }
493
483
 
494
- export const requireUpdateMetadata = async (supabase: SupabaseClient<Database>, channel: string): Promise<boolean> => {
495
- const { data, error } = await supabase
496
- .from('channels')
497
- .select('disableAutoUpdate')
498
- .eq('name', channel)
499
- .limit(1)
484
+ export async function requireUpdateMetadata(supabase: SupabaseClient<Database>, channel: string): Promise<boolean> {
485
+ const { data, error } = await supabase
486
+ .from('channels')
487
+ .select('disableAutoUpdate')
488
+ .eq('name', channel)
489
+ .limit(1)
500
490
 
501
- if (error) {
502
- p.log.error(`Cannot check if disableAutoUpdate is required ${JSON.stringify(error)}`);
503
- program.error('')
504
- }
491
+ if (error) {
492
+ p.log.error(`Cannot check if disableAutoUpdate is required ${formatError(error)}`)
493
+ program.error('')
494
+ }
505
495
 
506
- // Channel does not exist and the default is never 'version_number'
507
- if (data.length === 0)
508
- return false
496
+ // Channel does not exist and the default is never 'version_number'
497
+ if (data.length === 0)
498
+ return false
509
499
 
510
- const { disableAutoUpdate } = (data[0])
511
- return disableAutoUpdate === 'version_number'
500
+ const { disableAutoUpdate } = (data[0])
501
+ return disableAutoUpdate === 'version_number'
512
502
  }
513
503
 
514
- export const getHumanDate = (createdA: string | null) => {
515
- const date = new Date(createdA || '');
516
- return date.toLocaleString();
504
+ export function getHumanDate(createdA: string | null) {
505
+ const date = new Date(createdA || '')
506
+ return date.toLocaleString()
517
507
  }
518
508
 
519
509
  export async function getLocalDepenencies() {
520
- if (!existsSync('./package.json')) {
521
- p.log.error("Missing package.json, you need to be in a capacitor project");
522
- program.error('');
510
+ if (!existsSync('./package.json')) {
511
+ p.log.error('Missing package.json, you need to be in a capacitor project')
512
+ program.error('')
513
+ }
514
+
515
+ let packageJson
516
+ try {
517
+ packageJson = JSON.parse(readFileSync('./package.json', 'utf8'))
518
+ }
519
+ catch (err) {
520
+ p.log.error('Invalid package.json, JSON parsing failed')
521
+ console.error('json parse error: ', err)
522
+ program.error('')
523
+ }
524
+
525
+ const { dependencies } = packageJson
526
+ if (!dependencies) {
527
+ p.log.error('Missing dependencies section in package.json')
528
+ program.error('')
529
+ }
530
+
531
+ for (const [key, value] of Object.entries(dependencies)) {
532
+ if (typeof value !== 'string') {
533
+ p.log.error(`Invalid dependency ${key}: ${value}, expected string, got ${typeof value}`)
534
+ program.error('')
523
535
  }
536
+ }
524
537
 
538
+ if (!existsSync('./node_modules/')) {
539
+ p.log.error('Missing node_modules folder, please run npm install')
540
+ program.error('')
541
+ }
525
542
 
526
- let packageJson;
527
- try {
528
- packageJson = JSON.parse(readFileSync('./package.json', 'utf8'));
529
- } catch (err) {
530
- p.log.error("Invalid package.json, JSON parsing failed");
531
- console.error('json parse error: ', err)
532
- program.error('');
533
- }
543
+ let anyInvalid = false
534
544
 
535
- const { dependencies } = packageJson
536
- if (!dependencies) {
537
- p.log.error("Missing dependencies section in package.json");
538
- program.error('');
539
- }
545
+ const dependenciesObject = await Promise.all(Object.entries(dependencies as Record<string, string>)
540
546
 
541
- for (const [key, value] of Object.entries(dependencies)) {
542
- if (typeof value !== 'string') {
543
- p.log.error(`Invalid dependency ${key}: ${value}, expected string, got ${typeof value}`);
544
- program.error('');
545
- }
546
- }
547
+ .map(async ([key, value]) => {
548
+ const dependencyFolderExists = existsSync(`./node_modules/${key}`)
547
549
 
548
- if (!existsSync('./node_modules/')) {
549
- p.log.error('Missing node_modules folder, please run npm install');
550
- program.error('');
551
- }
550
+ if (!dependencyFolderExists) {
551
+ anyInvalid = true
552
+ p.log.error(`Missing dependency ${key}, please run npm install`)
553
+ return { name: key, version: value }
554
+ }
555
+
556
+ let hasNativeFiles = false
557
+ await promiseFiles(`./node_modules/${key}`)
558
+ .then((files) => {
559
+ if (files.find(fileName => nativeFileRegex.test(fileName)))
560
+ hasNativeFiles = true
561
+ })
562
+ .catch((error) => {
563
+ p.log.error(`Error reading node_modulses files for ${key} package`)
564
+ console.error(error)
565
+ program.error('')
566
+ })
552
567
 
553
- let anyInvalid = false;
554
-
555
- const dependenciesObject = await Promise.all(Object.entries(dependencies as Record<string, string>)
556
- // eslint-disable-next-line consistent-return
557
- .map(async ([key, value]) => {
558
- const dependencyFolderExists = existsSync(`./node_modules/${key}`)
559
-
560
- if (!dependencyFolderExists) {
561
- anyInvalid = true
562
- p.log.error(`Missing dependency ${key}, please run npm install`);
563
- return { name: key, version: value }
564
- }
565
-
566
- let hasNativeFiles = false;
567
- await promiseFiles(`./node_modules/${key}`)
568
- .then(files => {
569
- if (files.find(fileName => nativeFileRegex.test(fileName))) {
570
- hasNativeFiles = true;
571
- }
572
- })
573
- .catch(error => {
574
- p.log.error(`Error reading node_modulses files for ${key} package`)
575
- console.error(error)
576
- program.error('')
577
- })
578
-
579
- return {
580
- name: key,
581
- version: value,
582
- native: hasNativeFiles,
583
- }
584
- })).catch(() => [])
585
-
586
-
587
- if (anyInvalid || dependenciesObject.find((a) => a.native === undefined))
588
- program.error('');
589
-
590
- return dependenciesObject as { name: string; version: string; native: boolean; }[];
568
+ return {
569
+ name: key,
570
+ version: value,
571
+ native: hasNativeFiles,
572
+ }
573
+ })).catch(() => [])
574
+
575
+ if (anyInvalid || dependenciesObject.find(a => a.native === undefined))
576
+ program.error('')
577
+
578
+ return dependenciesObject as { name: string, version: string, native: boolean }[]
591
579
  }
592
580
 
593
581
  export async function getRemoteDepenencies(supabase: SupabaseClient<Database>, appId: string, channel: string) {
594
- const { data: remoteNativePackages, error } = await supabase
595
- .from('channels')
596
- .select(`version (
582
+ const { data: remoteNativePackages, error } = await supabase
583
+ .from('channels')
584
+ .select(`version (
597
585
  native_packages
598
586
  )`)
599
- .eq('name', channel)
600
- .eq('app_id', appId)
601
- .single()
602
-
603
-
604
- if (error) {
605
- p.log.error(`Error fetching native packages: ${error.message}`);
606
- program.error('');
587
+ .eq('name', channel)
588
+ .eq('app_id', appId)
589
+ .single()
590
+
591
+ if (error) {
592
+ p.log.error(`Error fetching native packages: ${error.message}`)
593
+ program.error('')
594
+ }
595
+
596
+ let castedRemoteNativePackages
597
+ try {
598
+ castedRemoteNativePackages = (remoteNativePackages as any).version.native_packages
599
+ }
600
+ catch (err) {
601
+ // If we do not do this we will get an unreadable
602
+ p.log.error(`Error parsing native packages`)
603
+ program.error('')
604
+ }
605
+
606
+ if (!castedRemoteNativePackages) {
607
+ p.log.error(`Error parsing native packages, perhaps the metadata does not exist?`)
608
+ program.error('')
609
+ }
610
+
611
+ // Check types
612
+ castedRemoteNativePackages.forEach((data: any) => {
613
+ if (typeof data !== 'object') {
614
+ p.log.error(`Invalid remote native package data: ${data}, expected object, got ${typeof data}`)
615
+ program.error('')
607
616
  }
608
617
 
609
- let castedRemoteNativePackages
610
- try {
611
- castedRemoteNativePackages = (remoteNativePackages as any).version.native_packages
612
- } catch (err) {
613
- // If we do not do this we will get an unreadable
614
- p.log.error(`Error parsing native packages`);
615
- program.error('');
618
+ const { name, version } = data
619
+ if (!name || typeof name !== 'string') {
620
+ p.log.error(`Invalid remote native package name: ${name}, expected string, got ${typeof name}`)
621
+ program.error('')
616
622
  }
617
623
 
618
- if (!castedRemoteNativePackages) {
619
- p.log.error(`Error parsing native packages, perhaps the metadata does not exist?`);
620
- program.error('');
624
+ if (!version || typeof version !== 'string') {
625
+ p.log.error(`Invalid remote native package version: ${version}, expected string, got ${typeof version}`)
626
+ program.error('')
621
627
  }
628
+ })
622
629
 
623
- // Check types
624
- castedRemoteNativePackages.forEach((data: any) => {
625
- if (typeof data !== 'object') {
626
- p.log.error(`Invalid remote native package data: ${data}, expected object, got ${typeof data}`);
627
- program.error('');
628
- }
630
+ const mappedRemoteNativePackages = new Map((castedRemoteNativePackages as { name: string, version: string }[])
631
+ .map(a => [a.name, a]))
629
632
 
630
- const { name, version } = data
631
- if (!name || typeof name !== 'string') {
632
- p.log.error(`Invalid remote native package name: ${name}, expected string, got ${typeof name}`);
633
- program.error('');
634
- }
633
+ return mappedRemoteNativePackages
634
+ }
635
635
 
636
- if (!version || typeof version !== 'string') {
637
- p.log.error(`Invalid remote native package version: ${version}, expected string, got ${typeof version}`);
638
- program.error('');
636
+ export async function checkCompatibility(supabase: SupabaseClient<Database>, appId: string, channel: string) {
637
+ const dependenciesObject = await getLocalDepenencies()
638
+ const mappedRemoteNativePackages = await getRemoteDepenencies(supabase, appId, channel)
639
+
640
+ const finalDepenencies:
641
+ ({
642
+ name: string
643
+ localVersion: string
644
+ remoteVersion: string
645
+ } | {
646
+ name: string
647
+ localVersion: string
648
+ remoteVersion: undefined
649
+ } | {
650
+ name: string
651
+ localVersion: undefined
652
+ remoteVersion: string
653
+ })[] = dependenciesObject
654
+ .filter(a => !!a.native)
655
+ .map((local) => {
656
+ const remotePackage = mappedRemoteNativePackages.get(local.name)
657
+ if (remotePackage) {
658
+ return {
659
+ name: local.name,
660
+ localVersion: local.version,
661
+ remoteVersion: remotePackage.version,
639
662
  }
640
- })
663
+ }
641
664
 
642
- const mappedRemoteNativePackages = new Map((castedRemoteNativePackages as { name: string, version: string }[])
643
- .map(a => [a.name, a]))
665
+ return {
666
+ name: local.name,
667
+ localVersion: local.version,
668
+ remoteVersion: undefined,
669
+ }
670
+ })
644
671
 
645
- return mappedRemoteNativePackages
646
- }
672
+ const removeNotInLocal = [...mappedRemoteNativePackages]
673
+ .filter(([remoteName]) => dependenciesObject.find(a => a.name === remoteName) === undefined)
674
+ .map(([name, version]) => ({ name, localVersion: undefined, remoteVersion: version.version }))
647
675
 
648
- export async function checkCompatibility(supabase: SupabaseClient<Database>, appId: string, channel: string) {
649
- const dependenciesObject = await getLocalDepenencies()
650
- const mappedRemoteNativePackages = await getRemoteDepenencies(supabase, appId, channel)
651
-
652
- const finalDepenencies:
653
- ({
654
- name: string;
655
- localVersion: string;
656
- remoteVersion: string;
657
- } | {
658
- name: string;
659
- localVersion: string;
660
- remoteVersion: undefined;
661
- } | {
662
- name: string;
663
- localVersion: undefined;
664
- remoteVersion: string;
665
- })[] = dependenciesObject
666
- .filter((a) => !!a.native)
667
- .map((local) => {
668
- const remotePackage = mappedRemoteNativePackages.get(local.name)
669
- if (remotePackage)
670
- return {
671
- name: local.name,
672
- localVersion: local.version,
673
- remoteVersion: remotePackage.version
674
- }
675
-
676
- return {
677
- name: local.name,
678
- localVersion: local.version,
679
- remoteVersion: undefined
680
- }
681
- })
682
-
683
- const removeNotInLocal = [...mappedRemoteNativePackages]
684
- .filter(([remoteName]) => dependenciesObject.find((a) => a.name === remoteName) === undefined)
685
- .map(([name, version]) => ({ name, localVersion: undefined, remoteVersion: version.version }));
686
-
687
- finalDepenencies.push(...removeNotInLocal)
676
+ finalDepenencies.push(...removeNotInLocal)
688
677
 
689
- return {
690
- finalCompatibility: finalDepenencies,
691
- localDependencies: dependenciesObject,
692
- }
678
+ return {
679
+ finalCompatibility: finalDepenencies,
680
+ localDependencies: dependenciesObject,
681
+ }
693
682
  }