opensaas
Version:
Create fully bundled SaaS application (Backend & Frontend) using an interactive CLI
130 lines (110 loc) • 4.07 kB
text/typescript
import prompts from 'prompts';
import ora from 'ora';
import chalk from 'chalk';
import fs from 'fs';
import request from 'request';
import { spawn } from 'child_process';
import { sync as commandExists } from 'command-exists';
type ArgsObject = {
name?: string;
apiKey?: string;
clientId?: string;
host?: string;
};
const REQUIRED_COMMANDS = ['git', 'make'];
const spinner = ora('');
const longCommand = (command: string, text: string, onSuccess?: () => void, onData?: (text: string) => void) => {
return new Promise((resolve, reject) => {
const process = spawn(command, { shell: true });
spinner.start(text);
process.stdout.on('data', data => {
if (onData) {
onData(Buffer.from(data).toString());
}
});
process.on('exit', () => {
spinner.stop();
onSuccess?.();
resolve({});
});
});
};
function download(uri, filename, callback) {
request.head(uri, (err, res, body) => {
if (!err) {
console.log('content-type:', res.headers['content-type']);
console.log('content-length:', res.headers['content-length']);
request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
}
});
}
async function replaceHost(projectName, host) {
const file = `${projectName}/frontend/src/withFrontegg.tsx`;
if (fs.existsSync(file)) {
const data = fs.readFileSync(file, { encoding: 'utf8', flag: 'r' });
fs.writeFileSync(
file,
data.replace(/http:\/\/localhost:8080/g, host),
);
}
}
export async function initRepo(args: ArgsObject): Promise<void> {
const { name, clientId, apiKey, host } = args;
let projectName = name || '';
while (!projectName.length) {
const response = await prompts({
type: 'text',
name: 'project',
message: 'Choose project name',
});
projectName = response.project;
}
for (const command of REQUIRED_COMMANDS) {
if (!commandExists(command)) {
console.log(chalk.red('✖ ') + chalk.white.bold(`please install command: "${command}" to continue`));
return;
}
}
await longCommand(
`git clone --depth 1 https://github.com/frontegg/opensaas ${projectName}`,
chalk.white.bold('Fetching data'),
() => console.log(chalk.green('✔ ') + chalk.white.bold('Finished fetching data')),
console.log,
);
if (clientId) {
await longCommand(`echo #Don't include this file in the source control >> ${projectName}/frontend/.env`, '');
await longCommand(`echo FRONTEGG_CLIENT_ID=${clientId} >> ${projectName}/frontend/.env`, '');
download(
`https://assets.frontegg.com/public-vendor-assets/${clientId}/assets/logo.png`,
`${projectName}/frontend/public/images/logo.png`,
() => console.log('done downloading logo'),
);
if (apiKey) {
await longCommand(`echo FRONTEGG_API_KEY=${apiKey} >> ${projectName}/frontend/.env`, '');
}
}
if (host) {
await replaceHost(projectName, host);
}
await longCommand(
`cd ${projectName} && npm i && npx lerna bootstrap`,
chalk.white.bold('Installing packages, this might take few minutes'),
() => console.log(chalk.green('✔ ') + chalk.white.bold('Finished installing packages')),
console.info,
);
if (commandExists('docker')) {
await longCommand('make provision', chalk.white.bold('Calling docker compose'), () =>
console.log(chalk.green('✔ ') + chalk.white.bold('Finished calling docker compose')),
);
await longCommand('make migrate', chalk.white.bold('Running migrations'), () =>
console.log(chalk.green('✔ ') + chalk.white.bold('Finished running migrations')),
);
} else {
console.log(chalk.red('✖ ') + chalk.white.bold('In order to get the most of Open SaaS, docker command is needed'));
}
console.log(chalk.white.bold('👏👏👏 project installed successfully 👏👏👏\n'));
console.log(chalk.white.bold('To start follow this:'));
console.log(chalk.white.blueBright(` cd ${projectName}`));
console.log(chalk.white.blueBright(' npm run start'));
}