distats-cli 0.0.1

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/bin/index.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import "../src/index.js";
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "distats-cli",
3
+ "version": "0.0.1",
4
+ "description": "",
5
+ "main": "src/index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "bin": {
10
+ "prismpanel": "./bin/index.js"
11
+ },
12
+ "keywords": [],
13
+ "author": "",
14
+ "license": "ISC",
15
+ "type": "module",
16
+ "dependencies": {
17
+ "chalk": "^5.6.2",
18
+ "inquirer": "^13.3.2",
19
+ "mongoose": "^9.4.1",
20
+ "ora": "^9.3.0",
21
+ "sqlite3": "^6.0.1"
22
+ }
23
+ }
@@ -0,0 +1,66 @@
1
+ import inquirer from "inquirer";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import crypto from "crypto";
5
+ import { getDatabase, runQuery } from "../utils/database.js";
6
+
7
+ /**
8
+ * Hash a password using scrypt (Node.js native).
9
+ *
10
+ * @param {string} password
11
+ * @returns {string} - salt.hash format
12
+ */
13
+ function hashPassword(password) {
14
+ const salt = crypto.randomBytes(16).toString("hex");
15
+ const hash = crypto.scryptSync(password, salt, 64).toString("hex");
16
+ return `${salt}.${hash}`;
17
+ }
18
+
19
+ export async function createUser() {
20
+ const spinner = ora("Opening local database...").start();
21
+ let db;
22
+
23
+ try {
24
+ db = await getDatabase();
25
+ spinner.succeed(chalk.green("Connected to SQLite database."));
26
+ } catch (error) {
27
+ spinner.fail(chalk.red("Failed to open local database."));
28
+ console.error(chalk.red("\nDetails: " + error.message));
29
+ return;
30
+ }
31
+
32
+ const answers = await inquirer.prompt([
33
+ {
34
+ type: "input",
35
+ name: "name",
36
+ message: "Enter user name:",
37
+ validate: (input) => input.length > 0 && input.length <= 60 ? true : "Name must be 1-60 characters.",
38
+ },
39
+ {
40
+ type: "password",
41
+ name: "password",
42
+ message: "Enter user password:",
43
+ mask: "*",
44
+ validate: (input) => input.length > 0 ? true : "Password is required.",
45
+ },
46
+ ]);
47
+
48
+ const saveSpinner = ora("Creating user and hashing password...").start();
49
+
50
+ try {
51
+ // POWERFUL HASHING LOGIC
52
+ const securePassword = hashPassword(answers.password);
53
+
54
+ const sql = "INSERT INTO users (name, password) VALUES (?, ?)";
55
+ await runQuery(db, sql, [answers.name, securePassword]);
56
+
57
+ saveSpinner.succeed(chalk.green(`User '${answers.name}' created with a powerful encrypted password.`));
58
+ } catch (error) {
59
+ saveSpinner.fail(chalk.red("Failed to save user."));
60
+ console.error(chalk.red(error.message));
61
+ } finally {
62
+ if (db) {
63
+ db.close();
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,57 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import inquirer from "inquirer";
4
+ import chalk from "chalk";
5
+ import { createLoader } from "../utils/loader.js";
6
+
7
+ export async function init() {
8
+ const getVisibleLength = (str) => str.replace(/\u001b\[\d+m/g, "").length;
9
+ const noteWidth = 55;
10
+ const borderCol = chalk.yellow;
11
+
12
+ const titleStr = ` ${chalk.yellow.bold("⚠ PRIVACY NOTICE")} `;
13
+ const line1 = ` Your Bot Token is used exclusively for features.`;
14
+ const line2 = ` It will never be stored or used elsewhere.`;
15
+
16
+ console.log(borderCol(` ┌${"─".repeat(noteWidth)}┐`));
17
+ [titleStr, line1, line2].forEach(line => {
18
+ const visibleLength = getVisibleLength(line);
19
+ const padding = " ".repeat(Math.max(0, noteWidth - visibleLength));
20
+ console.log(` ${borderCol("│")}${line}${padding}${borderCol("│")}`);
21
+ });
22
+ console.log(borderCol(` └${"─".repeat(noteWidth)}┘\n`));
23
+
24
+ const answers = await inquirer.prompt([
25
+ {
26
+ type: "input",
27
+ name: "bot_token",
28
+ message: "Bot Token:",
29
+ },
30
+ ]);
31
+
32
+ const loader = createLoader("Creating project...");
33
+
34
+ // simulate progress over 4 seconds
35
+ await new Promise((resolve) => {
36
+ setTimeout(() => loader.update("Setting up files..."), 1000);
37
+ setTimeout(() => loader.update("Installing dependencies..."), 2000);
38
+ setTimeout(() => loader.update("Finalizing..."), 3000);
39
+ setTimeout(resolve, 4000);
40
+ });
41
+
42
+ loader.succeed("Project created successfully!\nUse 'distats-panel-cli create-user' to create a user.");
43
+
44
+ // Save the config
45
+ try {
46
+ const configDir = path.join(process.cwd(), "@distats_panel");
47
+ if (!fs.existsSync(configDir)) {
48
+ fs.mkdirSync(configDir, { recursive: true });
49
+ }
50
+ const configPath = path.join(configDir, "config.json");
51
+ fs.writeFileSync(configPath, JSON.stringify(answers, null, 2));
52
+ } catch (err) {
53
+ console.log(chalk.red("\nError saving configuration: " + err.message));
54
+ }
55
+
56
+ console.log("\nResult:", answers);
57
+ }
package/src/index.js ADDED
@@ -0,0 +1,29 @@
1
+ import { init } from "./commands/init.js";
2
+ import { createUser } from "./commands/create-user.js";
3
+ import { getEnvVariables, GetEnvVaribels } from "./utils/config.js";
4
+ import { showWelcome } from "./utils/logger.js";
5
+
6
+ // Always show the welcome banner
7
+ showWelcome();
8
+
9
+ // Export the utility functions for use as a library
10
+ export { getEnvVariables, GetEnvVaribels };
11
+
12
+ const command = process.argv[2];
13
+
14
+ if (command) {
15
+ switch (command) {
16
+ case "init":
17
+ await init();
18
+ break;
19
+
20
+ case "create-user":
21
+ await createUser();
22
+ break;
23
+
24
+ default:
25
+ console.log("Unknown command. Available commands: init, create-user");
26
+ }
27
+ } else {
28
+ console.log("Usage: prismpanel <command>\nAvailable commands: init, create-user");
29
+ }
@@ -0,0 +1,15 @@
1
+ import mongoose from "mongoose";
2
+
3
+ const UserSchema = new mongoose.Schema({
4
+ name: {
5
+ type: String,
6
+ required: [true, "Please provide a name"],
7
+ maxlength: [60, "Name cannot be more than 60 characters"],
8
+ },
9
+ password: {
10
+ type: String,
11
+ required: [true, "Please provide a password"],
12
+ },
13
+ });
14
+
15
+ export default mongoose.models.User || mongoose.model("User", UserSchema);
@@ -0,0 +1,34 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+
4
+ /**
5
+ * Get configuration value(s) from the @prism_panel/config.json file in the current workspace.
6
+ *
7
+ * @param {string} [key] - The key to look for in the config file. If not provided, returns all config.
8
+ * @returns {any|null} - The value of the key, the entire config object, or null if not found.
9
+ */
10
+ export function getEnvVariables(key) {
11
+ try {
12
+ const configPath = path.join(process.cwd(), "@distats_panel", "config.json");
13
+
14
+ if (!fs.existsSync(configPath)) {
15
+ return null;
16
+ }
17
+
18
+ const configFile = fs.readFileSync(configPath, "utf-8").replace(/^\uFEFF/, "");
19
+ const config = JSON.parse(configFile);
20
+
21
+ if (key) {
22
+ return config[key] !== undefined ? config[key] : null;
23
+ }
24
+
25
+ return config;
26
+ } catch (error) {
27
+ return null;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Alias for getEnvVariables to match specific user request naming.
33
+ */
34
+ export const GetEnvVaribels = getEnvVariables;
@@ -0,0 +1,61 @@
1
+ import sqlite3 from "sqlite3";
2
+ import { getEnvVariables } from "./config.js";
3
+ import path from "path";
4
+ import fs from "fs";
5
+
6
+ /**
7
+ * Get and initialize the SQLite database.
8
+ *
9
+ * @returns {Promise<sqlite3.Database>}
10
+ */
11
+ export async function getDatabase() {
12
+ const config = getEnvVariables();
13
+
14
+ if (!config || !config.db_path) {
15
+ throw new Error("SQLite database not configured in config.json");
16
+ }
17
+
18
+ const dbPath = path.resolve(process.cwd(), config.db_path);
19
+
20
+ // Create folder if it doesn't exist
21
+ const dbDir = path.dirname(dbPath);
22
+ if (!fs.existsSync(dbDir)) {
23
+ fs.mkdirSync(dbDir, { recursive: true });
24
+ }
25
+
26
+ return new Promise((resolve, reject) => {
27
+ const db = new sqlite3.Database(dbPath, (err) => {
28
+ if (err) return reject(err);
29
+
30
+ // Initialize tables
31
+ db.run(`
32
+ CREATE TABLE IF NOT EXISTS users (
33
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
34
+ name TEXT NOT NULL,
35
+ password TEXT NOT NULL,
36
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
37
+ )
38
+ `, (err) => {
39
+ if (err) return reject(err);
40
+ resolve(db);
41
+ });
42
+ });
43
+ });
44
+ }
45
+
46
+ /**
47
+ * Execute a query that doesn't return rows (INSERT, UPDATE, DELETE).
48
+ *
49
+ * @param {sqlite3.Database} db
50
+ * @param {string} sql
51
+ * @param {any[]} params
52
+ * @returns {Promise<void>}
53
+ */
54
+ export function runQuery(db, sql, params = []) {
55
+ return new Promise((resolve, reject) => {
56
+ db.run(sql, params, function(err) {
57
+ if (err) return reject(err);
58
+ resolve({ lastID: this.lastID, changes: this.changes });
59
+ });
60
+ });
61
+ }
@@ -0,0 +1,20 @@
1
+ import ora from "ora";
2
+
3
+ export function createLoader(text = "Loading...") {
4
+ const spinner = ora({
5
+ text,
6
+ color: "blue",
7
+ }).start();
8
+
9
+ return {
10
+ update(message) {
11
+ spinner.text = message;
12
+ },
13
+ succeed(message) {
14
+ spinner.succeed(message);
15
+ },
16
+ fail(message) {
17
+ spinner.fail(message);
18
+ },
19
+ };
20
+ }
@@ -0,0 +1,55 @@
1
+ import chalk from "chalk";
2
+
3
+ export function logInfo(message) {
4
+ console.log(chalk.blue(message));
5
+ }
6
+
7
+ export function logSuccess(message) {
8
+ console.log(chalk.green(message));
9
+ }
10
+
11
+ export function logError(message) {
12
+ console.log(chalk.red(message));
13
+ }
14
+
15
+ /**
16
+ * High-fidelity, modern welcomer for the PRISM CLI.
17
+ * Features a minimalist text-only header box with a clickable link.
18
+ */
19
+ export function showWelcome() {
20
+ const version = "1.0.1";
21
+
22
+ // Helper for clickable terminal links
23
+ const link = (text, url) => `\u001b]8;;${url}\u001b\\${text}\u001b]8;;\u001b\\`;
24
+
25
+ const textLines = [
26
+ "",
27
+ `${chalk.bgCyan.black.bold(" DISTATS PANEL ")} ${chalk.white("CLI")} ${chalk.gray("•")} ${chalk.cyan("v" + version)}`,
28
+ chalk.gray(" https://distats.sh/docs/cli"),
29
+ `${chalk.gray(" Powered By")} ${chalk.blue.bold(link("AxonTeam", "https://ax0n.xyz"))}`,
30
+ "",
31
+ ];
32
+
33
+ const boxWidth = 55;
34
+ const borderChar = chalk.gray;
35
+
36
+ // Robust ANSI stripper to calculate visible length supporting complex escapes
37
+ const getVisibleLength = (str) => {
38
+ return str
39
+ .replace(/\u001b\[[\d;]*m/g, "") // Strip color/bold/dim sequences
40
+ .replace(/\u001b\]8;;.*?\u001b\\/g, "") // Strip OSC link delimiters
41
+ .replace(/\u001b\]8;;\u001b\\/g, "") // Strip closing OSC link delimiters
42
+ .length;
43
+ };
44
+
45
+ console.log("");
46
+ console.log(borderChar(` ┌${"─".repeat(boxWidth)}┐`));
47
+
48
+ textLines.forEach((line) => {
49
+ const visibleLength = getVisibleLength(line);
50
+ const padding = " ".repeat(Math.max(0, boxWidth - visibleLength - 2));
51
+ console.log(` ${borderChar("│")} ${line}${padding}${borderChar("│")}`);
52
+ });
53
+
54
+ console.log(borderChar(` └${"─".repeat(boxWidth)}┘`) + "\n");
55
+ }