@printweave/cli
Version:
269 lines (226 loc) • 9.35 kB
JavaScript
import shell, {env} from 'shelljs';
import path from 'path';
import {fileURLToPath} from 'url';
import {Command} from 'commander';
import concurrently from 'concurrently';
import {createRequire} from 'module';
import url from 'url';
import {checkUpdates} from "./update.mjs"
const require = createRequire(import.meta.url);
const program = new Command();
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
import {getPathPackage} from './paths.js';
import inquirer from 'inquirer';
import {User} from "@printweave/models";
import dotenv from 'dotenv';
import * as fs from "node:fs";
import * as os from "node:os";
import * as crypto from "node:crypto";
program
.version(require('../package.json').version)
.description('PrintWeave CLI');
program
.command('start')
.description('Start the frontend and API server')
.option('-m, --method <method>', 'Method to start the server, default is node, available methods: node, forever', 'node')
.option('--skip-update-check', 'Force update without prompt')
.action(async (options) => {
if (options.skipUpdateCheck) {
console.log('\x1b[33m%s\x1b[0m', `
╭──────────────────────────────────────────────────────────────────────────────────╮
│ │
│ ⚠️ \x1b[1m\x1b[33mWARNING:\x1b[0m\x1b[33m This flag will skip the update check. Please ensure the program │
│ is up-to-date to avoid potential issues. │
│ │
╰──────────────────────────────────────────────────────────────────────────────────╯
`);
} else {
await checkUpdates("@printweave/cli")
}
const method = options.method || 'node';
if (methodIsInstalled(method)) {
const apiPath = getPathPackage('@printweave/api');
const apiDir = path.resolve(path.dirname(apiPath), '..');
const {result} = concurrently([
{
command: "cd " + apiDir + " && " + launchByMethod(method, apiPath),
name: 'api',
prefixColor: 'magenta'
},
{command: 'echo "Frontend server is not implemented yet"', name: 'frontend', prefixColor: 'blue'}
]);
result.then(() => {
console.log('Exited with code 0');
}, () => {
console.log('Exited with code 1');
});
} else {
console.log(`Method ${method} is not installed`);
}
});
program
.command('api')
.description('Start the API server')
.option('-m, --method <method>', 'Method to start the server, default is node, available methods: node, forever', 'node')
.action((options) => {
const method = options.method || 'node';
const apiPath = getPathPackage('@printweave/api');
const apiDir = path.resolve(path.dirname(apiPath), '..');
if (methodIsInstalled(method)) {
shell.cd(apiDir);
shell.exec(launchByMethod(method, apiPath));
} else {
console.log(`Method ${method} is not installed`);
}
});
program
.command('migrate')
.option('-r, --rollback', 'Rollback the last migration', false)
.description('Run database migrations')
.action(async (options, sCommand) => {
const apiPath = getPathPackage('@printweave/api');
const apiDir = path.resolve(path.dirname(apiPath), '..');
process.chdir(apiDir);
const api = await import("file://" + apiDir + '/dist/main.js');
const {migrations} = await api.getMigrationsForMigrate();
if (options.rollback) {
migrations.rollback();
} else {
migrations.migrate();
}
});
program
.command('configure')
.description('Configure the PrintWeave API')
.action(async () => {
console.log('Let\'s configure the API');
const apiPath = getPathPackage('@printweave/api');
const apiDir = path.resolve(path.dirname(apiPath), '..');
process.chdir(apiDir);
const envPath = path.resolve(apiDir, '.env');
if (!fs.existsSync(envPath)) {
fs.writeFileSync(envPath, '');
}
const envContent = fs.readFileSync(envPath, 'utf8');
const env = dotenv.parse(envContent);
const hasSecretKey = env.SECRET_KEY !== undefined;
const {generateKey} = await inquirer.prompt([
{
type: 'confirm',
name: 'generateKey',
message: 'Do you want to generate a new secret key?' + (hasSecretKey ? ' (This will overwrite the existing key)' : ' (There is no secret key set)'),
default: !hasSecretKey
}]);
if (generateKey) {
const secretKey = crypto.randomBytes(64).toString('hex');
console.log('Setting the secret key');
setEnvValue('SECRET_KEY', secretKey, envPath);
}
const migrations = await import("file://" + apiDir + '/dist/migrations.js');
console.log('Checking for pending migrations');
if ((await migrations.umzug.pending()).length > 0) {
console.log('There are pending migrations, do you want to run them?');
const {runMigrations} = await inquirer.prompt([
{
type: 'confirm',
name: 'runMigrations',
message: 'Are you sure you want to run the migrations?',
default: true
}
]);
if (runMigrations) {
await migrations.migrate();
} else {
console.log('Please run the migrations before configuring the API');
return;
}
} else {
console.log('There are no pending migrations');
}
const {port} = await inquirer.prompt([
{
type: 'input',
name: 'port',
message: 'Enter the API port',
default: env.PORT || 3000
}
]);
console.log('Setting the API port');
setEnvValue('PORT', port, envPath);
const {websocketPort} = await inquirer.prompt([
{
type: 'input',
name: 'websocketPort',
message: 'Enter the API websocket port',
default: env.WEBSOCKET_PORT || 3001
}]);
console.log('Setting the API websocket port');
setEnvValue('WEBSOCKET_PORT', websocketPort, envPath);
const {username, password, email} = await inquirer.prompt([
{
type: 'input',
name: 'username',
message: 'Enter the admin username'
},
{
type: 'password',
name: 'password',
message: 'Enter the admin password'
},
{
type: 'input',
name: 'email',
message: 'Enter the admin email'
}
]);
console.log('Creating the admin user');
const user = User.build({
username,
password,
email
});
try {
await user.save();
} catch (error) {
console.error('Error creating the admin user', error.message);
return;
}
console.log('Admin user created');
console.log('Configuration completed');
console.log('You can start the API server by running `printweave api`');
console.log('You can start the frontend and API by running `printweave start`');
});
program.parse(process.argv);
function methodIsInstalled(method) {
return shell.which(method);
}
function launchByMethod(method, file) {
switch (method) {
case 'node':
return `node ${file}`;
case 'forever':
return `forever start ${file}`;
default:
console.log(`Method ${method} is not supported`);
return false;
}
}
function setEnvValue(key, value, path) {
// read file from hdd & split if from a linebreak to a array
const ENV_VARS = fs.readFileSync(path, "utf8").split(os.EOL);
// find the env we want based on the key
const target = ENV_VARS.indexOf(ENV_VARS.find((line) => {
return line.match(new RegExp(key));
}));
if (target === -1) {
// if the key does not exist, add it to the end of the file
ENV_VARS.push(`${key}=${value}`);
} else {
// replace the key/value with the new value
ENV_VARS.splice(target, 1, `${key}=${value}`);
}
// write everything back to the file system
fs.writeFileSync(path, ENV_VARS.join(os.EOL));
}