@envsafes-org/cli 0.0.6
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 +343 -0
- package/STORAGE_AGENT_README.md +328 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.js +55 -0
- package/dist/commands/projects.d.ts +1 -0
- package/dist/commands/projects.js +40 -0
- package/dist/commands/pull.d.ts +4 -0
- package/dist/commands/pull.js +46 -0
- package/dist/commands/push.d.ts +4 -0
- package/dist/commands/push.js +56 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.js +49 -0
- package/dist/commands/whoami.d.ts +1 -0
- package/dist/commands/whoami.js +34 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +98 -0
- package/dist/utils/api.d.ts +7 -0
- package/dist/utils/api.js +25 -0
- package/package.json +42 -0
- package/src/commands/login.ts +58 -0
- package/src/commands/projects.ts +40 -0
- package/src/commands/pull.ts +53 -0
- package/src/commands/push.ts +64 -0
- package/src/commands/run.ts +55 -0
- package/src/commands/whoami.ts +33 -0
- package/src/index.ts +105 -0
- package/src/utils/api.ts +36 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import dotenv from "dotenv";
|
|
5
|
+
import { config } from "../index.js";
|
|
6
|
+
import { apiRequest } from "../utils/api.js";
|
|
7
|
+
|
|
8
|
+
export async function push(
|
|
9
|
+
project: string,
|
|
10
|
+
options: { env: string; file: string }
|
|
11
|
+
) {
|
|
12
|
+
const token = config.get("token") as string;
|
|
13
|
+
|
|
14
|
+
if (!token) {
|
|
15
|
+
console.log(chalk.red("✗ Non connecté. Utilisez 'envsafe login' d'abord."));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Check if file exists
|
|
20
|
+
if (!fs.existsSync(options.file)) {
|
|
21
|
+
console.log(chalk.red(`✗ Fichier non trouvé: ${options.file}`));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const spinner = ora(`Envoi des variables vers ${project} (${options.env})...`).start();
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// Parse .env file
|
|
29
|
+
const envContent = fs.readFileSync(options.file, "utf-8");
|
|
30
|
+
const parsed = dotenv.parse(envContent);
|
|
31
|
+
|
|
32
|
+
const response = await apiRequest(`/api/v1/projects/${project}/${options.env}`, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
token,
|
|
35
|
+
body: { variables: parsed },
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (response.error) {
|
|
39
|
+
spinner.fail(chalk.red(response.error));
|
|
40
|
+
|
|
41
|
+
// Suggestion intelligente
|
|
42
|
+
const commonEnvs = ["development", "dev", "staging", "stg", "production", "prod", "test"];
|
|
43
|
+
if (commonEnvs.includes(project.toLowerCase()) || response.error.includes("trouvé")) {
|
|
44
|
+
console.log(chalk.yellow("\n💡 Astuce : La syntaxe est 'envsafe push <MON_PROJET>'"));
|
|
45
|
+
console.log(chalk.yellow(` Vous avez essayé de push vers le projet "${project}".`));
|
|
46
|
+
console.log(chalk.yellow(` Utilisez 'envsafe projects' pour voir la liste de vos projets/slugs.`));
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
spinner.succeed(
|
|
52
|
+
chalk.green(`✓ ${response.created} variable(s) créée(s)`)
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (response.errors?.length > 0) {
|
|
56
|
+
console.log(chalk.yellow("\nAvertissements:"));
|
|
57
|
+
response.errors.forEach((err: string) => {
|
|
58
|
+
console.log(chalk.dim(` - ${err}`));
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
} catch (error: any) {
|
|
62
|
+
spinner.fail(chalk.red(`Erreur: ${error.message}`));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { config } from "../index.js";
|
|
4
|
+
import { apiRequest } from "../utils/api.js";
|
|
5
|
+
|
|
6
|
+
export async function run(
|
|
7
|
+
project: string,
|
|
8
|
+
command: string[],
|
|
9
|
+
options: { env: string }
|
|
10
|
+
) {
|
|
11
|
+
const token = config.get("token") as string;
|
|
12
|
+
|
|
13
|
+
if (!token) {
|
|
14
|
+
console.log(chalk.red("✗ Non connecté. Utilisez 'envsafe login' d'abord."));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
console.log(chalk.dim(`Récupération des variables pour ${project} (${options.env})...`));
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const response = await apiRequest(`/api/v1/projects/${project}/${options.env}`, {
|
|
22
|
+
method: "GET",
|
|
23
|
+
token,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (response.error) {
|
|
27
|
+
console.log(chalk.red(`✗ ${response.error}`));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const variables = response.variables as Record<string, string>;
|
|
32
|
+
console.log(chalk.green(`✓ ${Object.keys(variables).length} variable(s) chargée(s)`));
|
|
33
|
+
console.log(chalk.dim(`Exécution: ${command.join(" ")}\n`));
|
|
34
|
+
|
|
35
|
+
// Spawn the command with injected env vars
|
|
36
|
+
const [cmd, ...args] = command;
|
|
37
|
+
const child = spawn(cmd, args, {
|
|
38
|
+
env: { ...process.env, ...variables },
|
|
39
|
+
stdio: "inherit",
|
|
40
|
+
shell: true,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
child.on("close", (code) => {
|
|
44
|
+
process.exit(code ?? 0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
child.on("error", (err) => {
|
|
48
|
+
console.error(chalk.red(`Erreur d'exécution: ${err.message}`));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
});
|
|
51
|
+
} catch (error: any) {
|
|
52
|
+
console.log(chalk.red(`✗ Erreur: ${error.message}`));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { config } from "../index.js";
|
|
3
|
+
import { apiRequest } from "../utils/api.js";
|
|
4
|
+
|
|
5
|
+
export async function whoami() {
|
|
6
|
+
const token = config.get("token") as string;
|
|
7
|
+
const apiUrl = config.get("apiUrl") as string;
|
|
8
|
+
|
|
9
|
+
if (!token) {
|
|
10
|
+
console.log(chalk.red("✗ Non connecté. Utilisez 'envsafe login' d'abord."));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const user = await apiRequest("/api/v1/user/me", {
|
|
16
|
+
method: "GET",
|
|
17
|
+
token
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
console.log(chalk.cyan("\n🔐 EnvSafe CLI\n"));
|
|
21
|
+
console.log(` Utilisateur: ${chalk.white(user.name)} (${chalk.dim(user.email)})`);
|
|
22
|
+
|
|
23
|
+
if (user.lastLoginAt) {
|
|
24
|
+
const date = new Date(user.lastLoginAt).toLocaleString();
|
|
25
|
+
console.log(` Dernière connexion: ${chalk.dim(date)}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log(` API: ${chalk.dim(apiUrl)}`);
|
|
29
|
+
console.log(` Token: ${chalk.dim(token.substring(0, 12) + "...")}\n`);
|
|
30
|
+
} catch (error: any) {
|
|
31
|
+
console.log(chalk.red(`✗ Impossible de récupérer les infos utilisateur: ${error.message}`));
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import Conf from "conf";
|
|
6
|
+
import { login } from "./commands/login.js";
|
|
7
|
+
import { pull } from "./commands/pull.js";
|
|
8
|
+
import { push } from "./commands/push.js";
|
|
9
|
+
import { run } from "./commands/run.js";
|
|
10
|
+
import { projects } from "./commands/projects.js";
|
|
11
|
+
import { whoami } from "./commands/whoami.js";
|
|
12
|
+
import pkg from "../package.json";
|
|
13
|
+
|
|
14
|
+
// Config store for token
|
|
15
|
+
export const config = new Conf({
|
|
16
|
+
projectName: "envsafe-cli",
|
|
17
|
+
schema: {
|
|
18
|
+
token: { type: "string", default: "" },
|
|
19
|
+
apiUrl: { type: "string", default: "https://envsafe.vercel.app" },
|
|
20
|
+
dashboardUrl: { type: "string", default: "https://envsafe.vercel.app" },
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const program = new Command();
|
|
25
|
+
|
|
26
|
+
program
|
|
27
|
+
.name("envsafe")
|
|
28
|
+
.description(chalk.cyan("🔐 EnvSafe CLI - Gestionnaire sécurisé de variables d'environnement"))
|
|
29
|
+
.version(pkg.version);
|
|
30
|
+
|
|
31
|
+
// Login command
|
|
32
|
+
program
|
|
33
|
+
.command("login")
|
|
34
|
+
.description("Se connecter avec un token API")
|
|
35
|
+
.option("-t, --token <token>", "Token API")
|
|
36
|
+
.action(login);
|
|
37
|
+
|
|
38
|
+
// Whoami command
|
|
39
|
+
program
|
|
40
|
+
.command("whoami")
|
|
41
|
+
.description("Afficher l'utilisateur connecté")
|
|
42
|
+
.action(whoami);
|
|
43
|
+
|
|
44
|
+
// Projects command
|
|
45
|
+
program
|
|
46
|
+
.command("projects")
|
|
47
|
+
.alias("ls")
|
|
48
|
+
.description("Lister les projets accessibles")
|
|
49
|
+
.action(projects);
|
|
50
|
+
|
|
51
|
+
// Pull command
|
|
52
|
+
program
|
|
53
|
+
.command("pull")
|
|
54
|
+
.description("Télécharger les variables d'environnement")
|
|
55
|
+
.argument("<project>", "Slug du projet")
|
|
56
|
+
.option("-e, --env <environment>", "Environnement (development, staging, production)", "development")
|
|
57
|
+
.option("-o, --output <file>", "Fichier de sortie", ".env")
|
|
58
|
+
.action(pull);
|
|
59
|
+
|
|
60
|
+
// Push command
|
|
61
|
+
program
|
|
62
|
+
.command("push")
|
|
63
|
+
.description("Envoyer les variables d'environnement")
|
|
64
|
+
.argument("<project>", "Slug du projet")
|
|
65
|
+
.option("-e, --env <environment>", "Environnement", "development")
|
|
66
|
+
.option("-f, --file <file>", "Fichier source", ".env")
|
|
67
|
+
.action(push);
|
|
68
|
+
|
|
69
|
+
// Run command
|
|
70
|
+
program
|
|
71
|
+
.command("run")
|
|
72
|
+
.description("Exécuter une commande avec les variables d'environnement injectées")
|
|
73
|
+
.argument("<project>", "Slug du projet")
|
|
74
|
+
.option("-e, --env <environment>", "Environnement", "development")
|
|
75
|
+
.argument("<command...>", "Commande à exécuter")
|
|
76
|
+
.action(run);
|
|
77
|
+
|
|
78
|
+
// Config command
|
|
79
|
+
program
|
|
80
|
+
.command("config")
|
|
81
|
+
.description("Gérer la configuration")
|
|
82
|
+
.option("--api-url <url>", "Définir l'URL de l'API EnvSafe")
|
|
83
|
+
.option("--show", "Afficher la configuration actuelle")
|
|
84
|
+
.action((options) => {
|
|
85
|
+
if (options.apiUrl) {
|
|
86
|
+
config.set("apiUrl", options.apiUrl);
|
|
87
|
+
console.log(chalk.green(`✓ URL de l'API définie: ${options.apiUrl}`));
|
|
88
|
+
}
|
|
89
|
+
if (options.show) {
|
|
90
|
+
console.log(chalk.cyan("Configuration actuelle:"));
|
|
91
|
+
console.log(` API URL: ${config.get("apiUrl")}`);
|
|
92
|
+
console.log(` Token: ${config.get("token") ? "••••••••" : "(non défini)"}`);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Logout command
|
|
97
|
+
program
|
|
98
|
+
.command("logout")
|
|
99
|
+
.description("Se déconnecter")
|
|
100
|
+
.action(() => {
|
|
101
|
+
config.set("token", "");
|
|
102
|
+
console.log(chalk.green("✓ Déconnexion réussie"));
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
program.parse();
|
package/src/utils/api.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { config } from "../index.js";
|
|
2
|
+
|
|
3
|
+
interface ApiRequestOptions {
|
|
4
|
+
method: "GET" | "POST" | "PUT" | "DELETE";
|
|
5
|
+
token?: string;
|
|
6
|
+
body?: any;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function apiRequest(endpoint: string, options: ApiRequestOptions) {
|
|
10
|
+
const apiUrl = config.get("apiUrl") as string;
|
|
11
|
+
const token = options.token || (config.get("token") as string);
|
|
12
|
+
|
|
13
|
+
const url = `${apiUrl}${endpoint}`;
|
|
14
|
+
|
|
15
|
+
const headers: Record<string, string> = {
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
if (token) {
|
|
20
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const response = await fetch(url, {
|
|
24
|
+
method: options.method,
|
|
25
|
+
headers,
|
|
26
|
+
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const data = await response.json();
|
|
30
|
+
|
|
31
|
+
if (!response.ok && !data.error) {
|
|
32
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return data;
|
|
36
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"resolveJsonModule": true
|
|
14
|
+
},
|
|
15
|
+
"include": [
|
|
16
|
+
"src/**/*"
|
|
17
|
+
],
|
|
18
|
+
"exclude": [
|
|
19
|
+
"node_modules",
|
|
20
|
+
"dist"
|
|
21
|
+
]
|
|
22
|
+
}
|