@capgo/cli 1.1.10 → 1.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.cz.toml CHANGED
@@ -1,7 +1,7 @@
1
1
  [tool.commitizen]
2
2
  name = "cz_conventional_commits"
3
3
  tag_format = "$major.$minor.$patch$prerelease"
4
- version = "1.1.10"
4
+ version = "1.1.13"
5
5
  version_files = [
6
6
  "package.json:version",
7
7
  ".cz.toml"
package/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 1.1.13 (2022-11-16)
2
+
3
+ ### Fix
4
+
5
+ - readme add keep option
6
+
7
+ ## 1.1.12 (2022-11-16)
8
+
9
+ ### Fix
10
+
11
+ - code norm + add keep option
12
+
13
+ ## 1.1.11 (2022-11-16)
14
+
15
+ ### Fix
16
+
17
+ - message order
18
+
1
19
  ## 1.1.10 (2022-11-10)
2
20
 
3
21
  ### Fix
package/README.md CHANGED
@@ -5,23 +5,23 @@
5
5
  <h2><a href="https://capgo.app/">Check out: Capgo — Instant updates for capacitor</a></h2>
6
6
  </div>
7
7
 
8
- A CLI to upload and download files from the Capacitor go Cloud.
8
+ A CLI to upload and download files from the Capgo Cloud.
9
9
 
10
10
  ## Usage
11
11
 
12
12
  Before use the CLI you should register here : https://capgo.app/
13
13
 
14
- Then go in you account in apikey section and click in the `all` key to copy it.
14
+ Then go to your account in `apikey` section and click in the `all` key to copy it.
15
15
 
16
16
  ## Login to Cloud
17
17
  `npx @capgo/cli login API_KEY`
18
- `API_KEY` your apikey copied in the previous step
18
+ `API_KEY` your `apikey` copied in the previous step
19
19
 
20
20
  ## Add new app to Cloud
21
21
  `npx @capgo/cli add [appId]`
22
22
  `[appId]` your app ID the format `com.test.app` is explained [here](https://capacitorjs.com/docs/cli/init)
23
23
 
24
- Optionally you can give:
24
+ Optionally, you can give:
25
25
  - icon with `--icon /path/to/my/icon` to have a custom icon in the list
26
26
  - name with `--name test` to have a custom name in the list
27
27
 
@@ -30,7 +30,7 @@ Optionally you can give:
30
30
  `npx @capgo/cli upload [appId]`
31
31
  `[appId]` is your app ID the format is explained [here](https://capacitorjs.com/docs/cli/init)
32
32
 
33
- Optionally you can give:
33
+ Optionally, you can give:
34
34
  - icon with `--path /path/to/my/dist/folder` to send your code to the cloud
35
35
  - name with `--name test` to have a custom name in the list
36
36
  - channel with `--channel prod` to link this version to channel
@@ -38,13 +38,27 @@ Optionally you can give:
38
38
  ### Send version to Cloud channel
39
39
  `npx @capgo/cli set [appId] [version] [channel]`
40
40
  `[appId]` your app ID the format is explained [here](https://capacitorjs.com/docs/cli/init)
41
- `[version]` your app version already sended to the cloud
41
+ `[version]` your app version already sent to the cloud
42
42
  `[channel]` the channel you want to link the version
43
43
 
44
44
  ### Delete package to Cloud
45
45
  `npx @capgo/cli delete [appId]`
46
46
  `[appId]` your app ID present in the Cloud
47
47
 
48
+ ### Delete older packages in a SemVer range for a major version to Cloud
49
+ `npx @capgo/cli cleanup [appId] --bundle=[majorVersion] --keep=[numberToKeep]`
50
+ `[appId]` your app ID present in the Cloud
51
+ `[majorVersion]` a version you wish to remove previous packages for, it will keep the last one + numberToKeep
52
+ `[numberToKeep]` the number of packages you wish to keep
53
+
54
+ For example:
55
+ If you have 10 versions, from 10.0.1 to 10.0.11, and you use
56
+ `npx @capgo/cli cleanup [appId] --bundle=10.0.0`
57
+ it will remove 10.0.1 to 10.0.6.
58
+ 10.0.7 untill 10.0.11 will be kept
59
+
60
+ This command will show a list of what it will be removing and ask for confirmation
61
+
48
62
  ### Configure channel
49
63
  `npx @capgo/cli set [appId] --channel dev
50
64
  `
@@ -82,7 +96,7 @@ Optionally, you can give:
82
96
 
83
97
  4. Attach debugger to the process started with `npm run dev`
84
98
 
85
- > VScode:
99
+ > VS Code:
86
100
 
87
101
  - Run `Debug on fixtures` launch configuration
88
102
  - Edit configuration to debug on different files
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- (()=>{"use strict";var e={107:function(e,o,r){var n=this&&this.__awaiter||function(e,o,r,n){return new(r||(r=Promise))((function(t,a){function i(e){try{p(n.next(e))}catch(e){a(e)}}function s(e){try{p(n.throw(e))}catch(e){a(e)}}function p(e){var o;e.done?t(e.value):(o=e.value,o instanceof r?o:new r((function(e){e(o)}))).then(i,s)}p((n=n.apply(e,o||[])).next())}))};Object.defineProperty(o,"__esModule",{value:!0}),o.addApp=void 0;const t=r(304),a=r(113),i=r(147),s=r(470),p=r(11),d=r(771),l="assets/icon.png";o.addApp=(e,o)=>n(void 0,void 0,void 0,(function*(){var r,n;let{name:c,icon:u}=o;const g=o.apikey||(0,d.findSavedKey)(),f=yield(0,d.getConfig)(),v=(0,d.useLogSnag)();e=e||(null===(r=null==f?void 0:f.app)||void 0===r?void 0:r.appId),c=c||(null===(n=null==f?void 0:f.app)||void 0===n?void 0:n.appName)||"Unknown",u=u||"resources/icon.png",g||t.program.error("Missing API key, you need to provide a API key to add your app"),e&&c||t.program.error("Missing argument, you need to provide a appid and a name, or be in a capacitor project"),console.log(`Add ${e} to Capgo`);const y=(0,d.createSupabaseClient)(g),m=yield(0,d.verifyUser)(y,g,["write","all"]);let h,b;yield(0,d.checkPlan)(y,m),console.log("Adding..."),u&&(0,s.existsSync)(u)?(h=(0,i.readFileSync)(u),b=(0,p.getType)(u)||"image/png",console.warn(`Found app icon ${u}`)):(0,s.existsSync)(l)?(h=(0,i.readFileSync)(l),b=(0,p.getType)(l)||"image/png",console.warn(`Found app icon ${l}`)):console.warn(`Cannot find app icon in any of the following locations: ${u}, ${l}`);const{data:_,error:$}=yield y.rpc("exist_app",{appid:e,apikey:g});(_||$)&&t.program.error(`App already exists ${(0,d.formatError)($)}`);const w=`icon_${(0,a.randomUUID)()}`;let k="https://xvwzpoazmxkqosrdewyv.supabase.co/storage/v1/object/public/images/capgo.png";if(h&&b){const{error:o}=yield y.storage.from(`images/${m}/${e}`).upload(w,h,{contentType:b});o&&t.program.error(`Could not add app ${(0,d.formatError)(o)}`);const{data:r}=yield y.storage.from(`images/${m}/${e}`).getPublicUrl(w);k=(null==r?void 0:r.publicURL)||k}const{error:x}=yield y.from("apps").insert({icon_url:k,user_id:m,name:c,app_id:e},{returning:"minimal"});x&&t.program.error(`Could not add app ${(0,d.formatError)(x)}`);const{error:C}=yield y.from("app_versions").insert([{user_id:m,deleted:!0,name:"unknown",app_id:e},{user_id:m,deleted:!0,name:"builtin",app_id:e}],{returning:"minimal"});C&&t.program.error(`Could not add app ${(0,d.formatError)(C)}`),v.publish({channel:"app",event:"App Added",icon:"🎉",tags:{"user-id":m,"app-id":e},notify:!1}).catch(),console.log("App added to server, you can upload a bundle now")}))},605:function(e,o,r){var n=this&&this.__awaiter||function(e,o,r,n){return new(r||(r=Promise))((function(t,a){function i(e){try{p(n.next(e))}catch(e){a(e)}}function s(e){try{p(n.throw(e))}catch(e){a(e)}}function p(e){var o;e.done?t(e.value):(o=e.value,o instanceof r?o:new r((function(e){e(o)}))).then(i,s)}p((n=n.apply(e,o||[])).next())}))};Object.defineProperty(o,"__esModule",{value:!0}),o.deleteApp=void 0;const t=r(304),a=r(771);o.deleteApp=(e,o)=>n(void 0,void 0,void 0,(function*(){var r;const{bundle:n}=o,i=o.apikey||(0,a.findSavedKey)(),s=yield(0,a.getConfig)(),p=(0,a.useLogSnag)();e=e||(null===(r=null==s?void 0:s.app)||void 0===r?void 0:r.appId),i||t.program.error("Missing API key, you need to provide an API key to delete your app"),e||t.program.error("Missing argument, you need to provide a appid, or be in a capacitor project");const d=(0,a.createSupabaseClient)(i),l=yield(0,a.verifyUser)(d,i),{data:c,error:u}=yield d.rpc("exist_app",{appid:e,apikey:i});if(c&&!u||t.program.error("No permission to delete"),n){console.log(`Delete ${e}@${n} from Capgo`);const{data:o,error:r}=yield d.from("app_versions").select().eq("app_id",e).eq("user_id",l).eq("name",n).eq("deleted",!1).single();o&&!r||t.program.error(`Version ${e}@${n} doesn't exist ${(0,a.formatError)(r)}`);const{data:i,error:s}=yield d.from("channels").select().eq("app_id",e).eq("created_by",l).eq("version",o.id);(i&&i.length||s)&&t.program.error(`Version ${e}@${n} is used in a channel, unlink it first ${(0,a.formatError)(s)}`);const{data:p,error:c}=yield d.from("devices_override").select().eq("app_id",e).eq("version",o.id);(p&&p.length||c)&&t.program.error(`Version ${e} @${n} is used in a device override, unlink it first ${(0,a.formatError)(c)}`);const{error:u}=yield d.storage.from("apps").remove([`${l}/${e}/versions/${o.bucket_id} `]);u&&t.program.error(`Something went wrong when trying to delete ${e} @${n} ${u} `);const{error:g}=yield d.from("app_versions").update({deleted:!0}).eq("app_id",e).eq("user_id",l).eq("name",n);return g&&t.program.error(`App ${e}@${n} not found in database '${g}'`),void console.log(`${e}@${n} deleted from server`)}console.log(`Delete ${e} from Capgo`);const{data:g,error:f}=yield d.from("app_versions").select().eq("app_id",e).eq("user_id",l);if(f&&t.program.error(`App ${e} not found in database ${f} `),g&&g.length){const o=g.map((o=>`${l}/${e}/versions/${o.bucket_id} `)),{error:r}=yield d.storage.from("apps").remove(o);r&&t.program.error(`Cannot delete stored version for app ${e} from storage ${r} `)}const{error:v}=yield d.from("app_versions").delete().eq("app_id",e).eq("user_id",l);v&&t.program.error(`Cannot delete version for app ${e} from database ${v} `);const{error:y}=yield d.from("apps").delete().eq("app_id",e).eq("user_id",l);y&&t.program.error(`Cannot delete from database ${y} `),p.publish({channel:"app",event:"App Deleted",icon:"😱",tags:{"user-id":l,"app-id":e},notify:!1}).catch(),console.log(`${e} deleted from server`)}))},250:function(e,o,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(o,"__esModule",{value:!0});const t=r(304),a=r(107),i=r(605),s=r(327),p=r(823),d=n(r(598)),l=r(829),c=r(501);t.program.version(d.default.version).command("add [appid]").alias("a").action(a.addApp).option("-n, --name <name>","app name").option("-i, --icon <icon>","app icon path").option("-a, --apikey <apikey>","apikey to link to your account"),t.program.version(d.default.version).command("login [apikey]").alias("l").action(l.login).option("--local","Only save in local folder"),t.program.command("upload [appid]").alias("u").action(p.uploadVersion).option("-a, --apikey <apikey>","apikey to link to your account").option("-p, --path <path>","path of the file 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("-f, --format <base64|hex|binary|utf8>","choose the upload format default base64").option("-b, --bundle <bundle>","bundle version number of the file to upload"),t.program.command("set [appid]").alias("s").action(s.setChannel).requiredOption("-c, --channel <channel>","channel to link to").option("-b, --bundle <bundle>","bundle version number of the file to set").option("-s, --state <state>","set the state of the channel, default or normal").option("--downgrade","Allow to downgrade to version under native one").option("--no-downgrade","Disable downgrade to version under native one").option("--upgrade","Allow to upgrade to version above native one").option("--no-upgrade","Disable upgrade to version above native one").option("--ios","Allow sending update to ios devices").option("--no-ios","Disable sending update to ios devices").option("--android","Allow sending update to android devices").option("--no-android","Disable sending update to android devices").option("--self-assign","Allow to device to self assign to this channel").option("--no-self-assign","Disable devices to self assign to this channel").option("-a, --apikey <apikey>","apikey to link to your account"),t.program.description("Manage package and version in capgo Cloud").command("delete [appid]").alias("d").action(i.deleteApp).option("-a, --apikey <apikey>","apikey to link to your account").option("-b, --bundle <bundle>","bundle version number of the app to delete"),t.program.description("List versions in capgo Cloud").command("list [appid]").alias("ls").action(c.listApp).option("-a, --apikey <apikey>","apikey to link to your account"),t.program.parse(process.argv)},501:function(e,o,r){var n=this&&this.__awaiter||function(e,o,r,n){return new(r||(r=Promise))((function(t,a){function i(e){try{p(n.next(e))}catch(e){a(e)}}function s(e){try{p(n.throw(e))}catch(e){a(e)}}function p(e){var o;e.done?t(e.value):(o=e.value,o instanceof r?o:new r((function(e){e(o)}))).then(i,s)}p((n=n.apply(e,o||[])).next())}))};Object.defineProperty(o,"__esModule",{value:!0}),o.listApp=void 0;const t=r(304),a=r(771);o.listApp=(e,o)=>n(void 0,void 0,void 0,(function*(){var r;const n=o.apikey||(0,a.findSavedKey)(),i=yield(0,a.getConfig)();e=e||(null===(r=null==i?void 0:i.app)||void 0===r?void 0:r.appId),n||t.program.error("Missing API key, you need to provide an API key to delete your app"),e||t.program.error("Missing argument, you need to provide a appid, or be in a capacitor project"),console.log("Querying available versions in Capgo");const s=(0,a.createSupabaseClient)(n),p=yield(0,a.verifyUser)(s,n),{data:d,error:l}=yield s.rpc("exist_app",{appid:e,apikey:n});d&&!l||t.program.error("No permission for this app");const{data:c,error:u}=yield s.from("app_versions").select().eq("app_id",e).eq("user_id",p).eq("deleted",!1);console.log(`Active versions in Capgo: ${null==c?void 0:c.length}`),null==c||c.forEach((e=>{const o=new Date(e.created_at||"").toLocaleString();console.log(`${e.name} created on ${o}`)})),u&&t.program.error(`App ${e} not found in database ${u} `)}))},829:function(e,o,r){var n=this&&this.__awaiter||function(e,o,r,n){return new(r||(r=Promise))((function(t,a){function i(e){try{p(n.next(e))}catch(e){a(e)}}function s(e){try{p(n.throw(e))}catch(e){a(e)}}function p(e){var o;e.done?t(e.value):(o=e.value,o instanceof r?o:new r((function(e){e(o)}))).then(i,s)}p((n=n.apply(e,o||[])).next())}))},t=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(o,"__esModule",{value:!0}),o.login=void 0;const a=r(304),i=t(r(147)),s=t(r(37)),p=r(771);o.login=(e,o)=>n(void 0,void 0,void 0,(function*(){const{local:r}=o,n=(0,p.useLogSnag)();if(r)i.default.existsSync(".git")||a.program.error("To use local you should be in a git repository"),i.default.writeFileSync(".capgo",`${e}\n`),i.default.appendFileSync(".gitignore",".capgo\n");else{const o=s.default.homedir();i.default.writeFileSync(`${o}/.capgo`,`${e}\n`)}const t=(0,p.createSupabaseClient)(e),d=yield(0,p.verifyUser)(t,e,["write","all","upload"]);n.publish({channel:"user-login",event:"User CLI login",icon:"✅",tags:{"user-id":d},notify:!1}).catch(),console.log(`login saved into .capgo file in ${r?"local":"home"} directory`)}))},327:function(e,o,r){var n=this&&this.__awaiter||function(e,o,r,n){return new(r||(r=Promise))((function(t,a){function i(e){try{p(n.next(e))}catch(e){a(e)}}function s(e){try{p(n.throw(e))}catch(e){a(e)}}function p(e){var o;e.done?t(e.value):(o=e.value,o instanceof r?o:new r((function(e){e(o)}))).then(i,s)}p((n=n.apply(e,o||[])).next())}))};Object.defineProperty(o,"__esModule",{value:!0}),o.setChannel=void 0;const t=r(304),a=r(771);o.setChannel=(e,o)=>n(void 0,void 0,void 0,(function*(){var r;const{bundle:n,downgrade:i,upgrade:s,ios:p,android:d,selfAssign:l,channel:c,state:u}=o,g=o.apikey||(0,a.findSavedKey)(),f=yield(0,a.getConfig)(),v=(0,a.useLogSnag)();e=e||(null===(r=null==f?void 0:f.app)||void 0===r?void 0:r.appId),g||t.program.error("Missing API key, you need to provide a API key to add your app"),e||t.program.error("Missing argument, you need to provide a appid, or be in a capacitor project"),void 0===n&&void 0===u&&void 0===i&&void 0===s&&void 0===p&&void 0===d&&void 0===l&&t.program.error("Missing argument, you need to provide a option to set");try{const o=(0,a.createSupabaseClient)(g),r=yield(0,a.verifyUser)(o,g,["write","all"]);yield(0,a.checkPlan)(o,r);const f={created_by:r,app_id:e,name:c};if(n){console.log(`Set ${e} channel: ${c} to @${n}`);const{data:a,error:i}=yield o.from("app_versions").select().eq("app_id",e).eq("name",n).eq("user_id",r).eq("deleted",!1);!i&&a&&a.length||t.program.error(`Cannot find version ${n}`),f.version=a[0].id}void 0!==u&&("public"!==u&&"private"!==u||console.log(`Set ${e} channel: ${c} to public or private is deprecated, use default or normal instead`),console.log(`Set ${e} channel: ${c} to ${"public"===u||"default"===u?"default":"normal"}`),f.public="public"===u||"default"===u),void 0!==i&&(console.log(`Set ${e} channel: ${c} to ${i?"allow":"disallow"} downgrade`),f.disableAutoUpdateUnderNative=!i),void 0!==s&&(console.log(`Set ${e} channel: ${c} to ${s?"allow":"disallow"} upgrade`),f.disableAutoUpdateToMajor=!s),void 0!==p&&(console.log(`Set ${e} channel: ${c} to ${p?"allow":"disallow"} ios update`),f.ios=!!p),void 0!==d&&(console.log(`Set ${e} channel: ${c} to ${d?"allow":"disallow"} android update`),f.android=!!d),void 0!==l&&(console.log(`Set ${e} channel: ${c} to ${l?"allow":"disallow"} self assign to this channel`),f.allow_device_self_set=!!l);try{const{error:e}=yield(0,a.updateOrCreateChannel)(o,f,g);e&&t.program.error(`Cannot set channel ${(0,a.formatError)(e)}`)}catch(e){t.program.error(`Cannot set channel ${(0,a.formatError)(e)}`)}v.publish({channel:"app",event:"Set app",icon:"✅",tags:{"user-id":r,"app-id":e},notify:!1}).catch()}catch(e){t.program.error(`Unknow error ${(0,a.formatError)(e)}`)}console.log("Done ✅")}))},823:function(e,o,r){var n=this&&this.__awaiter||function(e,o,r,n){return new(r||(r=Promise))((function(t,a){function i(e){try{p(n.next(e))}catch(e){a(e)}}function s(e){try{p(n.throw(e))}catch(e){a(e)}}function p(e){var o;e.done?t(e.value):(o=e.value,o instanceof r?o:new r((function(e){e(o)}))).then(i,s)}p((n=n.apply(e,o||[])).next())}))},t=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(o,"__esModule",{value:!0}),o.uploadVersion=void 0;const a=t(r(844)),i=r(304),s=r(113),p=t(r(295)),d=r(906),l=r(771);o.uploadVersion=(e,o)=>n(void 0,void 0,void 0,(function*(){var r,n,t,c;let{bundle:u,path:g,channel:f}=o;const{external:v}=o,y=o.apikey||(0,l.findSavedKey)(),m=(0,l.useLogSnag)();f=f||"dev";const h=yield(0,l.getConfig)();e=e||(null===(r=null==h?void 0:h.app)||void 0===r?void 0:r.appId),u=u||(null===(t=null===(n=null==h?void 0:h.app)||void 0===n?void 0:n.package)||void 0===t?void 0:t.version),l.regexSemver.test(u)||i.program.error(`Your bundle name ${u}, is not valid it should follow semver convention : https://semver.org/`),g=g||(null===(c=null==h?void 0:h.app)||void 0===c?void 0:c.webDir),y||i.program.error("Missing API key, you need to provide a API key to add your app"),e&&u&&g||i.program.error("Missing argument, you need to provide a appid and a bundle and a path, or be in a capacitor project"),console.log(`Upload ${e}@${u} started from path "${g}" to Capgo cloud`);const b=(0,l.createSupabaseClient)(y),_=yield(0,l.verifyUser)(b,y,["write","all","upload"]);yield(0,l.checkPlan)(b,_,!1);const $=new p.default.MultiBar({clearOnComplete:!1,hideCursor:!0},p.default.Presets.shades_grey),w=$.create(7,0,{format:"Uploading: [{bar}] {percentage}% | ETA: {eta}s | {value}/{total} Part"},p.default.Presets.shades_grey);w.start(7,0,{speed:"N/A"});const{data:k,error:x}=yield b.rpc("exist_app_versions",{apikey:y,name_version:u,appid:e});(k||x)&&($.stop(),i.program.error(`This app bundle already exist or was deleted, you cannot re-upload it ${(0,l.formatError)(x)}`)),w.increment();const{data:C,error:A}=yield b.rpc("is_trial",{userid:_}).single();(C&&C>0||A)&&$.log(`WARNING !!\nTrial expires in ${C} days, upgrade here: ${l.hostWeb}/dashboard/settings/plans\n`),w.increment();const{data:S,error:q}=yield b.rpc("exist_app",{appid:e,apikey:y});S&&!q||($.stop(),i.program.error(`Cannot find app ${e} in your account \n${(0,l.formatError)(q)}`)),w.increment();const{data:P,error:I}=yield b.rpc("exist_app_versions",{appid:e,apikey:y,name_version:u});(P||I)&&i.program.error(`Version already exists ${(0,l.formatError)(I)}`),w.increment();const j=(0,s.randomUUID)();let M="";if(v)v&&!v.startsWith("https://")&&($.stop(),i.program.error(`External link should should start with "https://" current is "${v}"`));else{const o=new a.default;o.addLocalFolder(g);const r=o.toBuffer();M=yield(0,d.checksum)(r,"crc32");const n=Math.floor(r.byteLength/1024/1024),t=`apps/${_}/${e}/versions`;w.increment(),n>20&&($.log(`WARNING !!\nThe app size is ${n} Mb, this may take a while to download for users\n`),m.publish({channel:"app-error",event:"App Too Large",icon:"🚛",tags:{"user-id":_,"app-id":e},notify:!1}).catch());const{error:s}=yield b.storage.from(t).upload(j,r,{contentType:"application/zip"});s&&($.stop(),i.program.error(`Cannot upload ${(0,l.formatError)(s)}`))}w.increment();const{data:U,error:E}=yield(0,l.updateOrCreateVersion)(b,{bucket_id:v?void 0:j,user_id:_,name:u,app_id:e,external_url:v,checksum:M},y);if(E&&($.stop(),i.program.error(`Cannot add bundle ${(0,l.formatError)(E)}`)),w.increment(),U&&U.length){const{error:o}=yield(0,l.updateOrCreateChannel)(b,{name:f,app_id:e,created_by:_,version:U[0].id},y);o&&$.log("Cannot set bundle with upload key, use key with more rights for that\n")}else $.log("Cannot set bundle with upload key, use key with more rights for that\n");$.stop();const O=e.replace(/\./g,"--");console.log("App uploaded to server"),console.log(`Try it in mobile app: ${l.host}/app_mobile`),console.log(`Or set the channel ${f} as public here: ${l.hostWeb}/app/package/${O}`),console.log("To use with live update in your own app"),console.log(`You can link specific device to this bundle to make user try it first, here: ${l.hostWeb}/app/p/${O}/devices`),m.publish({channel:"app",event:"App Uploaded",icon:"⏫",tags:{"user-id":_,"app-id":e},notify:!1}).catch()}))},771:function(e,o,r){var n=this&&this.__awaiter||function(e,o,r,n){return new(r||(r=Promise))((function(t,a){function i(e){try{p(n.next(e))}catch(e){a(e)}}function s(e){try{p(n.throw(e))}catch(e){a(e)}}function p(e){var o;e.done?t(e.value):(o=e.value,o instanceof r?o:new r((function(e){e(o)}))).then(i,s)}p((n=n.apply(e,o||[])).next())}))},t=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(o,"__esModule",{value:!0}),o.verifyUser=o.useLogSnag=o.updateOrCreateChannel=o.updateOrCreateVersion=o.getConfig=o.formatError=o.findSavedKey=o.checkPlan=o.isTrial=o.isPaying=o.isGoodPlan=o.checkKey=o.regexSemver=o.createSupabaseClient=o.supaAnon=o.hostSupa=o.hostWeb=o.host=void 0;const a=r(778),i=r(304),s=r(885),p=t(r(867)),d=t(r(147)),l=t(r(37)),c=r(133);o.host="https://capgo.app",o.hostWeb="https://web.capgo.app",o.hostSupa="https://xvwzpoazmxkqosrdewyv.supabase.co",o.supaAnon="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYzNzgwNTAwOSwiZXhwIjoxOTUzMzgxMDA5fQ.8tgID1d4jodPwuo_fz4KHN4o1XKB9fnqyt0_GaJSj-w",o.createSupabaseClient=e=>(0,s.createClient)(o.hostSupa,o.supaAnon,{headers:{capgkey:e}}),o.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-]+)*))?$/,o.checkKey=(e,r,t)=>n(void 0,void 0,void 0,(function*(){const{data:n,error:a}=yield e.rpc("is_allowed_capgkey",{apikey:r,keymode:t});n&&!a||i.program.error(`Invalid API key or insufficient permissions ${(0,o.formatError)(a)}`)})),o.isGoodPlan=(e,o)=>n(void 0,void 0,void 0,(function*(){const{data:r,error:n}=yield e.rpc("is_good_plan_v2",{userid:o}).single();if(n)throw n;return r||!1})),o.isPaying=(e,o)=>n(void 0,void 0,void 0,(function*(){const{data:r,error:n}=yield e.rpc("is_paying",{userid:o}).single();if(n)throw n;return r||!1})),o.isTrial=(e,o)=>n(void 0,void 0,void 0,(function*(){const{data:r,error:n}=yield e.rpc("is_trial",{userid:o}).single();if(n)throw n;return r||0})),o.checkPlan=(e,r,t=!0)=>n(void 0,void 0,void 0,(function*(){const n=yield(0,o.isGoodPlan)(e,r),a=yield(0,o.isPaying)(e,r),s=yield(0,o.isTrial)(e,r);a&&n||!(s<0)||i.program.error(`You need to upgrade your plan to continue to use capgo.\n Upgrade here: ${o.hostWeb}/dashboard/settings/plans\n`),s>0&&t&&!a&&console.log(`WARNING !!\nTrial expires in ${s} days, upgrade here: ${o.hostWeb}/dashboard/settings/plans\n`)})),o.findSavedKey=()=>{let e=`${l.default.homedir()}/.capgo`;return d.default.existsSync(e)?d.default.readFileSync(e,"utf8").trim():(e=".capgo",d.default.existsSync(e)?d.default.readFileSync(e,"utf8").trim():null)},o.formatError=e=>e?`\n${p.default.render(e)}`:"",o.getConfig=()=>n(void 0,void 0,void 0,(function*(){let e;try{e=yield(0,a.loadConfig)()}catch(e){i.program.error("No capacitor config file found, run `cap init` first")}return e})),o.updateOrCreateVersion=(e,o,r)=>n(void 0,void 0,void 0,(function*(){const{data:n,error:t}=yield e.rpc("exist_app_versions",{appid:o.app_id,name_version:o.name,apikey:r});return n&&!t?(o.deleted=!1,e.from("app_versions").update(o).eq("app_id",o.app_id).eq("name",o.name)):e.from("app_versions").insert(o)})),o.updateOrCreateChannel=(e,o,r)=>n(void 0,void 0,void 0,(function*(){if(!o.app_id||!o.name||!o.created_by)return console.error("missing app_id, name, or created_by"),Promise.reject(new Error("missing app_id, name, or created_by"));const{data:n,error:t}=yield e.rpc("exist_channel",{appid:o.app_id,name_channel:o.name,apikey:r});return n&&!t?e.from("channels").update(o,{returning:"minimal"}).eq("app_id",o.app_id).eq("name",o.name).eq("created_by",o.created_by):e.from("channels").insert(o,{returning:"minimal"})})),o.useLogSnag=()=>new c.LogSnag({token:"c124f5e9d0ce5bdd14bbb48f815d5583",project:"capgo"}),o.verifyUser=(e,r,t=["all"])=>n(void 0,void 0,void 0,(function*(){yield(0,o.checkKey)(e,r,t);const{data:n,error:a}=yield e.rpc("get_user_id",{apikey:r}),s=n?n.toString():"";return s&&!a||i.program.error(`Cannot verify user ${(0,o.formatError)(a)}`),s}))},778:e=>{e.exports=require("@capacitor/cli/dist/config")},885:e=>{e.exports=require("@supabase/supabase-js")},906:e=>{e.exports=require("@tomasklaen/checksum")},844:e=>{e.exports=require("adm-zip")},295:e=>{e.exports=require("cli-progress")},304:e=>{e.exports=require("commander")},470:e=>{e.exports=require("fs-extra")},133:e=>{e.exports=require("logsnag")},11:e=>{e.exports=require("mime")},867:e=>{e.exports=require("prettyjson")},113:e=>{e.exports=require("crypto")},147:e=>{e.exports=require("fs")},37:e=>{e.exports=require("os")},598:e=>{e.exports=JSON.parse('{"name":"@capgo/cli","version":"1.1.10","description":"A CLI to upload to capgo servers","main":"dist/index.js","bin":{"capgo":"dist/index.js"},"repository":{"type":"git","url":"git+https://github.com/Cap-go/capgo-cli.git"},"bugs":{"url":"https://github.com/Cap-go/capgo-cli/issues"},"keywords":["appflow alternative","ionic","capacitor","auto update","live update","capgo","cli","upload","capgo-cli"],"scripts":{"dev":"set NODE_ENV=development && npx webpack --config webpack.config.js","no-debug":"node dist/index.js","build":"npx webpack --config webpack.config.js","dev-build":"SUPA_DB=development npx webpack --config webpack.config.js","pack":"pkg","test_rls":"ts-node ./test/test_headers_rls.ts","lint":"eslint . --ext .ts --fix"},"author":"github.com/riderx","license":"Apache 2.0","dependencies":{"@capacitor/cli":"4.4.0","@supabase/supabase-js":"^1.35.7","@tomasklaen/checksum":"^1.1.0","adm-zip":"^0.5.9","cli-progress":"3.11.2","commander":"9.4.1","fs-extra":"10.1.0","logsnag":"^0.1.5","mime":"^3.0.0","prettyjson":"^1.2.5"},"devDependencies":{"@types/adm-zip":"0.5.0","@types/cli-progress":"^3.11.0","@types/fs-extra":"^9.0.13","@types/mime":"^3.0.1","@types/node":"^18.11.9","@types/prettyjson":"^0.0.30","@types/semver":"^7.3.13","@typescript-eslint/eslint-plugin":"5.42.1","@typescript-eslint/parser":"5.42.1","eslint":"8.27.0","eslint-config-airbnb-base":"^15.0.0","eslint-config-prettier":"^8.5.0","eslint-import-resolver-typescript":"3.5.2","eslint-plugin-import":"2.26.0","eslint-plugin-prettier":"^4.2.1","git-format-staged":"3.0.0","husky":"^8.0.2","nodemon":"2.0.20","pkg":"5.8.0","prettier":"2.7.1","ts-loader":"^9.4.1","ts-node":"^10.9.1","tsconfig-paths":"4.1.0","typescript":"4.8.4","webpack":"5.74.0","webpack-cli":"^4.10.0","webpack-node-externals":"^3.0.0"}}')}},o={};!function r(n){var t=o[n];if(void 0!==t)return t.exports;var a=o[n]={exports:{}};return e[n].call(a.exports,a,a.exports,r),a.exports}(250)})();
2
+ (()=>{"use strict";var e={111:function(e,o,n){var t=this&&this.__awaiter||function(e,o,n,t){return new(n||(n=Promise))((function(r,i){function a(e){try{d(t.next(e))}catch(e){i(e)}}function s(e){try{d(t.throw(e))}catch(e){i(e)}}function d(e){var o;e.done?r(e.value):(o=e.value,o instanceof n?o:new n((function(e){e(o)}))).then(a,s)}d((t=t.apply(e,o||[])).next())}))};Object.defineProperty(o,"__esModule",{value:!0}),o.checkAppExistsAndHasPermission=void 0;const r=n(304);o.checkAppExistsAndHasPermission=(e,o,n)=>t(void 0,void 0,void 0,(function*(){const{data:t,error:i}=yield e.rpc("exist_app",{appid:o,apikey:n});t&&!i||r.program.error("No permission for this app")}))},708:function(e,o,n){var t=this&&this.__awaiter||function(e,o,n,t){return new(n||(n=Promise))((function(r,i){function a(e){try{d(t.next(e))}catch(e){i(e)}}function s(e){try{d(t.throw(e))}catch(e){i(e)}}function d(e){var o;e.done?r(e.value):(o=e.value,o instanceof n?o:new n((function(e){e(o)}))).then(a,s)}d((t=t.apply(e,o||[])).next())}))};Object.defineProperty(o,"__esModule",{value:!0}),o.checkVersionNotUsedInChannel=void 0;const r=n(304),i=n(771);o.checkVersionNotUsedInChannel=(e,o,n,a,s)=>t(void 0,void 0,void 0,(function*(){const{data:t,error:d}=yield e.from("channels").select().eq("app_id",o).eq("created_by",n).eq("version",a.id);(t&&t.length||d)&&r.program.error(`Version ${o}@${s} is used in a channel, unlink it first ${(0,i.formatError)(d)}`)}))},314:function(e,o,n){var t=this&&this.__awaiter||function(e,o,n,t){return new(n||(n=Promise))((function(r,i){function a(e){try{d(t.next(e))}catch(e){i(e)}}function s(e){try{d(t.throw(e))}catch(e){i(e)}}function d(e){var o;e.done?r(e.value):(o=e.value,o instanceof n?o:new n((function(e){e(o)}))).then(a,s)}d((t=t.apply(e,o||[])).next())}))};Object.defineProperty(o,"__esModule",{value:!0}),o.checkVersionNotUsedInDeviceOverride=void 0;const r=n(304),i=n(771);o.checkVersionNotUsedInDeviceOverride=(e,o,n,a)=>t(void 0,void 0,void 0,(function*(){const{data:t,error:s}=yield e.from("devices_override").select().eq("app_id",o).eq("version",n.id);(t&&t.length||s)&&r.program.error(`Version ${o} @${a} is used in a device override, unlink it first ${(0,i.formatError)(s)}`)}))},109:function(e,o,n){var t=this&&this.__awaiter||function(e,o,n,t){return new(n||(n=Promise))((function(r,i){function a(e){try{d(t.next(e))}catch(e){i(e)}}function s(e){try{d(t.throw(e))}catch(e){i(e)}}function d(e){var o;e.done?r(e.value):(o=e.value,o instanceof n?o:new n((function(e){e(o)}))).then(a,s)}d((t=t.apply(e,o||[])).next())}))};Object.defineProperty(o,"__esModule",{value:!0}),o.deleteFromStorage=void 0;const r=n(304);o.deleteFromStorage=(e,o,n,i,a)=>t(void 0,void 0,void 0,(function*(){const{error:t}=yield e.storage.from("apps").remove([`${o}/${n}/versions/${i.bucket_id} `]);t&&r.program.error(`Something went wrong when trying to delete ${n} @${a} ${t} `)}))},87:function(e,o,n){var t=this&&this.__awaiter||function(e,o,n,t){return new(n||(n=Promise))((function(r,i){function a(e){try{d(t.next(e))}catch(e){i(e)}}function s(e){try{d(t.throw(e))}catch(e){i(e)}}function d(e){var o;e.done?r(e.value):(o=e.value,o instanceof n?o:new n((function(e){e(o)}))).then(a,s)}d((t=t.apply(e,o||[])).next())}))};Object.defineProperty(o,"__esModule",{value:!0}),o.getVersionData=o.getActiveAppVersions=o.deleteSpecificVersion=o.deleteAppVersion=void 0;const r=n(304),i=n(771),a=n(708),s=n(314),d=n(109);o.deleteAppVersion=(e,o,n,i)=>t(void 0,void 0,void 0,(function*(){const{error:t}=yield e.from("app_versions").update({deleted:!0}).eq("app_id",o).eq("user_id",n).eq("name",i);t&&r.program.error(`App ${o}@${i} not found in database '${t}'`)})),o.deleteSpecificVersion=(e,n,r,i)=>t(void 0,void 0,void 0,(function*(){const t=yield(0,o.getVersionData)(e,n,r,i);yield(0,a.checkVersionNotUsedInChannel)(e,n,r,t,i),yield(0,s.checkVersionNotUsedInDeviceOverride)(e,n,t,i),yield(0,d.deleteFromStorage)(e,r,n,t,i),yield(0,o.deleteAppVersion)(e,n,r,i)})),o.getActiveAppVersions=(e,o,n)=>t(void 0,void 0,void 0,(function*(){const{data:t,error:i}=yield e.from("app_versions").select().eq("app_id",o).eq("user_id",n).eq("deleted",!1);return i&&r.program.error(`App ${o} not found in database ${i} `),t})),o.getVersionData=(e,o,n,a)=>t(void 0,void 0,void 0,(function*(){const{data:t,error:s}=yield e.from("app_versions").select().eq("app_id",o).eq("user_id",n).eq("name",a).eq("deleted",!1).single();return t&&!s||r.program.error(`Version ${o}@${a} doesn't exist ${(0,i.formatError)(s)}`),t}))},107:function(e,o,n){var t=this&&this.__awaiter||function(e,o,n,t){return new(n||(n=Promise))((function(r,i){function a(e){try{d(t.next(e))}catch(e){i(e)}}function s(e){try{d(t.throw(e))}catch(e){i(e)}}function d(e){var o;e.done?r(e.value):(o=e.value,o instanceof n?o:new n((function(e){e(o)}))).then(a,s)}d((t=t.apply(e,o||[])).next())}))};Object.defineProperty(o,"__esModule",{value:!0}),o.addApp=void 0;const r=n(304),i=n(113),a=n(147),s=n(470),d=n(11),p=n(771),l="assets/icon.png";o.addApp=(e,o)=>t(void 0,void 0,void 0,(function*(){var n,t;let{name:c,icon:u}=o;const v=o.apikey||(0,p.findSavedKey)(),f=yield(0,p.getConfig)(),g=(0,p.useLogSnag)();e=e||(null===(n=null==f?void 0:f.app)||void 0===n?void 0:n.appId),c=c||(null===(t=null==f?void 0:f.app)||void 0===t?void 0:t.appName)||"Unknown",u=u||"resources/icon.png",v||r.program.error("Missing API key, you need to provide a API key to add your app"),e&&c||r.program.error("Missing argument, you need to provide a appid and a name, or be in a capacitor project"),console.log(`Add ${e} to Capgo`);const y=(0,p.createSupabaseClient)(v),m=yield(0,p.verifyUser)(y,v,["write","all"]);let h,_;yield(0,p.checkPlan)(y,m),console.log("Adding..."),u&&(0,s.existsSync)(u)?(h=(0,a.readFileSync)(u),_=(0,d.getType)(u)||"image/png",console.warn(`Found app icon ${u}`)):(0,s.existsSync)(l)?(h=(0,a.readFileSync)(l),_=(0,d.getType)(l)||"image/png",console.warn(`Found app icon ${l}`)):console.warn(`Cannot find app icon in any of the following locations: ${u}, ${l}`);const{data:b,error:w}=yield y.rpc("exist_app",{appid:e,apikey:v});(b||w)&&r.program.error(`App already exists ${(0,p.formatError)(w)}`);const $=`icon_${(0,i.randomUUID)()}`;let k="https://xvwzpoazmxkqosrdewyv.supabase.co/storage/v1/object/public/images/capgo.png";if(h&&_){const{error:o}=yield y.storage.from(`images/${m}/${e}`).upload($,h,{contentType:_});o&&r.program.error(`Could not add app ${(0,p.formatError)(o)}`);const{data:n}=yield y.storage.from(`images/${m}/${e}`).getPublicUrl($);k=(null==n?void 0:n.publicURL)||k}const{error:x}=yield y.from("apps").insert({icon_url:k,user_id:m,name:c,app_id:e},{returning:"minimal"});x&&r.program.error(`Could not add app ${(0,p.formatError)(x)}`);const{error:A}=yield y.from("app_versions").insert([{user_id:m,deleted:!0,name:"unknown",app_id:e},{user_id:m,deleted:!0,name:"builtin",app_id:e}],{returning:"minimal"});A&&r.program.error(`Could not add app ${(0,p.formatError)(A)}`),g.publish({channel:"app",event:"App Added",icon:"🎉",tags:{"user-id":m,"app-id":e},notify:!1}).catch(),console.log("App added to server, you can upload a bundle now")}))},176:function(e,o,n){var t=this&&this.__awaiter||function(e,o,n,t){return new(n||(n=Promise))((function(r,i){function a(e){try{d(t.next(e))}catch(e){i(e)}}function s(e){try{d(t.throw(e))}catch(e){i(e)}}function d(e){var o;e.done?r(e.value):(o=e.value,o instanceof n?o:new n((function(e){e(o)}))).then(a,s)}d((t=t.apply(e,o||[])).next())}))},r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(o,"__esModule",{value:!0}),o.cleanupApp=void 0;const i=n(304),a=r(n(505)),s=r(n(616)),d=n(771),p=n(87),l=n(111),c=(0,s.default)();o.cleanupApp=(e,o)=>t(void 0,void 0,void 0,(function*(){var n;const t=o.apikey||(0,d.findSavedKey)(),{bundle:r,keep:s=4}=o,u=yield(0,d.getConfig)();e=e||(null===(n=null==u?void 0:u.app)||void 0===n?void 0:n.appId),t||i.program.error("Missing API key, you need to provide an API key to delete your app"),e||i.program.error("Missing argument, you need to provide a appid, or be in a capacitor project");const v=`${a.default.inc(r,"major")}`;console.log(`Querying available versions in Capgo between ${r} and ${v}`);const f=(0,d.createSupabaseClient)(t),g=yield(0,d.verifyUser)(f,t);yield(0,l.checkAppExistsAndHasPermission)(f,e,t);const y=yield(0,p.getActiveAppVersions)(f,e,g);if(console.log(`Total active versions in Capgo: ${null==y?void 0:y.length}`),0===(null==y?void 0:y.length))return;const m=((e,o,n)=>{const t=[];return null==e||e.forEach((e=>{a.default.gte(e.name,o)&&a.default.lt(e.name,`${n}`)&&t.push(e)})),t})(y,r,v);console.log(`Active versions in Capgo between ${r} and ${v}: ${null==m?void 0:m.length}`);const h=(e=!0)=>{const o=m.pop();if(o){const n=(0,d.getHumanDate)(o);e?console.log(`${o.name} created on ${n} will be kept as it's the last release`):console.log(`${o.name} created on ${n} will be kept due to config`)}};var _;h(!0),_=()=>h(!1),[...Array(s)].forEach(((e,o)=>_())),0!==m.length?(null==m||m.forEach((e=>{console.log(`${e.name} created on ${(0,d.getHumanDate)(e)} will be removed`)})),"yes"===c("Do you want to continue removing the versions specified? Type yes to confirm")?(console.log("You have confiremd removal, removing versions now"),((e,o,n,t)=>{null==e||e.forEach((e=>{console.log(`Removing ${e.name} created on ${(0,d.getHumanDate)(e)}`),(0,p.deleteSpecificVersion)(o,n,t,e.name)}))})(m,f,e,g)):console.log("Not confirmed, aborting removal...")):console.log("Nothing to be removed, aborting removal...")}))},605:function(e,o,n){var t=this&&this.__awaiter||function(e,o,n,t){return new(n||(n=Promise))((function(r,i){function a(e){try{d(t.next(e))}catch(e){i(e)}}function s(e){try{d(t.throw(e))}catch(e){i(e)}}function d(e){var o;e.done?r(e.value):(o=e.value,o instanceof n?o:new n((function(e){e(o)}))).then(a,s)}d((t=t.apply(e,o||[])).next())}))};Object.defineProperty(o,"__esModule",{value:!0}),o.deleteApp=void 0;const r=n(304),i=n(771),a=n(87);o.deleteApp=(e,o)=>t(void 0,void 0,void 0,(function*(){var n;const{bundle:t}=o,s=o.apikey||(0,i.findSavedKey)(),d=yield(0,i.getConfig)(),p=(0,i.useLogSnag)();e=e||(null===(n=null==d?void 0:d.app)||void 0===n?void 0:n.appId),s||r.program.error("Missing API key, you need to provide an API key to delete your app"),e||r.program.error("Missing argument, you need to provide a appid, or be in a capacitor project");const l=(0,i.createSupabaseClient)(s),c=yield(0,i.verifyUser)(l,s),{data:u,error:v}=yield l.rpc("exist_app",{appid:e,apikey:s});if(u&&!v||r.program.error("No permission to delete"),t)return console.log(`Delete ${e}@${t} from Capgo`),yield(0,a.deleteSpecificVersion)(l,e,c,t),void console.log(`${e}@${t} deleted from server`);console.log(`Delete ${e} from Capgo`);const{data:f,error:g}=yield l.from("app_versions").select().eq("app_id",e).eq("user_id",c);if(g&&r.program.error(`App ${e} not found in database ${g} `),f&&f.length){const o=f.map((o=>`${c}/${e}/versions/${o.bucket_id} `)),{error:n}=yield l.storage.from("apps").remove(o);n&&r.program.error(`Cannot delete stored version for app ${e} from storage ${n} `)}const{error:y}=yield l.from("app_versions").delete().eq("app_id",e).eq("user_id",c);y&&r.program.error(`Cannot delete version for app ${e} from database ${y} `);const{error:m}=yield l.from("apps").delete().eq("app_id",e).eq("user_id",c);m&&r.program.error(`Cannot delete from database ${m} `),p.publish({channel:"app",event:"App Deleted",icon:"😱",tags:{"user-id":c,"app-id":e},notify:!1}).catch(),console.log(`${e} deleted from server`)}))},250:function(e,o,n){var t=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(o,"__esModule",{value:!0});const r=n(304),i=n(107),a=n(605),s=n(327),d=n(823),p=t(n(598)),l=n(829),c=n(501),u=n(176);r.program.version(p.default.version).command("add [appid]").alias("a").action(i.addApp).option("-n, --name <name>","app name").option("-i, --icon <icon>","app icon path").option("-a, --apikey <apikey>","apikey to link to your account"),r.program.version(p.default.version).command("login [apikey]").alias("l").action(l.login).option("--local","Only save in local folder"),r.program.command("upload [appid]").alias("u").action(d.uploadVersion).option("-a, --apikey <apikey>","apikey to link to your account").option("-p, --path <path>","path of the file 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("-f, --format <base64|hex|binary|utf8>","choose the upload format default base64").option("-b, --bundle <bundle>","bundle version number of the file to upload"),r.program.command("set [appid]").alias("s").action(s.setChannel).requiredOption("-c, --channel <channel>","channel to link to").option("-a, --apikey <apikey>","apikey to link to your account").option("-b, --bundle <bundle>","bundle version number of the file to set").option("-s, --state <state>","set the state of the channel, default or normal").option("--downgrade","Allow to downgrade to version under native one").option("--no-downgrade","Disable downgrade to version under native one").option("--upgrade","Allow to upgrade to version above native one").option("--no-upgrade","Disable upgrade to version above native one").option("--ios","Allow sending update to ios devices").option("--no-ios","Disable sending update to ios devices").option("--android","Allow sending update to android devices").option("--no-android","Disable sending update to android devices").option("--self-assign","Allow to device to self assign to this channel").option("--no-self-assign","Disable devices to self assign to this channel"),r.program.description("Manage package and version in capgo Cloud").command("delete [appid]").alias("d").action(a.deleteApp).option("-a, --apikey <apikey>","apikey to link to your account").option("-b, --bundle <bundle>","bundle version number of the app to delete"),r.program.description("List versions in capgo Cloud").command("list [appid]").alias("ls").action(c.listApp).option("-a, --apikey <apikey>","apikey to link to your account"),r.program.description("Cleanup versions in capgo Cloud").command("cleanup [appid]").alias("c").action(u.cleanupApp).requiredOption("-b, --bundle <bundle>","bundle version number of the app to delete").option("-a, --apikey <apikey>","apikey to link to your account").option("-k, --keep <keep>","number of version to keep"),r.program.parse(process.argv)},501:function(e,o,n){var t=this&&this.__awaiter||function(e,o,n,t){return new(n||(n=Promise))((function(r,i){function a(e){try{d(t.next(e))}catch(e){i(e)}}function s(e){try{d(t.throw(e))}catch(e){i(e)}}function d(e){var o;e.done?r(e.value):(o=e.value,o instanceof n?o:new n((function(e){e(o)}))).then(a,s)}d((t=t.apply(e,o||[])).next())}))};Object.defineProperty(o,"__esModule",{value:!0}),o.listApp=void 0;const r=n(304),i=n(771),a=n(111),s=n(87);o.listApp=(e,o)=>t(void 0,void 0,void 0,(function*(){var n;const t=o.apikey||(0,i.findSavedKey)(),d=yield(0,i.getConfig)();e=e||(null===(n=null==d?void 0:d.app)||void 0===n?void 0:n.appId),t||r.program.error("Missing API key, you need to provide an API key to delete your app"),e||r.program.error("Missing argument, you need to provide a appid, or be in a capacitor project"),console.log("Querying available versions in Capgo");const p=(0,i.createSupabaseClient)(t),l=yield(0,i.verifyUser)(p,t);yield(0,a.checkAppExistsAndHasPermission)(p,e,t);const c=yield(0,s.getActiveAppVersions)(p,e,l);console.log(`Active versions in Capgo: ${null==c?void 0:c.length}`),null==c||c.forEach((e=>{console.log(`${e.name} created on ${(0,i.getHumanDate)(e)}`)}))}))},829:function(e,o,n){var t=this&&this.__awaiter||function(e,o,n,t){return new(n||(n=Promise))((function(r,i){function a(e){try{d(t.next(e))}catch(e){i(e)}}function s(e){try{d(t.throw(e))}catch(e){i(e)}}function d(e){var o;e.done?r(e.value):(o=e.value,o instanceof n?o:new n((function(e){e(o)}))).then(a,s)}d((t=t.apply(e,o||[])).next())}))},r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(o,"__esModule",{value:!0}),o.login=void 0;const i=n(304),a=r(n(147)),s=r(n(37)),d=n(771);o.login=(e,o)=>t(void 0,void 0,void 0,(function*(){const{local:n}=o,t=(0,d.useLogSnag)();if(n)a.default.existsSync(".git")||i.program.error("To use local you should be in a git repository"),a.default.writeFileSync(".capgo",`${e}\n`),a.default.appendFileSync(".gitignore",".capgo\n");else{const o=s.default.homedir();a.default.writeFileSync(`${o}/.capgo`,`${e}\n`)}const r=(0,d.createSupabaseClient)(e),p=yield(0,d.verifyUser)(r,e,["write","all","upload"]);t.publish({channel:"user-login",event:"User CLI login",icon:"✅",tags:{"user-id":p},notify:!1}).catch(),console.log(`login saved into .capgo file in ${n?"local":"home"} directory`)}))},327:function(e,o,n){var t=this&&this.__awaiter||function(e,o,n,t){return new(n||(n=Promise))((function(r,i){function a(e){try{d(t.next(e))}catch(e){i(e)}}function s(e){try{d(t.throw(e))}catch(e){i(e)}}function d(e){var o;e.done?r(e.value):(o=e.value,o instanceof n?o:new n((function(e){e(o)}))).then(a,s)}d((t=t.apply(e,o||[])).next())}))};Object.defineProperty(o,"__esModule",{value:!0}),o.setChannel=void 0;const r=n(304),i=n(771);o.setChannel=(e,o)=>t(void 0,void 0,void 0,(function*(){var n;const{bundle:t,downgrade:a,upgrade:s,ios:d,android:p,selfAssign:l,channel:c,state:u}=o,v=o.apikey||(0,i.findSavedKey)(),f=yield(0,i.getConfig)(),g=(0,i.useLogSnag)();e=e||(null===(n=null==f?void 0:f.app)||void 0===n?void 0:n.appId),v||r.program.error("Missing API key, you need to provide a API key to add your app"),e||r.program.error("Missing argument, you need to provide a appid, or be in a capacitor project"),void 0===t&&void 0===u&&void 0===a&&void 0===s&&void 0===d&&void 0===p&&void 0===l&&r.program.error("Missing argument, you need to provide a option to set");try{const o=(0,i.createSupabaseClient)(v),n=yield(0,i.verifyUser)(o,v,["write","all"]);yield(0,i.checkPlan)(o,n);const f={created_by:n,app_id:e,name:c};if(t){const{data:i,error:a}=yield o.from("app_versions").select().eq("app_id",e).eq("name",t).eq("user_id",n).eq("deleted",!1);!a&&i&&i.length||r.program.error(`Cannot find version ${t}`),console.log(`Set ${e} channel: ${c} to @${t}`),f.version=i[0].id}void 0!==u&&("public"!==u&&"private"!==u||console.log(`Set ${e} channel: ${c} to public or private is deprecated, use default or normal instead`),console.log(`Set ${e} channel: ${c} to ${"public"===u||"default"===u?"default":"normal"}`),f.public="public"===u||"default"===u),void 0!==a&&(console.log(`Set ${e} channel: ${c} to ${a?"allow":"disallow"} downgrade`),f.disableAutoUpdateUnderNative=!a),void 0!==s&&(console.log(`Set ${e} channel: ${c} to ${s?"allow":"disallow"} upgrade`),f.disableAutoUpdateToMajor=!s),void 0!==d&&(console.log(`Set ${e} channel: ${c} to ${d?"allow":"disallow"} ios update`),f.ios=!!d),void 0!==p&&(console.log(`Set ${e} channel: ${c} to ${p?"allow":"disallow"} android update`),f.android=!!p),void 0!==l&&(console.log(`Set ${e} channel: ${c} to ${l?"allow":"disallow"} self assign to this channel`),f.allow_device_self_set=!!l);try{const{error:e}=yield(0,i.updateOrCreateChannel)(o,f,v);e&&r.program.error(`Cannot set channel ${(0,i.formatError)(e)}`)}catch(e){r.program.error(`Cannot set channel ${(0,i.formatError)(e)}`)}g.publish({channel:"app",event:"Set app",icon:"✅",tags:{"user-id":n,"app-id":e},notify:!1}).catch()}catch(e){r.program.error(`Unknow error ${(0,i.formatError)(e)}`)}console.log("Done ✅")}))},823:function(e,o,n){var t=this&&this.__awaiter||function(e,o,n,t){return new(n||(n=Promise))((function(r,i){function a(e){try{d(t.next(e))}catch(e){i(e)}}function s(e){try{d(t.throw(e))}catch(e){i(e)}}function d(e){var o;e.done?r(e.value):(o=e.value,o instanceof n?o:new n((function(e){e(o)}))).then(a,s)}d((t=t.apply(e,o||[])).next())}))},r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(o,"__esModule",{value:!0}),o.uploadVersion=void 0;const i=r(n(844)),a=n(304),s=n(113),d=r(n(295)),p=n(906),l=n(771);o.uploadVersion=(e,o)=>t(void 0,void 0,void 0,(function*(){var n,t,r,c;let{bundle:u,path:v,channel:f}=o;const{external:g}=o,y=o.apikey||(0,l.findSavedKey)(),m=(0,l.useLogSnag)();f=f||"dev";const h=yield(0,l.getConfig)();e=e||(null===(n=null==h?void 0:h.app)||void 0===n?void 0:n.appId),u=u||(null===(r=null===(t=null==h?void 0:h.app)||void 0===t?void 0:t.package)||void 0===r?void 0:r.version),l.regexSemver.test(u)||a.program.error(`Your bundle name ${u}, is not valid it should follow semver convention : https://semver.org/`),v=v||(null===(c=null==h?void 0:h.app)||void 0===c?void 0:c.webDir),y||a.program.error("Missing API key, you need to provide a API key to add your app"),e&&u&&v||a.program.error("Missing argument, you need to provide a appid and a bundle and a path, or be in a capacitor project"),console.log(`Upload ${e}@${u} started from path "${v}" to Capgo cloud`);const _=(0,l.createSupabaseClient)(y),b=yield(0,l.verifyUser)(_,y,["write","all","upload"]);yield(0,l.checkPlan)(_,b,!1);const w=new d.default.MultiBar({clearOnComplete:!1,hideCursor:!0},d.default.Presets.shades_grey),$=w.create(7,0,{format:"Uploading: [{bar}] {percentage}% | ETA: {eta}s | {value}/{total} Part"},d.default.Presets.shades_grey);$.start(7,0,{speed:"N/A"});const{data:k,error:x}=yield _.rpc("exist_app_versions",{apikey:y,name_version:u,appid:e});(k||x)&&(w.stop(),a.program.error(`This app bundle already exist or was deleted, you cannot re-upload it ${(0,l.formatError)(x)}`)),$.increment();const{data:A,error:C}=yield _.rpc("is_trial",{userid:b}).single();(A&&A>0||C)&&w.log(`WARNING !!\nTrial expires in ${A} days, upgrade here: ${l.hostWeb}/dashboard/settings/plans\n`),$.increment();const{data:S,error:P}=yield _.rpc("exist_app",{appid:e,apikey:y});S&&!P||(w.stop(),a.program.error(`Cannot find app ${e} in your account \n${(0,l.formatError)(P)}`)),$.increment();const{data:q,error:I}=yield _.rpc("exist_app_versions",{appid:e,apikey:y,name_version:u});(q||I)&&a.program.error(`Version already exists ${(0,l.formatError)(I)}`),$.increment();const j=(0,s.randomUUID)();let M="";if(g)g&&!g.startsWith("https://")&&(w.stop(),a.program.error(`External link should should start with "https://" current is "${g}"`));else{const o=new i.default;o.addLocalFolder(v);const n=o.toBuffer();M=yield(0,p.checksum)(n,"crc32");const t=Math.floor(n.byteLength/1024/1024),r=`apps/${b}/${e}/versions`;$.increment(),t>20&&(w.log(`WARNING !!\nThe app size is ${t} Mb, this may take a while to download for users\n`),m.publish({channel:"app-error",event:"App Too Large",icon:"🚛",tags:{"user-id":b,"app-id":e},notify:!1}).catch());const{error:s}=yield _.storage.from(r).upload(j,n,{contentType:"application/zip"});s&&(w.stop(),a.program.error(`Cannot upload ${(0,l.formatError)(s)}`))}$.increment();const{data:D,error:O}=yield(0,l.updateOrCreateVersion)(_,{bucket_id:g?void 0:j,user_id:b,name:u,app_id:e,external_url:g,checksum:M},y);if(O&&(w.stop(),a.program.error(`Cannot add bundle ${(0,l.formatError)(O)}`)),$.increment(),D&&D.length){const{error:o}=yield(0,l.updateOrCreateChannel)(_,{name:f,app_id:e,created_by:b,version:D[0].id},y);o&&w.log("Cannot set bundle with upload key, use key with more rights for that\n")}else w.log("Cannot set bundle with upload key, use key with more rights for that\n");w.stop();const U=e.replace(/\./g,"--");console.log("App uploaded to server"),console.log(`Try it in mobile app: ${l.host}/app_mobile`),console.log(`Or set the channel ${f} as public here: ${l.hostWeb}/app/package/${U}`),console.log("To use with live update in your own app"),console.log(`You can link specific device to this bundle to make user try it first, here: ${l.hostWeb}/app/p/${U}/devices`),m.publish({channel:"app",event:"App Uploaded",icon:"⏫",tags:{"user-id":b,"app-id":e},notify:!1}).catch()}))},771:function(e,o,n){var t=this&&this.__awaiter||function(e,o,n,t){return new(n||(n=Promise))((function(r,i){function a(e){try{d(t.next(e))}catch(e){i(e)}}function s(e){try{d(t.throw(e))}catch(e){i(e)}}function d(e){var o;e.done?r(e.value):(o=e.value,o instanceof n?o:new n((function(e){e(o)}))).then(a,s)}d((t=t.apply(e,o||[])).next())}))},r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(o,"__esModule",{value:!0}),o.getHumanDate=o.verifyUser=o.useLogSnag=o.updateOrCreateChannel=o.updateOrCreateVersion=o.getConfig=o.formatError=o.findSavedKey=o.checkPlan=o.isTrial=o.isPaying=o.isGoodPlan=o.checkKey=o.regexSemver=o.createSupabaseClient=o.supaAnon=o.hostSupa=o.hostWeb=o.host=void 0;const i=n(778),a=n(304),s=n(885),d=r(n(867)),p=r(n(147)),l=r(n(37)),c=n(133);o.host="https://capgo.app",o.hostWeb="https://web.capgo.app",o.hostSupa="https://xvwzpoazmxkqosrdewyv.supabase.co",o.supaAnon="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYzNzgwNTAwOSwiZXhwIjoxOTUzMzgxMDA5fQ.8tgID1d4jodPwuo_fz4KHN4o1XKB9fnqyt0_GaJSj-w",o.createSupabaseClient=e=>(0,s.createClient)(o.hostSupa,o.supaAnon,{headers:{capgkey:e}}),o.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-]+)*))?$/,o.checkKey=(e,n,r)=>t(void 0,void 0,void 0,(function*(){const{data:t,error:i}=yield e.rpc("is_allowed_capgkey",{apikey:n,keymode:r});t&&!i||a.program.error(`Invalid API key or insufficient permissions ${(0,o.formatError)(i)}`)})),o.isGoodPlan=(e,o)=>t(void 0,void 0,void 0,(function*(){const{data:n,error:t}=yield e.rpc("is_good_plan_v2",{userid:o}).single();if(t)throw t;return n||!1})),o.isPaying=(e,o)=>t(void 0,void 0,void 0,(function*(){const{data:n,error:t}=yield e.rpc("is_paying",{userid:o}).single();if(t)throw t;return n||!1})),o.isTrial=(e,o)=>t(void 0,void 0,void 0,(function*(){const{data:n,error:t}=yield e.rpc("is_trial",{userid:o}).single();if(t)throw t;return n||0})),o.checkPlan=(e,n,r=!0)=>t(void 0,void 0,void 0,(function*(){const t=yield(0,o.isGoodPlan)(e,n),i=yield(0,o.isPaying)(e,n),s=yield(0,o.isTrial)(e,n);i&&t||!(s<0)||a.program.error(`You need to upgrade your plan to continue to use capgo.\n Upgrade here: ${o.hostWeb}/dashboard/settings/plans\n`),s>0&&r&&!i&&console.log(`WARNING !!\nTrial expires in ${s} days, upgrade here: ${o.hostWeb}/dashboard/settings/plans\n`)})),o.findSavedKey=()=>{let e=`${l.default.homedir()}/.capgo`;return p.default.existsSync(e)?p.default.readFileSync(e,"utf8").trim():(e=".capgo",p.default.existsSync(e)?p.default.readFileSync(e,"utf8").trim():null)},o.formatError=e=>e?`\n${d.default.render(e)}`:"",o.getConfig=()=>t(void 0,void 0,void 0,(function*(){let e;try{e=yield(0,i.loadConfig)()}catch(e){a.program.error("No capacitor config file found, run `cap init` first")}return e})),o.updateOrCreateVersion=(e,o,n)=>t(void 0,void 0,void 0,(function*(){const{data:t,error:r}=yield e.rpc("exist_app_versions",{appid:o.app_id,name_version:o.name,apikey:n});return t&&!r?(o.deleted=!1,e.from("app_versions").update(o).eq("app_id",o.app_id).eq("name",o.name)):e.from("app_versions").insert(o)})),o.updateOrCreateChannel=(e,o,n)=>t(void 0,void 0,void 0,(function*(){if(!o.app_id||!o.name||!o.created_by)return console.error("missing app_id, name, or created_by"),Promise.reject(new Error("missing app_id, name, or created_by"));const{data:t,error:r}=yield e.rpc("exist_channel",{appid:o.app_id,name_channel:o.name,apikey:n});return t&&!r?e.from("channels").update(o,{returning:"minimal"}).eq("app_id",o.app_id).eq("name",o.name).eq("created_by",o.created_by):e.from("channels").insert(o,{returning:"minimal"})})),o.useLogSnag=()=>new c.LogSnag({token:"c124f5e9d0ce5bdd14bbb48f815d5583",project:"capgo"}),o.verifyUser=(e,n,r=["all"])=>t(void 0,void 0,void 0,(function*(){yield(0,o.checkKey)(e,n,r);const{data:t,error:i}=yield e.rpc("get_user_id",{apikey:n}),s=t?t.toString():"";return s&&!i||a.program.error(`Cannot verify user ${(0,o.formatError)(i)}`),s})),o.getHumanDate=e=>new Date(e.created_at||"").toLocaleString()},778:e=>{e.exports=require("@capacitor/cli/dist/config")},885:e=>{e.exports=require("@supabase/supabase-js")},906:e=>{e.exports=require("@tomasklaen/checksum")},844:e=>{e.exports=require("adm-zip")},295:e=>{e.exports=require("cli-progress")},304:e=>{e.exports=require("commander")},470:e=>{e.exports=require("fs-extra")},133:e=>{e.exports=require("logsnag")},11:e=>{e.exports=require("mime")},867:e=>{e.exports=require("prettyjson")},616:e=>{e.exports=require("prompt-sync")},505:e=>{e.exports=require("semver/preload")},113:e=>{e.exports=require("crypto")},147:e=>{e.exports=require("fs")},37:e=>{e.exports=require("os")},598:e=>{e.exports=JSON.parse('{"name":"@capgo/cli","version":"1.1.13","description":"A CLI to upload to capgo servers","main":"dist/index.js","bin":{"capgo":"dist/index.js"},"repository":{"type":"git","url":"git+https://github.com/Cap-go/capgo-cli.git"},"bugs":{"url":"https://github.com/Cap-go/capgo-cli/issues"},"keywords":["appflow alternative","ionic","capacitor","auto update","live update","capgo","cli","upload","capgo-cli"],"scripts":{"dev":"set NODE_ENV=development && npx webpack --config webpack.config.js","no-debug":"node dist/index.js","build":"npx webpack --config webpack.config.js","dev-build":"SUPA_DB=development npx webpack --config webpack.config.js","pack":"pkg","test_rls":"ts-node ./test/test_headers_rls.ts","lint":"eslint . --ext .ts --fix"},"author":"github.com/riderx","license":"Apache 2.0","dependencies":{"@capacitor/cli":"4.4.0","@supabase/supabase-js":"^1.35.7","@tomasklaen/checksum":"^1.1.0","adm-zip":"^0.5.9","cli-progress":"3.11.2","commander":"9.4.1","fs-extra":"10.1.0","logsnag":"^0.1.5","mime":"^3.0.0","prettyjson":"^1.2.5","prompt-sync":"^4.2.0","semver":"^7.3.8"},"devDependencies":{"@types/adm-zip":"0.5.0","@types/cli-progress":"^3.11.0","@types/fs-extra":"^9.0.13","@types/mime":"^3.0.1","@types/node":"^18.11.9","@types/prettyjson":"^0.0.30","@types/prompt-sync":"^4.2.0","@types/semver":"^7.3.13","@typescript-eslint/eslint-plugin":"5.42.1","@typescript-eslint/parser":"5.42.1","eslint":"8.27.0","eslint-config-airbnb-base":"^15.0.0","eslint-config-prettier":"^8.5.0","eslint-import-resolver-typescript":"3.5.2","eslint-plugin-import":"2.26.0","eslint-plugin-prettier":"^4.2.1","git-format-staged":"3.0.0","husky":"^8.0.2","nodemon":"2.0.20","pkg":"5.8.0","prettier":"2.7.1","ts-loader":"^9.4.1","ts-node":"^10.9.1","tsconfig-paths":"4.1.0","typescript":"4.8.4","webpack":"5.74.0","webpack-cli":"^4.10.0","webpack-node-externals":"^3.0.0"}}')}},o={};!function n(t){var r=o[t];if(void 0!==r)return r.exports;var i=o[t]={exports:{}};return e[t].call(i.exports,i,i.exports,n),i.exports}(250)})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/cli",
3
- "version": "1.1.10",
3
+ "version": "1.1.13",
4
4
  "description": "A CLI to upload to capgo servers",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -45,7 +45,9 @@
45
45
  "fs-extra": "10.1.0",
46
46
  "logsnag": "^0.1.5",
47
47
  "mime": "^3.0.0",
48
- "prettyjson": "^1.2.5"
48
+ "prettyjson": "^1.2.5",
49
+ "prompt-sync": "^4.2.0",
50
+ "semver": "^7.3.8"
49
51
  },
50
52
  "devDependencies": {
51
53
  "@types/adm-zip": "0.5.0",
@@ -54,6 +56,7 @@
54
56
  "@types/mime": "^3.0.1",
55
57
  "@types/node": "^18.11.9",
56
58
  "@types/prettyjson": "^0.0.30",
59
+ "@types/prompt-sync": "^4.2.0",
57
60
  "@types/semver": "^7.3.13",
58
61
  "@typescript-eslint/eslint-plugin": "5.42.1",
59
62
  "@typescript-eslint/parser": "5.42.1",
@@ -76,4 +79,4 @@
76
79
  "webpack-cli": "^4.10.0",
77
80
  "webpack-node-externals": "^3.0.0"
78
81
  }
79
- }
82
+ }
package/src/api/app.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { program } from 'commander';
3
+
4
+ export const checkAppExistsAndHasPermission = async (supabase: SupabaseClient, appid: string, apikey: string) => {
5
+ const { data: app, error: dbError0 } = await supabase
6
+ .rpc<string>('exist_app', { appid, apikey });
7
+ if (!app || dbError0) {
8
+ program.error('No permission for this app');
9
+ }
10
+ }
@@ -0,0 +1,17 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { program } from 'commander';
3
+ import { definitions } from '../bin/types_supabase';
4
+ import { formatError } from '../bin/utils';
5
+
6
+ export const checkVersionNotUsedInChannel = async (supabase: SupabaseClient,
7
+ appid: string, userId: string, versionData: definitions["app_versions"], bundle: string) => {
8
+ const { data: channelFound, error: errorChannel } = await supabase
9
+ .from<definitions['channels']>('channels')
10
+ .select()
11
+ .eq('app_id', appid)
12
+ .eq('created_by', userId)
13
+ .eq('version', versionData.id);
14
+ if ((channelFound && channelFound.length) || errorChannel) {
15
+ program.error(`Version ${appid}@${bundle} is used in a channel, unlink it first ${formatError(errorChannel)}`);
16
+ }
17
+ }
@@ -0,0 +1,16 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { program } from 'commander';
3
+ import { definitions } from '../bin/types_supabase';
4
+ import { formatError } from '../bin/utils';
5
+
6
+ export const checkVersionNotUsedInDeviceOverride = async (supabase: SupabaseClient,
7
+ appid: string, versionData: definitions["app_versions"], bundle: string) => {
8
+ const { data: deviceFound, error: errorDevice } = await supabase
9
+ .from<definitions['devices_override']>('devices_override')
10
+ .select()
11
+ .eq('app_id', appid)
12
+ .eq('version', versionData.id);
13
+ if ((deviceFound && deviceFound.length) || errorDevice) {
14
+ program.error(`Version ${appid} @${bundle} is used in a device override, unlink it first ${formatError(errorDevice)}`);
15
+ }
16
+ }
@@ -0,0 +1,26 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { program } from 'commander';
3
+
4
+ interface VersionData {
5
+ id: number;
6
+ created_at?: string;
7
+ app_id: string;
8
+ name: string;
9
+ bucket_id?: string;
10
+ user_id: string;
11
+ updated_at?: string;
12
+ deleted: boolean;
13
+ external_url?: string;
14
+ checksum?: string
15
+ }
16
+
17
+ export const deleteFromStorage = async (supabase: SupabaseClient,
18
+ userId: string, appid: string, versionData: VersionData, bundle: string) => {
19
+ const { error: delError } = await supabase
20
+ .storage
21
+ .from('apps')
22
+ .remove([`${userId}/${appid}/versions/${versionData.bucket_id} `]);
23
+ if (delError) {
24
+ program.error(`Something went wrong when trying to delete ${appid} @${bundle} ${delError} `);
25
+ }
26
+ }
@@ -0,0 +1,60 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { program } from 'commander';
3
+ import { definitions } from '../bin/types_supabase';
4
+ import { formatError } from '../bin/utils';
5
+ import { checkVersionNotUsedInChannel } from './channels';
6
+ import { checkVersionNotUsedInDeviceOverride } from './devices_override';
7
+ import { deleteFromStorage } from './storage';
8
+
9
+ export const deleteAppVersion = async (supabase: SupabaseClient, appid: string, userId: string, bundle: string) => {
10
+ const { error: delAppSpecVersionError } = await supabase
11
+ .from<definitions['app_versions']>('app_versions')
12
+ .update({
13
+ deleted: true
14
+ })
15
+ .eq('app_id', appid)
16
+ .eq('user_id', userId)
17
+ .eq('name', bundle);
18
+ if (delAppSpecVersionError) {
19
+ program.error(`App ${appid}@${bundle} not found in database '${delAppSpecVersionError}'`);
20
+ }
21
+ }
22
+
23
+ export const deleteSpecificVersion = async (supabase: SupabaseClient, appid: string, userId: string, bundle: string) => {
24
+ const versionData = await getVersionData(supabase, appid, userId, bundle);
25
+ await checkVersionNotUsedInChannel(supabase, appid, userId, versionData, bundle);
26
+ await checkVersionNotUsedInDeviceOverride(supabase, appid, versionData, bundle);
27
+ // Delete only a specific version in storage
28
+ await deleteFromStorage(supabase, userId, appid, versionData, bundle);
29
+
30
+ await deleteAppVersion(supabase, appid, userId, bundle);
31
+ }
32
+
33
+ export const getActiveAppVersions = async (supabase: SupabaseClient, appid: string, userId: string) => {
34
+ const { data, error: vError } = await supabase
35
+ .from<definitions['app_versions']>('app_versions')
36
+ .select()
37
+ .eq('app_id', appid)
38
+ .eq('user_id', userId)
39
+ .eq('deleted', false);
40
+
41
+ if (vError) {
42
+ program.error(`App ${appid} not found in database ${vError} `);
43
+ }
44
+ return data;
45
+ }
46
+
47
+ export const getVersionData = async (supabase: SupabaseClient, appid: string, userId: string, bundle: string) => {
48
+ const { data: versionData, error: versionIdError } = await supabase
49
+ .from<definitions['app_versions']>('app_versions')
50
+ .select()
51
+ .eq('app_id', appid)
52
+ .eq('user_id', userId)
53
+ .eq('name', bundle)
54
+ .eq('deleted', false)
55
+ .single();
56
+ if (!versionData || versionIdError) {
57
+ program.error(`Version ${appid}@${bundle} doesn't exist ${formatError(versionIdError)}`);
58
+ }
59
+ return versionData;
60
+ }
@@ -0,0 +1,118 @@
1
+ import { program } from 'commander';
2
+ import semver from 'semver/preload';
3
+ import promptSync from 'prompt-sync';
4
+ import { SupabaseClient } from '@supabase/supabase-js';
5
+ import { createSupabaseClient, findSavedKey, getConfig, getHumanDate, verifyUser } from './utils';
6
+ import { deleteSpecificVersion, getActiveAppVersions } from '../api/versions';
7
+ import { checkAppExistsAndHasPermission } from '../api/app';
8
+ import { definitions } from './types_supabase';
9
+
10
+ interface Options {
11
+ apikey: string;
12
+ version: string;
13
+ bundle: string;
14
+ keep: number;
15
+ }
16
+
17
+
18
+ const prompt = promptSync();
19
+
20
+ const removeVersions = (toRemove: definitions["app_versions"][], supabase: SupabaseClient, appid: string, userId: string) => {
21
+ toRemove?.forEach(row => {
22
+ console.log(`Removing ${row.name} created on ${(getHumanDate(row))}`);
23
+ deleteSpecificVersion(supabase, appid, userId, row.name);
24
+ });
25
+ }
26
+
27
+ const getRemovableVersionsInSemverRange = (data: definitions["app_versions"][], bundle: string, nextMajor: string) => {
28
+ const toRemove: definitions["app_versions"][] = [];
29
+
30
+ data?.forEach(row => {
31
+ if (semver.gte(row.name, bundle) && semver.lt(row.name, `${nextMajor}`)) {
32
+ toRemove.push(row);
33
+ }
34
+ });
35
+ return toRemove;
36
+ }
37
+
38
+ const loop = (times: number, callback: (i: number) => unknown) => {
39
+ [...Array(times)].forEach((item, i) => callback(i));
40
+ };
41
+
42
+ export const cleanupApp = async (appid: string, options: Options) => {
43
+ const apikey = options.apikey || findSavedKey()
44
+ const { bundle, keep = 4 } = options;
45
+
46
+ const config = await getConfig();
47
+
48
+ appid = appid || config?.app?.appId
49
+ if (!apikey) {
50
+ program.error('Missing API key, you need to provide an API key to delete your app');
51
+ }
52
+ if (!appid) {
53
+ program.error('Missing argument, you need to provide a appid, or be in a capacitor project');
54
+ }
55
+
56
+ const nextMajor = `${semver.inc(bundle, 'major')}`;
57
+ console.log(`Querying available versions in Capgo between ${bundle} and ${nextMajor}`);
58
+
59
+ const supabase = createSupabaseClient(apikey)
60
+
61
+ const userId = await verifyUser(supabase, apikey);
62
+
63
+ // Check we have app access to this appId
64
+ await checkAppExistsAndHasPermission(supabase, appid, apikey);
65
+
66
+ // Get all active app versions we might possibly be able to cleanup
67
+ const data = await getActiveAppVersions(supabase, appid, userId);
68
+
69
+ console.log(`Total active versions in Capgo: ${data?.length}`);
70
+
71
+ if (data?.length === 0) {
72
+ return;
73
+ }
74
+
75
+ // Get all app versions that are in the given range
76
+ const toRemove = getRemovableVersionsInSemverRange(data, bundle, nextMajor);
77
+
78
+ console.log(`Active versions in Capgo between ${bundle} and ${nextMajor}: ${toRemove?.length}`);
79
+
80
+ const removeLast = (recent = true) => {
81
+ const last = toRemove.pop();
82
+ if (last) {
83
+ const humanDate = getHumanDate(last);
84
+ if (recent) {
85
+ console.log(`${last.name} created on ${humanDate} will be kept as it's the last release`);
86
+ } else {
87
+ console.log(`${last.name} created on ${humanDate} will be kept due to config`);
88
+ }
89
+ }
90
+ }
91
+
92
+ // Always keep the latest version
93
+ removeLast(true);
94
+
95
+ // loop call removeLast until we have use up the keep count
96
+ loop(keep, () => removeLast(false));
97
+
98
+ if (toRemove.length === 0) {
99
+ console.log("Nothing to be removed, aborting removal...")
100
+ return;
101
+ }
102
+
103
+ // Show the user what will be removed
104
+ toRemove?.forEach(row => {
105
+ console.log(`${row.name} created on ${(getHumanDate(row))} will be removed`);
106
+ });
107
+
108
+ // Check user wants to clean that all up
109
+ const result = prompt("Do you want to continue removing the versions specified? Type yes to confirm");
110
+ if (result !== "yes") {
111
+ console.log("Not confirmed, aborting removal...");
112
+ return;
113
+ }
114
+
115
+ // Yes, lets clean it up
116
+ console.log("You have confiremd removal, removing versions now");
117
+ removeVersions(toRemove, supabase, appid, userId);
118
+ }
package/src/bin/delete.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { program } from 'commander';
2
- import { getConfig, createSupabaseClient, formatError, findSavedKey, useLogSnag, verifyUser } from './utils';
3
- import { definitions } from './types_supabase'
2
+ import { createSupabaseClient, findSavedKey, getConfig, useLogSnag, verifyUser } from './utils';
3
+ import { definitions } from './types_supabase';
4
+ import { deleteSpecificVersion } from '../api/versions';
4
5
 
5
6
  interface Options {
6
7
  apikey: string;
@@ -34,54 +35,7 @@ export const deleteApp = async (appid: string, options: Options) => {
34
35
  if (bundle) {
35
36
  console.log(`Delete ${appid}@${bundle} from Capgo`);
36
37
 
37
- const { data: versionData, error: versionIdError } = await supabase
38
- .from<definitions['app_versions']>('app_versions')
39
- .select()
40
- .eq('app_id', appid)
41
- .eq('user_id', userId)
42
- .eq('name', bundle)
43
- .eq('deleted', false)
44
- .single()
45
- if (!versionData || versionIdError) {
46
- program.error(`Version ${appid}@${bundle} doesn't exist ${formatError(versionIdError)}`)
47
- }
48
- const { data: channelFound, error: errorChannel } = await supabase
49
- .from<definitions['channels']>('channels')
50
- .select()
51
- .eq('app_id', appid)
52
- .eq('created_by', userId)
53
- .eq('version', versionData.id)
54
- if ((channelFound && channelFound.length) || errorChannel) {
55
- program.error(`Version ${appid}@${bundle} is used in a channel, unlink it first ${formatError(errorChannel)}`)
56
- }
57
- const { data: deviceFound, error: errorDevice } = await supabase
58
- .from<definitions['devices_override']>('devices_override')
59
- .select()
60
- .eq('app_id', appid)
61
- .eq('version', versionData.id)
62
- if ((deviceFound && deviceFound.length) || errorDevice) {
63
- program.error(`Version ${appid} @${bundle} is used in a device override, unlink it first ${formatError(errorDevice)}`)
64
- }
65
- // Delete only a specific version in storage
66
- const { error: delError } = await supabase
67
- .storage
68
- .from('apps')
69
- .remove([`${userId}/${appid}/versions/${versionData.bucket_id} `])
70
- if (delError) {
71
- program.error(`Something went wrong when trying to delete ${appid} @${bundle} ${delError} `)
72
- }
73
-
74
- const { error: delAppSpecVersionError } = await supabase
75
- .from<definitions['app_versions']>('app_versions')
76
- .update({
77
- deleted: true,
78
- })
79
- .eq('app_id', appid)
80
- .eq('user_id', userId)
81
- .eq('name', bundle)
82
- if (delAppSpecVersionError) {
83
- program.error(`App ${appid}@${bundle} not found in database '${delAppSpecVersionError}'`)
84
- }
38
+ await deleteSpecificVersion(supabase, appid, userId, bundle);
85
39
  console.log(`${appid}@${bundle} deleted from server`)
86
40
  return
87
41
  }
package/src/bin/index.ts CHANGED
@@ -6,6 +6,7 @@ import { uploadVersion } from './upload';
6
6
  import pack from '../../package.json'
7
7
  import { login } from './login';
8
8
  import { listApp } from './list';
9
+ import { cleanupApp } from './cleanup';
9
10
 
10
11
  program
11
12
  .version(pack.version)
@@ -31,10 +32,12 @@ program
31
32
  .option('-e, --external <url>', 'link to external url intead of upload to capgo cloud')
32
33
  .option('-f, --format <base64|hex|binary|utf8>', 'choose the upload format default base64')
33
34
  .option('-b, --bundle <bundle>', 'bundle version number of the file to upload');
35
+
34
36
  program
35
37
  .command('set [appid]').alias('s')
36
38
  .action(setChannel)
37
39
  .requiredOption('-c, --channel <channel>', 'channel to link to')
40
+ .option('-a, --apikey <apikey>', 'apikey to link to your account')
38
41
  .option('-b, --bundle <bundle>', 'bundle version number of the file to set')
39
42
  .option('-s, --state <state>', 'set the state of the channel, default or normal')
40
43
  .option('--downgrade', 'Allow to downgrade to version under native one')
@@ -46,8 +49,7 @@ program
46
49
  .option('--android', 'Allow sending update to android devices')
47
50
  .option('--no-android', 'Disable sending update to android devices')
48
51
  .option('--self-assign', 'Allow to device to self assign to this channel')
49
- .option('--no-self-assign', 'Disable devices to self assign to this channel')
50
- .option('-a, --apikey <apikey>', 'apikey to link to your account');
52
+ .option('--no-self-assign', 'Disable devices to self assign to this channel');
51
53
 
52
54
  program
53
55
  .description('Manage package and version in capgo Cloud')
@@ -62,4 +64,12 @@ program
62
64
  .action(listApp)
63
65
  .option('-a, --apikey <apikey>', 'apikey to link to your account');
64
66
 
67
+ program
68
+ .description('Cleanup versions in capgo Cloud')
69
+ .command('cleanup [appid]').alias('c')
70
+ .action(cleanupApp)
71
+ .requiredOption('-b, --bundle <bundle>', 'bundle version number of the app to delete')
72
+ .option('-a, --apikey <apikey>', 'apikey to link to your account')
73
+ .option('-k, --keep <keep>', 'number of version to keep');
74
+
65
75
  program.parse(process.argv);
package/src/bin/list.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { program } from 'commander';
2
- import { getConfig, createSupabaseClient, findSavedKey, verifyUser } from './utils';
3
- import { definitions } from './types_supabase'
2
+ import { createSupabaseClient, findSavedKey, getConfig, getHumanDate, verifyUser } from './utils';
3
+ import { checkAppExistsAndHasPermission } from '../api/app';
4
+ import { getActiveAppVersions } from '../api/versions';
4
5
 
5
6
  interface Options {
6
7
  apikey: string;
@@ -24,31 +25,17 @@ export const listApp = async (appid: string, options: Options) => {
24
25
 
25
26
  const userId = await verifyUser(supabase, apikey);
26
27
 
27
- const { data: app, error: dbError0 } = await supabase
28
- .rpc<string>('exist_app', { appid, apikey })
29
- if (!app || dbError0) {
30
- program.error('No permission for this app')
31
- }
28
+ // Check we have app access to this appId
29
+ await checkAppExistsAndHasPermission(supabase, appid, apikey);
32
30
 
33
- const { data, error: vError } = await supabase
34
- .from<definitions['app_versions']>('app_versions')
35
- .select()
36
- .eq('app_id', appid)
37
- .eq('user_id', userId)
38
- .eq('deleted', false)
31
+ // Get all active app versions we might possibly be able to cleanup
32
+ const data = await getActiveAppVersions(supabase, appid, userId);
39
33
 
40
34
  console.log(`Active versions in Capgo: ${data?.length}`);
41
35
 
42
36
  data?.forEach(row => {
43
37
  // convert created_at to human time
44
- const date = new Date(row.created_at || '');
45
- const humanDate = date.toLocaleString();
46
- console.log(`${row.name} created on ${humanDate}`);
38
+ console.log(`${row.name} created on ${(getHumanDate(row))}`);
47
39
  });
48
40
 
49
- if (vError) {
50
- program.error(`App ${appid} not found in database ${vError} `)
51
- }
52
-
53
-
54
41
  }
package/src/bin/set.ts CHANGED
@@ -49,7 +49,6 @@ export const setChannel = async (appid: string, options: Options) => {
49
49
  name: channel,
50
50
  }
51
51
  if (bundle) {
52
- console.log(`Set ${appid} channel: ${channel} to @${bundle}`);
53
52
  const { data, error: vError } = await supabase
54
53
  .from<definitions['app_versions']>('app_versions')
55
54
  .select()
@@ -59,6 +58,7 @@ export const setChannel = async (appid: string, options: Options) => {
59
58
  .eq('deleted', false)
60
59
  if (vError || !data || !data.length)
61
60
  program.error(`Cannot find version ${bundle}`);
61
+ console.log(`Set ${appid} channel: ${channel} to @${bundle}`);
62
62
  channelPayload.version = data[0].id
63
63
  }
64
64
  if (state !== undefined) {
package/src/bin/utils.ts CHANGED
@@ -1,13 +1,12 @@
1
1
  import { loadConfig } from '@capacitor/cli/dist/config';
2
2
  import { program } from 'commander';
3
- import { createClient, SupabaseClient } from '@supabase/supabase-js'
3
+ import { createClient, SupabaseClient } from '@supabase/supabase-js';
4
4
  import prettyjson from 'prettyjson';
5
- import fs from 'fs'
6
- import os from 'os'
7
- import { LogSnag } from 'logsnag'
5
+ import fs from 'fs';
6
+ import os from 'os';
7
+ import { LogSnag } from 'logsnag';
8
8
  import { definitions } from './types_supabase';
9
9
 
10
-
11
10
  export const host = 'https://capgo.app';
12
11
  export const hostWeb = 'https://web.capgo.app';
13
12
  export const hostSupa = process.env.SUPA_DB === 'production'
@@ -182,4 +181,9 @@ export const verifyUser = async (supabase: SupabaseClient, apikey: string, keymo
182
181
  program.error(`Cannot verify user ${formatError(userIdError)}`);
183
182
  }
184
183
  return userId;
184
+ }
185
+
186
+ export const getHumanDate = (row: definitions["app_versions"]) => {
187
+ const date = new Date(row.created_at || '');
188
+ return date.toLocaleString();
185
189
  }