@flowkode/cli 1.0.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/README.md +299 -0
- package/dist/api-client.d.ts +11 -0
- package/dist/api-client.js +65 -0
- package/dist/commands/blog.d.ts +3 -0
- package/dist/commands/blog.js +96 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +37 -0
- package/dist/commands/deployments.d.ts +3 -0
- package/dist/commands/deployments.js +51 -0
- package/dist/commands/domains.d.ts +3 -0
- package/dist/commands/domains.js +189 -0
- package/dist/commands/folders.d.ts +3 -0
- package/dist/commands/folders.js +103 -0
- package/dist/commands/jobs.d.ts +3 -0
- package/dist/commands/jobs.js +45 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.js +133 -0
- package/dist/commands/projects.d.ts +3 -0
- package/dist/commands/projects.js +146 -0
- package/dist/commands/utilities.d.ts +3 -0
- package/dist/commands/utilities.js +129 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +37 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +76 -0
- package/dist/output.d.ts +13 -0
- package/dist/output.js +68 -0
- package/package.json +27 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { printTable, printDetail, printSuccess, printError, printJson, } from "../output.js";
|
|
3
|
+
export function domainsCommand(api) {
|
|
4
|
+
const cmd = new Command("domains").description("Gerer les domaines");
|
|
5
|
+
cmd
|
|
6
|
+
.command("list")
|
|
7
|
+
.description("Lister les domaines")
|
|
8
|
+
.option("--registrar <registrar>", "Filtrer (internetbs, ovh, dynadot)")
|
|
9
|
+
.option("--provider <provider>", "Filtrer par provider de deploiement")
|
|
10
|
+
.option("--project <id>", "Filtrer par projet")
|
|
11
|
+
.option("--folder <id>", "Filtrer par dossier")
|
|
12
|
+
.action(async (opts) => {
|
|
13
|
+
try {
|
|
14
|
+
const res = (await api.get("/domains", {
|
|
15
|
+
registrar: opts.registrar,
|
|
16
|
+
deployment_provider: opts.provider,
|
|
17
|
+
project_id: opts.project,
|
|
18
|
+
folder_id: opts.folder,
|
|
19
|
+
}));
|
|
20
|
+
printTable(res.data, [
|
|
21
|
+
{ key: "id", label: "ID", width: 36 },
|
|
22
|
+
{ key: "name", label: "Domaine", width: 30 },
|
|
23
|
+
{ key: "registrar", label: "Registrar", width: 12 },
|
|
24
|
+
{ key: "status", label: "Statut", width: 10 },
|
|
25
|
+
{ key: "availability", label: "Dispo", width: 10 },
|
|
26
|
+
{ key: "project_name", label: "Projet", width: 20 },
|
|
27
|
+
]);
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
printError(e);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
cmd
|
|
34
|
+
.command("get <id>")
|
|
35
|
+
.description("Detail d'un domaine")
|
|
36
|
+
.action(async (id) => {
|
|
37
|
+
try {
|
|
38
|
+
const res = (await api.get(`/domains/${id}`));
|
|
39
|
+
printDetail(res.data, [
|
|
40
|
+
{ key: "id", label: "ID" },
|
|
41
|
+
{ key: "name", label: "Domaine" },
|
|
42
|
+
{ key: "registrar", label: "Registrar" },
|
|
43
|
+
{ key: "status", label: "Statut" },
|
|
44
|
+
{ key: "availability", label: "Disponibilite" },
|
|
45
|
+
{ key: "dns_status", label: "DNS" },
|
|
46
|
+
{ key: "project_name", label: "Projet" },
|
|
47
|
+
{ key: "expires_at", label: "Expiration" },
|
|
48
|
+
]);
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
printError(e);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
cmd
|
|
55
|
+
.command("check <domain>")
|
|
56
|
+
.description("Verifier la disponibilite d'un domaine")
|
|
57
|
+
.action(async (domain) => {
|
|
58
|
+
try {
|
|
59
|
+
const res = (await api.post("/domains/check", {
|
|
60
|
+
domain,
|
|
61
|
+
}));
|
|
62
|
+
printTable(res.data, [
|
|
63
|
+
{ key: "registrar", label: "Registrar", width: 12 },
|
|
64
|
+
{ key: "available", label: "Disponible", width: 12 },
|
|
65
|
+
{ key: "price", label: "Prix", width: 10 },
|
|
66
|
+
{ key: "currency", label: "Devise", width: 8 },
|
|
67
|
+
]);
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
printError(e);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
cmd
|
|
74
|
+
.command("purchase <domain>")
|
|
75
|
+
.description("Acheter un domaine")
|
|
76
|
+
.requiredOption("-r, --registrar <registrar>", "Registrar (internetbs, ovh, dynadot)")
|
|
77
|
+
.option("-y, --years <years>", "Duree en annees", "1")
|
|
78
|
+
.action(async (domain, opts) => {
|
|
79
|
+
try {
|
|
80
|
+
await api.post("/domains/purchase", {
|
|
81
|
+
domain,
|
|
82
|
+
registrar: opts.registrar,
|
|
83
|
+
years: parseInt(opts.years),
|
|
84
|
+
});
|
|
85
|
+
printSuccess(`Domaine ${domain} achete via ${opts.registrar}.`);
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
printError(e);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
cmd
|
|
92
|
+
.command("sync")
|
|
93
|
+
.description("Synchroniser les domaines depuis les registrars")
|
|
94
|
+
.action(async () => {
|
|
95
|
+
try {
|
|
96
|
+
await api.post("/domains/sync");
|
|
97
|
+
printSuccess("Domaines synchronises.");
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
printError(e);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
cmd
|
|
104
|
+
.command("associate <domainId>")
|
|
105
|
+
.description("Associer un domaine a un projet")
|
|
106
|
+
.requiredOption("--project <id>", "ID du projet")
|
|
107
|
+
.action(async (domainId, opts) => {
|
|
108
|
+
try {
|
|
109
|
+
const res = await api.post(`/domains/${domainId}/associate`, {
|
|
110
|
+
project_id: opts.project,
|
|
111
|
+
});
|
|
112
|
+
printJson(res);
|
|
113
|
+
printSuccess("Domaine associe.");
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
printError(e);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
cmd
|
|
120
|
+
.command("dissociate <domainId>")
|
|
121
|
+
.description("Dissocier un domaine de son projet")
|
|
122
|
+
.action(async (domainId) => {
|
|
123
|
+
try {
|
|
124
|
+
await api.delete(`/domains/${domainId}/associate`);
|
|
125
|
+
printSuccess("Domaine dissocie.");
|
|
126
|
+
}
|
|
127
|
+
catch (e) {
|
|
128
|
+
printError(e);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
cmd
|
|
132
|
+
.command("dns <domainId>")
|
|
133
|
+
.description("Configurer les DNS du domaine")
|
|
134
|
+
.action(async (domainId) => {
|
|
135
|
+
try {
|
|
136
|
+
const res = await api.post(`/domains/${domainId}/configure-dns`);
|
|
137
|
+
printJson(res);
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
printError(e);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
cmd
|
|
144
|
+
.command("propagation <domainId>")
|
|
145
|
+
.description("Verifier la propagation DNS")
|
|
146
|
+
.action(async (domainId) => {
|
|
147
|
+
try {
|
|
148
|
+
const res = await api.post(`/domains/${domainId}/check-propagation`);
|
|
149
|
+
printJson(res);
|
|
150
|
+
}
|
|
151
|
+
catch (e) {
|
|
152
|
+
printError(e);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
cmd
|
|
156
|
+
.command("delete <id>")
|
|
157
|
+
.description("Supprimer un domaine")
|
|
158
|
+
.action(async (id) => {
|
|
159
|
+
try {
|
|
160
|
+
await api.delete(`/domains/${id}`);
|
|
161
|
+
printSuccess("Domaine supprime.");
|
|
162
|
+
}
|
|
163
|
+
catch (e) {
|
|
164
|
+
printError(e);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
cmd
|
|
168
|
+
.command("update <id>")
|
|
169
|
+
.description("Modifier un domaine")
|
|
170
|
+
.option("--folder <id>", "ID du dossier")
|
|
171
|
+
.action(async (id, opts) => {
|
|
172
|
+
try {
|
|
173
|
+
const body = {};
|
|
174
|
+
if (opts.folder !== undefined)
|
|
175
|
+
body.folder_id = opts.folder || null;
|
|
176
|
+
const res = (await api.patch(`/domains/${id}`, body));
|
|
177
|
+
printDetail(res.data, [
|
|
178
|
+
{ key: "id", label: "ID" },
|
|
179
|
+
{ key: "name", label: "Domaine" },
|
|
180
|
+
{ key: "folder_id", label: "Dossier" },
|
|
181
|
+
]);
|
|
182
|
+
printSuccess("Domaine mis a jour.");
|
|
183
|
+
}
|
|
184
|
+
catch (e) {
|
|
185
|
+
printError(e);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
return cmd;
|
|
189
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { printTable, printDetail, printSuccess, printError, } from "../output.js";
|
|
3
|
+
export function foldersCommand(api) {
|
|
4
|
+
const cmd = new Command("folders").description("Gerer les dossiers");
|
|
5
|
+
cmd
|
|
6
|
+
.command("list")
|
|
7
|
+
.description("Lister les dossiers")
|
|
8
|
+
.action(async () => {
|
|
9
|
+
try {
|
|
10
|
+
const res = (await api.get("/folders"));
|
|
11
|
+
printTable(res.data, [
|
|
12
|
+
{ key: "id", label: "ID", width: 36 },
|
|
13
|
+
{ key: "name", label: "Nom", width: 25 },
|
|
14
|
+
{ key: "color", label: "Couleur", width: 10 },
|
|
15
|
+
{ key: "position", label: "Pos", width: 5 },
|
|
16
|
+
]);
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
printError(e);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
cmd
|
|
23
|
+
.command("get <id>")
|
|
24
|
+
.description("Detail d'un dossier")
|
|
25
|
+
.action(async (id) => {
|
|
26
|
+
try {
|
|
27
|
+
const res = (await api.get(`/folders/${id}`));
|
|
28
|
+
printDetail(res.data, [
|
|
29
|
+
{ key: "id", label: "ID" },
|
|
30
|
+
{ key: "name", label: "Nom" },
|
|
31
|
+
{ key: "color", label: "Couleur" },
|
|
32
|
+
{ key: "position", label: "Position" },
|
|
33
|
+
{ key: "created_at", label: "Cree le" },
|
|
34
|
+
]);
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
printError(e);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
cmd
|
|
41
|
+
.command("create")
|
|
42
|
+
.description("Creer un dossier")
|
|
43
|
+
.requiredOption("-n, --name <name>", "Nom du dossier")
|
|
44
|
+
.option("-c, --color <color>", "Couleur (gray, red, orange, amber, green, blue, violet, pink)", "gray")
|
|
45
|
+
.action(async (opts) => {
|
|
46
|
+
try {
|
|
47
|
+
const res = (await api.post("/folders", {
|
|
48
|
+
name: opts.name,
|
|
49
|
+
color: opts.color,
|
|
50
|
+
}));
|
|
51
|
+
printDetail(res.data, [
|
|
52
|
+
{ key: "id", label: "ID" },
|
|
53
|
+
{ key: "name", label: "Nom" },
|
|
54
|
+
{ key: "color", label: "Couleur" },
|
|
55
|
+
]);
|
|
56
|
+
printSuccess("Dossier cree.");
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
printError(e);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
cmd
|
|
63
|
+
.command("update <id>")
|
|
64
|
+
.description("Modifier un dossier")
|
|
65
|
+
.option("-n, --name <name>", "Nouveau nom")
|
|
66
|
+
.option("-c, --color <color>", "Nouvelle couleur")
|
|
67
|
+
.option("-p, --position <pos>", "Nouvelle position")
|
|
68
|
+
.action(async (id, opts) => {
|
|
69
|
+
try {
|
|
70
|
+
const body = {};
|
|
71
|
+
if (opts.name)
|
|
72
|
+
body.name = opts.name;
|
|
73
|
+
if (opts.color)
|
|
74
|
+
body.color = opts.color;
|
|
75
|
+
if (opts.position !== undefined)
|
|
76
|
+
body.position = parseInt(opts.position);
|
|
77
|
+
const res = (await api.patch(`/folders/${id}`, body));
|
|
78
|
+
printDetail(res.data, [
|
|
79
|
+
{ key: "id", label: "ID" },
|
|
80
|
+
{ key: "name", label: "Nom" },
|
|
81
|
+
{ key: "color", label: "Couleur" },
|
|
82
|
+
{ key: "position", label: "Position" },
|
|
83
|
+
]);
|
|
84
|
+
printSuccess("Dossier mis a jour.");
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
printError(e);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
cmd
|
|
91
|
+
.command("delete <id>")
|
|
92
|
+
.description("Supprimer un dossier")
|
|
93
|
+
.action(async (id) => {
|
|
94
|
+
try {
|
|
95
|
+
await api.delete(`/folders/${id}`);
|
|
96
|
+
printSuccess("Dossier supprime. Projets et domaines deplaces a la racine.");
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
printError(e);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
return cmd;
|
|
103
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { printTable, printDetail, printError } from "../output.js";
|
|
3
|
+
export function jobsCommand(api) {
|
|
4
|
+
const cmd = new Command("jobs").description("Suivre les jobs de generation");
|
|
5
|
+
cmd
|
|
6
|
+
.command("list")
|
|
7
|
+
.description("Lister les jobs")
|
|
8
|
+
.option("-s, --status <status>", "Filtrer (pending, running, completed, failed)")
|
|
9
|
+
.action(async (opts) => {
|
|
10
|
+
try {
|
|
11
|
+
const res = (await api.get("/jobs", {
|
|
12
|
+
status: opts.status,
|
|
13
|
+
}));
|
|
14
|
+
printTable(res.data, [
|
|
15
|
+
{ key: "id", label: "ID", width: 36 },
|
|
16
|
+
{ key: "status", label: "Statut", width: 12 },
|
|
17
|
+
{ key: "project_id", label: "Projet", width: 36 },
|
|
18
|
+
{ key: "created_at", label: "Cree le", width: 20 },
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
printError(e);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
cmd
|
|
26
|
+
.command("get <id>")
|
|
27
|
+
.description("Detail d'un job")
|
|
28
|
+
.action(async (id) => {
|
|
29
|
+
try {
|
|
30
|
+
const res = (await api.get(`/jobs/${id}`));
|
|
31
|
+
printDetail(res.data, [
|
|
32
|
+
{ key: "id", label: "ID" },
|
|
33
|
+
{ key: "status", label: "Statut" },
|
|
34
|
+
{ key: "project_id", label: "Projet" },
|
|
35
|
+
{ key: "error_message", label: "Erreur" },
|
|
36
|
+
{ key: "created_at", label: "Cree le" },
|
|
37
|
+
{ key: "updated_at", label: "Mis a jour" },
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
printError(e);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return cmd;
|
|
45
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
4
|
+
import { exec } from "node:child_process";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { loadConfig, saveConfig, getBaseUrl } from "../config.js";
|
|
7
|
+
import { printSuccess, printError } from "../output.js";
|
|
8
|
+
function openBrowser(url) {
|
|
9
|
+
try {
|
|
10
|
+
const cmd = process.platform === "darwin"
|
|
11
|
+
? "open"
|
|
12
|
+
: process.platform === "win32"
|
|
13
|
+
? "start"
|
|
14
|
+
: "xdg-open";
|
|
15
|
+
exec(`${cmd} "${url}"`);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// Silent fail
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function loginCommand() {
|
|
22
|
+
const cmd = new Command("login")
|
|
23
|
+
.description("Se connecter a Flowkode via le navigateur")
|
|
24
|
+
.option("--no-browser", "Ne pas ouvrir le navigateur automatiquement")
|
|
25
|
+
.action(async (opts) => {
|
|
26
|
+
const baseUrl = getBaseUrl();
|
|
27
|
+
const code = randomBytes(32).toString("hex");
|
|
28
|
+
console.log();
|
|
29
|
+
console.log(chalk.bold(" Flowkode Login"));
|
|
30
|
+
console.log();
|
|
31
|
+
// Get a random port
|
|
32
|
+
const tempServer = createServer();
|
|
33
|
+
await new Promise((resolve) => tempServer.listen(0, "127.0.0.1", resolve));
|
|
34
|
+
const port = tempServer.address().port;
|
|
35
|
+
tempServer.close();
|
|
36
|
+
// Now start the real server on that port
|
|
37
|
+
const resultPromise = new Promise((resolve, reject) => {
|
|
38
|
+
const server = createServer((req, res) => {
|
|
39
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
40
|
+
if (url.pathname !== "/callback") {
|
|
41
|
+
res.writeHead(404);
|
|
42
|
+
res.end("Not found");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const receivedCode = url.searchParams.get("code");
|
|
46
|
+
const error = url.searchParams.get("error");
|
|
47
|
+
const key = url.searchParams.get("key");
|
|
48
|
+
const email = url.searchParams.get("email") ?? "";
|
|
49
|
+
if (receivedCode !== code) {
|
|
50
|
+
res.writeHead(400);
|
|
51
|
+
res.end("Invalid code");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (error) {
|
|
55
|
+
res.writeHead(200, {
|
|
56
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
57
|
+
});
|
|
58
|
+
res.end(htmlPage("Autorisation refusee", "Vous pouvez fermer cette page.", "#dc2626"));
|
|
59
|
+
server.close();
|
|
60
|
+
reject(new Error("Autorisation refusee par l'utilisateur."));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (!key) {
|
|
64
|
+
res.writeHead(400);
|
|
65
|
+
res.end("Missing key");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
res.writeHead(200, {
|
|
69
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
70
|
+
});
|
|
71
|
+
res.end(htmlPage("Connecte !", "Vous pouvez fermer cette page et retourner dans votre terminal.", "#16a34a"));
|
|
72
|
+
server.close();
|
|
73
|
+
resolve({ key, email });
|
|
74
|
+
});
|
|
75
|
+
server.listen(port, "127.0.0.1");
|
|
76
|
+
setTimeout(() => {
|
|
77
|
+
server.close();
|
|
78
|
+
reject(new Error("Timeout — aucune reponse recue du navigateur (5 min)."));
|
|
79
|
+
}, 5 * 60 * 1000);
|
|
80
|
+
});
|
|
81
|
+
const authorizeUrl = `${baseUrl}/cli/authorize?port=${port}&code=${code}`;
|
|
82
|
+
if (opts.browser !== false) {
|
|
83
|
+
console.log(chalk.dim(" Ouverture du navigateur..."));
|
|
84
|
+
openBrowser(authorizeUrl);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
console.log(` Ouvrez ce lien dans votre navigateur :\n\n ${chalk.cyan(authorizeUrl)}`);
|
|
88
|
+
}
|
|
89
|
+
console.log();
|
|
90
|
+
console.log(chalk.dim(" En attente d'autorisation..."));
|
|
91
|
+
try {
|
|
92
|
+
const { key, email } = await resultPromise;
|
|
93
|
+
const config = loadConfig();
|
|
94
|
+
config.api_key = key;
|
|
95
|
+
saveConfig(config);
|
|
96
|
+
console.log();
|
|
97
|
+
printSuccess(`Connecte${email ? ` en tant que ${chalk.bold(email)}` : ""} !`);
|
|
98
|
+
console.log(chalk.dim(" Cle sauvegardee dans ~/.flowkode/config.json"));
|
|
99
|
+
console.log();
|
|
100
|
+
console.log(chalk.dim(" Essayez : flowkode projects list"));
|
|
101
|
+
console.log();
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
console.log();
|
|
105
|
+
printError(e);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
return cmd;
|
|
109
|
+
}
|
|
110
|
+
export function logoutCommand() {
|
|
111
|
+
return new Command("logout")
|
|
112
|
+
.description("Se deconnecter (supprime la cle API locale)")
|
|
113
|
+
.action(() => {
|
|
114
|
+
const config = loadConfig();
|
|
115
|
+
if (!config.api_key) {
|
|
116
|
+
console.log(chalk.dim("Pas de cle API configuree."));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
delete config.api_key;
|
|
120
|
+
saveConfig(config);
|
|
121
|
+
printSuccess("Deconnecte. Cle API supprimee de ~/.flowkode/config.json");
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function htmlPage(title, message, color) {
|
|
125
|
+
return `<!DOCTYPE html>
|
|
126
|
+
<html><head><meta charset="utf-8"><title>Flowkode CLI</title></head>
|
|
127
|
+
<body style="font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#fafafa">
|
|
128
|
+
<div style="text-align:center">
|
|
129
|
+
<h2 style="color:${color}">${title}</h2>
|
|
130
|
+
<p style="color:#666">${message}</p>
|
|
131
|
+
</div>
|
|
132
|
+
</body></html>`;
|
|
133
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { printTable, printDetail, printSuccess, printError, printJson, } from "../output.js";
|
|
3
|
+
export function projectsCommand(api) {
|
|
4
|
+
const cmd = new Command("projects").description("Gerer les projets");
|
|
5
|
+
cmd
|
|
6
|
+
.command("list")
|
|
7
|
+
.description("Lister les projets")
|
|
8
|
+
.option("--folder <id>", "Filtrer par dossier")
|
|
9
|
+
.action(async (opts) => {
|
|
10
|
+
try {
|
|
11
|
+
const res = (await api.get("/projects", {
|
|
12
|
+
folder_id: opts.folder,
|
|
13
|
+
}));
|
|
14
|
+
printTable(res.data, [
|
|
15
|
+
{ key: "id", label: "ID", width: 36 },
|
|
16
|
+
{ key: "name", label: "Nom", width: 30 },
|
|
17
|
+
{ key: "description", label: "Description", width: 40 },
|
|
18
|
+
{ key: "created_at", label: "Cree le", width: 20 },
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
printError(e);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
cmd
|
|
26
|
+
.command("get <id>")
|
|
27
|
+
.description("Detail d'un projet")
|
|
28
|
+
.action(async (id) => {
|
|
29
|
+
try {
|
|
30
|
+
const res = (await api.get(`/projects/${id}`));
|
|
31
|
+
printDetail(res.data, [
|
|
32
|
+
{ key: "id", label: "ID" },
|
|
33
|
+
{ key: "name", label: "Nom" },
|
|
34
|
+
{ key: "description", label: "Description" },
|
|
35
|
+
{ key: "site_type", label: "Type" },
|
|
36
|
+
{ key: "keywords", label: "Mots-cles" },
|
|
37
|
+
{ key: "folder_id", label: "Dossier" },
|
|
38
|
+
{ key: "created_at", label: "Cree le" },
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
printError(e);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
cmd
|
|
46
|
+
.command("create")
|
|
47
|
+
.description("Creer un projet (mode rapide)")
|
|
48
|
+
.requiredOption("-n, --name <name>", "Nom du projet")
|
|
49
|
+
.requiredOption("-t, --type <type>", "Type de site (vitrine, affiliation)")
|
|
50
|
+
.requiredOption("-k, --keywords <keywords>", "Mots-cles SEO")
|
|
51
|
+
.option("-l, --language <lang>", "Langue", "fr")
|
|
52
|
+
.option("-d, --description <desc>", "Description du site")
|
|
53
|
+
.option("--ai-model <model>", "ID du modele IA")
|
|
54
|
+
.action(async (opts) => {
|
|
55
|
+
try {
|
|
56
|
+
const res = await api.post("/projects", {
|
|
57
|
+
mode: "quick",
|
|
58
|
+
name: opts.name,
|
|
59
|
+
siteType: opts.type,
|
|
60
|
+
keywords: opts.keywords,
|
|
61
|
+
language: opts.language,
|
|
62
|
+
description: opts.description,
|
|
63
|
+
aiModel: opts.aiModel,
|
|
64
|
+
});
|
|
65
|
+
printJson(res);
|
|
66
|
+
printSuccess("Projet en cours de generation. Suivez avec: flowkode jobs get <jobId>");
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
printError(e);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
cmd
|
|
73
|
+
.command("update <id>")
|
|
74
|
+
.description("Modifier un projet")
|
|
75
|
+
.option("-n, --name <name>", "Nouveau nom")
|
|
76
|
+
.option("-d, --description <desc>", "Nouvelle description")
|
|
77
|
+
.option("--folder <id>", "ID du dossier (vide pour racine)")
|
|
78
|
+
.action(async (id, opts) => {
|
|
79
|
+
try {
|
|
80
|
+
const body = {};
|
|
81
|
+
if (opts.name)
|
|
82
|
+
body.name = opts.name;
|
|
83
|
+
if (opts.description)
|
|
84
|
+
body.description = opts.description;
|
|
85
|
+
if (opts.folder !== undefined)
|
|
86
|
+
body.folder_id = opts.folder || null;
|
|
87
|
+
const res = (await api.patch(`/projects/${id}`, body));
|
|
88
|
+
printDetail(res.data, [
|
|
89
|
+
{ key: "id", label: "ID" },
|
|
90
|
+
{ key: "name", label: "Nom" },
|
|
91
|
+
{ key: "description", label: "Description" },
|
|
92
|
+
{ key: "folder_id", label: "Dossier" },
|
|
93
|
+
]);
|
|
94
|
+
printSuccess("Projet mis a jour.");
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
printError(e);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
cmd
|
|
101
|
+
.command("deploy <id>")
|
|
102
|
+
.description("Deployer un projet")
|
|
103
|
+
.requiredOption("-p, --provider <provider>", "Provider (github, vercel, cloudflare)")
|
|
104
|
+
.option("--domain <id>", "ID du domaine a associer")
|
|
105
|
+
.option("--configure-ns", "Configurer les nameservers automatiquement")
|
|
106
|
+
.action(async (id, opts) => {
|
|
107
|
+
try {
|
|
108
|
+
const res = await api.post(`/projects/${id}/deploy`, {
|
|
109
|
+
provider: opts.provider,
|
|
110
|
+
domain_id: opts.domain,
|
|
111
|
+
configure_ns: opts.configureNs,
|
|
112
|
+
});
|
|
113
|
+
printJson(res);
|
|
114
|
+
printSuccess("Deploiement lance.");
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
printError(e);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
cmd
|
|
121
|
+
.command("redeploy <id>")
|
|
122
|
+
.description("Re-deployer sur le dernier provider")
|
|
123
|
+
.action(async (id) => {
|
|
124
|
+
try {
|
|
125
|
+
const res = await api.post(`/projects/${id}/redeploy`);
|
|
126
|
+
printJson(res);
|
|
127
|
+
printSuccess("Re-deploiement lance.");
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
printError(e);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
cmd
|
|
134
|
+
.command("indexing <id>")
|
|
135
|
+
.description("Statut d'indexation Google du projet")
|
|
136
|
+
.action(async (id) => {
|
|
137
|
+
try {
|
|
138
|
+
const res = await api.get(`/projects/${id}/indexing`);
|
|
139
|
+
printJson(res);
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
printError(e);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
return cmd;
|
|
146
|
+
}
|