From 651d24ad3bcf8ddf981e6de18c65a4e8901df9f3 Mon Sep 17 00:00:00 2001 From: 13078417792 <397201698@qq.com> Date: Sat, 19 Jun 2021 17:55:58 +0800 Subject: [PATCH] init --- .eslintrc.yml | 80 +++++++++++++++++++++++++++++ .gitignore | 67 ++++++++++++++++++++++++ @types/index.d.ts | 26 ++++++++++ app.js | 93 +++++++++++++++++++++++++++++++++ bin/www | 96 +++++++++++++++++++++++++++++++++++ config/example.env | 1 + dto/admin.js | 30 +++++++++++ dto/admin.ts | 19 +++++++ dto/init.js | 4 ++ dto/init.ts | 2 + entity/user.ts | 57 +++++++++++++++++++++ event.js | 3 ++ middlewares/ValidateDto.js | 47 +++++++++++++++++ nodemon.json | 19 +++++++ ormconfig.example.json | 11 ++++ package.json | 41 +++++++++++++++ public/stylesheets/style.css | 1 + public/stylesheets/style.less | 8 +++ routes/admin.js | 12 +++++ routes/index.js | 9 ++++ routes/users.js | 9 ++++ tsconfig.json | 70 +++++++++++++++++++++++++ views/error.hjs | 3 ++ views/index.hjs | 11 ++++ 24 files changed, 719 insertions(+) create mode 100644 .eslintrc.yml create mode 100644 .gitignore create mode 100644 @types/index.d.ts create mode 100644 app.js create mode 100644 bin/www create mode 100644 config/example.env create mode 100644 dto/admin.js create mode 100644 dto/admin.ts create mode 100644 dto/init.js create mode 100644 dto/init.ts create mode 100644 entity/user.ts create mode 100644 event.js create mode 100644 middlewares/ValidateDto.js create mode 100644 nodemon.json create mode 100644 ormconfig.example.json create mode 100644 package.json create mode 100644 public/stylesheets/style.css create mode 100644 public/stylesheets/style.less create mode 100644 routes/admin.js create mode 100644 routes/index.js create mode 100644 routes/users.js create mode 100644 tsconfig.json create mode 100644 views/error.hjs create mode 100644 views/index.hjs diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 0000000..cef6c08 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,80 @@ +env: + browser: true + commonjs: true + es2021: true + node: true +extends: 'eslint:recommended' +parserOptions: + ecmaVersion: 12 +rules: + no-multiple-empty-lines: + - error + - max: 1 + maxEOF: 0 + maxBOF: 0 + object-curly-spacing: + - error + - always + space-infix-ops: + - error + - int32Hint: false + space-before-blocks: + - error + - always + space-before-function-paren: + - error + - always + indent: + - error + - 2 + linebreak-style: + - error + - unix + quotes: + - error + - single + semi: + - error + - never +overrides: + - files: + - '*.ts' + extends: + - 'eslint:recommended' + - 'plugin:@typescript-eslint/recommended' + parser: '@typescript-eslint/parser' + parserOptions: + ecmaVersion: 12 + sourceType: module + plugins: + - '@typescript-eslint' + rules: + no-multiple-empty-lines: + - error + - max: 1 + maxEOF: 0 + maxBOF: 0 + object-curly-spacing: + - error + - always + space-infix-ops: + - error + - int32Hint: false + space-before-blocks: + - error + - always + space-before-function-paren: + - error + - always + indent: + - error + - 2 + linebreak-style: + - error + - unix + quotes: + - error + - single + semi: + - error + - never \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d9de9db --- /dev/null +++ b/.gitignore @@ -0,0 +1,67 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next + +dist + +entity/**/*.js + +ormconfig.json \ No newline at end of file diff --git a/@types/index.d.ts b/@types/index.d.ts new file mode 100644 index 0000000..1e01c0c --- /dev/null +++ b/@types/index.d.ts @@ -0,0 +1,26 @@ + +/** + * 错误响应 + */ +interface ExpressResponseError extends Record{ + error:string +} + +/** + * 响应列表 + */ +interface ExpressResponseList> extends Record{ + list:T[] +} + +interface ExpressResponseDetail> extends Record{ + detail:T +} + +declare namespace Express { + export interface Response { + error(error:string,statusCode:number,errObj:Record):ExpressResponseError + list>(list:T,resObj:Record):ExpressResponseList + detail>(detail:T,resObj:Record):ExpressResponseDetail + } +} \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..2a8bf78 --- /dev/null +++ b/app.js @@ -0,0 +1,93 @@ +require('./dto/init') +var createError = require('http-errors') +var express = require('express') +var path = require('path') +var cookieParser = require('cookie-parser') +var lessMiddleware = require('less-middleware') +var logger = require('morgan') +const typeorm = require('typeorm') +const event = require('./event') + +Object.defineProperty(express.response,'error',{ + value:function (error,statusCode = 400,errObj = {}) { + errObj.error = error + this.status(statusCode).send(errObj) + }, + enumerable:false, + writable:false, + configurable:false +}) + +Object.defineProperty(express.response,'list',{ + value:function (data,resObj = {}) { + + resObj.list = data + + this.status(200).send(resObj) + }, + enumerable:false, + writable:false, + configurable:false +}) + +Object.defineProperty(express.response,'detail',{ + value:function (data,resObj = {}) { + + resObj.detail = data + + this.status(200).send(resObj) + }, + enumerable:false, + writable:false, + configurable:false +}) + +/** + * 连接数据库 + */ +typeorm.createConnection().then(()=>{ + + // 连接成功,通知bin/www,开始监听http + event.emit('typeorm-connection') + + console.log('typeorm启动成功') +}) + +var indexRouter = require('./routes/index') +var usersRouter = require('./routes/users') +var adminRouter = require('./routes/admin') + +var app = express() + +// view engine setup +app.set('views', path.join(__dirname, 'views')) +app.set('view engine', 'hjs') + +app.use(logger('dev')) +app.use(express.json()) +app.use(express.urlencoded({ extended: false })) +app.use(cookieParser()) +app.use(lessMiddleware(path.join(__dirname, 'public'))) +app.use(express.static(path.join(__dirname, 'public'))) + +app.use('/', indexRouter) +app.use('/users', usersRouter) +app.use('/admin', adminRouter) + +// catch 404 and forward to error handler +app.use(function (req, res, next) { + next(createError(404)) +}) + +// error handler +app.use(function (err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message + res.locals.error = req.app.get('env') === 'development' ? err : {} + + // render the error page + res.status(err.status || 500) + res.render('error') +}) + +module.exports = app diff --git a/bin/www b/bin/www new file mode 100644 index 0000000..cd0bdaf --- /dev/null +++ b/bin/www @@ -0,0 +1,96 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ +require('dotenv').config({path:'./config/.env'}) +var app = require('../app'); +var debug = require('debug')('express-my-admin:server'); +var http = require('http'); +const event = require('../event') + + +/** + * Get port from environment and store in Express. + */ + +var port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +var server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ +event.on('typeorm-connection',()=>{ + server.listen(port,()=>{ + console.log(`Admin服务启动成功,监听端口${port}`); + }); +}) + +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/config/example.env b/config/example.env new file mode 100644 index 0000000..eb84707 --- /dev/null +++ b/config/example.env @@ -0,0 +1 @@ +PORT=5500 \ No newline at end of file diff --git a/dto/admin.js b/dto/admin.js new file mode 100644 index 0000000..d3921c6 --- /dev/null +++ b/dto/admin.js @@ -0,0 +1,30 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PostAdminMemberDto = void 0; +var class_transformer_1 = require("class-transformer"); +var class_validator_1 = require("class-validator"); +var PostAdminMemberDto = /** @class */ (function () { + function PostAdminMemberDto() { + } + __decorate([ + class_transformer_1.Expose(), + class_validator_1.IsNotEmpty({ message: '登录用户名不能为空' }), + __metadata("design:type", String) + ], PostAdminMemberDto.prototype, "username", void 0); + __decorate([ + class_transformer_1.Expose(), + class_validator_1.IsNotEmpty({ message: '登录密码不能为空' }), + __metadata("design:type", String) + ], PostAdminMemberDto.prototype, "password", void 0); + return PostAdminMemberDto; +}()); +exports.PostAdminMemberDto = PostAdminMemberDto; diff --git a/dto/admin.ts b/dto/admin.ts new file mode 100644 index 0000000..2ce7696 --- /dev/null +++ b/dto/admin.ts @@ -0,0 +1,19 @@ +import { Expose } from 'class-transformer' +import { IsNotEmpty } from 'class-validator' + +export class PostAdminMemberDto { + + @Expose() + @IsNotEmpty({message:'登录用户名不能为空'}) + readonly username!:string + + @Expose() + @IsNotEmpty({message:'登录密码不能为空'}) + readonly password!:string + +} + + + + + diff --git a/dto/init.js b/dto/init.js new file mode 100644 index 0000000..d94da8c --- /dev/null +++ b/dto/init.js @@ -0,0 +1,4 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +require("reflect-metadata"); +require("es6-shim"); diff --git a/dto/init.ts b/dto/init.ts new file mode 100644 index 0000000..3e26a90 --- /dev/null +++ b/dto/init.ts @@ -0,0 +1,2 @@ +import 'reflect-metadata' +import 'es6-shim' \ No newline at end of file diff --git a/entity/user.ts b/entity/user.ts new file mode 100644 index 0000000..87f6f48 --- /dev/null +++ b/entity/user.ts @@ -0,0 +1,57 @@ +import {Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn} from 'typeorm' + +@Entity({name:'account'}) +export class Account { + + @PrimaryGeneratedColumn('uuid') + @Column({ + type:'varchar', + length:255, + primary:true, + readonly:true, + update:false, + comment:'AdminID' + }) + aid!:string + + @Column({ + type:'varchar', + length:255, + comment:'登录账号名' + }) + readonly username!:string + + @Column({ + type:'varchar', + length:255, + comment:'登录密码' + }) + readonly password!:string + + @Column({ + default:true, + comment:'是否可用' + }) + readonly isActive!:boolean + + @CreateDateColumn({ + type:'timestamp', + comment:'账号创建时间' + + }) + readonly created_time!:string + + @UpdateDateColumn({ + type:'timestamp', + comment:'账号更新时间' + }) + readonly update_time!:string + + @Column({ + type:'timestamp', + comment:'最后登录时间', + default:()=>'CURRENT_TIMESTAMP' + }) + readonly lastlogin_time!:string + +} \ No newline at end of file diff --git a/event.js b/event.js new file mode 100644 index 0000000..3adcd78 --- /dev/null +++ b/event.js @@ -0,0 +1,3 @@ +const {EventEmitter} = require('events') + +module.exports = new EventEmitter() \ No newline at end of file diff --git a/middlewares/ValidateDto.js b/middlewares/ValidateDto.js new file mode 100644 index 0000000..1b00abe --- /dev/null +++ b/middlewares/ValidateDto.js @@ -0,0 +1,47 @@ +const { validate } = require('class-validator') +const { plainToClass } = require('class-transformer') + +const data_from_array = ['body','query','params','cookie'] + +/** + * + * @param {new ()=>void} dto + * @param {'body'|'query'|'params'|'headers'|'cookie'} from + * @returns {RequestHandler} + */ +module.exports = function (dtoFunc,from) { + if(!data_from_array.includes(from)) { + throw new Error('校验数据源无效') + } + + return async function (req,res,next) { + + + const dto = plainToClass(dtoFunc,req[from],{ enableImplicitConversion:true,excludeExtraneousValues:true }) + + req[from] = dto + + try{ + var errors = await validate(dto) + }catch(err) { + console.error('数据校验过程中报错') + res.status(500) + res.error('内部错误',500) + return + } + + if(errors.length === 0) { + console.log('数据校验通过') + return next() + } + + console.error('数据校验不通过',errors) + + const error = errors[0] + + const errorMsg = Object.entries(error.constraints)[0][1] + + res.error(errorMsg,400,{ is_validate:true,field:error.property,from }) + + } +} \ No newline at end of file diff --git a/nodemon.json b/nodemon.json new file mode 100644 index 0000000..7d8d4c5 --- /dev/null +++ b/nodemon.json @@ -0,0 +1,19 @@ +{ + "verbose": true, + "ignore": [ + ".vscode", + ".git", + "node_modules/*", + "views/*", + "entity/**/*.js", + "dto/**/*.js", + ".gitignore", + "ormconfig.example.json", + "package.json" + ], + "env": { + "NODE_ENV": "development" + }, + "ext":"js,json,ts", + "exec":"tsc && node ./bin/www" +} \ No newline at end of file diff --git a/ormconfig.example.json b/ormconfig.example.json new file mode 100644 index 0000000..ddbc1f6 --- /dev/null +++ b/ormconfig.example.json @@ -0,0 +1,11 @@ +{ + "type": "mysql", + "host": "localhost", + "port": 3306, + "username": "test", + "password": "test", + "database": "test", + "entities": ["entity/*.js"], + "logging": false, + "synchronize":false +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..9295cfe --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "express-my-admin", + "version": "0.0.0", + "private": true, + "repository": "git@git.zcj.plus:zhouchijian/express-my-admin.git", + "scripts": { + "build": "tsc", + "start": "node ./bin/www", + "dev": "nodemon" + }, + "dependencies": { + "class-transformer": "^0.4.0", + "class-validator": "^0.13.1", + "cookie-parser": "~1.4.4", + "debug": "~2.6.9", + "dotenv": "^10.0.0", + "es6-shim": "^0.35.6", + "express": "~4.16.1", + "hjs": "~0.0.6", + "http-errors": "~1.6.3", + "less-middleware": "~2.2.1", + "morgan": "~1.9.1", + "mysql": "^2.18.1", + "mysql2": "^2.2.5", + "reflect-metadata": "^0.1.13", + "typeorm": "^0.2.34" + }, + "devDependencies": { + "@types/cookie-parser": "^1.4.2", + "@types/express": "^4.17.12", + "@types/http-errors": "^1.8.0", + "@types/less-middleware": "^2.0.31", + "@types/morgan": "^1.9.2", + "@types/node": "^15.12.3", + "@typescript-eslint/eslint-plugin": "^4.27.0", + "@typescript-eslint/parser": "^4.27.0", + "eslint": "^7.29.0", + "nodemon": "^2.0.7", + "typescript": "^4.3.4" + } +} diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css new file mode 100644 index 0000000..73f017c --- /dev/null +++ b/public/stylesheets/style.css @@ -0,0 +1 @@ +body{padding:50px;font:14px "Lucida Grande",Helvetica,Arial,sans-serif}a{color:#00B7FF} \ No newline at end of file diff --git a/public/stylesheets/style.less b/public/stylesheets/style.less new file mode 100644 index 0000000..9453385 --- /dev/null +++ b/public/stylesheets/style.less @@ -0,0 +1,8 @@ +body { + padding: 50px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +} + +a { + color: #00B7FF; +} diff --git a/routes/admin.js b/routes/admin.js new file mode 100644 index 0000000..a0f9fc6 --- /dev/null +++ b/routes/admin.js @@ -0,0 +1,12 @@ +var express = require('express') +const dto = require('../middlewares/ValidateDto') +const { PostAdminMemberDto } = require('../dto/admin') +var router = express.Router() + +router.post('/member', dto(PostAdminMemberDto,'body'),async function (req, res) { + console.log(req.body) + res.send({ message:'Add Admin Member Success' }) + +}) + +module.exports = router diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..ecca96a --- /dev/null +++ b/routes/index.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +/* GET home page. */ +router.get('/', function(req, res, next) { + res.render('index', { title: 'Express' }); +}); + +module.exports = router; diff --git a/routes/users.js b/routes/users.js new file mode 100644 index 0000000..1d58c59 --- /dev/null +++ b/routes/users.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +/* GET users listing. */ +router.get('/', async function(req, res, next) { + res.send('respond with a resource'); +}); + +module.exports = router; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c0594d8 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,70 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "lib": ["es5","es6"], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "dist", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + + /* Module Resolution Options */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +} diff --git a/views/error.hjs b/views/error.hjs new file mode 100644 index 0000000..36a6196 --- /dev/null +++ b/views/error.hjs @@ -0,0 +1,3 @@ +

{{ message }}

+

{{ error.status }}

+
{{ error.stack }}
diff --git a/views/index.hjs b/views/index.hjs new file mode 100644 index 0000000..821a8de --- /dev/null +++ b/views/index.hjs @@ -0,0 +1,11 @@ + + + + {{ title }} + + + +

{{ title }}

+

Welcome to {{ title }}

+ +