mydata-cli
Advanced tools
+161
-33
| import { Command } from "commander"; | ||
| import fs from "fs-extra"; | ||
| import path from "path"; | ||
| import { api } from "../utils/api.js"; | ||
| import { parseEnvFile } from "../utils/parseEnvFile.js"; | ||
| import readline from "readline"; | ||
| function detectEnvFile() { | ||
| const files = [".env.local", ".env.development", ".env", ".env.example"]; | ||
| const found = files.find((f) => fs.existsSync(path.join(process.cwd(), f))); | ||
| return found ? path.join(process.cwd(), found) : null; | ||
| } | ||
| function promptYesNo(question) { | ||
| return new Promise((resolve) => { | ||
| const rl = readline.createInterface({ | ||
| input: process.stdin, | ||
| output: process.stdout, | ||
| }); | ||
| rl.question(`${question} (yes/no): `, (answer) => { | ||
| rl.close(); | ||
| const normalized = answer.trim().toLowerCase(); | ||
| resolve(normalized === "yes" || normalized === "y"); | ||
| }); | ||
| }); | ||
| } | ||
| const project = new Command("project").description("Manage projects"); | ||
| // โ Add new project (auto + interactive) | ||
| project | ||
| .command("add") | ||
| .description("Add a new project with auto-detected info") | ||
| .option("-t, --title <title>", "Project title") | ||
| .option("-d, --description <desc>", "Project description") | ||
| .option("-r, --repo <url>", "GitHub repo link") | ||
| .option("-l, --live <url>", "Live demo URL") | ||
| .option("--tech <stack...>", "Tech stack (space-separated)") | ||
| .option("--tags <tags...>", "Tags or hashtags") | ||
| .option("-g, --groupName <name>", "Env group name") | ||
| .option("-e, --env <path>", "Path to .env file") | ||
| .action(async (opts) => { | ||
| try { | ||
| const rl = readline.createInterface({ | ||
| input: process.stdin, | ||
| output: process.stdout, | ||
| }); | ||
| // 1. Auto-detect package.json | ||
| const pkgPath = path.resolve(process.cwd(), "package.json"); | ||
| const pkg = fs.existsSync(pkgPath) | ||
| ? JSON.parse(fs.readFileSync(pkgPath, "utf8")) | ||
| : {}; | ||
| const title = opts.title || pkg.name || "Untitled Project"; | ||
| let description = opts.description || pkg.description || ""; | ||
| // 2. If no description, ask the user | ||
| if (!description) { | ||
| description = await new Promise((resolve) => { | ||
| rl.question( | ||
| "๐ Description not found. Enter a short description: ", | ||
| (desc) => { | ||
| resolve(desc.trim()); | ||
| } | ||
| ); | ||
| }); | ||
| } | ||
| const repo = opts.repo || pkg.repository?.url || ""; | ||
| const live = opts.live || ""; | ||
| const techStack = opts.tech || Object.keys(pkg.dependencies || {}); | ||
| const tags = opts.tags || []; | ||
| // 3. Auto-detect env file | ||
| const envPath = opts.env || detectEnvFile(); | ||
| const groupName = opts.groupName || "default"; | ||
| const variables = envPath ? parseEnvFile(envPath) : []; | ||
| // 4. Summary | ||
| console.log("\n๐ฆ Detected Project Info:"); | ||
| console.log("๐ Title:", title); | ||
| if (description) console.log("๐งพ Description:", description); | ||
| if (repo) console.log("๐ Repo:", repo); | ||
| if (live) console.log("๐ Live:", live); | ||
| if (techStack.length) console.log("๐ง Tech:", techStack.join(", ")); | ||
| if (tags.length) console.log("๐ท๏ธ Tags:", tags.join(", ")); | ||
| if (envPath) | ||
| console.log( | ||
| "๐ฑ Env File:", | ||
| path.basename(envPath), | ||
| `(${variables.length} vars)` | ||
| ); | ||
| else console.log("โ ๏ธ No .env file found"); | ||
| // 5. Confirm | ||
| const confirm = await new Promise((resolve) => { | ||
| rl.question("\nโ Do you want to continue? (yes/no): ", (answer) => { | ||
| const normalized = answer.trim().toLowerCase(); | ||
| resolve(normalized === "yes" || normalized === "y"); | ||
| }); | ||
| }); | ||
| rl.close(); | ||
| if (!confirm) { | ||
| console.log("โ Cancelled by user."); | ||
| return; | ||
| } | ||
| // 6. Submit | ||
| const res = await api.post("/api/projects", { | ||
| title, | ||
| description, | ||
| repo, | ||
| live, | ||
| techStack, | ||
| tags, | ||
| envGroups: envPath | ||
| ? [ | ||
| { | ||
| groupName, | ||
| variables, | ||
| }, | ||
| ] | ||
| : [], | ||
| }); | ||
| console.log("\nโ Project added:", res.data.data.title); | ||
| } catch (err) { | ||
| console.error("โ", err.response?.data?.error || err.message); | ||
| } | ||
| }); | ||
| // โ List Projects | ||
@@ -53,36 +181,36 @@ project | ||
| // โ Add new Project with env group + repo/live/tech/tags | ||
| project | ||
| .command("add") | ||
| .description("Add a new project") | ||
| .requiredOption("-t, --title <title>", "Project title") | ||
| .option("-d, --description <desc>", "Description") | ||
| .option("-r, --repo <url>", "GitHub repo link") | ||
| .option("-l, --live <url>", "Live demo URL") | ||
| .option("--tech <stack...>", "Tech stack (space-separated)") | ||
| .option("--tags <tags...>", "Tags/hashtags (space-separated)") | ||
| .requiredOption("-g, --groupName <name>", "Env group name") | ||
| .requiredOption("-e, --env <path>", "Path to .env file") | ||
| .action(async (opts) => { | ||
| try { | ||
| const variables = parseEnvFile(opts.env); | ||
| const res = await api.post("/api/projects", { | ||
| title: opts.title, | ||
| description: opts.description || "", | ||
| repo: opts.repo, | ||
| live: opts.live, | ||
| techStack: opts.tech || [], | ||
| tags: opts.tags || [], | ||
| envGroups: [ | ||
| { | ||
| groupName: opts.groupName, | ||
| variables, | ||
| }, | ||
| ], | ||
| }); | ||
| // project | ||
| // .command("add") | ||
| // .description("Add a new project") | ||
| // .requiredOption("-t, --title <title>", "Project title") | ||
| // .option("-d, --description <desc>", "Description") | ||
| // .option("-r, --repo <url>", "GitHub repo link") | ||
| // .option("-l, --live <url>", "Live demo URL") | ||
| // .option("--tech <stack...>", "Tech stack (space-separated)") | ||
| // .option("--tags <tags...>", "Tags/hashtags (space-separated)") | ||
| // .requiredOption("-g, --groupName <name>", "Env group name") | ||
| // .requiredOption("-e, --env <path>", "Path to .env file") | ||
| // .action(async (opts) => { | ||
| // try { | ||
| // const variables = parseEnvFile(opts.env); | ||
| // const res = await api.post("/api/projects", { | ||
| // title: opts.title, | ||
| // description: opts.description || "", | ||
| // repo: opts.repo, | ||
| // live: opts.live, | ||
| // techStack: opts.tech || [], | ||
| // tags: opts.tags || [], | ||
| // envGroups: [ | ||
| // { | ||
| // groupName: opts.groupName, | ||
| // variables, | ||
| // }, | ||
| // ], | ||
| // }); | ||
| console.log("โ Project added:", res.data.data.title); | ||
| } catch (err) { | ||
| console.error("โ", err.response?.data?.error || err.message); | ||
| } | ||
| }); | ||
| // console.log("โ Project added:", res.data.data.title); | ||
| // } catch (err) { | ||
| // console.error("โ", err.response?.data?.error || err.message); | ||
| // } | ||
| // }); | ||
@@ -89,0 +217,0 @@ // โ Edit Project with env group + repo/live/tech/tags |
+2
-3
| { | ||
| "name": "mydata-cli", | ||
| "version": "1.0.7", | ||
| "version": "1.0.8", | ||
| "description": "", | ||
@@ -21,5 +21,4 @@ "main": "index.js", | ||
| "dotenv": "^17.0.1", | ||
| "fs-extra": "^11.3.0", | ||
| "mydata-cli": "^1.0.4" | ||
| "fs-extra": "^11.3.0" | ||
| } | ||
| } |
+16
-10
@@ -1,17 +0,23 @@ | ||
| import fs from "fs"; | ||
| import fs from "fs-extra"; | ||
| export function parseEnvFile(filePath) { | ||
| const content = fs.readFileSync(filePath, "utf-8"); | ||
| const variables = []; | ||
| const lines = content.split("\n"); | ||
| const result = []; | ||
| content.split("\n").forEach((line) => { | ||
| const trimmed = line.trim(); | ||
| if (!trimmed || trimmed.startsWith("#")) return; | ||
| for (let line of lines) { | ||
| line = line.trim(); | ||
| if (!line || line.startsWith("#")) continue; | ||
| const [key, ...valParts] = trimmed.split("="); | ||
| const value = valParts.join("=").trim().replace(/^"|"$/g, ""); | ||
| if (key) variables.push({ key: key.trim(), value }); | ||
| }); | ||
| const [key, ...rest] = line.split("="); | ||
| const value = rest | ||
| .join("=") | ||
| .trim() | ||
| .replace(/^['"]|['"]$/g, ""); | ||
| if (key && value !== undefined) { | ||
| result.push({ key, value }); | ||
| } | ||
| } | ||
| return variables; | ||
| return result; | ||
| } |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
23557
22.34%5
-16.67%680
21.43%7
16.67%- Removed