Start basic CLI framework
This commit is contained in:
parent
4dc1f6d7c8
commit
d57053703a
4
TODO.txt
4
TODO.txt
@ -1,12 +1,12 @@
|
|||||||
TLS support
|
TLS support
|
||||||
internationalization
|
internationalization
|
||||||
uploads & universal path!!
|
uploads
|
||||||
CLI - view routes, template generation, start server, directives, output, args
|
CLI - view routes, template generation, start server, directives, output, args
|
||||||
nicer error and home pages
|
nicer error and home pages
|
||||||
favicon
|
favicon
|
||||||
utility - is_windows, is_linux, is_mac
|
utility - is_windows, is_linux, is_mac
|
||||||
authentication - user/session, oauth, jwt, &c.
|
authentication - user/session, oauth, jwt, &c.
|
||||||
orm enum/bit fields, json handlers, scopes
|
orm enum/bit fields, json handlers, scopes, migrations, schema
|
||||||
redis - redis client, redis rehydrated classes, redis sessions
|
redis - redis client, redis rehydrated classes, redis sessions
|
||||||
less/scss
|
less/scss
|
||||||
notifications - gotify/push/other mechanisms
|
notifications - gotify/push/other mechanisms
|
||||||
|
18
cli/src/CLIAppUnit.ts
Normal file
18
cli/src/CLIAppUnit.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import LifecycleUnit from '../../lib/src/lifecycle/Unit.ts'
|
||||||
|
import {CLIService} from './service/CLI.service.ts'
|
||||||
|
import {Unit} from '../../lib/src/lifecycle/decorators.ts'
|
||||||
|
import {Logging} from '../../lib/src/service/logging/Logging.ts'
|
||||||
|
|
||||||
|
@Unit()
|
||||||
|
export default class CLIAppUnit extends LifecycleUnit {
|
||||||
|
constructor(
|
||||||
|
protected readonly cli: CLIService,
|
||||||
|
protected readonly logger: Logging,
|
||||||
|
) { super() }
|
||||||
|
|
||||||
|
public async up() {
|
||||||
|
this.logger.verbose(`Handling CLI invocation...`)
|
||||||
|
const args = Deno.args
|
||||||
|
console.log('args', {args})
|
||||||
|
}
|
||||||
|
}
|
15
cli/src/CLIUnit.ts
Normal file
15
cli/src/CLIUnit.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import LifecycleUnit from '../../lib/src/lifecycle/Unit.ts'
|
||||||
|
import {CLIService} from './service/CLI.service.ts'
|
||||||
|
import {Unit} from '../../lib/src/lifecycle/decorators.ts'
|
||||||
|
import {UsageDirective} from './directive/UsageDirective.ts'
|
||||||
|
|
||||||
|
@Unit()
|
||||||
|
export default class CLIUnit extends LifecycleUnit {
|
||||||
|
constructor(
|
||||||
|
protected readonly cli: CLIService,
|
||||||
|
) { super() }
|
||||||
|
|
||||||
|
public async up() {
|
||||||
|
this.cli.register_directive(this.make(UsageDirective))
|
||||||
|
}
|
||||||
|
}
|
37
cli/src/directive/Directive.ts
Normal file
37
cli/src/directive/Directive.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import AppClass from '../../../lib/src/lifecycle/AppClass.ts'
|
||||||
|
import {Logging} from '../../../lib/src/service/logging/Logging.ts'
|
||||||
|
|
||||||
|
export abstract class Directive extends AppClass {
|
||||||
|
public abstract readonly keyword: string
|
||||||
|
public abstract readonly help: string
|
||||||
|
|
||||||
|
static options() {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract invoke(): any
|
||||||
|
|
||||||
|
success(message: any) {
|
||||||
|
this.make(Logging).success(message, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
error(message: any) {
|
||||||
|
this.make(Logging).error(message, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
warn(message: any) {
|
||||||
|
this.make(Logging).warn(message, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
info(message: any) {
|
||||||
|
this.make(Logging).info(message, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(message: any) {
|
||||||
|
this.make(Logging).debug(message, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
verbose(message: any) {
|
||||||
|
this.make(Logging).verbose(message, true)
|
||||||
|
}
|
||||||
|
}
|
10
cli/src/directive/UsageDirective.ts
Normal file
10
cli/src/directive/UsageDirective.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import {Directive} from './Directive.ts'
|
||||||
|
|
||||||
|
export class UsageDirective extends Directive {
|
||||||
|
public readonly keyword = 'help'
|
||||||
|
public readonly help = 'Display usage information'
|
||||||
|
|
||||||
|
public async invoke() {
|
||||||
|
console.log('Hello, from Daton CLI.')
|
||||||
|
}
|
||||||
|
}
|
0
cli/src/mod.ts
Normal file
0
cli/src/mod.ts
Normal file
60
cli/src/service/CLI.service.ts
Normal file
60
cli/src/service/CLI.service.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import AppClass from '../../../lib/src/lifecycle/AppClass.ts'
|
||||||
|
import {Service} from '../../../di/src/decorator/Service.ts'
|
||||||
|
import {Collection} from '../../../lib/src/collection/Collection.ts'
|
||||||
|
import {Directive} from '../directive/Directive.ts'
|
||||||
|
import {Logging} from '../../../lib/src/service/logging/Logging.ts'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error thrown when a directive registered with the same keyword as
|
||||||
|
* an existing directive.
|
||||||
|
* @extends Error
|
||||||
|
*/
|
||||||
|
export class DuplicateCLIDirectiveError extends Error {
|
||||||
|
constructor(keyword: string) {
|
||||||
|
super(`A CLI directive with the keyword "${keyword}" already exists.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for registering and managing CLI directives.
|
||||||
|
* @extends AppClass
|
||||||
|
*/
|
||||||
|
@Service()
|
||||||
|
export class CLIService extends AppClass {
|
||||||
|
protected directives: Collection<Directive> = new Collection<Directive>()
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected readonly logger: Logging,
|
||||||
|
) { super() }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a registered directive using its keyword, if one exists.
|
||||||
|
* @param {string} keyword
|
||||||
|
* @return Directive | undefined
|
||||||
|
*/
|
||||||
|
public get_directive_by_keyword(keyword: string): Directive | undefined {
|
||||||
|
return this.directives.firstWhere('keyword', '=', keyword)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a collection of all registered directives.
|
||||||
|
* @return Collection<Directive>
|
||||||
|
*/
|
||||||
|
public get_directives() {
|
||||||
|
return this.directives.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a directive with the service.
|
||||||
|
* @param {Directive} directive
|
||||||
|
*/
|
||||||
|
public register_directive(directive: Directive) {
|
||||||
|
if ( this.get_directive_by_keyword(directive.keyword) ) {
|
||||||
|
throw new DuplicateCLIDirectiveError(directive.keyword)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.verbose(`Registering CLI directive with keyword: ${directive.keyword}`)
|
||||||
|
this.directives.push(directive)
|
||||||
|
}
|
||||||
|
}
|
4
lib/src/error/ErrorWithContext.ts
Normal file
4
lib/src/error/ErrorWithContext.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
export default class ErrorWithContext extends Error {
|
||||||
|
|
||||||
|
}
|
@ -68,7 +68,16 @@ export default class Application {
|
|||||||
* @return Promise<void>
|
* @return Promise<void>
|
||||||
*/
|
*/
|
||||||
async down() {
|
async down() {
|
||||||
|
this.logger.info('Stopping Daton...', true)
|
||||||
|
for ( const unit of this.instantiated_units ) {
|
||||||
|
if ( !unit ) continue
|
||||||
|
await this.stop_unit(unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.logger.warn(`Force exiting...`)
|
||||||
|
Deno.exit()
|
||||||
|
}, 2000)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,8 +115,25 @@ export default class Application {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
unit.status = Status.Error
|
unit.status = Status.Error
|
||||||
this.logger.error(`Error encountered while starting ${unit.constructor.name}. Will attempt to proceed.`)
|
this.logger.error(`Error encountered while starting ${unit.constructor.name}. Will attempt to proceed.`)
|
||||||
this.logger.debug(e.message)
|
this.logger.debug(e)
|
||||||
this.logger.verbose(e)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shut down the given lifecycle unit.
|
||||||
|
* @param {LifecycleUnit} unit
|
||||||
|
*/
|
||||||
|
protected async stop_unit(unit: LifecycleUnit) {
|
||||||
|
try {
|
||||||
|
unit.status = Status.Stopping
|
||||||
|
this.logger.info(`Stopping ${unit.constructor.name}...`)
|
||||||
|
await unit.down()
|
||||||
|
this.logger.verbose(`Successfully stopped ${unit.constructor.name}`)
|
||||||
|
unit.status = Status.Stopped
|
||||||
|
} catch (e) {
|
||||||
|
unit.status = Status.Error
|
||||||
|
this.logger.error(`Error encountered while stopping ${unit.constructor.name}. Will attempt to proceed.`)
|
||||||
|
this.logger.debug(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,11 @@ export default class HttpServer extends LifecycleUnit {
|
|||||||
|
|
||||||
const request_timeout: number = this.config.get('server.request_timeout', 15000)
|
const request_timeout: number = this.config.get('server.request_timeout', 15000)
|
||||||
|
|
||||||
|
Deno.signal(Deno.Signal.SIGINT).then(() => {
|
||||||
|
this.logger.info('Closing server...', true)
|
||||||
|
this._server.close()
|
||||||
|
})
|
||||||
|
|
||||||
for await ( const native_request of this._server ) {
|
for await ( const native_request of this._server ) {
|
||||||
let req: Request = this.make(Request, native_request)
|
let req: Request = this.make(Request, native_request)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user