deploro 0.4.0 → 0.5.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/dist/index.js +416 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -379,6 +379,24 @@ async function authUsersCommand() {
|
|
|
379
379
|
}))
|
|
380
380
|
);
|
|
381
381
|
}
|
|
382
|
+
async function authBanCommand(id, opts) {
|
|
383
|
+
requireConfig();
|
|
384
|
+
await confirmOrDie(`Ban user ${id}?`, opts.yes);
|
|
385
|
+
const expiresAt = opts.days ? new Date(Date.now() + Number(opts.days) * 24 * 60 * 60 * 1e3).toISOString() : void 0;
|
|
386
|
+
await apiRequest(`/api/auth/users/${encodeURIComponent(id)}/ban`, {
|
|
387
|
+
method: "PATCH",
|
|
388
|
+
body: JSON.stringify({ banned: true, reason: opts.reason, expiresAt })
|
|
389
|
+
});
|
|
390
|
+
console.log(`\u2713 User ${id} banned${opts.days ? ` for ${opts.days} day(s)` : ""}`);
|
|
391
|
+
}
|
|
392
|
+
async function authUnbanCommand(id) {
|
|
393
|
+
requireConfig();
|
|
394
|
+
await apiRequest(`/api/auth/users/${encodeURIComponent(id)}/ban`, {
|
|
395
|
+
method: "PATCH",
|
|
396
|
+
body: JSON.stringify({ banned: false })
|
|
397
|
+
});
|
|
398
|
+
console.log(`\u2713 User ${id} unbanned`);
|
|
399
|
+
}
|
|
382
400
|
async function authInviteCommand(email, opts) {
|
|
383
401
|
requireConfig();
|
|
384
402
|
const role = opts.admin ? "admin" : "member";
|
|
@@ -540,6 +558,11 @@ async function authProviderEnableCommand(provider, enabled) {
|
|
|
540
558
|
{ method: "PUT", body: JSON.stringify({ enabled }) }
|
|
541
559
|
);
|
|
542
560
|
console.log(`\u2713 ${provider} ${enabled ? "enabled" : "disabled"}`);
|
|
561
|
+
if (provider === "email_password" && enabled) {
|
|
562
|
+
console.log(
|
|
563
|
+
" Your app can now call /auth/:slug/email-password/{signup,login,request-reset,reset} directly \u2014\n see the dashboard's Auth page for the exact copyable URLs."
|
|
564
|
+
);
|
|
565
|
+
}
|
|
543
566
|
}
|
|
544
567
|
async function authProviderConfigureCommand(provider, opts) {
|
|
545
568
|
const config = requireProject();
|
|
@@ -617,12 +640,355 @@ async function authStatsCommand() {
|
|
|
617
640
|
}
|
|
618
641
|
}
|
|
619
642
|
|
|
643
|
+
// src/commands/github.ts
|
|
644
|
+
async function githubStatusCommand() {
|
|
645
|
+
requireConfig();
|
|
646
|
+
const data = await apiRequestNoProject("/api/github/status");
|
|
647
|
+
if (!data.configured) {
|
|
648
|
+
console.log("GitHub OAuth is not configured on this platform (operator hasn't set GITHUB_APP_CLIENT_ID/SECRET).");
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
console.log(data.connected ? `\u2713 Connected as ${data.login}` : "Not connected. Run: deploro github connect");
|
|
652
|
+
}
|
|
653
|
+
async function githubConnectCommand() {
|
|
654
|
+
requireConfig();
|
|
655
|
+
const data = await apiRequestNoProject("/api/github/oauth/start");
|
|
656
|
+
console.log("Open this URL in a browser to connect your GitHub account:\n");
|
|
657
|
+
console.log(` ${data.url}
|
|
658
|
+
`);
|
|
659
|
+
console.log("After approving, run 'deploro github status' to confirm.");
|
|
660
|
+
}
|
|
661
|
+
async function githubReposCommand() {
|
|
662
|
+
requireConfig();
|
|
663
|
+
const data = await apiRequestNoProject(
|
|
664
|
+
"/api/github/repos"
|
|
665
|
+
);
|
|
666
|
+
if (!data.configured) {
|
|
667
|
+
console.log("GitHub OAuth is not configured on this platform.");
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
if (!data.connected) {
|
|
671
|
+
console.log("Not connected. Run: deploro github connect");
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
if (data.error) {
|
|
675
|
+
console.error(data.error);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
table(
|
|
679
|
+
data.repos.map((r) => ({
|
|
680
|
+
repo: r.full_name,
|
|
681
|
+
branch: r.default_branch,
|
|
682
|
+
visibility: r.private ? "private" : "public"
|
|
683
|
+
}))
|
|
684
|
+
);
|
|
685
|
+
console.log("\nLink one to the active project with: deploro repo link <owner/repo>");
|
|
686
|
+
}
|
|
687
|
+
async function githubDisconnectCommand(opts) {
|
|
688
|
+
requireConfig();
|
|
689
|
+
await confirmOrDie(
|
|
690
|
+
"Disconnect your GitHub account? Projects with a repo already linked keep deploying \u2014 this only removes your stored OAuth token, so you won't be able to link new repos until you reconnect.",
|
|
691
|
+
opts.yes
|
|
692
|
+
);
|
|
693
|
+
await apiRequestNoProject("/api/github/disconnect", { method: "POST" });
|
|
694
|
+
console.log("\u2713 GitHub account disconnected");
|
|
695
|
+
}
|
|
696
|
+
async function repoStatusCommand() {
|
|
697
|
+
const config = requireProject();
|
|
698
|
+
const data = await apiRequest(
|
|
699
|
+
`/api/projects/${config.projectId}/github`
|
|
700
|
+
);
|
|
701
|
+
if (!data.connection) {
|
|
702
|
+
console.log("No repo linked. Run: deploro repo link <owner/repo>");
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
console.log(`Repo : ${data.connection.github_owner}/${data.connection.github_repo}`);
|
|
706
|
+
console.log(`Branch : ${data.connection.branch}`);
|
|
707
|
+
console.log(`Webhook: ${data.connection.webhook_id ? "auto-created on GitHub" : "(not created \u2014 deploys may not trigger)"}`);
|
|
708
|
+
}
|
|
709
|
+
async function repoLinkCommand(ownerRepo, opts) {
|
|
710
|
+
const config = requireProject();
|
|
711
|
+
const [owner, repo2] = ownerRepo.split("/");
|
|
712
|
+
if (!owner || !repo2) {
|
|
713
|
+
console.error('Expected "owner/repo", e.g. "acme/my-app"');
|
|
714
|
+
process.exit(1);
|
|
715
|
+
}
|
|
716
|
+
const data = await apiRequest(
|
|
717
|
+
`/api/projects/${config.projectId}/github`,
|
|
718
|
+
{ method: "POST", body: JSON.stringify({ owner, repo: repo2, branch: opts.branch }) }
|
|
719
|
+
);
|
|
720
|
+
console.log(`\u2713 Linked ${data.connection.owner}/${data.connection.repo} (branch: ${data.connection.branch})`);
|
|
721
|
+
console.log(
|
|
722
|
+
data.connection.webhook_created ? " Webhook created on GitHub \u2014 pushes will now auto-deploy." : " Webhook already existed on GitHub \u2014 reusing it."
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
async function repoUnlinkCommand(opts) {
|
|
726
|
+
const config = requireProject();
|
|
727
|
+
await confirmOrDie("Unlink this project's repo and remove its GitHub webhook?", opts.yes);
|
|
728
|
+
await apiRequest(`/api/projects/${config.projectId}/github`, { method: "DELETE" });
|
|
729
|
+
console.log("\u2713 Repo unlinked");
|
|
730
|
+
}
|
|
731
|
+
async function repoAutoDeployCommand(state) {
|
|
732
|
+
const config = requireProject();
|
|
733
|
+
const enabled = state === "on" || state === "true" || state === "enable";
|
|
734
|
+
const disabled = state === "off" || state === "false" || state === "disable";
|
|
735
|
+
if (!enabled && !disabled) {
|
|
736
|
+
console.error('Expected "on" or "off"');
|
|
737
|
+
process.exit(1);
|
|
738
|
+
}
|
|
739
|
+
await apiRequest(`/api/projects/${config.projectId}`, {
|
|
740
|
+
method: "PATCH",
|
|
741
|
+
body: JSON.stringify({ auto_deploy_enabled: enabled })
|
|
742
|
+
});
|
|
743
|
+
console.log(`\u2713 Automatic deployment ${enabled ? "enabled" : "disabled"}`);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// src/commands/domains.ts
|
|
747
|
+
async function domainsListCommand() {
|
|
748
|
+
const config = requireProject();
|
|
749
|
+
const data = await apiRequest(`/api/projects/${config.projectId}/domains`);
|
|
750
|
+
table(
|
|
751
|
+
data.domains.map((d) => ({
|
|
752
|
+
hostname: d.hostname,
|
|
753
|
+
status: d.status,
|
|
754
|
+
ssl: d.ssl_status,
|
|
755
|
+
verified: d.verified_at ? "yes" : "no"
|
|
756
|
+
}))
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
async function domainsAddCommand(hostname) {
|
|
760
|
+
const config = requireProject();
|
|
761
|
+
const data = await apiRequest(`/api/projects/${config.projectId}/domains`, {
|
|
762
|
+
method: "POST",
|
|
763
|
+
body: JSON.stringify({ hostname })
|
|
764
|
+
});
|
|
765
|
+
console.log(`\u2713 Domain added: ${data.domain.hostname} (id: ${data.domain.id})
|
|
766
|
+
`);
|
|
767
|
+
console.log("Add these DNS records at your registrar/DNS provider, then run 'deploro domains verify " + data.domain.id + "':\n");
|
|
768
|
+
const { cname, txt } = data.dns_instructions;
|
|
769
|
+
console.log(` ${cname.type} ${cname.name} \u2192 ${cname.value}`);
|
|
770
|
+
console.log(` ${txt.type} ${txt.name} \u2192 ${txt.value}`);
|
|
771
|
+
}
|
|
772
|
+
async function domainsVerifyCommand(idOrHostname) {
|
|
773
|
+
const config = requireProject();
|
|
774
|
+
const id = await resolveDomainId(config.projectId, idOrHostname);
|
|
775
|
+
const data = await apiRequest(
|
|
776
|
+
`/api/projects/${config.projectId}/domains/${id}/verify`,
|
|
777
|
+
{ method: "POST" }
|
|
778
|
+
);
|
|
779
|
+
console.log(data.verified ? `\u2713 ${data.message}` : `\u2717 ${data.message}`);
|
|
780
|
+
}
|
|
781
|
+
async function domainsStatusCommand(idOrHostname) {
|
|
782
|
+
const config = requireProject();
|
|
783
|
+
const id = await resolveDomainId(config.projectId, idOrHostname);
|
|
784
|
+
const data = await apiRequest(
|
|
785
|
+
`/api/projects/${config.projectId}/domains/${id}/status`
|
|
786
|
+
);
|
|
787
|
+
console.log(`Status: ${data.status}`);
|
|
788
|
+
console.log(`SSL : ${data.ssl_status}`);
|
|
789
|
+
}
|
|
790
|
+
async function domainsRemoveCommand(idOrHostname, opts) {
|
|
791
|
+
const config = requireProject();
|
|
792
|
+
const id = await resolveDomainId(config.projectId, idOrHostname);
|
|
793
|
+
await confirmOrDie(`Remove domain "${idOrHostname}"? Traffic to it will stop resolving.`, opts.yes);
|
|
794
|
+
await apiRequest(`/api/projects/${config.projectId}/domains/${id}`, { method: "DELETE" });
|
|
795
|
+
console.log("\u2713 Domain removed");
|
|
796
|
+
}
|
|
797
|
+
async function resolveDomainId(projectId, idOrHostname) {
|
|
798
|
+
const data = await apiRequest(`/api/projects/${projectId}/domains`);
|
|
799
|
+
const match = data.domains.find((d) => d.id === idOrHostname || d.hostname === idOrHostname.toLowerCase());
|
|
800
|
+
if (!match) {
|
|
801
|
+
console.error(`No domain matches "${idOrHostname}". Run: deploro domains list`);
|
|
802
|
+
process.exit(1);
|
|
803
|
+
}
|
|
804
|
+
return match.id;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// src/commands/storage.ts
|
|
808
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
809
|
+
import { basename } from "path";
|
|
810
|
+
function authHeaders(config) {
|
|
811
|
+
return { Authorization: `Bearer ${config.accessToken}` };
|
|
812
|
+
}
|
|
813
|
+
async function throwOnError(res) {
|
|
814
|
+
if (res.ok) return;
|
|
815
|
+
const body = await res.json().catch(() => ({ error: res.statusText }));
|
|
816
|
+
throw new Error(`${res.status} ${body.error ?? res.statusText}`);
|
|
817
|
+
}
|
|
818
|
+
async function storageListCommand() {
|
|
819
|
+
const config = requireProject();
|
|
820
|
+
const res = await fetch(`${config.baseUrl}/api/projects/${config.projectId}/storage`, {
|
|
821
|
+
headers: authHeaders(config)
|
|
822
|
+
});
|
|
823
|
+
await throwOnError(res);
|
|
824
|
+
const data = await res.json();
|
|
825
|
+
table(
|
|
826
|
+
data.objects.map((o) => ({
|
|
827
|
+
key: o.key,
|
|
828
|
+
size: `${(o.size / 1024).toFixed(1)} KB`,
|
|
829
|
+
type: o.contentType,
|
|
830
|
+
uploaded: new Date(o.uploaded).toLocaleString()
|
|
831
|
+
}))
|
|
832
|
+
);
|
|
833
|
+
if (data.truncated) console.log("\n(more objects exist \u2014 pagination isn't wired up here yet, use the dashboard for the full list)");
|
|
834
|
+
}
|
|
835
|
+
async function storageUploadCommand(filePath, opts) {
|
|
836
|
+
const config = requireProject();
|
|
837
|
+
const bytes = readFileSync2(filePath);
|
|
838
|
+
const filename = opts.key ?? basename(filePath);
|
|
839
|
+
const form = new FormData();
|
|
840
|
+
form.append("file", new Blob([bytes]), filename);
|
|
841
|
+
const res = await fetch(`${config.baseUrl}/api/projects/${config.projectId}/storage/upload`, {
|
|
842
|
+
method: "POST",
|
|
843
|
+
headers: authHeaders(config),
|
|
844
|
+
body: form
|
|
845
|
+
});
|
|
846
|
+
await throwOnError(res);
|
|
847
|
+
const data = await res.json();
|
|
848
|
+
console.log(`\u2713 Uploaded as ${data.key} (${data.size} bytes, ${data.contentType})`);
|
|
849
|
+
}
|
|
850
|
+
async function storageDownloadCommand(key, opts) {
|
|
851
|
+
const config = requireProject();
|
|
852
|
+
const res = await fetch(
|
|
853
|
+
`${config.baseUrl}/api/projects/${config.projectId}/storage/download?key=${encodeURIComponent(key)}`,
|
|
854
|
+
{ headers: authHeaders(config) }
|
|
855
|
+
);
|
|
856
|
+
await throwOnError(res);
|
|
857
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
858
|
+
const outPath = opts.out ?? basename(key);
|
|
859
|
+
writeFileSync2(outPath, bytes);
|
|
860
|
+
console.log(`\u2713 Saved to ${outPath} (${bytes.length} bytes)`);
|
|
861
|
+
}
|
|
862
|
+
async function storageDeleteCommand(key, opts) {
|
|
863
|
+
const config = requireProject();
|
|
864
|
+
await confirmOrDie(`Delete "${key}" from storage?`, opts.yes);
|
|
865
|
+
const res = await fetch(
|
|
866
|
+
`${config.baseUrl}/api/projects/${config.projectId}/storage?key=${encodeURIComponent(key)}`,
|
|
867
|
+
{ method: "DELETE", headers: authHeaders(config) }
|
|
868
|
+
);
|
|
869
|
+
await throwOnError(res);
|
|
870
|
+
console.log(`\u2713 Deleted ${key}`);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// src/commands/webhooks.ts
|
|
874
|
+
var KNOWN_EVENTS = [
|
|
875
|
+
"db.row.insert",
|
|
876
|
+
"db.row.update",
|
|
877
|
+
"db.row.delete",
|
|
878
|
+
"deployment.started",
|
|
879
|
+
"deployment.success",
|
|
880
|
+
"deployment.failed",
|
|
881
|
+
"test"
|
|
882
|
+
];
|
|
883
|
+
async function webhooksListCommand() {
|
|
884
|
+
const config = requireProject();
|
|
885
|
+
const data = await apiRequest(`/api/projects/${config.projectId}/webhooks`);
|
|
886
|
+
table(
|
|
887
|
+
data.webhooks.map((w) => ({
|
|
888
|
+
id: w.id,
|
|
889
|
+
url: w.url,
|
|
890
|
+
events: w.events.join(","),
|
|
891
|
+
enabled: w.enabled,
|
|
892
|
+
deliveries: w.delivery_count,
|
|
893
|
+
lastStatus: w.last_status_code ?? "-"
|
|
894
|
+
}))
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
async function webhooksCreateCommand(url, opts) {
|
|
898
|
+
const config = requireProject();
|
|
899
|
+
const events = opts.events ? opts.events.split(",").map((e) => e.trim()).filter(Boolean) : ["test"];
|
|
900
|
+
const data = await apiRequest(`/api/projects/${config.projectId}/webhooks`, {
|
|
901
|
+
method: "POST",
|
|
902
|
+
body: JSON.stringify({ url, events, secret: opts.secret })
|
|
903
|
+
});
|
|
904
|
+
console.log(`\u2713 Webhook created: ${data.webhook.id}`);
|
|
905
|
+
console.log(` Events: ${data.webhook.events.join(", ")}`);
|
|
906
|
+
if (!opts.events) {
|
|
907
|
+
console.log(` (no --events passed \u2014 defaulted to "test". Known events: ${KNOWN_EVENTS.join(", ")})`);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
async function webhooksUpdateCommand(id, opts) {
|
|
911
|
+
const config = requireProject();
|
|
912
|
+
const body = {};
|
|
913
|
+
if (opts.url) body.url = opts.url;
|
|
914
|
+
if (opts.events) body.events = opts.events.split(",").map((e) => e.trim()).filter(Boolean);
|
|
915
|
+
if (opts.secret) body.secret = opts.secret;
|
|
916
|
+
if (opts.enable) body.enabled = true;
|
|
917
|
+
if (opts.disable) body.enabled = false;
|
|
918
|
+
if (Object.keys(body).length === 0) {
|
|
919
|
+
console.error("Nothing to update \u2014 pass --url, --events, --secret, --enable, or --disable");
|
|
920
|
+
process.exit(1);
|
|
921
|
+
}
|
|
922
|
+
await apiRequest(`/api/projects/${config.projectId}/webhooks/${id}`, {
|
|
923
|
+
method: "PATCH",
|
|
924
|
+
body: JSON.stringify(body)
|
|
925
|
+
});
|
|
926
|
+
console.log(`\u2713 Webhook ${id} updated`);
|
|
927
|
+
}
|
|
928
|
+
async function webhooksDeleteCommand(id, opts) {
|
|
929
|
+
const config = requireProject();
|
|
930
|
+
await confirmOrDie(`Delete webhook ${id}?`, opts.yes);
|
|
931
|
+
await apiRequest(`/api/projects/${config.projectId}/webhooks/${id}`, { method: "DELETE" });
|
|
932
|
+
console.log(`\u2713 Webhook ${id} deleted`);
|
|
933
|
+
}
|
|
934
|
+
async function webhooksTestCommand(id) {
|
|
935
|
+
const config = requireProject();
|
|
936
|
+
await apiRequest(`/api/projects/${config.projectId}/webhooks/${id}/test`, { method: "POST" });
|
|
937
|
+
console.log("\u2713 Test payload dispatched \u2014 check 'deploro webhooks deliveries' for the result.");
|
|
938
|
+
}
|
|
939
|
+
async function webhooksDeliveriesCommand(id) {
|
|
940
|
+
const config = requireProject();
|
|
941
|
+
const data = await apiRequest(
|
|
942
|
+
`/api/projects/${config.projectId}/webhooks/${id}/deliveries`
|
|
943
|
+
);
|
|
944
|
+
table(
|
|
945
|
+
data.deliveries.map((d) => ({
|
|
946
|
+
event: d.event,
|
|
947
|
+
status: d.status_code ?? "-",
|
|
948
|
+
ms: d.duration_ms ?? "-",
|
|
949
|
+
error: d.error ?? "",
|
|
950
|
+
at: new Date(d.delivered_at).toLocaleString()
|
|
951
|
+
}))
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// src/commands/team.ts
|
|
956
|
+
async function teamListCommand() {
|
|
957
|
+
const config = requireProject();
|
|
958
|
+
const data = await apiRequest(`/api/projects/${config.projectId}/members`);
|
|
959
|
+
table(
|
|
960
|
+
data.members.map((m) => ({
|
|
961
|
+
id: m.id,
|
|
962
|
+
name: m.name,
|
|
963
|
+
email: m.email,
|
|
964
|
+
role: m.role,
|
|
965
|
+
joined: new Date(m.joined_at).toLocaleDateString()
|
|
966
|
+
}))
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
async function teamInviteCommand(email, opts) {
|
|
970
|
+
const config = requireProject();
|
|
971
|
+
const role = opts.admin ? "admin" : "member";
|
|
972
|
+
await apiRequest(`/api/projects/${config.projectId}/members`, {
|
|
973
|
+
method: "POST",
|
|
974
|
+
body: JSON.stringify({ email, role })
|
|
975
|
+
});
|
|
976
|
+
console.log(`\u2713 Added ${email} to the project as ${role}`);
|
|
977
|
+
console.log(" (they must already have a Deploro account \u2014 use 'deploro auth invite' first if not)");
|
|
978
|
+
}
|
|
979
|
+
async function teamRemoveCommand(memberId, opts) {
|
|
980
|
+
const config = requireProject();
|
|
981
|
+
await confirmOrDie(`Remove member ${memberId} from this project?`, opts.yes);
|
|
982
|
+
await apiRequest(`/api/projects/${config.projectId}/members/${memberId}`, { method: "DELETE" });
|
|
983
|
+
console.log(`\u2713 Member ${memberId} removed`);
|
|
984
|
+
}
|
|
985
|
+
|
|
620
986
|
// src/commands/migrate.ts
|
|
621
|
-
import { readFileSync as
|
|
987
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
622
988
|
function readSqlOpt(inline, file, label) {
|
|
623
989
|
if (file) {
|
|
624
990
|
try {
|
|
625
|
-
return
|
|
991
|
+
return readFileSync3(file, "utf-8");
|
|
626
992
|
} catch (err) {
|
|
627
993
|
console.error(`Could not read --${label}-file "${file}": ${err instanceof Error ? err.message : String(err)}`);
|
|
628
994
|
process.exit(1);
|
|
@@ -717,9 +1083,7 @@ async function migrateDeleteCommand(idOrName, opts) {
|
|
|
717
1083
|
// src/commands/hosting.ts
|
|
718
1084
|
async function hostingStatusCommand() {
|
|
719
1085
|
requireConfig();
|
|
720
|
-
const data = await apiRequest(
|
|
721
|
-
"/api/hosting/status"
|
|
722
|
-
);
|
|
1086
|
+
const data = await apiRequest("/api/hosting/status");
|
|
723
1087
|
console.log("\n\u2500\u2500 WORKER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
724
1088
|
if (data.worker) {
|
|
725
1089
|
console.log(` Script : ${data.worker.name}`);
|
|
@@ -749,6 +1113,10 @@ async function hostingStatusCommand() {
|
|
|
749
1113
|
} else {
|
|
750
1114
|
console.log(" (no VPS info)");
|
|
751
1115
|
}
|
|
1116
|
+
if (data.untrackedContainers && data.untrackedContainers.length > 0) {
|
|
1117
|
+
console.log("\n \u26A0 Untracked containers (didn't come from a Deployer-issued deploy):");
|
|
1118
|
+
for (const name of data.untrackedContainers) console.log(` - ${name}`);
|
|
1119
|
+
}
|
|
752
1120
|
console.log("");
|
|
753
1121
|
}
|
|
754
1122
|
async function hostingSetEnvCommand(key, value) {
|
|
@@ -894,7 +1262,7 @@ async function deployCommand() {
|
|
|
894
1262
|
async function deployStatusCommand() {
|
|
895
1263
|
const config = requireProject();
|
|
896
1264
|
const data = await apiRequest(
|
|
897
|
-
`/api/projects/${config.projectId}/deployments`
|
|
1265
|
+
`/api/projects/${config.projectId}/deployments/all`
|
|
898
1266
|
);
|
|
899
1267
|
const latest = data.deployments[0];
|
|
900
1268
|
if (!latest) {
|
|
@@ -904,6 +1272,7 @@ async function deployStatusCommand() {
|
|
|
904
1272
|
console.log(`
|
|
905
1273
|
\u2500\u2500 LATEST DEPLOYMENT \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
906
1274
|
console.log(` ID : ${latest.id}`);
|
|
1275
|
+
console.log(` Target : ${latest.target ?? "worker"}`);
|
|
907
1276
|
console.log(` Status : ${latest.status}`);
|
|
908
1277
|
console.log(` Branch : ${latest.git_branch ?? "(none)"}`);
|
|
909
1278
|
console.log(` Started : ${new Date(latest.created_at).toLocaleString()}`);
|
|
@@ -915,7 +1284,7 @@ async function deployLogsCommand(deploymentId) {
|
|
|
915
1284
|
let id = deploymentId;
|
|
916
1285
|
if (!id) {
|
|
917
1286
|
const list = await apiRequest(
|
|
918
|
-
`/api/projects/${config.projectId}/deployments`
|
|
1287
|
+
`/api/projects/${config.projectId}/deployments/all`
|
|
919
1288
|
);
|
|
920
1289
|
id = list.deployments[0]?.id;
|
|
921
1290
|
if (!id) {
|
|
@@ -929,6 +1298,7 @@ async function deployLogsCommand(deploymentId) {
|
|
|
929
1298
|
const d = data.deployment;
|
|
930
1299
|
console.log(`
|
|
931
1300
|
\u2500\u2500 DEPLOYMENT ${d.id} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
1301
|
+
console.log(` Target : ${d.target ?? "worker"}`);
|
|
932
1302
|
console.log(` Status : ${d.status}`);
|
|
933
1303
|
console.log(
|
|
934
1304
|
` Commit : ${d.git_branch ?? "(none)"}${d.git_sha ? ` @ ${String(d.git_sha).slice(0, 7)}` : ""}`
|
|
@@ -1136,6 +1506,10 @@ async function vpsStatusCommand() {
|
|
|
1136
1506
|
console.log(data.postgres ? ` ${data.postgres.running ? "\u25CF" : "\u25CB"} ${data.postgres.status}` : " (not provisioned)");
|
|
1137
1507
|
console.log("\n\u2500\u2500 RAW REDIS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1138
1508
|
console.log(data.redis ? ` ${data.redis.running ? "\u25CF" : "\u25CB"} ${data.redis.status}` : " (not provisioned)");
|
|
1509
|
+
console.log("\n\u2500\u2500 WEB \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1510
|
+
console.log(
|
|
1511
|
+
data.web_url ? ` ${data.web_url} (nginx proxy on the VPS, TLS via Let's Encrypt)` : " (none detected \u2014 compose must publish an HTTP port, then redeploy)"
|
|
1512
|
+
);
|
|
1139
1513
|
console.log("");
|
|
1140
1514
|
}
|
|
1141
1515
|
async function vpsLogsCommand(service, opts) {
|
|
@@ -1154,7 +1528,7 @@ program.name("deploro").description("Deploro developer platform CLI").version(pk
|
|
|
1154
1528
|
program.command("login").description("Log in to Deploro").option("--url <url>", "Worker base URL").option("--token <token>", "Personal access token from the Deploro dashboard").action(loginCommand);
|
|
1155
1529
|
program.command("logout").description("Log out and clear local credentials").action(logoutCommand);
|
|
1156
1530
|
program.command("projects").description("List projects").action(projectsListCommand);
|
|
1157
|
-
program.command("create <name>").description("Create a new project (and set it active)").option("--slug <slug>", "Project slug (derived from name if omitted)").option("--repo <url>", "
|
|
1531
|
+
program.command("create <name>").description("Create a new project (and set it active)").option("--slug <slug>", "Project slug (derived from name if omitted)").option("--repo <url>", "Public repo URL to link for deployments (no embedded tokens/credentials \u2014 for a private repo or auto-deploy on push, use 'deploro github connect' + 'deploro repo link' instead)").option("--branch <branch>", "Git branch to deploy from").action(projectsCreateCommand);
|
|
1158
1532
|
program.command("use <project>").description("Set the active project (accepts id, slug, or name)").action(useProjectCommand);
|
|
1159
1533
|
program.command("delete <project>").description("Delete a project and its database (accepts id, slug, or name)").option("-y, --yes", "Skip confirmation prompt").action(projectsDeleteCommand);
|
|
1160
1534
|
var db = program.command("db").description("Database commands");
|
|
@@ -1178,6 +1552,8 @@ var auth = program.command("auth").description("Auth management commands");
|
|
|
1178
1552
|
auth.command("whoami").description("Show the current identity, and \u2014 for a token login \u2014 exactly what it's scoped to").action(authWhoamiCommand);
|
|
1179
1553
|
auth.command("users").description("List platform users").action(authUsersCommand);
|
|
1180
1554
|
auth.command("invite <email>").description("Invite a user by email").option("--admin", "Grant admin role").action(authInviteCommand);
|
|
1555
|
+
auth.command("ban <id>").description("Ban a platform user (blocks sign-in immediately, no session revocation needed)").option("--reason <text>", "Ban reason").option("--days <n>", "Expire the ban after n days (omit for indefinite)").option("-y, --yes", "Skip confirmation prompt").action(authBanCommand);
|
|
1556
|
+
auth.command("unban <id>").description("Lift a ban").action(authUnbanCommand);
|
|
1181
1557
|
auth.command("site-url [url]").description("Get or set the active project's Auth Site URL (where end-user confirmation-email links redirect back to)").option("--clear", "Clear the Site URL \u2014 confirmation links fall back to the project's own deployment URL").action(authSiteUrlCommand);
|
|
1182
1558
|
auth.command("stats").description("Show Auth-as-a-Service stats for the active project's end users").action(authStatsCommand);
|
|
1183
1559
|
var authProviders = auth.command("providers").description("Manage the active project's end-user sign-in providers");
|
|
@@ -1222,6 +1598,38 @@ vps.command("deployments").description("List VPS deploy history").action(vpsDepl
|
|
|
1222
1598
|
vps.command("deploy-status [deployment-id]").description("Show a VPS deployment's status and logs (defaults to latest)").action(vpsDeployStatusCommand);
|
|
1223
1599
|
vps.command("status").description("Show running compute services + raw Postgres/Redis container status").action(vpsStatusCommand);
|
|
1224
1600
|
vps.command("logs [service]").description("Tail logs for a compute service (or 'postgres'/'redis')").option("--tail <n>", "Number of lines", "200").action(vpsLogsCommand);
|
|
1601
|
+
var github = program.command("github").description("GitHub account connection (OAuth)");
|
|
1602
|
+
github.command("status").description("Show whether your GitHub account is connected").action(githubStatusCommand);
|
|
1603
|
+
github.command("connect").description("Print the URL to open in a browser to connect GitHub").action(githubConnectCommand);
|
|
1604
|
+
github.command("repos").description("List repos your connected GitHub account can access").action(githubReposCommand);
|
|
1605
|
+
github.command("disconnect").description("Remove your stored GitHub OAuth token").option("-y, --yes", "Skip confirmation prompt").action(githubDisconnectCommand);
|
|
1606
|
+
var repo = program.command("repo").description("Active project's connected repo (auto-deploy from GitHub)");
|
|
1607
|
+
repo.command("status").description("Show the active project's linked repo").action(repoStatusCommand);
|
|
1608
|
+
repo.command("link <owner/repo>").description("Link a repo (requires 'deploro github connect' first)").option("--branch <branch>", "Branch to deploy from (default: main)").action(repoLinkCommand);
|
|
1609
|
+
repo.command("unlink").description("Unlink the repo and remove its GitHub webhook").option("-y, --yes", "Skip confirmation prompt").action(repoUnlinkCommand);
|
|
1610
|
+
repo.command("auto-deploy <on|off>").description("Toggle automatic deployment on push").action(repoAutoDeployCommand);
|
|
1611
|
+
var domains = program.command("domains").description("Custom domains for the active project");
|
|
1612
|
+
domains.command("list").description("List custom domains").action(domainsListCommand);
|
|
1613
|
+
domains.command("add <hostname>").description("Add a domain \u2014 prints the CNAME + TXT records to create").action(domainsAddCommand);
|
|
1614
|
+
domains.command("verify <id-or-hostname>").description("Verify DNS ownership and register SSL").action(domainsVerifyCommand);
|
|
1615
|
+
domains.command("status <id-or-hostname>").description("Poll domain verification/SSL status").action(domainsStatusCommand);
|
|
1616
|
+
domains.command("remove <id-or-hostname>").description("Remove a domain").option("-y, --yes", "Skip confirmation prompt").action(domainsRemoveCommand);
|
|
1617
|
+
var storage = program.command("storage").description("R2 file storage for the active project");
|
|
1618
|
+
storage.command("list").description("List stored files").action(storageListCommand);
|
|
1619
|
+
storage.command("upload <file>").description("Upload a local file").option("--key <name>", "Stored file name (defaults to the local file's name)").action(storageUploadCommand);
|
|
1620
|
+
storage.command("download <key>").description("Download a stored file").option("--out <path>", "Local path to save to (defaults to the file's name)").action(storageDownloadCommand);
|
|
1621
|
+
storage.command("delete <key>").description("Delete a stored file").option("-y, --yes", "Skip confirmation prompt").action(storageDeleteCommand);
|
|
1622
|
+
var webhooks = program.command("webhooks").description("Outbound webhooks for the active project");
|
|
1623
|
+
webhooks.command("list").description("List webhooks").action(webhooksListCommand);
|
|
1624
|
+
webhooks.command("create <url>").description("Create a webhook").option("--events <list>", "Comma-separated event names (default: test)").option("--secret <secret>", "HMAC signing secret").action(webhooksCreateCommand);
|
|
1625
|
+
webhooks.command("update <id>").description("Update a webhook").option("--url <url>").option("--events <list>", "Comma-separated event names").option("--secret <secret>").option("--enable").option("--disable").action(webhooksUpdateCommand);
|
|
1626
|
+
webhooks.command("delete <id>").description("Delete a webhook").option("-y, --yes", "Skip confirmation prompt").action(webhooksDeleteCommand);
|
|
1627
|
+
webhooks.command("test <id>").description("Fire a test payload").action(webhooksTestCommand);
|
|
1628
|
+
webhooks.command("deliveries <id>").description("Show the last 30 delivery attempts").action(webhooksDeliveriesCommand);
|
|
1629
|
+
var team = program.command("team").description("Manage the active project's team members");
|
|
1630
|
+
team.command("list").description("List project members").action(teamListCommand);
|
|
1631
|
+
team.command("invite <email>").description("Add an existing Deploro user to this project").option("--admin", "Grant project admin role").action(teamInviteCommand);
|
|
1632
|
+
team.command("remove <member-id>").description("Remove a member from this project").option("-y, --yes", "Skip confirmation prompt").action(teamRemoveCommand);
|
|
1225
1633
|
program.parseAsync().catch((err) => {
|
|
1226
1634
|
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1227
1635
|
process.exit(1);
|
package/package.json
CHANGED