@capgo/cli 3.9.2 → 3.10.0
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 +7 -0
- package/dist/index.js +1 -1
- package/package.json +3 -1
- package/src/api/crypto.ts +1 -1
- package/src/api/update.ts +4 -2
- package/src/app/add.ts +24 -13
- package/src/app/delete.ts +4 -2
- package/src/app/list.ts +13 -8
- package/src/app/set.ts +14 -8
- package/src/app/watch.ts +1 -1
- package/src/bundle/cleanup.ts +12 -10
- package/src/bundle/decrypt.ts +3 -1
- package/src/bundle/delete.ts +15 -8
- package/src/bundle/encrypt.ts +21 -5
- package/src/bundle/list.ts +9 -5
- package/src/bundle/unlink.ts +13 -6
- package/src/bundle/upload.ts +37 -5
- package/src/channel/add.ts +12 -7
- package/src/channel/delete.ts +10 -6
- package/src/channel/list.ts +8 -5
- package/src/channel/set.ts +33 -20
- package/src/init.ts +1 -1
- package/src/key.ts +26 -14
- package/src/login.ts +10 -4
- package/src/utils.ts +15 -5
package/src/bundle/upload.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { randomUUID } from 'crypto';
|
|
|
4
4
|
import * as p from '@clack/prompts';
|
|
5
5
|
import { existsSync, readFileSync } from 'fs';
|
|
6
6
|
import { checksum as getChecksum } from '@tomasklaen/checksum';
|
|
7
|
+
import ciDetect from '@npmcli/ci-detect';
|
|
7
8
|
import { checkLatest } from '../api/update';
|
|
8
9
|
import { OptionsBase } from '../api/utils';
|
|
9
10
|
import { checkAppExistsAndHasPermissionErr } from "../api/app";
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
hostWeb, getConfig, createSupabaseClient,
|
|
13
14
|
updateOrCreateChannel, updateOrCreateVersion,
|
|
14
15
|
formatError, findSavedKey, checkPlanValid,
|
|
15
|
-
useLogSnag, verifyUser, regexSemver, baseKeyPub, convertAppName
|
|
16
|
+
useLogSnag, verifyUser, regexSemver, baseKeyPub, convertAppName, defaulPublicKey
|
|
16
17
|
} from '../utils';
|
|
17
18
|
|
|
18
19
|
const alertMb = 20;
|
|
@@ -27,6 +28,7 @@ interface Options extends OptionsBase {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export const uploadBundle = async (appid: string, options: Options, shouldExit = true) => {
|
|
31
|
+
p.intro(`Uploading`);
|
|
30
32
|
await checkLatest();
|
|
31
33
|
let { bundle, path, channel } = options;
|
|
32
34
|
const { external, key = false, displayIvSession } = options;
|
|
@@ -34,7 +36,6 @@ export const uploadBundle = async (appid: string, options: Options, shouldExit =
|
|
|
34
36
|
const snag = useLogSnag()
|
|
35
37
|
|
|
36
38
|
channel = channel || 'dev';
|
|
37
|
-
p.intro(`Uploading`);
|
|
38
39
|
|
|
39
40
|
const config = await getConfig();
|
|
40
41
|
appid = appid || config?.app?.appId
|
|
@@ -115,16 +116,36 @@ export const uploadBundle = async (appid: string, options: Options, shouldExit =
|
|
|
115
116
|
s.stop(`Checksum: ${checksum}`);
|
|
116
117
|
if (key || existsSync(baseKeyPub)) {
|
|
117
118
|
const publicKey = typeof key === 'string' ? key : baseKeyPub
|
|
119
|
+
let keyData = ''
|
|
118
120
|
// check if publicKey exist
|
|
119
121
|
if (!existsSync(publicKey)) {
|
|
120
122
|
p.log.error(`Cannot find public key ${publicKey}`);
|
|
121
|
-
|
|
123
|
+
if (ciDetect()) {
|
|
124
|
+
program.error('');
|
|
125
|
+
}
|
|
126
|
+
const res = await p.confirm({ message: 'Do you want to use our public key ?' })
|
|
127
|
+
if (!res) {
|
|
128
|
+
p.log.error(`Error: Missing public key`);
|
|
129
|
+
program.error('');
|
|
130
|
+
}
|
|
131
|
+
keyData = defaulPublicKey
|
|
122
132
|
}
|
|
133
|
+
await snag.publish({
|
|
134
|
+
channel: 'app',
|
|
135
|
+
event: 'App encryption',
|
|
136
|
+
icon: '🔑',
|
|
137
|
+
tags: {
|
|
138
|
+
'user-id': userId,
|
|
139
|
+
'app-id': appid,
|
|
140
|
+
},
|
|
141
|
+
notify: false,
|
|
142
|
+
}).catch()
|
|
123
143
|
// open with fs publicKey path
|
|
124
144
|
const keyFile = readFileSync(publicKey)
|
|
145
|
+
keyData = keyFile.toString()
|
|
125
146
|
// encrypt
|
|
126
147
|
p.log.info(`Encrypting your bundle`);
|
|
127
|
-
const res = encryptSource(zipped,
|
|
148
|
+
const res = encryptSource(zipped, keyData)
|
|
128
149
|
sessionKey = res.ivSessionKey
|
|
129
150
|
if (displayIvSession) {
|
|
130
151
|
p.log.info(`Your Iv Session key is ${sessionKey},
|
|
@@ -166,6 +187,17 @@ It will be also visible in your dashboard\n`);
|
|
|
166
187
|
} else if (external && !external.startsWith('https://')) {
|
|
167
188
|
p.log.error(`External link should should start with "https://" current is "${external}"`);
|
|
168
189
|
program.error('');
|
|
190
|
+
} else {
|
|
191
|
+
await snag.publish({
|
|
192
|
+
channel: 'app',
|
|
193
|
+
event: 'App external',
|
|
194
|
+
icon: '📤',
|
|
195
|
+
tags: {
|
|
196
|
+
'user-id': userId,
|
|
197
|
+
'app-id': appid,
|
|
198
|
+
},
|
|
199
|
+
notify: false,
|
|
200
|
+
}).catch()
|
|
169
201
|
}
|
|
170
202
|
const { error: dbError } = await updateOrCreateVersion(supabase, {
|
|
171
203
|
bucket_id: external ? undefined : fileName,
|
|
@@ -225,6 +257,6 @@ export const uploadCommand = async (apikey: string, options: Options) => {
|
|
|
225
257
|
}
|
|
226
258
|
|
|
227
259
|
export const uploadDeprecatedCommand = async (apikey: string, options: Options) => {
|
|
228
|
-
|
|
260
|
+
p.log.warn('⚠️ This command is deprecated, use "npx @capgo/cli bundle upload" instead ⚠️')
|
|
229
261
|
uploadBundle(apikey, options, true)
|
|
230
262
|
}
|
package/src/channel/add.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { program } from "commander";
|
|
2
|
+
import * as p from '@clack/prompts';
|
|
2
3
|
import { checkAppExistsAndHasPermissionErr } from "../api/app";
|
|
3
4
|
import { createChannel, findUnknownVersion } from "../api/channels";
|
|
4
5
|
import { OptionsBase } from "../api/utils";
|
|
@@ -9,16 +10,19 @@ interface Options extends OptionsBase {
|
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export const addChannel = async (channelId: string, appId: string, options: Options, shouldExit = true) => {
|
|
13
|
+
p.intro(`Create channel`);
|
|
12
14
|
options.apikey = options.apikey || findSavedKey()
|
|
13
15
|
const config = await getConfig();
|
|
14
16
|
appId = appId || config?.app?.appId
|
|
15
17
|
const snag = useLogSnag()
|
|
16
18
|
|
|
17
19
|
if (!options.apikey) {
|
|
18
|
-
|
|
20
|
+
p.log.error("Missing API key, you need to provide a API key to upload your bundle");
|
|
21
|
+
program.error('');
|
|
19
22
|
}
|
|
20
23
|
if (!appId) {
|
|
21
|
-
|
|
24
|
+
p.log.error("Missing argument, you need to provide a appId, or be in a capacitor project");
|
|
25
|
+
program.error('');
|
|
22
26
|
}
|
|
23
27
|
const supabase = createSupabaseClient(options.apikey)
|
|
24
28
|
|
|
@@ -26,11 +30,12 @@ export const addChannel = async (channelId: string, appId: string, options: Opti
|
|
|
26
30
|
// Check we have app access to this appId
|
|
27
31
|
await checkAppExistsAndHasPermissionErr(supabase, appId, options.apikey);
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
p.log.info(`Creating channel ${appId}#${channelId} to Capgo`);
|
|
30
34
|
try {
|
|
31
35
|
const data = await findUnknownVersion(supabase, appId)
|
|
32
36
|
if (!data) {
|
|
33
|
-
|
|
37
|
+
p.log.error(`Cannot find default version for channel creation, please contact Capgo support 🤨`);
|
|
38
|
+
program.error('');
|
|
34
39
|
}
|
|
35
40
|
await createChannel(supabase, {
|
|
36
41
|
name: channelId,
|
|
@@ -38,7 +43,7 @@ export const addChannel = async (channelId: string, appId: string, options: Opti
|
|
|
38
43
|
version: data.id,
|
|
39
44
|
created_by: userId
|
|
40
45
|
});
|
|
41
|
-
|
|
46
|
+
p.log.success(`Channel created ✅`);
|
|
42
47
|
await snag.publish({
|
|
43
48
|
channel: 'channel',
|
|
44
49
|
event: 'Create channel',
|
|
@@ -51,11 +56,11 @@ export const addChannel = async (channelId: string, appId: string, options: Opti
|
|
|
51
56
|
notify: false,
|
|
52
57
|
}).catch()
|
|
53
58
|
} catch (error) {
|
|
54
|
-
|
|
59
|
+
p.log.error(`Cannot create Channel 🙀`);
|
|
55
60
|
return false
|
|
56
61
|
}
|
|
57
62
|
if (shouldExit) {
|
|
58
|
-
|
|
63
|
+
p.outro(`Done ✅`);
|
|
59
64
|
process.exit()
|
|
60
65
|
}
|
|
61
66
|
return true
|
package/src/channel/delete.ts
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import { program } from "commander";
|
|
2
|
+
import * as p from '@clack/prompts';
|
|
2
3
|
import { checkAppExistsAndHasPermissionErr } from "../api/app";
|
|
3
4
|
import { delChannel } from "../api/channels";
|
|
4
5
|
import { OptionsBase } from "../api/utils";
|
|
5
6
|
import { findSavedKey, getConfig, useLogSnag, createSupabaseClient, verifyUser } from "../utils";
|
|
6
7
|
|
|
7
8
|
export const deleteChannel = async (channelId: string, appId: string, options: OptionsBase) => {
|
|
9
|
+
p.intro(`Delete channel`);
|
|
8
10
|
options.apikey = options.apikey || findSavedKey()
|
|
9
11
|
const config = await getConfig();
|
|
10
12
|
appId = appId || config?.app?.appId
|
|
11
13
|
const snag = useLogSnag()
|
|
12
14
|
|
|
13
15
|
if (!options.apikey) {
|
|
14
|
-
|
|
16
|
+
p.log.error("Missing API key, you need to provide a API key to upload your bundle");
|
|
17
|
+
program.error('');
|
|
15
18
|
}
|
|
16
19
|
if (!appId) {
|
|
17
|
-
|
|
20
|
+
p.log.error("Missing argument, you need to provide a appId, or be in a capacitor project");
|
|
21
|
+
program.error('');
|
|
18
22
|
}
|
|
19
23
|
const supabase = createSupabaseClient(options.apikey)
|
|
20
24
|
|
|
@@ -22,10 +26,10 @@ export const deleteChannel = async (channelId: string, appId: string, options: O
|
|
|
22
26
|
// Check we have app access to this appId
|
|
23
27
|
await checkAppExistsAndHasPermissionErr(supabase, appId, options.apikey);
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
p.log.info(`Deleting channel ${appId}#${channelId} from Capgo`);
|
|
26
30
|
try {
|
|
27
31
|
await delChannel(supabase, channelId, appId, userId);
|
|
28
|
-
|
|
32
|
+
p.log.success(`Channel deleted`);
|
|
29
33
|
await snag.publish({
|
|
30
34
|
channel: 'channel',
|
|
31
35
|
event: 'Delete channel',
|
|
@@ -38,8 +42,8 @@ export const deleteChannel = async (channelId: string, appId: string, options: O
|
|
|
38
42
|
notify: false,
|
|
39
43
|
}).catch()
|
|
40
44
|
} catch (error) {
|
|
41
|
-
|
|
45
|
+
p.log.error(`Cannot delete Channel 🙀`);
|
|
42
46
|
}
|
|
43
|
-
|
|
47
|
+
p.outro(`Done ✅`);
|
|
44
48
|
process.exit()
|
|
45
49
|
}
|
package/src/channel/list.ts
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { program } from 'commander';
|
|
2
|
+
import * as p from '@clack/prompts';
|
|
2
3
|
import { checkAppExistsAndHasPermissionErr } from '../api/app';
|
|
3
4
|
import { getActiveChannels, displayChannels } from '../api/channels';
|
|
4
5
|
import { OptionsBase } from '../api/utils';
|
|
5
6
|
import { findSavedKey, getConfig, createSupabaseClient, verifyUser, useLogSnag } from '../utils';
|
|
6
7
|
|
|
7
8
|
export const listChannels = async (appId: string, options: OptionsBase) => {
|
|
9
|
+
p.intro(`List channels`);
|
|
8
10
|
options.apikey = options.apikey || findSavedKey()
|
|
9
11
|
const config = await getConfig();
|
|
10
12
|
appId = appId || config?.app?.appId
|
|
11
13
|
const snag = useLogSnag()
|
|
12
14
|
|
|
13
15
|
if (!options.apikey) {
|
|
14
|
-
|
|
16
|
+
p.log.error("Missing API key, you need to provide a API key to upload your bundle");
|
|
15
17
|
}
|
|
16
18
|
if (!appId) {
|
|
17
|
-
|
|
19
|
+
p.log.error("Missing argument, you need to provide a appId, or be in a capacitor project");
|
|
20
|
+
program.error('');
|
|
18
21
|
}
|
|
19
22
|
const supabase = createSupabaseClient(options.apikey)
|
|
20
23
|
|
|
@@ -22,7 +25,7 @@ export const listChannels = async (appId: string, options: OptionsBase) => {
|
|
|
22
25
|
// Check we have app access to this appId
|
|
23
26
|
await checkAppExistsAndHasPermissionErr(supabase, appId, options.apikey);
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
p.log.info(`Querying available channels in Capgo`);
|
|
26
29
|
|
|
27
30
|
// Check we have app access to this appId
|
|
28
31
|
await checkAppExistsAndHasPermissionErr(supabase, appId, options.apikey);
|
|
@@ -30,7 +33,7 @@ export const listChannels = async (appId: string, options: OptionsBase) => {
|
|
|
30
33
|
// Get all active app versions we might possibly be able to cleanup
|
|
31
34
|
const allVersions = await getActiveChannels(supabase, appId, userId);
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
p.log.info(`Active channels in Capgo: ${allVersions?.length}`);
|
|
34
37
|
|
|
35
38
|
displayChannels(allVersions);
|
|
36
39
|
await snag.publish({
|
|
@@ -43,6 +46,6 @@ export const listChannels = async (appId: string, options: OptionsBase) => {
|
|
|
43
46
|
},
|
|
44
47
|
notify: false,
|
|
45
48
|
}).catch()
|
|
46
|
-
|
|
49
|
+
p.outro(`Done ✅`);
|
|
47
50
|
process.exit()
|
|
48
51
|
}
|
package/src/channel/set.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { program } from 'commander';
|
|
2
2
|
import { Database } from 'types/supabase.types';
|
|
3
|
+
import * as p from '@clack/prompts';
|
|
3
4
|
import { OptionsBase } from '../api/utils';
|
|
4
5
|
import { checkAppExistsAndHasPermissionErr } from "../api/app";
|
|
5
6
|
import {
|
|
@@ -20,16 +21,19 @@ interface Options extends OptionsBase {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export const setChannel = async (channel: string, appId: string, options: Options) => {
|
|
24
|
+
p.intro(`Set channel`);
|
|
23
25
|
options.apikey = options.apikey || findSavedKey()
|
|
24
26
|
const config = await getConfig();
|
|
25
27
|
appId = appId || config?.app?.appId
|
|
26
28
|
const snag = useLogSnag()
|
|
27
29
|
|
|
28
30
|
if (!options.apikey) {
|
|
29
|
-
|
|
31
|
+
p.log.error("Missing API key, you need to provide a API key to upload your bundle");
|
|
32
|
+
program.error('');
|
|
30
33
|
}
|
|
31
34
|
if (!appId) {
|
|
32
|
-
|
|
35
|
+
p.log.error("Missing argument, you need to provide a appId, or be in a capacitor project");
|
|
36
|
+
program.error('');
|
|
33
37
|
}
|
|
34
38
|
const supabase = createSupabaseClient(options.apikey)
|
|
35
39
|
|
|
@@ -39,10 +43,12 @@ export const setChannel = async (channel: string, appId: string, options: Option
|
|
|
39
43
|
|
|
40
44
|
const { bundle, latest, downgrade, upgrade, ios, android, selfAssign, state } = options;
|
|
41
45
|
if (!channel) {
|
|
42
|
-
|
|
46
|
+
p.log.error("Missing argument, you need to provide a channel");
|
|
47
|
+
program.error('');
|
|
43
48
|
}
|
|
44
49
|
if (latest && bundle) {
|
|
45
|
-
|
|
50
|
+
p.log.error("Cannot set latest and bundle at the same time");
|
|
51
|
+
program.error('');
|
|
46
52
|
}
|
|
47
53
|
if (bundle == null &&
|
|
48
54
|
state == null &&
|
|
@@ -52,7 +58,8 @@ export const setChannel = async (channel: string, appId: string, options: Option
|
|
|
52
58
|
ios == null &&
|
|
53
59
|
android == null &&
|
|
54
60
|
selfAssign == null) {
|
|
55
|
-
|
|
61
|
+
p.log.error("Missing argument, you need to provide a option to set");
|
|
62
|
+
program.error('');
|
|
56
63
|
}
|
|
57
64
|
try {
|
|
58
65
|
await checkPlanValid(supabase, userId)
|
|
@@ -74,45 +81,50 @@ export const setChannel = async (channel: string, appId: string, options: Option
|
|
|
74
81
|
.eq('user_id', userId)
|
|
75
82
|
.eq('deleted', false)
|
|
76
83
|
.single()
|
|
77
|
-
if (vError || !data)
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
if (vError || !data) {
|
|
85
|
+
p.log.error(`Cannot find version ${bundleVersion}`);
|
|
86
|
+
program.error('');
|
|
87
|
+
}
|
|
88
|
+
p.log.info(`Set ${appId} channel: ${channel} to @${bundleVersion}`);
|
|
80
89
|
channelPayload.version = data.id
|
|
81
90
|
}
|
|
82
91
|
if (state != null) {
|
|
83
92
|
if (state === 'public' || state === 'private') {
|
|
84
|
-
|
|
93
|
+
p.log.info(`Set ${appId} channel: ${channel} to public or private is deprecated, use default or normal instead`);
|
|
85
94
|
}
|
|
86
|
-
|
|
95
|
+
p.log.info(`Set ${appId} channel: ${channel} to ${state === 'public' || state === 'default' ? 'default' : 'normal'}`);
|
|
87
96
|
channelPayload.public = state === 'public' || state === 'default'
|
|
88
97
|
}
|
|
89
98
|
if (downgrade != null) {
|
|
90
|
-
|
|
99
|
+
p.log.info(`Set ${appId} channel: ${channel} to ${downgrade ? 'allow' : 'disallow'} downgrade`);
|
|
91
100
|
channelPayload.disableAutoUpdateUnderNative = !downgrade
|
|
92
101
|
}
|
|
93
102
|
if (upgrade != null) {
|
|
94
|
-
|
|
103
|
+
p.log.info(`Set ${appId} channel: ${channel} to ${upgrade ? 'allow' : 'disallow'} upgrade`);
|
|
95
104
|
channelPayload.disableAutoUpdateToMajor = !upgrade
|
|
96
105
|
}
|
|
97
106
|
if (ios != null) {
|
|
98
|
-
|
|
107
|
+
p.log.info(`Set ${appId} channel: ${channel} to ${ios ? 'allow' : 'disallow'} ios update`);
|
|
99
108
|
channelPayload.ios = !!ios
|
|
100
109
|
}
|
|
101
110
|
if (android != null) {
|
|
102
|
-
|
|
111
|
+
p.log.info(`Set ${appId} channel: ${channel} to ${android ? 'allow' : 'disallow'} android update`);
|
|
103
112
|
channelPayload.android = !!android
|
|
104
113
|
}
|
|
105
114
|
if (selfAssign != null) {
|
|
106
|
-
|
|
115
|
+
p.log.info(`Set ${appId} channel: ${channel} to ${selfAssign ? 'allow' : 'disallow'} self assign to this channel`);
|
|
107
116
|
channelPayload.allow_device_self_set = !!selfAssign
|
|
108
117
|
}
|
|
109
118
|
try {
|
|
110
119
|
const { error: dbError } = await updateOrCreateChannel(supabase, channelPayload, options.apikey)
|
|
111
|
-
if (dbError)
|
|
112
|
-
|
|
120
|
+
if (dbError) {
|
|
121
|
+
p.log.error(`Cannot set channel the upload key is not allowed to do that, use the "all" for this.`);
|
|
122
|
+
program.error('');
|
|
123
|
+
}
|
|
113
124
|
}
|
|
114
125
|
catch (e) {
|
|
115
|
-
|
|
126
|
+
p.log.error(`Cannot set channel the upload key is not allowed to do that, use the "all" for this.`);
|
|
127
|
+
program.error('');
|
|
116
128
|
}
|
|
117
129
|
await snag.publish({
|
|
118
130
|
channel: 'channel',
|
|
@@ -125,8 +137,9 @@ export const setChannel = async (channel: string, appId: string, options: Option
|
|
|
125
137
|
notify: false,
|
|
126
138
|
}).catch()
|
|
127
139
|
} catch (err) {
|
|
128
|
-
|
|
140
|
+
p.log.error(`Unknow error ${formatError(err)}`);
|
|
141
|
+
program.error('');
|
|
129
142
|
}
|
|
130
|
-
|
|
143
|
+
p.outro(`Done ✅`);
|
|
131
144
|
process.exit()
|
|
132
145
|
}
|
package/src/init.ts
CHANGED
|
@@ -392,13 +392,13 @@ const step10 = async (userId: string, snag: LogSnag,
|
|
|
392
392
|
}
|
|
393
393
|
|
|
394
394
|
export const initApp = async (apikey: string, appId: string, options: SuperOptions) => {
|
|
395
|
+
p.intro(`Capgo onboarding 🛫`);
|
|
395
396
|
await checkLatest();
|
|
396
397
|
const snag = useLogSnag()
|
|
397
398
|
const config = await getConfig();
|
|
398
399
|
appId = appId || config?.app?.appId
|
|
399
400
|
apikey = apikey || findSavedKey()
|
|
400
401
|
|
|
401
|
-
p.intro(`Capgo init`);
|
|
402
402
|
const log = p.spinner();
|
|
403
403
|
log.start('Running: npx @capgo/cli@latest login ***');
|
|
404
404
|
const loginRes = await login(apikey, options, false);
|
package/src/key.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { program } from 'commander'
|
|
2
2
|
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
|
3
3
|
import { writeConfig } from '@capacitor/cli/dist/config';
|
|
4
|
+
import * as p from '@clack/prompts';
|
|
4
5
|
import { createRSA } from './api/crypto';
|
|
5
6
|
import { baseKey, baseKeyPub, getConfig } from './utils';
|
|
6
7
|
import { checkLatest } from './api/update';
|
|
@@ -15,7 +16,8 @@ interface Options {
|
|
|
15
16
|
|
|
16
17
|
export const saveKey = async (options: saveOptions, log = true) => {
|
|
17
18
|
if (!existsSync('.git')) {
|
|
18
|
-
|
|
19
|
+
p.log.error('To use local you should be in a git repository');
|
|
20
|
+
program.error('');
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
const config = await getConfig();
|
|
@@ -28,7 +30,8 @@ export const saveKey = async (options: saveOptions, log = true) => {
|
|
|
28
30
|
|
|
29
31
|
if (!existsSync(keyPath) && !privateKey) {
|
|
30
32
|
if (log) {
|
|
31
|
-
|
|
33
|
+
p.log.error(`Cannot find public key ${keyPath} or as keyData option or in ${config.app.extConfigFilePath}`);
|
|
34
|
+
program.error('');
|
|
32
35
|
} else {
|
|
33
36
|
return false
|
|
34
37
|
}
|
|
@@ -50,28 +53,33 @@ export const saveKey = async (options: saveOptions, log = true) => {
|
|
|
50
53
|
writeConfig(extConfig, config.app.extConfigFilePath)
|
|
51
54
|
}
|
|
52
55
|
if (log) {
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
p.log.success(`private key saved into ${config.app.extConfigFilePath} file in local directory`);
|
|
57
|
+
p.log.success(`your app will decode the zip archive with this key`);
|
|
55
58
|
}
|
|
56
59
|
return true
|
|
57
60
|
}
|
|
58
61
|
export const saveKeyCommand = async (options: saveOptions) => {
|
|
62
|
+
p.intro(`Save keys 🔑`);
|
|
59
63
|
await checkLatest();
|
|
60
64
|
await saveKey(options)
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
export const createKey = async (options: Options, log = true) => {
|
|
64
68
|
// write in file .capgo the apikey in home directory
|
|
65
|
-
|
|
69
|
+
if (log) {
|
|
70
|
+
p.intro(`Create keys 🔑`);
|
|
71
|
+
}
|
|
66
72
|
if (!existsSync('.git')) {
|
|
67
|
-
|
|
73
|
+
p.log.error('To use local you should be in a git repository');
|
|
74
|
+
program.error('');
|
|
68
75
|
}
|
|
69
76
|
const { publicKey, privateKey } = createRSA()
|
|
70
77
|
|
|
71
78
|
// check if baseName already exist
|
|
72
79
|
if (existsSync(baseKeyPub) && !options.force) {
|
|
73
80
|
if (log) {
|
|
74
|
-
|
|
81
|
+
p.log.error('Public Key already exists, use --force to overwrite');
|
|
82
|
+
program.error('');
|
|
75
83
|
} else {
|
|
76
84
|
return false
|
|
77
85
|
}
|
|
@@ -79,7 +87,8 @@ export const createKey = async (options: Options, log = true) => {
|
|
|
79
87
|
writeFileSync(baseKeyPub, publicKey);
|
|
80
88
|
if (existsSync(baseKey) && !options.force) {
|
|
81
89
|
if (log) {
|
|
82
|
-
|
|
90
|
+
p.log.error('Private Key already exists, use --force to overwrite');
|
|
91
|
+
program.error('');
|
|
83
92
|
} else {
|
|
84
93
|
return false
|
|
85
94
|
}
|
|
@@ -101,12 +110,15 @@ export const createKey = async (options: Options, log = true) => {
|
|
|
101
110
|
}
|
|
102
111
|
|
|
103
112
|
if (log) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
p.log.success('Your RSA key has been generated')
|
|
114
|
+
p.log.success(`Public key saved in ${baseKeyPub}`)
|
|
115
|
+
p.log.success('This key will be use to crypt your bundle before sending it to Capgo')
|
|
116
|
+
p.log.success('Than make them unreadable by Capgo and unmodifiable by anyone')
|
|
117
|
+
p.log.success(`Private key saved in ${config.app.extConfigFilePath}`);
|
|
118
|
+
p.log.success('Your app will be the only one having it');
|
|
119
|
+
p.log.success('Only your users can decrypt your update');
|
|
120
|
+
p.log.success('Only you can send them an update');
|
|
121
|
+
p.outro(`Done ✅`);
|
|
110
122
|
}
|
|
111
123
|
return true
|
|
112
124
|
}
|
package/src/login.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { program } from 'commander';
|
|
2
2
|
import { existsSync, writeFileSync, appendFileSync } from 'fs'
|
|
3
3
|
import { homedir } from 'os'
|
|
4
|
+
import * as p from '@clack/prompts';
|
|
4
5
|
import { createSupabaseClient, useLogSnag, verifyUser } from './utils';
|
|
5
6
|
import { checkLatest } from './api/update';
|
|
6
7
|
|
|
@@ -9,6 +10,10 @@ interface Options {
|
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export const login = async (apikey: string, options: Options, shouldExit = true) => {
|
|
13
|
+
|
|
14
|
+
if (shouldExit) {
|
|
15
|
+
p.intro(`Login to Capgo`);
|
|
16
|
+
}
|
|
12
17
|
if (!apikey) {
|
|
13
18
|
if (shouldExit) {
|
|
14
19
|
program.error("Missing API key, you need to provide a API key to upload your bundle");
|
|
@@ -23,7 +28,8 @@ export const login = async (apikey: string, options: Options, shouldExit = true)
|
|
|
23
28
|
|
|
24
29
|
if (local) {
|
|
25
30
|
if (!existsSync('.git')) {
|
|
26
|
-
|
|
31
|
+
p.log.error('To use local you should be in a git repository');
|
|
32
|
+
program.error('');
|
|
27
33
|
}
|
|
28
34
|
writeFileSync('.capgo', `${apikey}\n`);
|
|
29
35
|
appendFileSync('.gitignore', '.capgo\n');
|
|
@@ -42,13 +48,13 @@ export const login = async (apikey: string, options: Options, shouldExit = true)
|
|
|
42
48
|
},
|
|
43
49
|
notify: false,
|
|
44
50
|
}).catch()
|
|
45
|
-
|
|
51
|
+
p.log.success(`login saved into .capgo file in ${local ? 'local' : 'home'} directory`);
|
|
46
52
|
} catch (e) {
|
|
47
|
-
|
|
53
|
+
p.log.error(`Error while saving login`);
|
|
48
54
|
process.exit(1);
|
|
49
55
|
}
|
|
50
56
|
if (shouldExit) {
|
|
51
|
-
|
|
57
|
+
p.outro('Done ✅');
|
|
52
58
|
process.exit()
|
|
53
59
|
}
|
|
54
60
|
return true
|
package/src/utils.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { LogSnag } from 'logsnag';
|
|
|
8
8
|
import { Database } from 'types/supabase.types';
|
|
9
9
|
import { resolve } from 'path';
|
|
10
10
|
import open from 'open';
|
|
11
|
+
import * as p from '@clack/prompts';
|
|
11
12
|
|
|
12
13
|
export const baseKey = '.capgo_key';
|
|
13
14
|
export const baseKeyPub = `${baseKey}.pub`;
|
|
@@ -16,6 +17,15 @@ export const hostWeb = 'https://web.capgo.app';
|
|
|
16
17
|
export const hostSupa = process.env.SUPA_DB === 'production'
|
|
17
18
|
? 'https://xvwzpoazmxkqosrdewyv.supabase.co' : process.env.SUPA_DB || 'https://aucsybvnhavogdmzwtcw.supabase.co';
|
|
18
19
|
|
|
20
|
+
export const defaulPublicKey = `-----BEGIN RSA PUBLIC KEY-----
|
|
21
|
+
MIIBCgKCAQEA4pW9olT0FBXXivRCzd3xcImlWZrqkwcF2xTkX/FwXmj9eh9HkBLr
|
|
22
|
+
sQmfsC+PJisRXIOGq6a0z3bsGq6jBpp3/Jr9jiaW5VuPGaKeMaZZBRvi/N5fIMG3
|
|
23
|
+
hZXSOcy0IYg+E1Q7RkYO1xq5GLHseqG+PXvJsNe4R8R/Bmd/ngq0xh/cvcrHHpXw
|
|
24
|
+
O0Aj9tfprlb+rHaVV79EkVRWYPidOLnK1n0EFHFJ1d/MyDIp10TEGm2xHpf/Brlb
|
|
25
|
+
1an8wXEuzoC0DgYaczgTjovwR+ewSGhSHJliQdM0Qa3o1iN87DldWtydImMsPjJ3
|
|
26
|
+
DUwpsjAMRe5X8Et4+udFW2ciYnQo9H0CkwIDAQAB
|
|
27
|
+
-----END RSA PUBLIC KEY-----`
|
|
28
|
+
|
|
19
29
|
if (process.env.SUPA_DB !== 'production') {
|
|
20
30
|
console.log('hostSupa', hostSupa);
|
|
21
31
|
}
|
|
@@ -89,7 +99,7 @@ export const isAllowedAction = async (supabase: SupabaseClient<Database>, userId
|
|
|
89
99
|
export const checkPlanValid = async (supabase: SupabaseClient<Database>, userId: string, warning = true) => {
|
|
90
100
|
const validPlan = await isAllowedAction(supabase, userId)
|
|
91
101
|
if (!validPlan) {
|
|
92
|
-
|
|
102
|
+
p.log.error(`You need to upgrade your plan to continue to use capgo.\n Upgrade here: ${hostWeb}/dashboard/settings/plans\n`);
|
|
93
103
|
setTimeout(() => {
|
|
94
104
|
open(`${hostWeb}/dashboard/settings/plans`)
|
|
95
105
|
program.error('')
|
|
@@ -97,7 +107,7 @@ export const checkPlanValid = async (supabase: SupabaseClient<Database>, userId:
|
|
|
97
107
|
}
|
|
98
108
|
const trialDays = await isTrial(supabase, userId)
|
|
99
109
|
if (trialDays > 0 && warning) {
|
|
100
|
-
|
|
110
|
+
p.log.warn(`WARNING !!\nTrial expires in ${trialDays} days, upgrade here: ${hostWeb}/dashboard/settings/plans\n`);
|
|
101
111
|
}
|
|
102
112
|
}
|
|
103
113
|
|
|
@@ -107,12 +117,12 @@ export const findSavedKey = () => {
|
|
|
107
117
|
let key
|
|
108
118
|
let keyPath = `${userHomeDir}/.capgo`;
|
|
109
119
|
if (existsSync(keyPath)) {
|
|
110
|
-
|
|
120
|
+
p.log.info(`Use global apy key ${keyPath}`)
|
|
111
121
|
key = readFileSync(keyPath, 'utf8').trim();
|
|
112
122
|
}
|
|
113
123
|
keyPath = `.capgo`;
|
|
114
124
|
if (!key && existsSync(keyPath)) {
|
|
115
|
-
|
|
125
|
+
p.log.info(`Use local apy key ${keyPath}`)
|
|
116
126
|
key = readFileSync(keyPath, 'utf8').trim();
|
|
117
127
|
}
|
|
118
128
|
if (!key)
|
|
@@ -145,7 +155,7 @@ export const findMainFile = async () => {
|
|
|
145
155
|
const folders = f.split('/').length - pwdL
|
|
146
156
|
if (folders <= 2 && mainRegex.test(f)) {
|
|
147
157
|
mainFile = f
|
|
148
|
-
|
|
158
|
+
p.log.info(`Found main file here ${f}`)
|
|
149
159
|
break
|
|
150
160
|
}
|
|
151
161
|
}
|