@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.
@@ -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();
@@ -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
+ }