UNPKG

mydata-cli

Version:

A CLI tool for interacting with MyData API and managing data. Supports login, data retrieval, and more. Built with Node.js.

368 lines (329 loc) โ€ข 12.2 kB
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 || ""; // let tags = opts.tags || pkg.keywords || []; // 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 = pkg.keywords || Object.keys(pkg.keywords || []); // 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 project .command("list") .description("List all projects") .action(async () => { try { const res = await api.get("/api/projects"); const projects = res.data.data; projects.forEach((p) => { console.log(`๐Ÿ“Œ ${p.title} โ€” ${p.description}`); if (p.repo) console.log(` ๐Ÿ”— Repo: ${p.repo}`); if (p.live) console.log(` ๐ŸŒ Live: ${p.live}`); if (p.techStack?.length) console.log(` ๐Ÿง  Tech: ${p.techStack.join(", ")}`); if (p.tags?.length) console.log(` ๐Ÿท๏ธ Tags: ${p.tags.join(", ")}`); p.envGroups?.forEach((group) => { console.log(` ๐Ÿ”ง ${group.groupName}`); group.variables.forEach((v) => console.log(` ${v.key} = ${v.value}`) ); }); console.log(); }); } catch (err) { console.error("โŒ", err.response?.data?.error || err.message); } }); // List IDs project .command("li") .description("List all projects") .action(async () => { try { const res = await api.get("/api/projects"); const projects = res.data.data; projects.forEach((p) => { console.log(`๐Ÿ“Œ ID- ${p._id} \n ${p.title} โ€” ${p.description}`); }); } catch (err) { console.error("โŒ", err.response?.data?.error || err.message); } }); // โœ… 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, // }, // ], // }); // console.log("โœ… Project added:", res.data.data.title); // } catch (err) { // console.error("โŒ", err.response?.data?.error || err.message); // } // }); // โœ… Edit Project with env group + repo/live/tech/tags project .command("edit") .description("Edit a project") .requiredOption("-i, --id <id>", "Project ID") .option("-t, --title <title>", "New title") .option("-d, --description <desc>", "New description") .option("-r, --repo <url>", "New GitHub repo link") .option("-l, --live <url>", "New live demo URL") .option("--tech <stack...>", "New tech stack") .option("--tags <tags...>", "New tags") .option("-g, --groupName <name>", "Replace env group name") .option("-e, --env <path>", "Replace env file") .action(async (opts) => { try { const updates = {}; if (opts.title) updates.title = opts.title; if (opts.description) updates.description = opts.description; if (opts.repo) updates.repo = opts.repo; if (opts.live) updates.live = opts.live; if (opts.tech) updates.techStack = opts.tech; if (opts.tags) updates.tags = opts.tags; if (opts.groupName && opts.env) { updates.envGroups = [ { groupName: opts.groupName, variables: parseEnvFile(opts.env), }, ]; } const res = await api.patch("/api/projects", { id: opts.id, updates, }); console.log("โœ… Project updated:", res.data.data.title); } catch (err) { console.error("โŒ", err.response?.data?.error || err.message); } }); // โœ… Delete Project project .command("delete") .description("Delete a project") .requiredOption("-i, --id <id>", "Project ID") .action(async (opts) => { try { const res = await api.delete(`/api/projects?id=${opts.id}`); console.log("๐Ÿ—‘๏ธ", res.data.message); } catch (err) { console.error("โŒ", err.response?.data?.error || err.message); } }); // โœ… Add new env group project .command("group:add") .description("Add a new env group to a project") .requiredOption("-i, --id <id>", "Project ID") .requiredOption("-g, --groupName <name>", "New group name") .requiredOption("-e, --env <path>", "Path to .env file") .action(async (opts) => { try { const projectRes = await api.get("/api/projects"); const project = projectRes.data.data.find((p) => p._id === opts.id); if (!project) return console.error("โŒ Project not found"); const existing = project.envGroups || []; const updatedGroups = [ ...existing, { groupName: opts.groupName, variables: parseEnvFile(opts.env), }, ]; const res = await api.patch("/api/projects", { id: opts.id, updates: { envGroups: updatedGroups }, }); console.log("โœ… Added new env group:", opts.groupName); } catch (err) { console.error("โŒ", err.response?.data?.error || err.message); } }); // โœ… Edit existing env group project .command("group:edit") .description("Edit an existing env group") .requiredOption("-i, --id <id>", "Project ID") .requiredOption("-g, --groupName <name>", "Group name to update") .requiredOption("-e, --env <path>", "New env file") .action(async (opts) => { try { const projectRes = await api.get("/api/projects"); const project = projectRes.data.data.find((p) => p._id === opts.id); if (!project) return console.error("โŒ Project not found"); const updatedGroups = project.envGroups.map((group) => group.groupName === opts.groupName ? { groupName: group.groupName, variables: parseEnvFile(opts.env), } : group ); const res = await api.patch("/api/projects", { id: opts.id, updates: { envGroups: updatedGroups }, }); console.log("โœ… Updated env group:", opts.groupName); } catch (err) { console.error("โŒ", err.response?.data?.error || err.message); } }); // โœ… Delete env group project .command("group:delete") .description("Delete a specific env group from a project") .requiredOption("-i, --id <id>", "Project ID") .requiredOption("-g, --groupName <name>", "Group name to delete") .action(async (opts) => { try { const projectRes = await api.get("/api/projects"); const project = projectRes.data.data.find((p) => p._id === opts.id); if (!project) return console.error("โŒ Project not found"); const filteredGroups = project.envGroups.filter( (group) => group.groupName !== opts.groupName ); const res = await api.patch("/api/projects", { id: opts.id, updates: { envGroups: filteredGroups }, }); console.log("๐Ÿ—‘๏ธ Deleted group:", opts.groupName); } catch (err) { console.error("โŒ", err.response?.data?.error || err.message); } }); export default project;