import prompts from 'prompts' import colors from 'colors' import fs from 'fs' import { v4 as uuidv4 } from 'uuid' import { createHash } from 'crypto' import { InitState } from './types' import { copyGitIgnore, updatePackageJson, writeConfigFile, writeDotEnvExampleFile, } from './helpers' export const Init = async () => { const state: InitState = { serviceName: '', serviceDescription: '', port: 3030, apiKey: '', apiKeyType: '', authRotation: '', privateStatus: false, domains: '', repository: '', deployment: '', routing: '', prisma: false, sql: false, customFiles: false, } const deploymentOptions: prompts.Choice[] = [ { title: 'Server -> AutoScaling -> AWS EC2 (Recommended)', value: 'scalingAwsEC2' }, { title: 'Serverless -> AWS Lambda', value: 'awsLambda' }, { title: 'Server -> Single -> AWS EC2', value: 'singleAwsEC2' }, ] const routingOptions: prompts.Choice[] = [ { title: 'Default - Recommended', value: 'default' }, // {title: 'AWS API Gateway', value: 'awsAPIGateway' }, ] const publicPrivateOptions: prompts.Choice[] = [ { title: 'Public - Default', value: false }, { title: 'Private', value: true }, ] const apiKeyOptions: prompts.Choice[] = [ { title: 'Crypto Generated sha256 Hash', value: 'Hash-sha256' }, { title: 'npm uuid.v4()', value: 'uuidv4' }, { title: 'Manually enter in config', value: 'CustomAPIKey' }, ] const RepoOptions: prompts.Choice[] = [ { title: 'GitLab: CI/CD Pipeline; Environments: Dev,Stage,Prod (Recommended)', value: 'gitlab' }, { title: 'Other', value: 'other' }, ] const authRotation: prompts.Choice[] = [ { title: '30 mins - default', value: '*/30 * * * *' }, { title: '1 min', value: '*/1 * * * *' }, { title: '2 mins', value: '*/2 * * * *' }, { title: '5 mins', value: '*/5 * * * *' }, { title: '10 mins', value: '*/10 * * * *' }, { title: '15 mins', value: '*/15 * * * *' }, { title: '20 mins', value: '*/20 * * * *' }, { title: '30 mins', value: '*/30 * * * *' }, { title: '1 hr', value: '0 * * * *' }, { title: '2 hrs', value: '0 */2 * * *' }, { title: '6 hrs', value: '0 */6 * * *' }, { title: '12 hrs', value: '0 */12 * * *' }, { title: '1 day', value: '0 0 * * *' }, { title: '1 month - Not Recommended', value: '0 0 1 */1 *' }, { title: '1 year - Not Recommended', value: '0 0 1 */12 *' }, { title: 'never', value: 'never' }, ] const questions = await prompts([ { type: 'text', name: 'serviceName', message: 'What is your service name?', }, { type: 'text', name: 'serviceDescription', message: 'Enter a description for the service:', }, { type: 'text', name: 'port', message: 'What PORT number would you like to use? (Default: 3030)', initial: '3030', }, { type: 'select', name: 'apiKey', message: 'Select the type of API Key you would like to use?', choices: apiKeyOptions, }, { type: 'select', name: 'auth', message: 'How often would you like to rotate your authentication keys? (This is not login sessions)', choices: authRotation, }, { type: 'confirm', name: 'prisma', message: 'Are you using Prisma?', initial: true, }, { type: 'confirm', name: 'sql', message: 'Are you using SQL / MySQL?', initial: false, }, { type: 'select', name: 'repository', message: 'Which repository do you use?', choices: RepoOptions, }, { type: 'select', name: 'deployment', message: 'Select a deployment method', choices: deploymentOptions, }, { type: 'select', name: 'routing', message: 'Select a routing method', choices: routingOptions, }, { type: 'select', name: 'publicPrivate', message: 'Is this API public or private?', choices: publicPrivateOptions, }, ]) if (questions.publicPrivate === true) { console.info(`\n${colors.yellow.bold('Private APIs mean you will need to whitelist at least 1 domain.')}`) const domainsQuestion = await prompts({ type: 'text', name: 'q', message: 'Comma seperate whitelisted domains. Example: shop.domain.co, www.domain.co, ', initial: false, }) state.domains = domainsQuestion.q } const apiKey = () => { switch (questions.apiKey) { case 'Hash-sha256': return createHash('sha256').digest('hex') case 'uuidv4': return uuidv4() default: return 'EnterCustomAPIKeyHere' } } state.apiKeyType = questions.apiKey state.apiKey = apiKey() state.privateStatus = questions.publicPrivate state.serviceName = questions.serviceName state.serviceDescription = questions.serviceDescription state.port = parseInt(questions.port, 10) state.deployment = questions.deployment state.repository = questions.repository state.routing = questions.routing state.authRotation = questions.auth state.prisma = questions.prisma state.sql = questions.sql const deploymentObj = deploymentOptions.find((v) => v.value === state.deployment) const authObj = authRotation.find((v) => v.value === state.authRotation) console.info(`\n ${colors.yellow.bold('Check The Infomation Below')}`) console.info(`\n ${colors.grey.underline('Service Name : ')}${colors.green(state.serviceName)} ${colors.grey.underline('Description : ')}${colors.green(state.serviceDescription)} ${colors.grey.underline('PORT : ')}${colors.green(`${state.port}`)} ${colors.grey.underline('API Key Type : ')}${colors.green(state.apiKeyType)} ${colors.grey.underline('Generated API Key : ')}${colors.green(state.apiKey)} ${colors.grey.underline('Auth Rotation : ')}${colors.green(`${authObj?.title}`)} ${colors.grey.underline('Prisma : ')}${colors.green(`${state.prisma}`)} ${colors.grey.underline('SQL : ')}${colors.green(`${state.sql}`)} ${colors.grey.underline('Repository : ')}${colors.green(state.repository)} ${colors.grey.underline('Deployment : ')}${colors.green(`${deploymentObj?.title}`)} ${colors.grey.underline('Routing : ')}${colors.green(state.routing)} ${colors.grey.underline('Private : ')}${colors.green(`${state.privateStatus}`)} ${colors.grey.underline('Whitelisted Domains : ')}${colors.green(`${state.domains}`)} \n`) const confirmationQuestions: any[] = [] if (state.prisma && state.deployment === 'awsLambda') { console.info(`\n${colors.yellow.bold('WARNING:: Using Prisma with Lambda, can result in SQL pools being overloaded. ')}`) confirmationQuestions.push({ type: 'confirm', name: 'prismaEC2Confimation', message: 'Are you sure you would like to continue?', initial: false, }) } const confirmation = await prompts([ ...confirmationQuestions, { type: 'confirm', name: 'proceed', message: 'Are you sure you would proceed?', initial: true, }, ]) if (!confirmation.proceed) process.exit(1) const APIPath = `${__dirname}/Templates/API` const prismaPath = `${__dirname}/Templates/prisma` const filesPath = `${__dirname}/Templates/Files` const serviceNamePath = `./${state.serviceName}` fs.mkdir(serviceNamePath, (err) => { if (err) throw err }) try { fs.cpSync(APIPath, `${serviceNamePath}/API`, { recursive: true }) fs.cpSync(APIPath, `${serviceNamePath}/API`, { recursive: true }) if (state.repository === 'gitlab') { fs.cpSync(`${filesPath}/GitLab`, `${serviceNamePath}`, { recursive: true }) } fs.cpSync(`${filesPath}/Generic`, `${serviceNamePath}`, { recursive: true }) await copyGitIgnore(serviceNamePath, filesPath) if (state.prisma) { fs.cpSync(prismaPath, `${serviceNamePath}/prisma`, { recursive: true }) } } catch (error) { console.info('Creation Error', error) } await writeConfigFile(state, serviceNamePath) await writeDotEnvExampleFile(state, serviceNamePath) await updatePackageJson(state, serviceNamePath) console.info(colors.green(`SUCCESS!!! \n${state.serviceName} Created. \n\n`)) return confirmation } export default Init