@capgo/cli 3.14.63 → 3.14.67

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/cli",
3
- "version": "3.14.63",
3
+ "version": "3.14.67",
4
4
  "description": "A CLI to upload to capgo servers",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -14,8 +14,8 @@
14
14
  "url": "https://github.com/Cap-go/capgo-cli/issues"
15
15
  },
16
16
  "engines": {
17
- "npm": ">=7.0.0",
18
- "node": ">=16.0.0"
17
+ "npm": ">=8.0.0",
18
+ "node": ">=20.0.0"
19
19
  },
20
20
  "keywords": [
21
21
  "appflow alternative",
@@ -29,11 +29,11 @@
29
29
  "capgo-cli"
30
30
  ],
31
31
  "scripts": {
32
- "dev": "set NODE_ENV=development && npx webpack --config webpack.config.js",
32
+ "build": "node build.mjs",
33
+ "dev": "NODE_ENV=development node build.mjs",
33
34
  "no-debug": "node dist/index.js",
34
35
  "test": "npx --yes ts-node -T src/index.ts",
35
- "build": "npx --yes webpack --config webpack.config.js",
36
- "dev-build": "SUPA_DB=development npx webpack --config webpack.config.js",
36
+ "dev-build": "SUPA_DB=development node build.mjs",
37
37
  "pack": "pkg",
38
38
  "types": "npx --yes supabase gen types typescript --project-id=xvwzpoazmxkqosrdewyv > src/types/supabase.types.ts",
39
39
  "test_rls": "ts-node ./test/test_headers_rls.ts",
@@ -42,56 +42,53 @@
42
42
  "author": "github.com/riderx",
43
43
  "license": "Apache 2.0",
44
44
  "dependencies": {
45
- "@capacitor/cli": "5.5.1",
46
- "@capgo/find-package-manager": "^0.0.7",
45
+ "@capacitor/cli": "5.7.0",
46
+ "@capgo/find-package-manager": "^0.0.9",
47
47
  "@clack/prompts": "^0.7.0",
48
- "@supabase/supabase-js": "^2.38.5",
48
+ "@supabase/supabase-js": "^2.39.3",
49
49
  "@tomasklaen/checksum": "^1.1.0",
50
50
  "@trufflesuite/spinnies": "^0.1.1",
51
51
  "adm-zip": "^0.5.10",
52
- "axios": "^1.6.2",
53
52
  "ci-info": "^4.0.0",
54
- "commander": "11.1.0",
55
- "console-table-printer": "^2.11.2",
53
+ "commander": "12.0.0",
54
+ "console-table-printer": "^2.12.0",
56
55
  "get-latest-version": "^5.1.0",
56
+ "ky": "^1.2.0",
57
57
  "logsnag": "1.0.0",
58
- "mime": "^3.0.0",
58
+ "mime": "^4.0.1",
59
59
  "node-dir": "^0.1.17",
60
- "open": "^9.1.0",
60
+ "open": "^10.0.3",
61
61
  "prettyjson": "^1.2.5",
62
62
  "prompt-sync": "^4.2.0",
63
63
  "qrcode": "^1.5.3",
64
- "semver": "^7.5.4"
64
+ "semver": "^7.6.0"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@types/adm-zip": "0.5.5",
68
68
  "@types/mime": "^3.0.4",
69
- "@types/node": "^20.9.4",
69
+ "@types/node": "^20.11.17",
70
70
  "@types/node-dir": "^0.0.37",
71
71
  "@types/npmcli__ci-detect": "^2.0.3",
72
72
  "@types/prettyjson": "^0.0.33",
73
73
  "@types/prompt-sync": "^4.2.3",
74
74
  "@types/qrcode": "^1.5.5",
75
75
  "@types/semver": "^7.5.6",
76
- "@typescript-eslint/eslint-plugin": "6.12.0",
77
- "@typescript-eslint/parser": "6.12.0",
78
- "eslint": "8.55.0",
76
+ "@typescript-eslint/eslint-plugin": "6.21.0",
77
+ "@typescript-eslint/parser": "6.21.0",
78
+ "esbuild": "^0.20.0",
79
+ "eslint": "8.56.0",
79
80
  "eslint-config-airbnb-base": "^15.0.0",
80
- "eslint-config-prettier": "^9.0.0",
81
+ "eslint-config-prettier": "^9.1.0",
81
82
  "eslint-import-resolver-typescript": "3.6.1",
82
- "eslint-plugin-import": "2.29.0",
83
- "eslint-plugin-prettier": "^5.0.1",
83
+ "eslint-plugin-import": "2.29.1",
84
+ "eslint-plugin-prettier": "^5.1.3",
84
85
  "git-format-staged": "3.0.0",
85
- "husky": "^8.0.3",
86
- "nodemon": "3.0.2",
86
+ "husky": "^9.0.10",
87
87
  "pkg": "5.8.1",
88
- "prettier": "3.1.1",
88
+ "prettier": "3.2.5",
89
89
  "ts-loader": "^9.5.1",
90
- "ts-node": "^10.9.1",
90
+ "ts-node": "^10.9.2",
91
91
  "tsconfig-paths": "4.2.0",
92
- "typescript": "5.3.3",
93
- "webpack": "5.89.0",
94
- "webpack-cli": "^5.1.4",
95
- "webpack-node-externals": "^3.0.0"
92
+ "typescript": "5.3.3"
96
93
  }
97
94
  }
package/src/api/app.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { SupabaseClient } from '@supabase/supabase-js';
2
2
  import * as p from '@clack/prompts';
3
3
  import { program } from 'commander';
4
- import { Database } from 'types/supabase.types';
4
+ import { Database } from '../types/supabase.types';
5
5
  import { isAllowedApp, isAllowedAppOrg, OptionsBase, OrganizationPerm } from '../utils';
6
6
 
7
7
  export const checkAppExists = async (supabase: SupabaseClient<Database>, appid: string) => {
@@ -2,7 +2,7 @@ import { SupabaseClient } from '@supabase/supabase-js';
2
2
  import { program } from 'commander';
3
3
  import { Table } from 'console-table-printer';
4
4
  import * as p from '@clack/prompts';
5
- import { Database } from 'types/supabase.types';
5
+ import { Database } from '../types/supabase.types';
6
6
  import { formatError, getHumanDate } from '../utils';
7
7
 
8
8
  export const checkVersionNotUsedInChannel = async (supabase: SupabaseClient<Database>,
package/src/api/crypto.ts CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  constants,
5
5
  publicEncrypt, privateDecrypt,
6
6
  randomBytes, createCipheriv, createDecipheriv
7
- } from 'crypto';
7
+ } from 'node:crypto';
8
8
 
9
9
  const algorithm = "aes-128-cbc";
10
10
  const oaepHash = 'sha256';
@@ -1,7 +1,7 @@
1
1
  import { SupabaseClient } from '@supabase/supabase-js';
2
2
  import { program } from 'commander';
3
3
  import * as p from '@clack/prompts';
4
- import { Database } from 'types/supabase.types';
4
+ import { Database } from '../types/supabase.types';
5
5
  import { formatError } from '../utils';
6
6
 
7
7
  export const checkVersionNotUsedInDeviceOverride = async (supabase: SupabaseClient<Database>,
@@ -2,7 +2,7 @@ import { SupabaseClient } from '@supabase/supabase-js';
2
2
  import { program } from 'commander';
3
3
  import { Table } from 'console-table-printer';
4
4
  import * as p from '@clack/prompts';
5
- import { Database } from 'types/supabase.types';
5
+ import { Database } from '../types/supabase.types';
6
6
  // import { definitions } from '../types/types_supabase';
7
7
  import { getHumanDate } from '../utils';
8
8
  import { checkVersionNotUsedInChannel } from './channels';
package/src/app/add.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { randomUUID } from 'crypto';
2
- import { getType } from 'mime';
1
+ import { randomUUID } from 'node:crypto';
2
+ import mime from 'mime';
3
3
  import { program } from 'commander';
4
4
  import * as p from '@clack/prompts';
5
5
  import { existsSync, readFileSync } from 'node:fs';
@@ -56,13 +56,13 @@ export const addApp = async (appId: string, options: Options, throwErr = true) =
56
56
 
57
57
  if (icon && existsSync(icon)) {
58
58
  iconBuff = readFileSync(icon);
59
- const contentType = getType(icon);
59
+ const contentType = mime.getType(icon);
60
60
  iconType = contentType || 'image/png';
61
61
  p.log.warn(`Found app icon ${icon}`);
62
62
  }
63
63
  else if (existsSync(newIconPath)) {
64
64
  iconBuff = readFileSync(newIconPath);
65
- const contentType = getType(newIconPath);
65
+ const contentType = mime.getType(newIconPath);
66
66
  iconType = contentType || 'image/png';
67
67
  p.log.warn(`Found app icon ${newIconPath}`);
68
68
  } else {
package/src/app/debug.ts CHANGED
@@ -2,7 +2,7 @@ import * as p from '@clack/prompts';
2
2
  import { SupabaseClient } from '@supabase/supabase-js';
3
3
  import { program } from 'commander';
4
4
  import LogSnag from 'logsnag';
5
- import { Database } from 'types/supabase.types';
5
+ import { Database } from '../types/supabase.types';
6
6
  import { checkAppExistsAndHasPermissionErr } from '../api/app';
7
7
  import { checkLatest } from '../api/update';
8
8
  import { convertAppName, createSupabaseClient, findSavedKey, getLocalConfig, useLogSnag, verifyUser, getConfig } from '../utils';
@@ -50,7 +50,9 @@ interface QueryStats {
50
50
  export async function getStats(supabase: SupabaseClient<Database>, query: QueryStats)
51
51
  : Promise<Database['public']['Tables']['stats']['Row'] | null> {
52
52
  try {
53
- const res = await supabase.functions.invoke('get_stats', { body: JSON.stringify(query) })
53
+ const pathStats = 'get_stats'
54
+ // const pathStats = 'private/stats' // TODO: switch to new endpoint when new backend released
55
+ const res = await supabase.functions.invoke(pathStats, { body: JSON.stringify(query) })
54
56
  const listData = res.data.data as Database['public']['Tables']['stats']['Row'][]
55
57
  if (listData?.length > 0) {
56
58
  return listData[0]
package/src/app/info.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from "fs"
2
- import { join } from "path"
3
- import os from 'os';
2
+ import { join } from "node:path"
3
+ import os from 'node:os';
4
4
  import getLatest from "get-latest-version"
5
5
  import Spinnies from '@trufflesuite/spinnies';
6
6
  import pack from '../../package.json'
package/src/app/list.ts CHANGED
@@ -2,7 +2,7 @@ import { program } from 'commander';
2
2
  import { Table } from 'console-table-printer';
3
3
  import { SupabaseClient } from '@supabase/supabase-js';
4
4
  import * as p from '@clack/prompts';
5
- import { Database } from 'types/supabase.types';
5
+ import { Database } from '../types/supabase.types';
6
6
  import { OptionsBase, createSupabaseClient, findSavedKey, getHumanDate, verifyUser } from '../utils';
7
7
  import { checkLatest } from '../api/update';
8
8
 
package/src/app/set.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from "crypto";
2
- import { getType } from 'mime';
2
+ import mime from 'mime';
3
3
  import { program } from "commander";
4
4
  import * as p from '@clack/prompts';
5
5
  import { existsSync, readFileSync } from "node:fs";
@@ -44,13 +44,13 @@ export const setApp = async (appId: string, options: Options) => {
44
44
 
45
45
  if (icon && existsSync(icon)) {
46
46
  iconBuff = readFileSync(icon);
47
- const contentType = getType(icon);
47
+ const contentType = mime.getType(icon);
48
48
  iconType = contentType || 'image/png';
49
49
  p.log.warn(`Found app icon ${icon}`);
50
50
  }
51
51
  else if (existsSync(newIconPath)) {
52
52
  iconBuff = readFileSync(newIconPath);
53
- const contentType = getType(newIconPath);
53
+ const contentType = mime.getType(newIconPath);
54
54
  iconType = contentType || 'image/png';
55
55
  p.log.warn(`Found app icon ${newIconPath}`);
56
56
  } else {
@@ -1,4 +1,4 @@
1
- import fs from "fs";
1
+ import fs from 'node:fs';
2
2
  import path from "path";
3
3
 
4
4
  const searchInFile = (filePath: string, searchString: string) => {
@@ -3,7 +3,7 @@ import semver from 'semver/preload';
3
3
  import * as p from '@clack/prompts';
4
4
  import promptSync from 'prompt-sync';
5
5
  import { SupabaseClient } from '@supabase/supabase-js';
6
- import { Database } from 'types/supabase.types';
6
+ import { Database } from '../types/supabase.types';
7
7
  import { OptionsBase, createSupabaseClient, findSavedKey, getConfig, getHumanDate, verifyUser } from '../utils';
8
8
  import { deleteSpecificVersion, displayBundles, getActiveAppVersions, getChannelsVersion } from '../api/versions';
9
9
  import { checkAppExistsAndHasPermissionErr } from '../api/app';
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync, writeFileSync } from 'fs'
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs'
2
2
  import { program } from 'commander'
3
3
  import * as p from '@clack/prompts';
4
4
  import { decryptSource } from '../api/crypto';
@@ -1,11 +1,11 @@
1
1
  import { program } from 'commander';
2
2
  import * as p from '@clack/prompts';
3
- import { getVersionData } from 'api/versions';
3
+ import { getVersionData } from '../api/versions';
4
4
  import { checkVersionNotUsedInDeviceOverride } from '../api/devices_override';
5
5
  import { checkVersionNotUsedInChannel } from '../api/channels';
6
6
  import { checkAppExistsAndHasPermissionErr } from "../api/app";
7
7
  import {
8
- OptionsBase,
8
+ OptionsBase,
9
9
  getConfig, createSupabaseClient,
10
10
  formatError, findSavedKey, checkPlanValid, useLogSnag, verifyUser
11
11
  } from '../utils';
@@ -47,7 +47,7 @@ export const unlinkDevice = async (channel: string, appId: string, options: Opti
47
47
  program.error('');
48
48
  }
49
49
  try {
50
- await checkPlanValid(supabase, userId)
50
+ await checkPlanValid(supabase, userId, appId, options.apikey)
51
51
 
52
52
  const versionData = await getVersionData(supabase, appId, userId, bundle);
53
53
  await checkVersionNotUsedInChannel(supabase, appId, userId, versionData);
@@ -1,11 +1,11 @@
1
- import { randomUUID } from 'crypto';
2
- import { existsSync, readFileSync } from 'fs';
1
+ import { randomUUID } from 'node:crypto';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
3
  import AdmZip from 'adm-zip';
4
4
  import { program } from 'commander';
5
5
  import * as p from '@clack/prompts';
6
6
  import { checksum as getChecksum } from '@tomasklaen/checksum';
7
7
  import ciDetect from 'ci-info';
8
- import axios from "axios";
8
+ import ky from 'ky';
9
9
  import { checkLatest } from '../api/update';
10
10
  import { checkAppExistsAndHasPermissionOrgErr } from "../api/app";
11
11
  import { encryptSource } from '../api/crypto';
@@ -19,7 +19,8 @@ import {
19
19
  getLocalDepenencies,
20
20
  verifyUser,
21
21
  OrganizationPerm,
22
- hasOrganizationPerm
22
+ hasOrganizationPerm,
23
+ getAppOwner
23
24
  } from '../utils';
24
25
  import { checkIndexPosition, searchInDirectory } from './check';
25
26
 
@@ -54,10 +55,10 @@ export const uploadBundle = async (appid: string, options: Options, shouldExit =
54
55
  channel = channel || 'dev';
55
56
 
56
57
  const config = await getConfig();
57
- const localS3: boolean = (config.app.extConfig.plugins && config.app.extConfig.plugins.CapacitorUpdater
58
+ const localS3: boolean = (config.app.extConfig.plugins && config.app.extConfig.plugins.CapacitorUpdater
58
59
  && config.app.extConfig.plugins.CapacitorUpdater.localS3) === true;
59
60
 
60
- const checkNotifyAppReady = options.codeCheck
61
+ const checkNotifyAppReady = options.codeCheck
61
62
  appid = appid || config?.app?.appId
62
63
  // create bundle name format : 1.0.0-beta.x where x is a uuid
63
64
  const uuid = randomUUID().split('-')[0];
@@ -90,8 +91,8 @@ export const uploadBundle = async (appid: string, options: Options, shouldExit =
90
91
  }
91
92
  const foundIndex = checkIndexPosition(path);
92
93
  if (!foundIndex) {
93
- p.log.error(`index.html is missing in the root folder or in the only folder in the root folder`);
94
- program.error('');
94
+ p.log.error(`index.html is missing in the root folder or in the only folder in the root folder`);
95
+ program.error('');
95
96
  }
96
97
  }
97
98
 
@@ -100,22 +101,22 @@ export const uploadBundle = async (appid: string, options: Options, shouldExit =
100
101
  const localConfig = await getLocalConfig()
101
102
  const supabase = await createSupabaseClient(options.apikey)
102
103
  const userId = await verifyUser(supabase, options.apikey, ['write', 'all', 'upload']);
103
- await checkPlanValid(supabase, userId, false)
104
104
  // Check we have app access to this appId
105
105
  // await checkAppExistsAndHasPermissionErr(supabase, options.apikey, appid);
106
106
 
107
107
  const permissions = await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appid, OrganizationPerm.upload)
108
+ await checkPlanValid(supabase, userId, appid, options.apikey, false)
108
109
 
109
110
  const updateMetadataRequired = await requireUpdateMetadata(supabase, channel)
110
111
 
111
112
  // Check compatibility here
112
113
  const { data: channelData, error: channelError } = await supabase
113
- .from('channels')
114
- .select('version ( minUpdateVersion, native_packages )')
115
- .eq('name', channel)
116
- .eq('app_id', appid)
117
- .single()
118
-
114
+ .from('channels')
115
+ .select('version ( minUpdateVersion, native_packages )')
116
+ .eq('name', channel)
117
+ .eq('app_id', appid)
118
+ .single()
119
+
119
120
  // eslint-disable-next-line no-undef-init
120
121
  let localDependencies: Awaited<ReturnType<typeof getLocalDepenencies>> | undefined = undefined;
121
122
  let finalCompatibility: Awaited<ReturnType<typeof checkCompatibility>>['finalCompatibility'];
@@ -124,14 +125,14 @@ export const uploadBundle = async (appid: string, options: Options, shouldExit =
124
125
  if (!channelError && channelData && channelData.version && (channelData.version as any).native_packages && !ignoreMetadataCheck) {
125
126
  const spinner = p.spinner();
126
127
  spinner.start(`Checking bundle compatibility with channel ${channel}`);
127
- const {
128
+ const {
128
129
  finalCompatibility: finalCompatibilityWithChannel,
129
- localDependencies: localDependenciesWithChannel
130
+ localDependencies: localDependenciesWithChannel
130
131
  } = await checkCompatibility(supabase, appid, channel)
131
132
 
132
133
  finalCompatibility = finalCompatibilityWithChannel
133
134
  localDependencies = localDependenciesWithChannel
134
-
135
+
135
136
  if (finalCompatibility.find((x) => x.localVersion !== x.remoteVersion)) {
136
137
  p.log.error(`Your bundle is not compatible with the channel ${channel}`);
137
138
  p.log.warn(`You can check compatibility with "npx @capgo/cli bundle compatibility"`);
@@ -147,7 +148,7 @@ export const uploadBundle = async (appid: string, options: Options, shouldExit =
147
148
  p.log.error('Invalid remote min update version, skipping auto setting compatibility');
148
149
  program.error('');
149
150
  }
150
-
151
+
151
152
  minUpdateVersion = lastMinUpdateVersion
152
153
  p.log.info(`Auto set min-update-version to ${minUpdateVersion}`);
153
154
  } catch (error) {
@@ -285,15 +286,17 @@ It will be also visible in your dashboard\n`);
285
286
  }
286
287
 
287
288
  const hashedLocalDependencies = localDependencies ? new Map(localDependencies
288
- .filter((a) => !!a.native && a.native !== undefined)
289
- .map((a) => [a.name, a])) : new Map()
289
+ .filter((a) => !!a.native && a.native !== undefined)
290
+ .map((a) => [a.name, a])) : new Map()
290
291
 
291
292
  // eslint-disable-next-line max-len
292
293
  const nativePackages = (hashedLocalDependencies.size > 0 || !options.ignoreMetadataCheck) ? Array.from(hashedLocalDependencies, ([name, value]) => ({ name, version: value.version })) : undefined
293
294
 
295
+ const appOwner = await getAppOwner(supabase, appid)
296
+
294
297
  const versionData = {
295
298
  bucket_id: external ? undefined : fileName,
296
- user_id: userId,
299
+ user_id: appOwner,
297
300
  name: bundle,
298
301
  app_id: appid,
299
302
  session_key: sessionKey,
@@ -317,20 +320,18 @@ It will be also visible in your dashboard\n`);
317
320
  p.log.error(`Cannot get upload url`);
318
321
  program.error('');
319
322
  }
320
-
321
- await axios({
322
- method: "put",
323
- url,
324
- data: zipped,
323
+ await ky.put(url, {
324
+ body: zipped,
325
325
  headers: (!localS3 ? {
326
- "Content-Type": "application/octet-stream",
327
- "Cache-Control": "public, max-age=456789, immutable",
328
- "x-amz-meta-crc32": checksum,
326
+ "Content-Type": "application/octet-stream",
327
+ "Cache-Control": "public, max-age=456789, immutable",
328
+ "x-amz-meta-crc32": checksum,
329
329
  } : undefined)
330
330
  })
331
331
  versionData.storage_provider = 'r2'
332
332
  const { error: dbError2 } = await updateOrCreateVersion(supabase, versionData, options.apikey)
333
333
  if (dbError2) {
334
+ console.log(dbError2)
334
335
  p.log.error(`Cannot update bundle ${formatError(dbError)}`);
335
336
  program.error('');
336
337
  }
@@ -344,11 +345,12 @@ It will be also visible in your dashboard\n`);
344
345
  const { error: dbError3, data } = await updateOrCreateChannel(supabase, {
345
346
  name: channel,
346
347
  app_id: appid,
347
- created_by: userId,
348
+ created_by: appOwner,
348
349
  version: versionId,
349
350
  })
350
351
  if (dbError3) {
351
352
  p.log.error(`Cannot set channel, the upload key is not allowed to do that, use the "all" for this.`);
353
+ console.log(dbError3)
352
354
  program.error('');
353
355
  }
354
356
  const appidWeb = convertAppName(appid)
@@ -359,7 +361,7 @@ It will be also visible in your dashboard\n`);
359
361
  p.log.info(`Link device to this bundle to try it: ${bundleUrl}`);
360
362
  }
361
363
 
362
- if(options.bundleUrl) {
364
+ if (options.bundleUrl) {
363
365
  p.log.info(`Bundle url: ${bundleUrl}`);
364
366
  }
365
367
  } else if (!versionId) {
package/src/bundle/zip.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { randomUUID } from 'crypto';
2
- import { writeFileSync } from 'fs';
1
+ import { randomUUID } from 'node:crypto';
2
+ import { writeFileSync } from 'node:fs';
3
3
  import AdmZip from 'adm-zip';
4
4
  import { program } from 'commander';
5
5
  import * as p from '@clack/prompts';
@@ -1,6 +1,6 @@
1
1
  import { program } from 'commander';
2
2
  import * as p from '@clack/prompts';
3
- import { Database } from 'types/supabase.types';
3
+ import { Database } from '../types/supabase.types';
4
4
  import { checkAppExistsAndHasPermissionErr } from "../api/app";
5
5
  import {
6
6
  OptionsBase,
@@ -66,7 +66,7 @@ export const setChannel = async (channel: string, appId: string, options: Option
66
66
  program.error('');
67
67
  }
68
68
  try {
69
- await checkPlanValid(supabase, userId)
69
+ await checkPlanValid(supabase, userId, appId, options.apikey)
70
70
  const channelPayload: Database['public']['Tables']['channels']['Insert'] = {
71
71
  created_by: userId,
72
72
  app_id: appId,
package/src/index.ts CHANGED
@@ -64,7 +64,6 @@ app
64
64
 
65
65
  app
66
66
  .command('delete [appId]')
67
- .alias('d')
68
67
  .description('Delete an app in Capgo Cloud')
69
68
  .action(deleteApp)
70
69
  .option('-a, --apikey <apikey>', 'apikey to link to your account');
@@ -78,7 +77,6 @@ app
78
77
 
79
78
  app
80
79
  .command('debug [appId]')
81
- .alias('d')
82
80
  .description('Listen for live updates event in Capgo Cloud to debug your app')
83
81
  .option('-a, --apikey <apikey>', 'apikey to link to your account')
84
82
  .option('-d, --device <device>', 'the specific device to debug')
@@ -151,7 +149,6 @@ bundle
151
149
 
152
150
  bundle
153
151
  .command('unlink [appId]')
154
- .alias('u')
155
152
  .description('Unlink a bundle in Capgo Cloud')
156
153
  .action(listBundle)
157
154
  .option('-a, --apikey <apikey>', 'apikey to link to your account')
@@ -169,7 +166,6 @@ bundle
169
166
 
170
167
  bundle
171
168
  .command('decrypt [zipPath] [sessionKey]')
172
- .alias('l')
173
169
  .description('Decrypt a signed zip bundle')
174
170
  .action(decryptZip)
175
171
  .option('--key <key>', 'custom path for private signing key')
package/src/init.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { writeFileSync, readFileSync } from 'fs';
1
+ import { writeFileSync, readFileSync } from 'node:fs';
2
2
  import { execSync, ExecSyncOptions, spawnSync } from 'child_process';
3
3
  import { findPackageManagerType } from '@capgo/find-package-manager'
4
4
  import * as p from '@clack/prompts';
5
5
  import { SupabaseClient } from '@supabase/supabase-js';
6
6
  import LogSnag from 'logsnag';
7
7
  import semver from 'semver'
8
- import { Database } from 'types/supabase.types';
8
+ import { Database } from './types/supabase.types';
9
9
  import { markSnag , waitLog } from './app/debug';
10
10
  import { createKey } from './key';
11
11
  import { addChannel } from './channel/add';
package/src/key.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, readFileSync, writeFileSync } from 'fs'
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs'
2
2
  import { program } from 'commander'
3
3
  import { writeConfig } from '@capacitor/cli/dist/config';
4
4
  import * as p from '@clack/prompts';
package/src/login.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { existsSync, writeFileSync, appendFileSync } from 'fs'
2
- import { homedir } from 'os'
1
+ import { existsSync, writeFileSync, appendFileSync } from 'node:fs'
2
+ import { homedir } from 'node:os'
3
3
  import { program } from 'commander';
4
4
  import * as p from '@clack/prompts';
5
5
  import { createSupabaseClient, useLogSnag, verifyUser } from './utils';
package/src/utils.ts CHANGED
@@ -1,15 +1,15 @@
1
- import { existsSync, readdirSync, readFileSync } from 'fs';
2
- import { homedir } from 'os';
3
- import { resolve } from 'path';
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { resolve } from 'node:path';
4
4
  import { loadConfig } from '@capacitor/cli/dist/config';
5
5
  import { program } from 'commander';
6
6
  import { createClient, SupabaseClient } from '@supabase/supabase-js';
7
7
  import prettyjson from 'prettyjson';
8
8
  import { LogSnag } from 'logsnag';
9
9
  import * as p from '@clack/prompts';
10
- import { Database } from 'types/supabase.types';
11
- import axios from 'axios';
10
+ import ky from 'ky';
12
11
  import { promiseFiles } from 'node-dir'
12
+ import { Database } from './types/supabase.types';
13
13
 
14
14
  export const baseKey = '.capgo_key';
15
15
  export const baseKeyPub = `${baseKey}.pub`;
@@ -68,9 +68,9 @@ interface CapgoConfig {
68
68
  export const getRemoteConfig = async () => {
69
69
  // call host + /api/get_config and parse the result as json using axios
70
70
  const localConfig = await getLocalConfig()
71
- return axios
71
+ return ky
72
72
  .get(`${defaultApiHost}/get_config`)
73
- .then((res) => res.data as CapgoConfig)
73
+ .then((res) => res.json<CapgoConfig>())
74
74
  .then(data => ({ ...data, ...localConfig } as CapgoConfig))
75
75
  .catch(() => {
76
76
  console.log('Local config', localConfig);
@@ -144,6 +144,31 @@ export const isAllowedAction = async (supabase: SupabaseClient<Database>, userId
144
144
  return !!data
145
145
  }
146
146
 
147
+ export const isAllowedActionAppIdApiKey = async (supabase: SupabaseClient<Database>, appId: string, apikey: string): Promise<boolean> => {
148
+ const { data } = await supabase
149
+ .rpc('is_allowed_action', { apikey, appid: appId })
150
+ .single()
151
+
152
+ return !!data
153
+ }
154
+
155
+ export const getAppOwner = async (supabase: SupabaseClient<Database>, appId: string): Promise<string> => {
156
+ const { data, error } = await supabase
157
+ .from('apps')
158
+ .select('user_id')
159
+ .eq('app_id', appId)
160
+ .single()
161
+
162
+ if (error) {
163
+ p.log.error('Cannot get app owner, exiting')
164
+ p.log.error('Please report the following error to capgo\'s staff')
165
+ console.error(error)
166
+ process.exit(1)
167
+ }
168
+
169
+ return data.user_id
170
+ }
171
+
147
172
  export const isAllowedApp = async (supabase: SupabaseClient<Database>, apikey: string, appId: string): Promise<boolean> => {
148
173
  const { data } = await supabase
149
174
  .rpc('is_app_owner', { apikey, appid: appId })
@@ -251,9 +276,9 @@ export const isAllowedAppOrg = async (
251
276
  }
252
277
  }
253
278
 
254
- export const checkPlanValid = async (supabase: SupabaseClient<Database>, userId: string, warning = true) => {
279
+ export const checkPlanValid = async (supabase: SupabaseClient<Database>, userId: string, appId: string, apikey: string, warning = true) => {
255
280
  const config = await getRemoteConfig()
256
- const validPlan = await isAllowedAction(supabase, userId)
281
+ const validPlan = await isAllowedActionAppIdApiKey(supabase, appId, apikey)
257
282
  if (!validPlan) {
258
283
  p.log.error(`You need to upgrade your plan to continue to use capgo.\n Upgrade here: ${config.hostWeb}/dashboard/settings/plans\n`);
259
284
  setTimeout(() => {
@@ -379,7 +404,9 @@ export async function uploadUrl(supabase: SupabaseClient<Database>, appId: strin
379
404
  bucket_id: bucketId,
380
405
  }
381
406
  try {
382
- const res = await supabase.functions.invoke('upload_link', { body: JSON.stringify(data) })
407
+ const pathUploadLink = 'private/upload_link'
408
+ // const pathUploadLink = 'private/upload_link' // TODO: switch to new endpoint when new backend released
409
+ const res = await supabase.functions.invoke(pathUploadLink, { body: JSON.stringify(data) })
383
410
  return res.data.url
384
411
  } catch (error) {
385
412
  p.log.error(`Cannot get upload url ${JSON.stringify(error)}`);