import * as express from 'express'; import {Express, Application as ExpressApplication, Router, Request, Response} from 'express'; import {RequestHandler, NextFunction} from 'express-serve-static-core'; import {Socket, Server as IOServer} from 'socket.io'; import {EventEmitter} from 'events'; import {Server as HttpServer, IncomingMessage, ServerResponse} from 'http'; import {createTransport, Transporter} from 'nodemailer'; import {Connection, createConnection} from 'typeorm'; import {resolve, join} from 'path'; import {readFileSync, writeFileSync} from 'fs'; import {spawnSync} from 'child_process'; import init from './middlewares/init'; import login from './middlewares/login'; import User from './models/user'; import Group from './models/group'; declare global { namespace Express { interface Request { user: User | null; mitamaApp: Application; workspace: Workspace; } interface Response { mitamaApp: Application; workspace: Workspace; } } } export class Application extends EventEmitter { express: ExpressApplication; entities: any[] = []; label: string; path: string; name: string = ''; resourcePath: string; _settings: {} = {}; _database: Connection; _io: (socket: Socket) => void; workspace: Workspace; constructor() { super(); this.express = express(); this.express.use(init(this)); this.express.use(login); } handle(req: Request | IncomingMessage, res: Response | ServerResponse): any { return this.express(req, res); } get settings(): {} { return this._settings; } set settings(data: {}) { this._settings = data; this.workspace.saveAppSettings(this.label, data); } resolve(path: string) { return join(this.resourcePath, path); } use(path: string, ...handlers: RequestHandler[]): void { this.express.use(path, ...handlers); } param(name: string, handler: RequestHandler): void { this.express.param(name, handler); } post(path: string, ...handlers: RequestHandler[]): void { this.express.post(path, ...handlers); } get(path: string, ...handlers: RequestHandler[]): void { this.express.get(path, ...handlers); } put(path: string, ...handlers: RequestHandler[]): void { this.express.put(path, ...handlers); } patch(path: string, ...handlers: RequestHandler[]): void { this.express.patch(path, ...handlers); } delete(path: string, ...handlers: RequestHandler[]): void { this.express.delete(path, ...handlers); } head(path: string, ...handlers: RequestHandler[]): void { this.express.head(path, ...handlers); } options(path: string, ...handlers: RequestHandler[]): void { this.express.options(path, ...handlers); } io(callback: (socket: Socket) => void) { this._io = callback; } async migrate() { this._database = await createConnection({ type: 'sqlite', database: this.resolve('db.sqlite3'), entities: this.entities, }); await this._database.synchronize(); } async database(): Promise { if(!this._database){ this.migrate(); } return this._database; } } export class Workspace extends Application { port: number = 8080; apps: { [key: string]: Application } = {}; rootPath: string = resolve('./'); config: {} = {}; _ioServer: IOServer = new IOServer(); _http: HttpServer; mailer: Transporter; mailFrom: string; jwtSecret: string; entities = [ User, Group ]; constructor() { super(); this.resourcePath = join(this.rootPath, '.mitama'); User.workspace = this; } saveAppSettings(label: string, settings: {}) { this.config['apps'].some(app => { if(app['label'] == label) { app['settings'] = settings; return true; } }); const path = resolve('mitama.config.json'); writeFileSync(path, JSON.stringify(this.config, null, '\t')); } mount(label: string, path: string, app: Application, settings: {} = {}) { app.label = label; this.apps[label] = app; app.workspace = this; app.path = path; app.resourcePath = join(this.rootPath, app.label); app._settings = settings; this.express.use(path, app.express); app.emit('mount', this); if(app.entities.length > 0) app.migrate(); if(app._io){ this._ioServer.of(app.label).on('connection', app._io); } } setMailer(options: any): void { this.mailer = createTransport(options); } loadConfig() { const path = resolve('mitama.config.json'); console.log('loading '+path+'...'); const data = readFileSync(path); this.config = JSON.parse(data.toString('utf8')); this.port = this.config['port']; this.setMailer(this.config['mailer']); this.mailFrom = this.config['mailFrom']; this.jwtSecret = this.config['jwtSecret']; this.config['apps'].forEach(app => { let pkg; try { pkg = require(resolve('node_modules/' + app['package'])); } catch(e) { console.log(e); const {error}= spawnSync('npm', ['install', '--save', app['package']], { cwd: resolve('./'), stdio: 'inherit' }); if(error){ pkg = require(resolve('node_modules/' + app['package'])); } } if(pkg.default) pkg = pkg.default; pkg.package = app['package']; this.mount(app.label, app.path, pkg, app['settings']); }); } listen(port: number = 8080): this { this._http = this.express.listen(port); this._ioServer.listen(this._http); console.log('listening on '+port); return this; } async moksha() { console.log(` 摩訶般若波羅密多心経観自在菩薩行深般若波羅密多時照見五 蘊皆空度一切苦厄舎利子色不異空空不異色色即是空空即是色 受想行識亦復如是舎利子是諸法空相不生不滅不垢不浄不増不 減是故空中無色無受想行識無眼耳鼻舌身意無色声香味触法無 眼 界乃至 無 意   識界 無無 明亦無 無明 尽乃 至  無  老 死亦 無老 死 尽  無  苦 集 滅 道 無 智 亦 無得 以無   所 得 故 菩   提 薩 垂依般 若 波羅 蜜多 故 心 無圭礙 無 圭 礙 故無有恐怖遠離一切顛倒夢想究竟涅槃三世諸仏依般若波羅蜜 多故得阿耨多羅三藐三菩提故知般若波羅蜜多是大神呪是大明 呪是無上呪是無等等呪能除一切苦真実不虚故説般若波羅蜜多 呪即説呪曰羯諦羯諦波羅羯諦波羅僧羯諦菩提薩婆訶般若心経 `.trim()); console.log(""); await this.loadConfig(); console.log(""); this.migrate(); this.listen(); } get baseUrl() { return ( (this.config['ssl'] ? 'https' : 'http') + '://' + this.config['host'] + (this.config['port'] == 80 ? '' : ':' + this.config['port']) ); } } export * as pandit from 'pandit'; export * as orm from 'typeorm'; export { default as authMiddleware } from './middlewares/auth'; export {Router, Request, Response, static as serveStatic, json, raw, text, urlencoded} from 'express'; export {User, Group};