Add packages/server/cli

This commit is contained in:
Jerko Steiner 2019-09-17 10:55:25 +07:00
parent ae6b00c24c
commit f51b41c576
12 changed files with 110 additions and 43 deletions

View File

@ -21,7 +21,7 @@ export async function syncEsmConfig(...argv: string[]) {
const pkgDir = args.packages
getFolders(pkgDir)
.map(file => path.join(pkgDir, file, TSCONFIG_FILENAME))
.map(folder => path.join(folder, TSCONFIG_FILENAME))
.filter(file => fs.existsSync(file))
.forEach(file => {
const tsconfig = JSON.parse(fs.readFileSync(file, 'utf8'))

View File

@ -10,7 +10,7 @@ import { Config } from './Config'
import { ServerConfigurator } from './configureServer'
import { createServer } from './createServer'
export interface CLIBootstrapParams {
export interface ServerBootstrapParams {
readonly config: Config
readonly configureServer: ServerConfigurator
readonly namespace?: Namespace
@ -26,7 +26,7 @@ function getFunctions(obj: object): Function[] {
.filter(f => typeof f === 'function')
}
export class CLIBootstrap implements Bootstrap {
export class ServerBootstrap implements Bootstrap {
protected config: Config
protected configureServer: ServerConfigurator
protected namespace: Namespace
@ -37,7 +37,7 @@ export class CLIBootstrap implements Bootstrap {
readonly application: Application
readonly database: Database
constructor(params: CLIBootstrapParams) {
constructor(params: ServerBootstrapParams) {
this.config = {
...params.config,
app: {
@ -76,22 +76,6 @@ export class CLIBootstrap implements Bootstrap {
return createServer(configureServer(this.getConfig(), database))
}
async exec(command = 'listen') {
switch (command) {
case 'listen':
await this.listen()
return
case 'migrate':
await this.migrate()
return
case 'migrate-undo':
await this.migrateUndo()
return
default:
throw new Error('Unknown command: ' + command)
}
}
async listen(
port: number | string | undefined = process.env.PORT || 3000,
hostname: string | undefined= process.env.BIND_HOST,
@ -106,18 +90,6 @@ export class CLIBootstrap implements Bootstrap {
}
}
async migrate() {
const connection = await this.database.connect()
await connection.runMigrations()
await connection.close()
}
async migrateUndo() {
const connection = await this.database.connect()
await connection.undoLastMigration()
await connection.close()
}
protected async start(
port: number | string | undefined = process.env.PORT,
hostname?: string,
@ -140,11 +112,12 @@ export class CLIBootstrap implements Bootstrap {
})
const apiLogger = loggerFactory.getLogger('api')
const address = this.getAddress()
if (hostname) {
apiLogger.info('Listening on %s %s', port, hostname)
if (typeof address === 'string') {
apiLogger.info('Listening on %s', address)
} else {
apiLogger.info('Listening on %s', port)
apiLogger.info('Listening on %s %s', address.address, address.port)
}
}
@ -157,7 +130,7 @@ export class CLIBootstrap implements Bootstrap {
}
async close(): Promise<void> {
return new Promise((resolve, reject) => {
await new Promise((resolve, reject) => {
this.server!.close(err => {
if (!err) {
return resolve()

View File

@ -1,4 +1,4 @@
export * from './CLIBootstrap'
export * from './ServerBootstrap'
export * from './Bootstrap'
export * from './Application'
export * from './Config'

View File

@ -1,8 +1,12 @@
import { CLIBootstrap } from './application'
import { ServerBootstrap } from './application'
import { configureServer } from './application/configureServer'
import { config } from './config'
import * as entities from './entities'
import * as migrations from './migrations'
export default new CLIBootstrap({
export default new ServerBootstrap({
config,
entities,
migrations,
configureServer,
})

View File

@ -0,0 +1,72 @@
import { Bootstrap } from "../application";
import { argparse, arg } from "@rondo.dev/argparse";
export class CLI {
constructor(readonly bootstrap: Bootstrap) {
}
execute(argv: string[]) {
const choices: Array<keyof typeof commands> = ['start', 'migrate']
const {parse} = argparse({
command: arg('string', {
default: 'start',
choices,
positional: true,
description: 'Command to run',
}),
args: arg('string[]', {
n: '*',
positional: true,
description: 'Command arguments',
}),
help: arg('boolean', {alias: 'h'}),
})
const args = parse(argv)
const command = args.command as keyof typeof commands
commands[command](this.bootstrap, [args.command, ...args.args])
}
}
const commands = {
async start(bootstrap: Bootstrap, argv: string[]) {
const {parse} = argparse({
host: arg('string', {
description: '',
}),
socket: arg('number', {
alias: 's',
description: 'Socket to listen on',
}),
port: arg('number', {
alias: 'p',
description: 'Port to listen on',
}),
help: arg('boolean', {alias: 'h'}),
})
const args = parse(argv)
await bootstrap.listen(args.port || args.socket, args.host)
},
async migrate(bootstrap: Bootstrap, argv: string[]) {
const {parse} = argparse({
undo: arg('boolean', {
alias: 'u',
description: 'Undo last migration',
}),
help: arg('boolean', {alias: 'h'}),
})
const args = parse(argv)
const {database} = bootstrap
const connection = await database.connect()
try {
await (args.undo
? connection.undoLastMigration()
: connection.runMigrations())
} finally {
await connection.close()
}
},
}

View File

@ -0,0 +1 @@
export * from './CLI'

View File

@ -4,6 +4,7 @@ if (require.main === module) {
}
}
export * from './application'
export * from './cli'
export * from './database'
export * from './entities'
export * from './error'
@ -18,7 +19,8 @@ import * as rpc from './rpc'
export {rpc}
import bootstrap from './bootstrap'
import { CLI } from './cli'
if (require.main === module) {
bootstrap.exec(process.argv[2])
new CLI(bootstrap).execute(process.argv.slice(1))
}

View File

@ -0,0 +1,11 @@
// This file has been autogenerated by exportDir script
export * from './1547031984999-user'
export * from './1547474320589-session'
export * from './1547480973242-sessionUserId'
export * from './1548148128477-comments'
export * from './1548155222136-unique-votes'
export * from './1548347305012-indices'
export * from './1548412884820-role-unique'
export * from './1552227347990-comment-parentid-nullable'
export * from './1552227652042-nullable'
export * from './1552899385211-user-first-last-name'

View File

@ -1,13 +1,13 @@
import { APIDef } from '@rondo.dev/common'
import { createNamespace } from 'cls-hooked'
import { CLIBootstrap } from './application'
import { ServerBootstrap } from './application'
import { configureServer } from './application/configureServer'
import { config } from './config'
import { TestUtils } from './test-utils'
export const exit = jest.fn()
export const bootstrap = new CLIBootstrap({
export const bootstrap = new ServerBootstrap({
config,
configureServer,
namespace: createNamespace('test'),

View File

@ -7,6 +7,9 @@
{
"path": "../common/tsconfig.esm.json"
},
{
"path": "../argparse/tsconfig.esm.json"
},
{
"path": "../client/tsconfig.esm.json"
},
@ -32,4 +35,4 @@
"path": "../validator/tsconfig.esm.json"
}
]
}
}

View File

@ -6,6 +6,7 @@
},
"references": [
{"path": "../common"},
{"path": "../argparse"},
{"path": "../client"},
{"path": "../jsonrpc"},
{"path": "../logger"},