@capgo/cli 4.8.0 → 4.8.2
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/CHANGELOG.md +4 -0
- package/dist/index.js +176 -118
- package/package.json +1 -1
- package/src/app/add.ts +12 -42
- package/src/app/debug.ts +13 -11
- package/src/bundle/upload.ts +27 -18
- package/src/index.ts +1 -0
- package/src/init.ts +53 -48
- package/src/utils.ts +135 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [4.8.2](https://github.com/Cap-go/CLI/compare/v4.8.1...v4.8.2) (2024-05-11)
|
|
6
|
+
|
|
7
|
+
### [4.8.1](https://github.com/Cap-go/CLI/compare/v4.8.0...v4.8.1) (2024-05-11)
|
|
8
|
+
|
|
5
9
|
## [4.8.0](https://github.com/Cap-go/CLI/compare/v4.7.0...v4.8.0) (2024-05-10)
|
|
6
10
|
|
|
7
11
|
|
package/dist/index.js
CHANGED
|
@@ -92373,7 +92373,7 @@ var {
|
|
|
92373
92373
|
// package.json
|
|
92374
92374
|
var package_default = {
|
|
92375
92375
|
name: "@capgo/cli",
|
|
92376
|
-
version: "4.8.
|
|
92376
|
+
version: "4.8.2",
|
|
92377
92377
|
description: "A CLI to upload to capgo servers",
|
|
92378
92378
|
author: "github.com/riderx",
|
|
92379
92379
|
license: "Apache 2.0",
|
|
@@ -93916,7 +93916,8 @@ async function updateOrCreateVersion(supabase, update) {
|
|
|
93916
93916
|
async function uploadUrl(supabase, appId, name) {
|
|
93917
93917
|
const data = {
|
|
93918
93918
|
app_id: appId,
|
|
93919
|
-
name
|
|
93919
|
+
name,
|
|
93920
|
+
version: 0
|
|
93920
93921
|
};
|
|
93921
93922
|
try {
|
|
93922
93923
|
const pathUploadLink = "private/upload_link";
|
|
@@ -93927,6 +93928,52 @@ async function uploadUrl(supabase, appId, name) {
|
|
|
93927
93928
|
}
|
|
93928
93929
|
return "";
|
|
93929
93930
|
}
|
|
93931
|
+
async function prepareMultipart(supabase, appId, name) {
|
|
93932
|
+
const data = {
|
|
93933
|
+
app_id: appId,
|
|
93934
|
+
name,
|
|
93935
|
+
version: 1
|
|
93936
|
+
};
|
|
93937
|
+
try {
|
|
93938
|
+
const pathUploadLink = "private/upload_link";
|
|
93939
|
+
const res = await supabase.functions.invoke(pathUploadLink, { body: JSON.stringify(data) });
|
|
93940
|
+
return res.data;
|
|
93941
|
+
} catch (error) {
|
|
93942
|
+
f2.error(`Cannot get upload url ${formatError(error)}`);
|
|
93943
|
+
return null;
|
|
93944
|
+
}
|
|
93945
|
+
}
|
|
93946
|
+
async function finishMultipartDownload(key2, uploadId, url, parts) {
|
|
93947
|
+
const metadata = {
|
|
93948
|
+
action: "mpu-complete",
|
|
93949
|
+
uploadId,
|
|
93950
|
+
key: key2
|
|
93951
|
+
};
|
|
93952
|
+
await distribution_default.post(url, {
|
|
93953
|
+
json: {
|
|
93954
|
+
parts
|
|
93955
|
+
},
|
|
93956
|
+
searchParams: new URLSearchParams({ body: btoa(JSON.stringify(metadata)) })
|
|
93957
|
+
});
|
|
93958
|
+
}
|
|
93959
|
+
var PART_SIZE = 10 * 1024 * 1024;
|
|
93960
|
+
async function uploadMultipart(supabase, appId, name, data) {
|
|
93961
|
+
try {
|
|
93962
|
+
const multipartPrep = await prepareMultipart(supabase, appId, name);
|
|
93963
|
+
if (!multipartPrep) {
|
|
93964
|
+
return false;
|
|
93965
|
+
}
|
|
93966
|
+
const fileSize = data.length;
|
|
93967
|
+
const partCount = Math.ceil(fileSize / PART_SIZE);
|
|
93968
|
+
const uploadPromises = Array.from({ length: partCount }, (_3, index) => uploadPart(data, PART_SIZE, multipartPrep.url, multipartPrep.key, multipartPrep.uploadId, index));
|
|
93969
|
+
const parts = await Promise.all(uploadPromises);
|
|
93970
|
+
await finishMultipartDownload(multipartPrep.key, multipartPrep.uploadId, multipartPrep.url, parts);
|
|
93971
|
+
return true;
|
|
93972
|
+
} catch (e2) {
|
|
93973
|
+
f2.error(`Could not upload via multipart ${formatError(e2)}`);
|
|
93974
|
+
return false;
|
|
93975
|
+
}
|
|
93976
|
+
}
|
|
93930
93977
|
async function deletedFailedVersion(supabase, appId, name) {
|
|
93931
93978
|
const data = {
|
|
93932
93979
|
app_id: appId,
|
|
@@ -93941,6 +93988,23 @@ async function deletedFailedVersion(supabase, appId, name) {
|
|
|
93941
93988
|
return Promise.reject(new Error("Cannot delete failed version"));
|
|
93942
93989
|
}
|
|
93943
93990
|
}
|
|
93991
|
+
async function uploadPart(data, partsize, url, key2, uploadId, index) {
|
|
93992
|
+
const dataToUpload = data.subarray(
|
|
93993
|
+
partsize * index,
|
|
93994
|
+
partsize * (index + 1)
|
|
93995
|
+
);
|
|
93996
|
+
const metadata = {
|
|
93997
|
+
action: "mpu-uploadpart",
|
|
93998
|
+
uploadId,
|
|
93999
|
+
partNumber: index + 1,
|
|
94000
|
+
key: key2
|
|
94001
|
+
};
|
|
94002
|
+
const response = await distribution_default.put(url, {
|
|
94003
|
+
body: dataToUpload,
|
|
94004
|
+
searchParams: new URLSearchParams({ body: btoa(JSON.stringify(metadata)) })
|
|
94005
|
+
});
|
|
94006
|
+
return await response.json();
|
|
94007
|
+
}
|
|
93944
94008
|
async function updateOrCreateChannel(supabase, update) {
|
|
93945
94009
|
if (!update.app_id || !update.name || !update.created_by) {
|
|
93946
94010
|
f2.error("missing app_id, name, or created_by");
|
|
@@ -93967,11 +94031,38 @@ async function updateOrCreateChannel(supabase, update) {
|
|
|
93967
94031
|
}
|
|
93968
94032
|
function useLogSnag() {
|
|
93969
94033
|
const logsnag = new import_logsnag.LogSnag({
|
|
93970
|
-
token: "c124f5e9d0ce5bdd14bbb48f815d5583",
|
|
93971
|
-
project: "capgo"
|
|
94034
|
+
token: import_node_process8.default.env.CAPGO_LOGSNAG ?? "c124f5e9d0ce5bdd14bbb48f815d5583",
|
|
94035
|
+
project: import_node_process8.default.env.CAPGO_LOGSNAG_PROJECT ?? "capgo"
|
|
93972
94036
|
});
|
|
93973
94037
|
return logsnag;
|
|
93974
94038
|
}
|
|
94039
|
+
async function getOrganization(supabase, roles) {
|
|
94040
|
+
const { error: orgError, data: allOrganizations } = await supabase.rpc("get_orgs_v5");
|
|
94041
|
+
if (orgError) {
|
|
94042
|
+
f2.error("Cannot get the list of organizations - exiting");
|
|
94043
|
+
f2.error(`Error ${JSON.stringify(orgError)}`);
|
|
94044
|
+
program.error("");
|
|
94045
|
+
}
|
|
94046
|
+
const adminOrgs = allOrganizations.filter((org) => !!roles.find((role) => role === org.role));
|
|
94047
|
+
if (adminOrgs.length === 0) {
|
|
94048
|
+
f2.error(`Could not get organization with roles: ${roles.join(" or ")} because the user does not have any org`);
|
|
94049
|
+
program.error("");
|
|
94050
|
+
}
|
|
94051
|
+
const organizationUidRaw = adminOrgs.length > 1 ? await ie({
|
|
94052
|
+
message: "Please pick the organization that you want to insert to",
|
|
94053
|
+
options: adminOrgs.map((org) => {
|
|
94054
|
+
return { value: org.gid, label: org.name };
|
|
94055
|
+
})
|
|
94056
|
+
}) : adminOrgs[0].gid;
|
|
94057
|
+
if (eD(organizationUidRaw)) {
|
|
94058
|
+
f2.error("Canceled organization selection, exiting");
|
|
94059
|
+
program.error("");
|
|
94060
|
+
}
|
|
94061
|
+
const organizationUid = organizationUidRaw;
|
|
94062
|
+
const organization = allOrganizations.find((org) => org.gid === organizationUid);
|
|
94063
|
+
f2.info(`Using the organization "${organization.name}" as the app owner`);
|
|
94064
|
+
return organization;
|
|
94065
|
+
}
|
|
93975
94066
|
var convertAppName = (appName) => appName.replace(/\./g, "--");
|
|
93976
94067
|
async function verifyUser(supabase, apikey, keymod = ["all"]) {
|
|
93977
94068
|
await checkKey(supabase, apikey, keymod);
|
|
@@ -94337,18 +94428,18 @@ function wait2(ms) {
|
|
|
94337
94428
|
setTimeout(resolve2, ms);
|
|
94338
94429
|
});
|
|
94339
94430
|
}
|
|
94340
|
-
async function markSnag(channel2,
|
|
94431
|
+
async function markSnag(channel2, orgId, snag, event, icon = "\u2705") {
|
|
94341
94432
|
await snag.track({
|
|
94342
94433
|
channel: channel2,
|
|
94343
94434
|
event,
|
|
94344
94435
|
icon,
|
|
94345
|
-
user_id:
|
|
94436
|
+
user_id: orgId,
|
|
94346
94437
|
notify: false
|
|
94347
94438
|
}).catch();
|
|
94348
94439
|
}
|
|
94349
|
-
async function cancelCommand(channel2, command,
|
|
94440
|
+
async function cancelCommand(channel2, command, orgId, snag) {
|
|
94350
94441
|
if (eD(command)) {
|
|
94351
|
-
await markSnag(channel2,
|
|
94442
|
+
await markSnag(channel2, orgId, snag, "canceled", "\u{1F937}");
|
|
94352
94443
|
import_node_process10.default.exit();
|
|
94353
94444
|
}
|
|
94354
94445
|
}
|
|
@@ -94364,13 +94455,13 @@ async function getStats(supabase, query) {
|
|
|
94364
94455
|
}
|
|
94365
94456
|
return null;
|
|
94366
94457
|
}
|
|
94367
|
-
async function waitLog(channel2, supabase, appId, snag,
|
|
94458
|
+
async function waitLog(channel2, supabase, appId, snag, orgId, deviceId) {
|
|
94368
94459
|
let loop = true;
|
|
94369
94460
|
let now = (/* @__PURE__ */ new Date()).toISOString();
|
|
94370
94461
|
const appIdUrl = convertAppName(appId);
|
|
94371
94462
|
const config = await getLocalConfig();
|
|
94372
94463
|
const baseUrl = `${config.hostWeb}/app/p/${appIdUrl}`;
|
|
94373
|
-
await markSnag(channel2,
|
|
94464
|
+
await markSnag(channel2, orgId, snag, "Use waitlog");
|
|
94374
94465
|
const query = {
|
|
94375
94466
|
appId,
|
|
94376
94467
|
devicesId: deviceId ? [deviceId] : void 0,
|
|
@@ -94388,12 +94479,12 @@ async function waitLog(channel2, supabase, appId, snag, userId, deviceId) {
|
|
|
94388
94479
|
f2.info(`Log from Device: ${data.device_id}`);
|
|
94389
94480
|
if (data.action === "get") {
|
|
94390
94481
|
f2.info("Update Sent your your device, wait until event download complete");
|
|
94391
|
-
await markSnag(channel2,
|
|
94482
|
+
await markSnag(channel2, orgId, snag, "done");
|
|
94392
94483
|
} else if (data.action.startsWith("download_")) {
|
|
94393
94484
|
const action = data.action.split("_")[1];
|
|
94394
94485
|
if (action === "complete") {
|
|
94395
94486
|
f2.info("Your bundle has been downloaded on your device, background the app now and open it again to see the update");
|
|
94396
|
-
await markSnag(channel2,
|
|
94487
|
+
await markSnag(channel2, orgId, snag, "downloaded");
|
|
94397
94488
|
} else if (action === "fail") {
|
|
94398
94489
|
f2.error("Your bundle has failed to download on your device.");
|
|
94399
94490
|
f2.error("Please check if you have network connection and try again");
|
|
@@ -94403,7 +94494,7 @@ async function waitLog(channel2, supabase, appId, snag, userId, deviceId) {
|
|
|
94403
94494
|
} else if (data.action === "set") {
|
|
94404
94495
|
f2.info("Your bundle has been set on your device \u2764\uFE0F");
|
|
94405
94496
|
loop = false;
|
|
94406
|
-
await markSnag(channel2,
|
|
94497
|
+
await markSnag(channel2, orgId, snag, "set");
|
|
94407
94498
|
return Promise.resolve(data);
|
|
94408
94499
|
} else if (data.action === "NoChannelOrOverride") {
|
|
94409
94500
|
f2.error(`No default channel or override (channel/device) found, please create it here ${baseUrl}`);
|
|
@@ -94471,12 +94562,13 @@ async function debugApp(appId, options) {
|
|
|
94471
94562
|
const userId = await verifyUser(supabase, options.apikey);
|
|
94472
94563
|
f2.info(`Getting active bundle in Capgo`);
|
|
94473
94564
|
await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, 4 /* admin */);
|
|
94565
|
+
const orgId = await getOrganizationId(supabase, appId);
|
|
94474
94566
|
const doRun = await se({ message: `Automatic check if update working in device ?` });
|
|
94475
94567
|
await cancelCommand("debug", doRun, userId, snag);
|
|
94476
94568
|
if (doRun) {
|
|
94477
94569
|
f2.info(`Wait logs sent to Capgo from ${appId} device, Put the app in background and open it again.`);
|
|
94478
94570
|
f2.info("Waiting...");
|
|
94479
|
-
await waitLog("debug", supabase, appId, snag,
|
|
94571
|
+
await waitLog("debug", supabase, appId, snag, orgId, deviceId);
|
|
94480
94572
|
$e(`Done \u2705`);
|
|
94481
94573
|
} else {
|
|
94482
94574
|
$e(`Canceled \u274C`);
|
|
@@ -95066,23 +95158,27 @@ The app size is ${mbSize} Mb, this may take a while to download for users
|
|
|
95066
95158
|
if (!external && zipped) {
|
|
95067
95159
|
const spinner = de();
|
|
95068
95160
|
spinner.start(`Uploading Bundle`);
|
|
95069
|
-
const url = await uploadUrl(supabase, appid, bundle2);
|
|
95070
|
-
if (!url) {
|
|
95071
|
-
f2.error(`Cannot get upload url`);
|
|
95072
|
-
program.error("");
|
|
95073
|
-
}
|
|
95074
95161
|
const startTime = import_node_perf_hooks.performance.now();
|
|
95075
95162
|
try {
|
|
95076
|
-
|
|
95077
|
-
|
|
95078
|
-
|
|
95079
|
-
|
|
95080
|
-
|
|
95081
|
-
|
|
95082
|
-
"
|
|
95083
|
-
|
|
95084
|
-
|
|
95085
|
-
|
|
95163
|
+
if (options.multipart !== void 0 && options.multipart) {
|
|
95164
|
+
await uploadMultipart(supabase, appid, bundle2, zipped);
|
|
95165
|
+
} else {
|
|
95166
|
+
const url = await uploadUrl(supabase, appid, bundle2);
|
|
95167
|
+
if (!url) {
|
|
95168
|
+
f2.error(`Cannot get upload url`);
|
|
95169
|
+
program.error("");
|
|
95170
|
+
}
|
|
95171
|
+
await distribution_default.put(url, {
|
|
95172
|
+
timeout: options.timeout || UPLOAD_TIMEOUT,
|
|
95173
|
+
retry: 5,
|
|
95174
|
+
body: zipped,
|
|
95175
|
+
headers: !localS3 ? {
|
|
95176
|
+
"Content-Type": "application/octet-stream",
|
|
95177
|
+
"Cache-Control": "public, max-age=456789, immutable",
|
|
95178
|
+
"x-amz-meta-crc32": checksum
|
|
95179
|
+
} : void 0
|
|
95180
|
+
});
|
|
95181
|
+
}
|
|
95086
95182
|
} catch (errorUpload) {
|
|
95087
95183
|
const endTime2 = import_node_perf_hooks.performance.now();
|
|
95088
95184
|
const uploadTime2 = ((endTime2 - startTime) / 1e3).toFixed(2);
|
|
@@ -95358,13 +95454,15 @@ var src_default = new Mime_default(standard_default, other_default)._freeze();
|
|
|
95358
95454
|
|
|
95359
95455
|
// src/app/add.ts
|
|
95360
95456
|
async function addApp(appId, options, throwErr = true) {
|
|
95457
|
+
await addAppInternal(appId, options, void 0, throwErr);
|
|
95458
|
+
}
|
|
95459
|
+
async function addAppInternal(appId, options, organization, throwErr = true) {
|
|
95361
95460
|
if (throwErr)
|
|
95362
95461
|
oe(`Adding`);
|
|
95363
95462
|
await checkLatest();
|
|
95364
95463
|
options.apikey = options.apikey || findSavedKey();
|
|
95365
95464
|
const config = await getConfig();
|
|
95366
95465
|
appId = appId || config?.app?.appId;
|
|
95367
|
-
const snag = useLogSnag();
|
|
95368
95466
|
if (!options.apikey) {
|
|
95369
95467
|
f2.error(`Missing API key, you need to provide a API key to upload your bundle`);
|
|
95370
95468
|
program.error("");
|
|
@@ -95378,7 +95476,7 @@ async function addApp(appId, options, throwErr = true) {
|
|
|
95378
95476
|
program.error("");
|
|
95379
95477
|
}
|
|
95380
95478
|
const supabase = await createSupabaseClient(options.apikey);
|
|
95381
|
-
|
|
95479
|
+
const userId = await verifyUser(supabase, options.apikey, ["write", "all"]);
|
|
95382
95480
|
const appExist = await checkAppExists(supabase, appId);
|
|
95383
95481
|
if (throwErr && appExist) {
|
|
95384
95482
|
f2.error(`App ${appId} already exist`);
|
|
@@ -95386,27 +95484,9 @@ async function addApp(appId, options, throwErr = true) {
|
|
|
95386
95484
|
} else if (appExist) {
|
|
95387
95485
|
return false;
|
|
95388
95486
|
}
|
|
95389
|
-
|
|
95390
|
-
|
|
95391
|
-
|
|
95392
|
-
f2.error(`Error ${JSON.stringify(orgError)}`);
|
|
95393
|
-
program.error("");
|
|
95394
|
-
}
|
|
95395
|
-
const adminOrgs = allOrganizations.filter((org) => org.role === "admin" || org.role === "super_admin");
|
|
95396
|
-
const organizationUidRaw = adminOrgs.length > 1 ? await ie({
|
|
95397
|
-
message: "Please pick the organization that you want to insert to",
|
|
95398
|
-
options: adminOrgs.map((org) => {
|
|
95399
|
-
return { value: org.gid, label: org.name };
|
|
95400
|
-
})
|
|
95401
|
-
}) : adminOrgs[0].gid;
|
|
95402
|
-
if (eD(organizationUidRaw)) {
|
|
95403
|
-
f2.error("Canceled organization selection, exiting");
|
|
95404
|
-
program.error("");
|
|
95405
|
-
}
|
|
95406
|
-
const organizationUid = organizationUidRaw;
|
|
95407
|
-
const organization = allOrganizations.find((org) => org.gid === organizationUid);
|
|
95408
|
-
userId = organization.created_by;
|
|
95409
|
-
f2.info(`Using the organization "${organization.name}" as the app owner`);
|
|
95487
|
+
if (!organization)
|
|
95488
|
+
organization = await getOrganization(supabase, ["admin", "super_admin"]);
|
|
95489
|
+
const organizationUid = organization.gid;
|
|
95410
95490
|
await checkPlanValid(supabase, organizationUid, options.apikey, void 0, false);
|
|
95411
95491
|
let { name, icon } = options;
|
|
95412
95492
|
appId = appId || config?.app?.appId;
|
|
@@ -95471,16 +95551,6 @@ async function addApp(appId, options, throwErr = true) {
|
|
|
95471
95551
|
f2.error(`Could not add app ${formatError(dbVersionError)}`);
|
|
95472
95552
|
program.error("");
|
|
95473
95553
|
}
|
|
95474
|
-
await snag.track({
|
|
95475
|
-
channel: "app",
|
|
95476
|
-
event: "App Added",
|
|
95477
|
-
icon: "\u{1F389}",
|
|
95478
|
-
user_id: userId,
|
|
95479
|
-
tags: {
|
|
95480
|
-
"app-id": appId
|
|
95481
|
-
},
|
|
95482
|
-
notify: false
|
|
95483
|
-
}).catch();
|
|
95484
95554
|
f2.success(`App ${appId} added to Capgo. ${throwErr ? "You can upload a bundle now" : ""}`);
|
|
95485
95555
|
if (throwErr) {
|
|
95486
95556
|
$e(`Done \u2705`);
|
|
@@ -95498,23 +95568,23 @@ var codeInject = "CapacitorUpdater.notifyAppReady()";
|
|
|
95498
95568
|
var regexImport = /import.*from.*/g;
|
|
95499
95569
|
var defaultChannel = "production";
|
|
95500
95570
|
var execOption = { stdio: "pipe" };
|
|
95501
|
-
async function cancelCommand2(command,
|
|
95571
|
+
async function cancelCommand2(command, orgId, snag) {
|
|
95502
95572
|
if (eD(command)) {
|
|
95503
|
-
await markSnag("onboarding-v2",
|
|
95573
|
+
await markSnag("onboarding-v2", orgId, snag, "canceled", "\u{1F937}");
|
|
95504
95574
|
import_node_process16.default.exit();
|
|
95505
95575
|
}
|
|
95506
95576
|
}
|
|
95507
|
-
async function markStep(
|
|
95508
|
-
return markSnag("onboarding-v2",
|
|
95577
|
+
async function markStep(orgId, snag, step) {
|
|
95578
|
+
return markSnag("onboarding-v2", orgId, snag, `onboarding-step-${step}`);
|
|
95509
95579
|
}
|
|
95510
|
-
async function step2(
|
|
95580
|
+
async function step2(organization, snag, appId, options) {
|
|
95511
95581
|
const pm2 = getPMAndCommand();
|
|
95512
95582
|
const doAdd = await se({ message: `Add ${appId} in Capgo?` });
|
|
95513
|
-
await cancelCommand2(doAdd,
|
|
95583
|
+
await cancelCommand2(doAdd, organization.gid, snag);
|
|
95514
95584
|
if (doAdd) {
|
|
95515
95585
|
const s = de();
|
|
95516
95586
|
s.start(`Running: ${pm2.runner} @capgo/cli@latest app add ${appId}`);
|
|
95517
|
-
const addRes = await
|
|
95587
|
+
const addRes = await addAppInternal(appId, options, organization, false);
|
|
95518
95588
|
if (!addRes)
|
|
95519
95589
|
s.stop(`App already add \u2705`);
|
|
95520
95590
|
else
|
|
@@ -95522,12 +95592,12 @@ async function step2(userId, snag, appId, options) {
|
|
|
95522
95592
|
} else {
|
|
95523
95593
|
f2.info(`Run yourself "${pm2.runner} @capgo/cli@latest app add ${appId}"`);
|
|
95524
95594
|
}
|
|
95525
|
-
await markStep(
|
|
95595
|
+
await markStep(organization.gid, snag, 2);
|
|
95526
95596
|
}
|
|
95527
|
-
async function step3(
|
|
95597
|
+
async function step3(orgId, snag, apikey, appId) {
|
|
95528
95598
|
const pm2 = getPMAndCommand();
|
|
95529
95599
|
const doChannel = await se({ message: `Create default channel ${defaultChannel} for ${appId} in Capgo?` });
|
|
95530
|
-
await cancelCommand2(doChannel,
|
|
95600
|
+
await cancelCommand2(doChannel, orgId, snag);
|
|
95531
95601
|
if (doChannel) {
|
|
95532
95602
|
const s = de();
|
|
95533
95603
|
s.start(`Running: ${pm2.runner} @capgo/cli@latest channel add ${defaultChannel} ${appId} --default`);
|
|
@@ -95542,14 +95612,14 @@ async function step3(userId, snag, apikey, appId) {
|
|
|
95542
95612
|
} else {
|
|
95543
95613
|
f2.info(`Run yourself "${pm2.runner} @capgo/cli@latest channel add ${defaultChannel} ${appId} --default"`);
|
|
95544
95614
|
}
|
|
95545
|
-
await markStep(
|
|
95615
|
+
await markStep(orgId, snag, 3);
|
|
95546
95616
|
}
|
|
95547
95617
|
var urlMigrateV6 = "https://capacitorjs.com/docs/updating/6-0";
|
|
95548
95618
|
var urlMigrateV5 = "https://capacitorjs.com/docs/updating/5-0";
|
|
95549
|
-
async function step4(
|
|
95619
|
+
async function step4(orgId, snag, apikey, appId) {
|
|
95550
95620
|
const pm2 = getPMAndCommand();
|
|
95551
95621
|
const doInstall = await se({ message: `Automatic Install "@capgo/capacitor-updater" dependency in ${appId}?` });
|
|
95552
|
-
await cancelCommand2(doInstall,
|
|
95622
|
+
await cancelCommand2(doInstall, orgId, snag);
|
|
95553
95623
|
if (doInstall) {
|
|
95554
95624
|
const s = de();
|
|
95555
95625
|
s.start(`Checking if @capgo/capacitor-updater is installed`);
|
|
@@ -95586,11 +95656,11 @@ async function step4(userId, snag, apikey, appId) {
|
|
|
95586
95656
|
} else {
|
|
95587
95657
|
f2.info(`Run yourself "${pm2.installCommand} @capgo/capacitor-updater@latest"`);
|
|
95588
95658
|
}
|
|
95589
|
-
await markStep(
|
|
95659
|
+
await markStep(orgId, snag, 4);
|
|
95590
95660
|
}
|
|
95591
|
-
async function step5(
|
|
95661
|
+
async function step5(orgId, snag, apikey, appId) {
|
|
95592
95662
|
const doAddCode = await se({ message: `Automatic Add "${codeInject}" code and import in ${appId}?` });
|
|
95593
|
-
await cancelCommand2(doAddCode,
|
|
95663
|
+
await cancelCommand2(doAddCode, orgId, snag);
|
|
95594
95664
|
if (doAddCode) {
|
|
95595
95665
|
const s = de();
|
|
95596
95666
|
s.start(`Adding @capacitor-updater to your main file`);
|
|
@@ -95627,7 +95697,7 @@ ${codeInject};
|
|
|
95627
95697
|
(0, import_node_fs11.writeFileSync)(mainFilePath, newMainFileContent);
|
|
95628
95698
|
s.stop(`Code added to ${mainFilePath} \u2705`);
|
|
95629
95699
|
}
|
|
95630
|
-
await markStep(
|
|
95700
|
+
await markStep(orgId, snag, 5);
|
|
95631
95701
|
} else {
|
|
95632
95702
|
f2.info(`Add to your main file the following code:
|
|
95633
95703
|
|
|
@@ -95637,10 +95707,10 @@ ${codeInject};
|
|
|
95637
95707
|
`);
|
|
95638
95708
|
}
|
|
95639
95709
|
}
|
|
95640
|
-
async function step6(
|
|
95710
|
+
async function step6(orgId, snag, apikey, appId) {
|
|
95641
95711
|
const pm2 = getPMAndCommand();
|
|
95642
95712
|
const doEncrypt = await se({ message: `Automatic configure end-to-end encryption in ${appId} updates?` });
|
|
95643
|
-
await cancelCommand2(doEncrypt,
|
|
95713
|
+
await cancelCommand2(doEncrypt, orgId, snag);
|
|
95644
95714
|
if (doEncrypt) {
|
|
95645
95715
|
const s = de();
|
|
95646
95716
|
s.start(`Running: ${pm2.runner} @capgo/cli@latest key create`);
|
|
@@ -95653,14 +95723,14 @@ async function step6(userId, snag, apikey, appId) {
|
|
|
95653
95723
|
} else {
|
|
95654
95724
|
s.stop(`key created \u{1F511}`);
|
|
95655
95725
|
}
|
|
95656
|
-
markSnag("onboarding-v2",
|
|
95726
|
+
markSnag("onboarding-v2", orgId, snag, "Use encryption");
|
|
95657
95727
|
}
|
|
95658
|
-
await markStep(
|
|
95728
|
+
await markStep(orgId, snag, 6);
|
|
95659
95729
|
}
|
|
95660
|
-
async function step7(
|
|
95730
|
+
async function step7(orgId, snag, apikey, appId) {
|
|
95661
95731
|
const pm2 = getPMAndCommand();
|
|
95662
95732
|
const doBuild = await se({ message: `Automatic build ${appId} with "${pm2.pm} run build" ?` });
|
|
95663
|
-
await cancelCommand2(doBuild,
|
|
95733
|
+
await cancelCommand2(doBuild, orgId, snag);
|
|
95664
95734
|
if (doBuild) {
|
|
95665
95735
|
const s = de();
|
|
95666
95736
|
const projectType = await findProjectType();
|
|
@@ -95678,12 +95748,12 @@ async function step7(userId, snag, apikey, appId) {
|
|
|
95678
95748
|
} else {
|
|
95679
95749
|
f2.info(`Build yourself with command: ${pm2.pm} run build && ${pm2.runner} cap sync`);
|
|
95680
95750
|
}
|
|
95681
|
-
await markStep(
|
|
95751
|
+
await markStep(orgId, snag, 7);
|
|
95682
95752
|
}
|
|
95683
|
-
async function step8(
|
|
95753
|
+
async function step8(orgId, snag, apikey, appId) {
|
|
95684
95754
|
const pm2 = getPMAndCommand();
|
|
95685
95755
|
const doBundle = await se({ message: `Automatic upload ${appId} bundle to Capgo?` });
|
|
95686
|
-
await cancelCommand2(doBundle,
|
|
95756
|
+
await cancelCommand2(doBundle, orgId, snag);
|
|
95687
95757
|
if (doBundle) {
|
|
95688
95758
|
const s = de();
|
|
95689
95759
|
s.start(`Running: ${pm2.runner} @capgo/cli@latest bundle upload`);
|
|
@@ -95702,12 +95772,12 @@ async function step8(userId, snag, apikey, appId) {
|
|
|
95702
95772
|
} else {
|
|
95703
95773
|
f2.info(`Upload yourself with command: ${pm2.runner} @capgo/cli@latest bundle upload`);
|
|
95704
95774
|
}
|
|
95705
|
-
await markStep(
|
|
95775
|
+
await markStep(orgId, snag, 8);
|
|
95706
95776
|
}
|
|
95707
|
-
async function step9(
|
|
95777
|
+
async function step9(orgId, snag) {
|
|
95708
95778
|
const pm2 = getPMAndCommand();
|
|
95709
95779
|
const doRun = await se({ message: `Run in device now ?` });
|
|
95710
|
-
await cancelCommand2(doRun,
|
|
95780
|
+
await cancelCommand2(doRun, orgId, snag);
|
|
95711
95781
|
if (doRun) {
|
|
95712
95782
|
const plaformType = await ie({
|
|
95713
95783
|
message: "Pick a platform to run your app",
|
|
@@ -95728,20 +95798,7 @@ async function step9(userId, snag) {
|
|
|
95728
95798
|
} else {
|
|
95729
95799
|
f2.info(`Run yourself with command: ${pm2.runner} cap run <ios|android>`);
|
|
95730
95800
|
}
|
|
95731
|
-
await markStep(
|
|
95732
|
-
}
|
|
95733
|
-
async function step10(userId, snag, supabase, appId) {
|
|
95734
|
-
const doRun = await se({ message: `Automatic check if update working in device ?` });
|
|
95735
|
-
await cancelCommand2(doRun, userId, snag);
|
|
95736
|
-
if (doRun) {
|
|
95737
|
-
f2.info(`Wait logs sent to Capgo from ${appId} device, Put the app in background and open it again.`);
|
|
95738
|
-
f2.info("Waiting...");
|
|
95739
|
-
await waitLog("onboarding-v2", supabase, appId, snag, userId);
|
|
95740
|
-
} else {
|
|
95741
|
-
const appIdUrl = convertAppName(appId);
|
|
95742
|
-
f2.info(`Check logs in https://web.capgo.app/app/p/${appIdUrl}/logs to see if update works.`);
|
|
95743
|
-
}
|
|
95744
|
-
await markStep(userId, snag, 10);
|
|
95801
|
+
await markStep(orgId, snag, 9);
|
|
95745
95802
|
}
|
|
95746
95803
|
async function initApp(apikeyCommand, appId, options) {
|
|
95747
95804
|
const pm2 = getPMAndCommand();
|
|
@@ -95758,18 +95815,19 @@ async function initApp(apikeyCommand, appId, options) {
|
|
|
95758
95815
|
log.stop("Login Done \u2705");
|
|
95759
95816
|
}
|
|
95760
95817
|
const supabase = await createSupabaseClient(apikey);
|
|
95761
|
-
|
|
95762
|
-
await
|
|
95763
|
-
|
|
95764
|
-
await
|
|
95765
|
-
await
|
|
95766
|
-
await
|
|
95767
|
-
await
|
|
95768
|
-
await
|
|
95769
|
-
await
|
|
95770
|
-
await
|
|
95771
|
-
await
|
|
95772
|
-
await
|
|
95818
|
+
await verifyUser(supabase, apikey, ["upload", "all", "read", "write"]);
|
|
95819
|
+
const organization = await getOrganization(supabase, ["admin", "super_admin"]);
|
|
95820
|
+
const orgId = organization.gid;
|
|
95821
|
+
await markStep(orgId, snag, 1);
|
|
95822
|
+
await step2(organization, snag, appId, options);
|
|
95823
|
+
await step3(orgId, snag, apikey, appId);
|
|
95824
|
+
await step4(orgId, snag, apikey, appId);
|
|
95825
|
+
await step5(orgId, snag, apikey, appId);
|
|
95826
|
+
await step6(orgId, snag, apikey, appId);
|
|
95827
|
+
await step7(orgId, snag, apikey, appId);
|
|
95828
|
+
await step8(orgId, snag, apikey, appId);
|
|
95829
|
+
await step9(orgId, snag);
|
|
95830
|
+
await markStep(orgId, snag, 0);
|
|
95773
95831
|
f2.info(`Welcome onboard \u2708\uFE0F!`);
|
|
95774
95832
|
f2.info(`Your Capgo update system is setup`);
|
|
95775
95833
|
f2.info(`Next time use \`${pm2.runner} @capgo/cli@latest bundle upload\` to only upload your bundle`);
|
|
@@ -96641,7 +96699,7 @@ var bundle = program.command("bundle").description("Manage bundle");
|
|
|
96641
96699
|
bundle.command("upload [appId]").alias("u").description("Upload a new bundle in Capgo Cloud").action(uploadCommand).option("-a, --apikey <apikey>", "apikey to link to your account").option("-p, --path <path>", "path of the folder to upload").option("-c, --channel <channel>", "channel to link to").option("-e, --external <url>", "link to external url intead of upload to Capgo Cloud").option("--iv-session-key <key>", "Set the iv and session key for bundle url external").option("--s3-region <region>", "Region for your AWS S3 bucket").option("--s3-apikey <apikey>", "apikey for your AWS S3 account").option("--s3-apisecret <apisecret>", "api secret for your AWS S3 account").option("--s3-bucket-name <bucketName>", "Name for your AWS S3 bucket").option("--key <key>", "custom path for public signing key").option("--key-data <keyData>", "base64 public signing key").option("--bundle-url", "prints bundle url into stdout").option("--no-key", "ignore signing key and send clear update").option("--no-code-check", "Ignore checking if notifyAppReady() is called in soure code and index present in root folder").option("--display-iv-session", "Show in the console the iv and session key used to encrypt the update").option("-b, --bundle <bundle>", "bundle version number of the bundle to upload").option(
|
|
96642
96700
|
"--min-update-version <minUpdateVersion>",
|
|
96643
96701
|
"Minimal version required to update to this version. Used only if the disable auto update is set to metadata in channel"
|
|
96644
|
-
).option("--auto-min-update-version", "Set the min update version based on native packages").option("--ignore-metadata-check", "Ignores the metadata (node_modules) check when uploading").option("--timeout <timeout>", "Timeout for the upload process in seconds");
|
|
96702
|
+
).option("--auto-min-update-version", "Set the min update version based on native packages").option("--ignore-metadata-check", "Ignores the metadata (node_modules) check when uploading").option("--timeout <timeout>", "Timeout for the upload process in seconds").option("--multipart", "Uses multipart protocol to upload data to S3");
|
|
96645
96703
|
bundle.command("compatibility [appId]").action(checkCompatibilityCommand).option("-a, --apikey <apikey>", "apikey to link to your account").option("-c, --channel <channel>", "channel to check the compatibility with").option("--text", "output text instead of emojis");
|
|
96646
96704
|
bundle.command("delete [bundleId] [appId]").alias("d").description("Delete a bundle in Capgo Cloud").action(deleteBundle).option("-a, --apikey <apikey>", "apikey to link to your account");
|
|
96647
96705
|
bundle.command("list [appId]").alias("l").description("List bundle in Capgo Cloud").action(listBundle).option("-a, --apikey <apikey>", "apikey to link to your account");
|
package/package.json
CHANGED
package/src/app/add.ts
CHANGED
|
@@ -7,17 +7,24 @@ import * as p from '@clack/prompts'
|
|
|
7
7
|
import { checkLatest } from '../api/update'
|
|
8
8
|
import type { Options } from '../api/app'
|
|
9
9
|
import { checkAppExists, newIconPath } from '../api/app'
|
|
10
|
+
import type {
|
|
11
|
+
Organization,
|
|
12
|
+
} from '../utils'
|
|
10
13
|
import {
|
|
11
14
|
checkPlanValid,
|
|
12
15
|
createSupabaseClient,
|
|
13
16
|
findSavedKey,
|
|
14
17
|
formatError,
|
|
15
18
|
getConfig,
|
|
16
|
-
|
|
19
|
+
getOrganization,
|
|
17
20
|
verifyUser,
|
|
18
21
|
} from '../utils'
|
|
19
22
|
|
|
20
23
|
export async function addApp(appId: string, options: Options, throwErr = true) {
|
|
24
|
+
await addAppInternal(appId, options, undefined, throwErr)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function addAppInternal(appId: string, options: Options, organization?: Organization, throwErr = true) {
|
|
21
28
|
if (throwErr)
|
|
22
29
|
p.intro(`Adding`)
|
|
23
30
|
|
|
@@ -25,7 +32,6 @@ export async function addApp(appId: string, options: Options, throwErr = true) {
|
|
|
25
32
|
options.apikey = options.apikey || findSavedKey()
|
|
26
33
|
const config = await getConfig()
|
|
27
34
|
appId = appId || config?.app?.appId
|
|
28
|
-
const snag = useLogSnag()
|
|
29
35
|
|
|
30
36
|
if (!options.apikey) {
|
|
31
37
|
p.log.error(`Missing API key, you need to provide a API key to upload your bundle`)
|
|
@@ -43,7 +49,7 @@ export async function addApp(appId: string, options: Options, throwErr = true) {
|
|
|
43
49
|
|
|
44
50
|
const supabase = await createSupabaseClient(options.apikey)
|
|
45
51
|
|
|
46
|
-
|
|
52
|
+
const userId = await verifyUser(supabase, options.apikey, ['write', 'all'])
|
|
47
53
|
|
|
48
54
|
// Check we have app access to this appId
|
|
49
55
|
const appExist = await checkAppExists(supabase, appId)
|
|
@@ -55,36 +61,10 @@ export async function addApp(appId: string, options: Options, throwErr = true) {
|
|
|
55
61
|
return false
|
|
56
62
|
}
|
|
57
63
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (orgError) {
|
|
62
|
-
p.log.error('Cannot get the list of organizations - exiting')
|
|
63
|
-
p.log.error(`Error ${JSON.stringify(orgError)}`)
|
|
64
|
-
program.error('')
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const adminOrgs = allOrganizations.filter(org => org.role === 'admin' || org.role === 'super_admin')
|
|
68
|
-
|
|
69
|
-
const organizationUidRaw = (adminOrgs.length > 1)
|
|
70
|
-
? await p.select({
|
|
71
|
-
message: 'Please pick the organization that you want to insert to',
|
|
72
|
-
options: adminOrgs.map((org) => {
|
|
73
|
-
return { value: org.gid, label: org.name }
|
|
74
|
-
}),
|
|
75
|
-
})
|
|
76
|
-
: adminOrgs[0].gid
|
|
77
|
-
|
|
78
|
-
if (p.isCancel(organizationUidRaw)) {
|
|
79
|
-
p.log.error('Canceled organization selection, exiting')
|
|
80
|
-
program.error('')
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const organizationUid = organizationUidRaw as string
|
|
84
|
-
const organization = allOrganizations.find(org => org.gid === organizationUid)!
|
|
85
|
-
userId = organization.created_by
|
|
64
|
+
if (!organization)
|
|
65
|
+
organization = await getOrganization(supabase, ['admin', 'super_admin'])
|
|
86
66
|
|
|
87
|
-
|
|
67
|
+
const organizationUid = organization.gid
|
|
88
68
|
|
|
89
69
|
await checkPlanValid(supabase, organizationUid, options.apikey, undefined, false)
|
|
90
70
|
|
|
@@ -168,16 +148,6 @@ export async function addApp(appId: string, options: Options, throwErr = true) {
|
|
|
168
148
|
p.log.error(`Could not add app ${formatError(dbVersionError)}`)
|
|
169
149
|
program.error('')
|
|
170
150
|
}
|
|
171
|
-
await snag.track({
|
|
172
|
-
channel: 'app',
|
|
173
|
-
event: 'App Added',
|
|
174
|
-
icon: '🎉',
|
|
175
|
-
user_id: userId,
|
|
176
|
-
tags: {
|
|
177
|
-
'app-id': appId,
|
|
178
|
-
},
|
|
179
|
-
notify: false,
|
|
180
|
-
}).catch()
|
|
181
151
|
p.log.success(`App ${appId} added to Capgo. ${throwErr ? 'You can upload a bundle now' : ''}`)
|
|
182
152
|
if (throwErr) {
|
|
183
153
|
p.outro(`Done ✅`)
|
package/src/app/debug.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type LogSnag from 'logsnag'
|
|
|
6
6
|
import type { Database } from '../types/supabase.types'
|
|
7
7
|
import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
|
|
8
8
|
import { checkLatest } from '../api/update'
|
|
9
|
-
import { OrganizationPerm, convertAppName, createSupabaseClient, findSavedKey, formatError, getConfig, getLocalConfig, useLogSnag, verifyUser } from '../utils'
|
|
9
|
+
import { OrganizationPerm, convertAppName, createSupabaseClient, findSavedKey, formatError, getConfig, getLocalConfig, getOrganizationId, useLogSnag, verifyUser } from '../utils'
|
|
10
10
|
|
|
11
11
|
function wait(ms: number) {
|
|
12
12
|
return new Promise((resolve) => {
|
|
@@ -19,19 +19,19 @@ export interface OptionsBaseDebug {
|
|
|
19
19
|
device?: string
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export async function markSnag(channel: string,
|
|
22
|
+
export async function markSnag(channel: string, orgId: string, snag: LogSnag, event: string, icon = '✅') {
|
|
23
23
|
await snag.track({
|
|
24
24
|
channel,
|
|
25
25
|
event,
|
|
26
26
|
icon,
|
|
27
|
-
user_id:
|
|
27
|
+
user_id: orgId,
|
|
28
28
|
notify: false,
|
|
29
29
|
}).catch()
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export async function cancelCommand(channel: string, command: boolean | symbol,
|
|
32
|
+
export async function cancelCommand(channel: string, command: boolean | symbol, orgId: string, snag: LogSnag) {
|
|
33
33
|
if (p.isCancel(command)) {
|
|
34
|
-
await markSnag(channel,
|
|
34
|
+
await markSnag(channel, orgId, snag, 'canceled', '🤷')
|
|
35
35
|
process.exit()
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -65,13 +65,13 @@ export async function getStats(supabase: SupabaseClient<Database>, query: QueryS
|
|
|
65
65
|
return null
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export async function waitLog(channel: string, supabase: SupabaseClient<Database>, appId: string, snag: LogSnag,
|
|
68
|
+
export async function waitLog(channel: string, supabase: SupabaseClient<Database>, appId: string, snag: LogSnag, orgId: string, deviceId?: string) {
|
|
69
69
|
let loop = true
|
|
70
70
|
let now = new Date().toISOString()
|
|
71
71
|
const appIdUrl = convertAppName(appId)
|
|
72
72
|
const config = await getLocalConfig()
|
|
73
73
|
const baseUrl = `${config.hostWeb}/app/p/${appIdUrl}`
|
|
74
|
-
await markSnag(channel,
|
|
74
|
+
await markSnag(channel, orgId, snag, 'Use waitlog')
|
|
75
75
|
const query: QueryStats = {
|
|
76
76
|
appId,
|
|
77
77
|
devicesId: deviceId ? [deviceId] : undefined,
|
|
@@ -90,13 +90,13 @@ export async function waitLog(channel: string, supabase: SupabaseClient<Database
|
|
|
90
90
|
p.log.info(`Log from Device: ${data.device_id}`)
|
|
91
91
|
if (data.action === 'get') {
|
|
92
92
|
p.log.info('Update Sent your your device, wait until event download complete')
|
|
93
|
-
await markSnag(channel,
|
|
93
|
+
await markSnag(channel, orgId, snag, 'done')
|
|
94
94
|
}
|
|
95
95
|
else if (data.action.startsWith('download_')) {
|
|
96
96
|
const action = data.action.split('_')[1]
|
|
97
97
|
if (action === 'complete') {
|
|
98
98
|
p.log.info('Your bundle has been downloaded on your device, background the app now and open it again to see the update')
|
|
99
|
-
await markSnag(channel,
|
|
99
|
+
await markSnag(channel, orgId, snag, 'downloaded')
|
|
100
100
|
}
|
|
101
101
|
else if (action === 'fail') {
|
|
102
102
|
p.log.error('Your bundle has failed to download on your device.')
|
|
@@ -109,7 +109,7 @@ export async function waitLog(channel: string, supabase: SupabaseClient<Database
|
|
|
109
109
|
else if (data.action === 'set') {
|
|
110
110
|
p.log.info('Your bundle has been set on your device ❤️')
|
|
111
111
|
loop = false
|
|
112
|
-
await markSnag(channel,
|
|
112
|
+
await markSnag(channel, orgId, snag, 'set')
|
|
113
113
|
return Promise.resolve(data)
|
|
114
114
|
}
|
|
115
115
|
else if (data.action === 'NoChannelOrOverride') {
|
|
@@ -202,12 +202,14 @@ export async function debugApp(appId: string, options: OptionsBaseDebug) {
|
|
|
202
202
|
// Check we have app access to this appId
|
|
203
203
|
await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.admin)
|
|
204
204
|
|
|
205
|
+
const orgId = await getOrganizationId(supabase, appId)
|
|
206
|
+
|
|
205
207
|
const doRun = await p.confirm({ message: `Automatic check if update working in device ?` })
|
|
206
208
|
await cancelCommand('debug', doRun, userId, snag)
|
|
207
209
|
if (doRun) {
|
|
208
210
|
p.log.info(`Wait logs sent to Capgo from ${appId} device, Put the app in background and open it again.`)
|
|
209
211
|
p.log.info('Waiting...')
|
|
210
|
-
await waitLog('debug', supabase, appId, snag,
|
|
212
|
+
await waitLog('debug', supabase, appId, snag, orgId, deviceId)
|
|
211
213
|
p.outro(`Done ✅`)
|
|
212
214
|
}
|
|
213
215
|
else {
|
package/src/bundle/upload.ts
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
requireUpdateMetadata,
|
|
40
40
|
updateOrCreateChannel,
|
|
41
41
|
updateOrCreateVersion,
|
|
42
|
+
uploadMultipart,
|
|
42
43
|
uploadUrl,
|
|
43
44
|
useLogSnag,
|
|
44
45
|
verifyUser,
|
|
@@ -66,6 +67,7 @@ interface Options extends OptionsBase {
|
|
|
66
67
|
autoMinUpdateVersion?: boolean
|
|
67
68
|
ignoreMetadataCheck?: boolean
|
|
68
69
|
timeout?: number
|
|
70
|
+
multipart?: boolean
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
const UPLOAD_TIMEOUT = 120000
|
|
@@ -381,26 +383,32 @@ It will be also visible in your dashboard\n`)
|
|
|
381
383
|
if (!external && zipped) {
|
|
382
384
|
const spinner = p.spinner()
|
|
383
385
|
spinner.start(`Uploading Bundle`)
|
|
384
|
-
|
|
385
|
-
const url = await uploadUrl(supabase, appid, bundle)
|
|
386
|
-
if (!url) {
|
|
387
|
-
p.log.error(`Cannot get upload url`)
|
|
388
|
-
program.error('')
|
|
389
|
-
}
|
|
390
386
|
const startTime = performance.now()
|
|
387
|
+
|
|
391
388
|
try {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
389
|
+
if (options.multipart !== undefined && options.multipart) {
|
|
390
|
+
await uploadMultipart(supabase, appid, bundle, zipped)
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
const url = await uploadUrl(supabase, appid, bundle)
|
|
394
|
+
if (!url) {
|
|
395
|
+
p.log.error(`Cannot get upload url`)
|
|
396
|
+
program.error('')
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
await ky.put(url, {
|
|
400
|
+
timeout: options.timeout || UPLOAD_TIMEOUT,
|
|
401
|
+
retry: 5,
|
|
402
|
+
body: zipped,
|
|
403
|
+
headers: (!localS3
|
|
404
|
+
? {
|
|
405
|
+
'Content-Type': 'application/octet-stream',
|
|
406
|
+
'Cache-Control': 'public, max-age=456789, immutable',
|
|
407
|
+
'x-amz-meta-crc32': checksum,
|
|
408
|
+
}
|
|
409
|
+
: undefined),
|
|
410
|
+
})
|
|
411
|
+
}
|
|
404
412
|
}
|
|
405
413
|
catch (errorUpload) {
|
|
406
414
|
const endTime = performance.now()
|
|
@@ -411,6 +419,7 @@ It will be also visible in your dashboard\n`)
|
|
|
411
419
|
await deletedFailedVersion(supabase, appid, bundle)
|
|
412
420
|
program.error('')
|
|
413
421
|
}
|
|
422
|
+
|
|
414
423
|
versionData.storage_provider = 'r2'
|
|
415
424
|
const { error: dbError2 } = await updateOrCreateVersion(supabase, versionData)
|
|
416
425
|
if (dbError2) {
|
package/src/index.ts
CHANGED
|
@@ -131,6 +131,7 @@ bundle
|
|
|
131
131
|
.option('--auto-min-update-version', 'Set the min update version based on native packages')
|
|
132
132
|
.option('--ignore-metadata-check', 'Ignores the metadata (node_modules) check when uploading')
|
|
133
133
|
.option('--timeout <timeout>', 'Timeout for the upload process in seconds')
|
|
134
|
+
.option('--multipart', 'Uses multipart protocol to upload data to S3')
|
|
134
135
|
|
|
135
136
|
bundle
|
|
136
137
|
.command('compatibility [appId]')
|
package/src/init.ts
CHANGED
|
@@ -12,10 +12,11 @@ import { createKey } from './key'
|
|
|
12
12
|
import { addChannel } from './channel/add'
|
|
13
13
|
import { uploadBundle } from './bundle/upload'
|
|
14
14
|
import { doLoginExists, login } from './login'
|
|
15
|
-
import {
|
|
15
|
+
import { addAppInternal } from './app/add'
|
|
16
16
|
import { checkLatest } from './api/update'
|
|
17
17
|
import type { Options } from './api/app'
|
|
18
|
-
import {
|
|
18
|
+
import type { Organization } from './utils'
|
|
19
|
+
import { convertAppName, createSupabaseClient, findBuildCommandForProjectType, findMainFile, findMainFileForProjectType, findProjectType, findSavedKey, getConfig, getOrganization, getPMAndCommand, useLogSnag, verifyUser } from './utils'
|
|
19
20
|
|
|
20
21
|
interface SuperOptions extends Options {
|
|
21
22
|
local: boolean
|
|
@@ -27,25 +28,25 @@ const regexImport = /import.*from.*/g
|
|
|
27
28
|
const defaultChannel = 'production'
|
|
28
29
|
const execOption = { stdio: 'pipe' }
|
|
29
30
|
|
|
30
|
-
async function cancelCommand(command: boolean | symbol,
|
|
31
|
+
async function cancelCommand(command: boolean | symbol, orgId: string, snag: LogSnag) {
|
|
31
32
|
if (p.isCancel(command)) {
|
|
32
|
-
await markSnag('onboarding-v2',
|
|
33
|
+
await markSnag('onboarding-v2', orgId, snag, 'canceled', '🤷')
|
|
33
34
|
process.exit()
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
async function markStep(
|
|
38
|
-
return markSnag('onboarding-v2',
|
|
38
|
+
async function markStep(orgId: string, snag: LogSnag, step: number | string) {
|
|
39
|
+
return markSnag('onboarding-v2', orgId, snag, `onboarding-step-${step}`)
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
async function step2(
|
|
42
|
+
async function step2(organization: Organization, snag: LogSnag, appId: string, options: SuperOptions) {
|
|
42
43
|
const pm = getPMAndCommand()
|
|
43
44
|
const doAdd = await p.confirm({ message: `Add ${appId} in Capgo?` })
|
|
44
|
-
await cancelCommand(doAdd,
|
|
45
|
+
await cancelCommand(doAdd, organization.gid, snag)
|
|
45
46
|
if (doAdd) {
|
|
46
47
|
const s = p.spinner()
|
|
47
48
|
s.start(`Running: ${pm.runner} @capgo/cli@latest app add ${appId}`)
|
|
48
|
-
const addRes = await
|
|
49
|
+
const addRes = await addAppInternal(appId, options, organization, false)
|
|
49
50
|
if (!addRes)
|
|
50
51
|
s.stop(`App already add ✅`)
|
|
51
52
|
else
|
|
@@ -54,13 +55,13 @@ async function step2(userId: string, snag: LogSnag, appId: string, options: Supe
|
|
|
54
55
|
else {
|
|
55
56
|
p.log.info(`Run yourself "${pm.runner} @capgo/cli@latest app add ${appId}"`)
|
|
56
57
|
}
|
|
57
|
-
await markStep(
|
|
58
|
+
await markStep(organization.gid, snag, 2)
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
async function step3(
|
|
61
|
+
async function step3(orgId: string, snag: LogSnag, apikey: string, appId: string) {
|
|
61
62
|
const pm = getPMAndCommand()
|
|
62
63
|
const doChannel = await p.confirm({ message: `Create default channel ${defaultChannel} for ${appId} in Capgo?` })
|
|
63
|
-
await cancelCommand(doChannel,
|
|
64
|
+
await cancelCommand(doChannel, orgId, snag)
|
|
64
65
|
if (doChannel) {
|
|
65
66
|
const s = p.spinner()
|
|
66
67
|
// create production channel public
|
|
@@ -77,15 +78,15 @@ async function step3(userId: string, snag: LogSnag, apikey: string, appId: strin
|
|
|
77
78
|
else {
|
|
78
79
|
p.log.info(`Run yourself "${pm.runner} @capgo/cli@latest channel add ${defaultChannel} ${appId} --default"`)
|
|
79
80
|
}
|
|
80
|
-
await markStep(
|
|
81
|
+
await markStep(orgId, snag, 3)
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
const urlMigrateV6 = 'https://capacitorjs.com/docs/updating/6-0'
|
|
84
85
|
const urlMigrateV5 = 'https://capacitorjs.com/docs/updating/5-0'
|
|
85
|
-
async function step4(
|
|
86
|
+
async function step4(orgId: string, snag: LogSnag, apikey: string, appId: string) {
|
|
86
87
|
const pm = getPMAndCommand()
|
|
87
88
|
const doInstall = await p.confirm({ message: `Automatic Install "@capgo/capacitor-updater" dependency in ${appId}?` })
|
|
88
|
-
await cancelCommand(doInstall,
|
|
89
|
+
await cancelCommand(doInstall, orgId, snag)
|
|
89
90
|
if (doInstall) {
|
|
90
91
|
const s = p.spinner()
|
|
91
92
|
s.start(`Checking if @capgo/capacitor-updater is installed`)
|
|
@@ -129,12 +130,12 @@ async function step4(userId: string, snag: LogSnag, apikey: string, appId: strin
|
|
|
129
130
|
else {
|
|
130
131
|
p.log.info(`Run yourself "${pm.installCommand} @capgo/capacitor-updater@latest"`)
|
|
131
132
|
}
|
|
132
|
-
await markStep(
|
|
133
|
+
await markStep(orgId, snag, 4)
|
|
133
134
|
}
|
|
134
135
|
|
|
135
|
-
async function step5(
|
|
136
|
+
async function step5(orgId: string, snag: LogSnag, apikey: string, appId: string) {
|
|
136
137
|
const doAddCode = await p.confirm({ message: `Automatic Add "${codeInject}" code and import in ${appId}?` })
|
|
137
|
-
await cancelCommand(doAddCode,
|
|
138
|
+
await cancelCommand(doAddCode, orgId, snag)
|
|
138
139
|
if (doAddCode) {
|
|
139
140
|
const s = p.spinner()
|
|
140
141
|
s.start(`Adding @capacitor-updater to your main file`)
|
|
@@ -172,17 +173,17 @@ async function step5(userId: string, snag: LogSnag, apikey: string, appId: strin
|
|
|
172
173
|
writeFileSync(mainFilePath, newMainFileContent)
|
|
173
174
|
s.stop(`Code added to ${mainFilePath} ✅`)
|
|
174
175
|
}
|
|
175
|
-
await markStep(
|
|
176
|
+
await markStep(orgId, snag, 5)
|
|
176
177
|
}
|
|
177
178
|
else {
|
|
178
179
|
p.log.info(`Add to your main file the following code:\n\n${importInject};\n\n${codeInject};\n`)
|
|
179
180
|
}
|
|
180
181
|
}
|
|
181
182
|
|
|
182
|
-
async function step6(
|
|
183
|
+
async function step6(orgId: string, snag: LogSnag, apikey: string, appId: string) {
|
|
183
184
|
const pm = getPMAndCommand()
|
|
184
185
|
const doEncrypt = await p.confirm({ message: `Automatic configure end-to-end encryption in ${appId} updates?` })
|
|
185
|
-
await cancelCommand(doEncrypt,
|
|
186
|
+
await cancelCommand(doEncrypt, orgId, snag)
|
|
186
187
|
if (doEncrypt) {
|
|
187
188
|
const s = p.spinner()
|
|
188
189
|
s.start(`Running: ${pm.runner} @capgo/cli@latest key create`)
|
|
@@ -196,15 +197,15 @@ async function step6(userId: string, snag: LogSnag, apikey: string, appId: strin
|
|
|
196
197
|
else {
|
|
197
198
|
s.stop(`key created 🔑`)
|
|
198
199
|
}
|
|
199
|
-
markSnag('onboarding-v2',
|
|
200
|
+
markSnag('onboarding-v2', orgId, snag, 'Use encryption')
|
|
200
201
|
}
|
|
201
|
-
await markStep(
|
|
202
|
+
await markStep(orgId, snag, 6)
|
|
202
203
|
}
|
|
203
204
|
|
|
204
|
-
async function step7(
|
|
205
|
+
async function step7(orgId: string, snag: LogSnag, apikey: string, appId: string) {
|
|
205
206
|
const pm = getPMAndCommand()
|
|
206
207
|
const doBuild = await p.confirm({ message: `Automatic build ${appId} with "${pm.pm} run build" ?` })
|
|
207
|
-
await cancelCommand(doBuild,
|
|
208
|
+
await cancelCommand(doBuild, orgId, snag)
|
|
208
209
|
if (doBuild) {
|
|
209
210
|
const s = p.spinner()
|
|
210
211
|
const projectType = await findProjectType()
|
|
@@ -224,13 +225,13 @@ async function step7(userId: string, snag: LogSnag, apikey: string, appId: strin
|
|
|
224
225
|
else {
|
|
225
226
|
p.log.info(`Build yourself with command: ${pm.pm} run build && ${pm.runner} cap sync`)
|
|
226
227
|
}
|
|
227
|
-
await markStep(
|
|
228
|
+
await markStep(orgId, snag, 7)
|
|
228
229
|
}
|
|
229
230
|
|
|
230
|
-
async function step8(
|
|
231
|
+
async function step8(orgId: string, snag: LogSnag, apikey: string, appId: string) {
|
|
231
232
|
const pm = getPMAndCommand()
|
|
232
233
|
const doBundle = await p.confirm({ message: `Automatic upload ${appId} bundle to Capgo?` })
|
|
233
|
-
await cancelCommand(doBundle,
|
|
234
|
+
await cancelCommand(doBundle, orgId, snag)
|
|
234
235
|
if (doBundle) {
|
|
235
236
|
const s = p.spinner()
|
|
236
237
|
s.start(`Running: ${pm.runner} @capgo/cli@latest bundle upload`)
|
|
@@ -251,13 +252,13 @@ async function step8(userId: string, snag: LogSnag, apikey: string, appId: strin
|
|
|
251
252
|
else {
|
|
252
253
|
p.log.info(`Upload yourself with command: ${pm.runner} @capgo/cli@latest bundle upload`)
|
|
253
254
|
}
|
|
254
|
-
await markStep(
|
|
255
|
+
await markStep(orgId, snag, 8)
|
|
255
256
|
}
|
|
256
257
|
|
|
257
|
-
async function step9(
|
|
258
|
+
async function step9(orgId: string, snag: LogSnag) {
|
|
258
259
|
const pm = getPMAndCommand()
|
|
259
260
|
const doRun = await p.confirm({ message: `Run in device now ?` })
|
|
260
|
-
await cancelCommand(doRun,
|
|
261
|
+
await cancelCommand(doRun, orgId, snag)
|
|
261
262
|
if (doRun) {
|
|
262
263
|
const plaformType = await p.select({
|
|
263
264
|
message: 'Pick a platform to run your app',
|
|
@@ -280,22 +281,22 @@ async function step9(userId: string, snag: LogSnag) {
|
|
|
280
281
|
else {
|
|
281
282
|
p.log.info(`Run yourself with command: ${pm.runner} cap run <ios|android>`)
|
|
282
283
|
}
|
|
283
|
-
await markStep(
|
|
284
|
+
await markStep(orgId, snag, 9)
|
|
284
285
|
}
|
|
285
286
|
|
|
286
|
-
async function
|
|
287
|
+
async function _step10(orgId: string, snag: LogSnag, supabase: SupabaseClient<Database>, appId: string) {
|
|
287
288
|
const doRun = await p.confirm({ message: `Automatic check if update working in device ?` })
|
|
288
|
-
await cancelCommand(doRun,
|
|
289
|
+
await cancelCommand(doRun, orgId, snag)
|
|
289
290
|
if (doRun) {
|
|
290
291
|
p.log.info(`Wait logs sent to Capgo from ${appId} device, Put the app in background and open it again.`)
|
|
291
292
|
p.log.info('Waiting...')
|
|
292
|
-
await waitLog('onboarding-v2', supabase, appId, snag,
|
|
293
|
+
await waitLog('onboarding-v2', supabase, appId, snag, orgId)
|
|
293
294
|
}
|
|
294
295
|
else {
|
|
295
296
|
const appIdUrl = convertAppName(appId)
|
|
296
297
|
p.log.info(`Check logs in https://web.capgo.app/app/p/${appIdUrl}/logs to see if update works.`)
|
|
297
298
|
}
|
|
298
|
-
await markStep(
|
|
299
|
+
await markStep(orgId, snag, 10)
|
|
299
300
|
}
|
|
300
301
|
|
|
301
302
|
export async function initApp(apikeyCommand: string, appId: string, options: SuperOptions) {
|
|
@@ -315,20 +316,24 @@ export async function initApp(apikeyCommand: string, appId: string, options: Sup
|
|
|
315
316
|
}
|
|
316
317
|
|
|
317
318
|
const supabase = await createSupabaseClient(apikey)
|
|
318
|
-
|
|
319
|
-
await markStep(userId, snag, 1)
|
|
319
|
+
await verifyUser(supabase, apikey, ['upload', 'all', 'read', 'write'])
|
|
320
320
|
|
|
321
|
-
await
|
|
322
|
-
|
|
323
|
-
await step4(userId, snag, apikey, appId)
|
|
324
|
-
await step5(userId, snag, apikey, appId)
|
|
325
|
-
await step6(userId, snag, apikey, appId)
|
|
326
|
-
await step7(userId, snag, apikey, appId)
|
|
327
|
-
await step8(userId, snag, apikey, appId)
|
|
328
|
-
await step9(userId, snag)
|
|
329
|
-
await step10(userId, snag, supabase, appId)
|
|
321
|
+
const organization = await getOrganization(supabase, ['admin', 'super_admin'])
|
|
322
|
+
const orgId = organization.gid
|
|
330
323
|
|
|
331
|
-
await markStep(
|
|
324
|
+
await markStep(orgId, snag, 1)
|
|
325
|
+
|
|
326
|
+
await step2(organization, snag, appId, options)
|
|
327
|
+
await step3(orgId, snag, apikey, appId)
|
|
328
|
+
await step4(orgId, snag, apikey, appId)
|
|
329
|
+
await step5(orgId, snag, apikey, appId)
|
|
330
|
+
await step6(orgId, snag, apikey, appId)
|
|
331
|
+
await step7(orgId, snag, apikey, appId)
|
|
332
|
+
await step8(orgId, snag, apikey, appId)
|
|
333
|
+
await step9(orgId, snag)
|
|
334
|
+
// await step10(orgId, snag, supabase, appId)
|
|
335
|
+
|
|
336
|
+
await markStep(orgId, snag, 0)
|
|
332
337
|
p.log.info(`Welcome onboard ✈️!`)
|
|
333
338
|
p.log.info(`Your Capgo update system is setup`)
|
|
334
339
|
p.log.info(`Next time use \`${pm.runner} @capgo/cli@latest bundle upload\` to only upload your bundle`)
|
package/src/utils.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { existsSync, readFileSync, readdirSync } from 'node:fs'
|
|
|
2
2
|
import { homedir } from 'node:os'
|
|
3
3
|
import { resolve } from 'node:path'
|
|
4
4
|
import process from 'node:process'
|
|
5
|
+
import type { Buffer } from 'node:buffer'
|
|
5
6
|
import { loadConfig } from '@capacitor/cli/dist/config'
|
|
6
7
|
import { program } from 'commander'
|
|
7
8
|
import type { SupabaseClient } from '@supabase/supabase-js'
|
|
@@ -21,6 +22,10 @@ export const defaultHost = 'https://capgo.app'
|
|
|
21
22
|
export const defaultApiHost = 'https://api.capgo.app'
|
|
22
23
|
export const defaultHostWeb = 'https://web.capgo.app'
|
|
23
24
|
|
|
25
|
+
export type ArrayElement<ArrayType extends readonly unknown[]> =
|
|
26
|
+
ArrayType extends readonly (infer ElementType)[] ? ElementType : never
|
|
27
|
+
export type Organization = ArrayElement<Database['public']['Functions']['get_orgs_v5']['Returns']>
|
|
28
|
+
|
|
24
29
|
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-]+)*))?$/
|
|
25
30
|
export const formatError = (error: any) => error ? `\n${prettyjson.render(error)}` : ''
|
|
26
31
|
|
|
@@ -483,6 +488,7 @@ export async function uploadUrl(supabase: SupabaseClient<Database>, appId: strin
|
|
|
483
488
|
const data = {
|
|
484
489
|
app_id: appId,
|
|
485
490
|
name,
|
|
491
|
+
version: 0,
|
|
486
492
|
}
|
|
487
493
|
try {
|
|
488
494
|
const pathUploadLink = 'private/upload_link'
|
|
@@ -495,6 +501,67 @@ export async function uploadUrl(supabase: SupabaseClient<Database>, appId: strin
|
|
|
495
501
|
return ''
|
|
496
502
|
}
|
|
497
503
|
|
|
504
|
+
async function prepareMultipart(supabase: SupabaseClient<Database>, appId: string, name: string): Promise<{ key: string, uploadId: string, url: string } | null> {
|
|
505
|
+
const data = {
|
|
506
|
+
app_id: appId,
|
|
507
|
+
name,
|
|
508
|
+
version: 1,
|
|
509
|
+
}
|
|
510
|
+
try {
|
|
511
|
+
const pathUploadLink = 'private/upload_link'
|
|
512
|
+
const res = await supabase.functions.invoke(pathUploadLink, { body: JSON.stringify(data) })
|
|
513
|
+
return res.data as any
|
|
514
|
+
}
|
|
515
|
+
catch (error) {
|
|
516
|
+
p.log.error(`Cannot get upload url ${formatError(error)}`)
|
|
517
|
+
return null
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
async function finishMultipartDownload(key: string, uploadId: string, url: string, parts: any[]) {
|
|
522
|
+
const metadata = {
|
|
523
|
+
action: 'mpu-complete',
|
|
524
|
+
uploadId,
|
|
525
|
+
key,
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
await ky.post(url, {
|
|
529
|
+
json: {
|
|
530
|
+
parts,
|
|
531
|
+
},
|
|
532
|
+
searchParams: new URLSearchParams({ body: btoa(JSON.stringify(metadata)) }),
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
// console.log(await response.json())
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const PART_SIZE = 10 * 1024 * 1024
|
|
539
|
+
export async function uploadMultipart(supabase: SupabaseClient<Database>, appId: string, name: string, data: Buffer): Promise<boolean> {
|
|
540
|
+
try {
|
|
541
|
+
const multipartPrep = await prepareMultipart(supabase, appId, name)
|
|
542
|
+
if (!multipartPrep) {
|
|
543
|
+
// Just pass the error
|
|
544
|
+
return false
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const fileSize = data.length
|
|
548
|
+
const partCount = Math.ceil(fileSize / PART_SIZE)
|
|
549
|
+
|
|
550
|
+
const uploadPromises = Array.from({ length: partCount }, (_, index) =>
|
|
551
|
+
uploadPart(data, PART_SIZE, multipartPrep.url, multipartPrep.key, multipartPrep.uploadId, index))
|
|
552
|
+
|
|
553
|
+
const parts = await Promise.all(uploadPromises)
|
|
554
|
+
|
|
555
|
+
await finishMultipartDownload(multipartPrep.key, multipartPrep.uploadId, multipartPrep.url, parts)
|
|
556
|
+
|
|
557
|
+
return true
|
|
558
|
+
}
|
|
559
|
+
catch (e) {
|
|
560
|
+
p.log.error(`Could not upload via multipart ${formatError(e)}`)
|
|
561
|
+
return false
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
498
565
|
export async function deletedFailedVersion(supabase: SupabaseClient<Database>, appId: string, name: string): Promise<void> {
|
|
499
566
|
const data = {
|
|
500
567
|
app_id: appId,
|
|
@@ -511,6 +578,34 @@ export async function deletedFailedVersion(supabase: SupabaseClient<Database>, a
|
|
|
511
578
|
}
|
|
512
579
|
}
|
|
513
580
|
|
|
581
|
+
async function uploadPart(
|
|
582
|
+
data: Buffer,
|
|
583
|
+
partsize: number,
|
|
584
|
+
url: string,
|
|
585
|
+
key: string,
|
|
586
|
+
uploadId: string,
|
|
587
|
+
index: number,
|
|
588
|
+
) {
|
|
589
|
+
const dataToUpload = data.subarray(
|
|
590
|
+
partsize * index,
|
|
591
|
+
partsize * (index + 1),
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
const metadata = {
|
|
595
|
+
action: 'mpu-uploadpart',
|
|
596
|
+
uploadId,
|
|
597
|
+
partNumber: index + 1,
|
|
598
|
+
key,
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const response = await ky.put(url, {
|
|
602
|
+
body: dataToUpload,
|
|
603
|
+
searchParams: new URLSearchParams({ body: btoa(JSON.stringify(metadata)) }),
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
return await response.json()
|
|
607
|
+
}
|
|
608
|
+
|
|
514
609
|
export async function updateOrCreateChannel(supabase: SupabaseClient<Database>, update: Database['public']['Tables']['channels']['Insert']) {
|
|
515
610
|
// console.log('updateOrCreateChannel', update)
|
|
516
611
|
if (!update.app_id || !update.name || !update.created_by) {
|
|
@@ -562,12 +657,50 @@ export async function updateOrCreateChannel(supabase: SupabaseClient<Database>,
|
|
|
562
657
|
|
|
563
658
|
export function useLogSnag(): LogSnag {
|
|
564
659
|
const logsnag = new LogSnag({
|
|
565
|
-
token: 'c124f5e9d0ce5bdd14bbb48f815d5583',
|
|
566
|
-
project: 'capgo',
|
|
660
|
+
token: process.env.CAPGO_LOGSNAG ?? 'c124f5e9d0ce5bdd14bbb48f815d5583',
|
|
661
|
+
project: process.env.CAPGO_LOGSNAG_PROJECT ?? 'capgo',
|
|
567
662
|
})
|
|
568
663
|
return logsnag
|
|
569
664
|
}
|
|
570
665
|
|
|
666
|
+
export async function getOrganization(supabase: SupabaseClient<Database>, roles: string[]): Promise<Organization> {
|
|
667
|
+
const { error: orgError, data: allOrganizations } = await supabase
|
|
668
|
+
.rpc('get_orgs_v5')
|
|
669
|
+
|
|
670
|
+
if (orgError) {
|
|
671
|
+
p.log.error('Cannot get the list of organizations - exiting')
|
|
672
|
+
p.log.error(`Error ${JSON.stringify(orgError)}`)
|
|
673
|
+
program.error('')
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const adminOrgs = allOrganizations.filter(org => !!roles.find(role => role === org.role))
|
|
677
|
+
|
|
678
|
+
if (adminOrgs.length === 0) {
|
|
679
|
+
p.log.error(`Could not get organization with roles: ${roles.join(' or ')} because the user does not have any org`)
|
|
680
|
+
program.error('')
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const organizationUidRaw = (adminOrgs.length > 1)
|
|
684
|
+
? await p.select({
|
|
685
|
+
message: 'Please pick the organization that you want to insert to',
|
|
686
|
+
options: adminOrgs.map((org) => {
|
|
687
|
+
return { value: org.gid, label: org.name }
|
|
688
|
+
}),
|
|
689
|
+
})
|
|
690
|
+
: adminOrgs[0].gid
|
|
691
|
+
|
|
692
|
+
if (p.isCancel(organizationUidRaw)) {
|
|
693
|
+
p.log.error('Canceled organization selection, exiting')
|
|
694
|
+
program.error('')
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const organizationUid = organizationUidRaw as string
|
|
698
|
+
const organization = allOrganizations.find(org => org.gid === organizationUid)!
|
|
699
|
+
|
|
700
|
+
p.log.info(`Using the organization "${organization.name}" as the app owner`)
|
|
701
|
+
return organization
|
|
702
|
+
}
|
|
703
|
+
|
|
571
704
|
export const convertAppName = (appName: string) => appName.replace(/\./g, '--')
|
|
572
705
|
|
|
573
706
|
export async function verifyUser(supabase: SupabaseClient<Database>, apikey: string, keymod: Database['public']['Enums']['key_mode'][] = ['all']) {
|