├── db └── .gitkeep ├── src ├── core │ ├── sql-parser │ │ ├── index.ts │ │ ├── create-parser.ts │ │ └── column-parser.ts │ ├── generatorEntities │ │ └── index.ts │ ├── index.ts │ └── generatorDbConfig │ │ └── index.ts ├── entities │ ├── index.ts │ └── LocalUser.entity.ts ├── helper │ ├── index.ts │ ├── resHandler.ts │ └── helper.spec.ts.bak ├── http │ ├── app.controller.spec.ts │ ├── configEntities │ │ ├── user.entity.ts │ │ ├── config.entity.ts │ │ ├── db-config.entity.ts │ │ ├── role.entity.ts │ │ ├── role-config.entity.ts │ │ ├── custom-mid.entity.ts │ │ ├── index.ts │ │ ├── app-config.entity.ts │ │ ├── table-config.entity.ts │ │ └── table-right.entity.ts │ ├── app.controller.ts │ ├── middlewares │ │ ├── user-auth.middleware.ts │ │ ├── MiddlewareQueue.ts │ │ │ └── index.ts │ │ └── auth.middleware.ts │ ├── user │ │ ├── user.module.ts │ │ ├── user.service.ts │ │ └── user.controller.ts │ ├── dbConfig.ts │ ├── app-config │ │ ├── app-config.module.ts │ │ └── app-config.service.ts │ ├── json │ │ └── json.module.ts │ ├── app.module.ts │ └── main.ts └── cache │ └── index.ts ├── index.js ├── Test.db ├── scripts ├── dev-front.sh └── build-front.sh ├── nestconfig.json ├── sessionDB.db ├── doc └── img │ ├── 1.png │ ├── 2.png │ ├── 3.png │ └── 4.png ├── public ├── index │ └── home.css ├── admin-v2 │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── static │ │ ├── media │ │ │ └── bg.8b5a8d0a.png │ │ ├── js │ │ │ ├── 6.0dd1aff9.chunk.js │ │ │ ├── 18.91b39f27.chunk.js │ │ │ ├── 11.0057f626.chunk.js │ │ │ ├── 50.b6d92937.chunk.js │ │ │ ├── 41.4137029b.chunk.js │ │ │ ├── 40.ad6fc218.chunk.js │ │ │ ├── 13.dca24a65.chunk.js │ │ │ ├── 7.c4afaab6.chunk.js │ │ │ ├── 21.d8c8edbe.chunk.js │ │ │ ├── 25.74dd882b.chunk.js │ │ │ ├── 15.05f4e6d7.chunk.js │ │ │ ├── 34.5fd8f91a.chunk.js │ │ │ ├── 43.918bf7a7.chunk.js │ │ │ ├── 19.525729c9.chunk.js │ │ │ ├── 14.ba9f8461.chunk.js │ │ │ ├── runtime-main.36d15c89.js │ │ │ ├── 33.9f0d2e2b.chunk.js │ │ │ ├── 31.a0b54766.chunk.js │ │ │ ├── 22.0197d5ed.chunk.js │ │ │ ├── 51.45e6108c.chunk.js │ │ │ ├── 36.c7cb4a4e.chunk.js │ │ │ ├── 9.4823414c.chunk.js │ │ │ ├── 47.81a9dec1.chunk.js │ │ │ ├── 20.04dceb0f.chunk.js │ │ │ ├── 39.e090dec2.chunk.js │ │ │ ├── 5.e4d0ce22.chunk.js │ │ │ ├── 12.70c75e19.chunk.js │ │ │ ├── 10.26569b66.chunk.js │ │ │ ├── 17.cfc7b5dd.chunk.js │ │ │ ├── 32.c7bf58f4.chunk.js │ │ │ ├── 23.6ee99c07.chunk.js │ │ │ ├── 48.034bad67.chunk.js │ │ │ ├── 0.4a2e4b01.chunk.js │ │ │ ├── 49.11fd2409.chunk.js │ │ │ ├── 4.14efecbc.chunk.js │ │ │ └── 16.16eacabc.chunk.js │ │ └── css │ │ │ └── main.2a0c1ea2.chunk.css │ ├── manifest.json │ └── service-worker.js ├── admin │ ├── favicon.png │ └── assets │ │ ├── img │ │ └── logo.d6291a77.png │ │ ├── fonts │ │ ├── element-icons.732389de.ttf │ │ └── element-icons.535877f5.woff │ │ ├── css │ │ ├── welcome.fcc466af.css │ │ ├── user-manage.ea123a64.css │ │ ├── helper.58fcf0db.css │ │ ├── reset-psd.a1f420a5.css │ │ ├── config-manage.a617972b.css │ │ ├── home.4ab99e99.css │ │ └── config-right.25a789e2.css │ │ └── js │ │ ├── about.a9d4b32d.js │ │ ├── reset-psd.0e3e3ccf.js │ │ ├── welcome.5778f591.js │ │ ├── config-manage.fd8feb23.js │ │ ├── helper.83845cb4.js │ │ ├── home.232bf7bd.js │ │ └── user-manage.ce98cc89.js └── lib │ └── element │ └── fonts │ └── element-icons.woff ├── .gitignore ├── ConfigDatabase.db ├── nodemon.json ├── compile ├── build.sh └── deploy.sh ├── ormconfig.js.example ├── jest.json ├── pm2.config.test.json ├── pm2.config.json ├── tsconfig.json ├── README.md ├── ab-test.txt ├── package.json └── views └── admin-v2.ejs /db/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/core/sql-parser/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('ts-node/register') 2 | require('./src/http/main') -------------------------------------------------------------------------------- /Test.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/Test.db -------------------------------------------------------------------------------- /scripts/dev-front.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./views/admin 4 | npm run dev -------------------------------------------------------------------------------- /nestconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "ts", 3 | "entryFile": "src/main.ts" 4 | } -------------------------------------------------------------------------------- /sessionDB.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/sessionDB.db -------------------------------------------------------------------------------- /doc/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/doc/img/1.png -------------------------------------------------------------------------------- /doc/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/doc/img/2.png -------------------------------------------------------------------------------- /doc/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/doc/img/3.png -------------------------------------------------------------------------------- /doc/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/doc/img/4.png -------------------------------------------------------------------------------- /public/index/home.css: -------------------------------------------------------------------------------- 1 | .home-container { 2 | width: 1000px; 3 | margin: 0 auto; 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | ormconfig.js 4 | views/admin-v2 5 | *.map 6 | ./db/*.json -------------------------------------------------------------------------------- /ConfigDatabase.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/ConfigDatabase.db -------------------------------------------------------------------------------- /public/admin-v2/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /scripts/build-front.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./views/admin 4 | npm install 5 | npm run build -------------------------------------------------------------------------------- /public/admin/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/public/admin/favicon.png -------------------------------------------------------------------------------- /public/admin-v2/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/public/admin-v2/favicon.ico -------------------------------------------------------------------------------- /public/admin-v2/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/public/admin-v2/logo192.png -------------------------------------------------------------------------------- /public/admin-v2/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/public/admin-v2/logo512.png -------------------------------------------------------------------------------- /public/admin/assets/img/logo.d6291a77.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/public/admin/assets/img/logo.d6291a77.png -------------------------------------------------------------------------------- /public/admin-v2/static/media/bg.8b5a8d0a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/public/admin-v2/static/media/bg.8b5a8d0a.png -------------------------------------------------------------------------------- /public/lib/element/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/public/lib/element/fonts/element-icons.woff -------------------------------------------------------------------------------- /public/admin/assets/fonts/element-icons.732389de.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/public/admin/assets/fonts/element-icons.732389de.ttf -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts", 4 | "ignore": ["src/**/*.spec.ts", "dbConfig.ts", "src/entities/*.ts"], 5 | "exec": "node ./index" 6 | } -------------------------------------------------------------------------------- /public/admin/assets/fonts/element-icons.535877f5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinaskin/apijson-node/HEAD/public/admin/assets/fonts/element-icons.535877f5.woff -------------------------------------------------------------------------------- /src/entities/index.ts: -------------------------------------------------------------------------------- 1 | import { LocalUserEntity, LocalUser } from './LocalUser.entity' 2 | 3 | 4 | const config = { 5 | LocalUser 6 | } 7 | 8 | export { 9 | config, 10 | LocalUserEntity 11 | } -------------------------------------------------------------------------------- /src/helper/index.ts: -------------------------------------------------------------------------------- 1 | // some helper function here 2 | 3 | import { successHandler, baseHandler, HTTP_CODE } from './resHandler' 4 | 5 | export { 6 | successHandler, 7 | baseHandler, 8 | HTTP_CODE, 9 | } 10 | -------------------------------------------------------------------------------- /compile/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ $1 = "YES" ]]; then 4 | # rm -rf node_modules 5 | echo "NPM INSTALLING ..." 6 | npm install --registry=https://registry.npm.taobao.org/ 2>&1 7 | fi 8 | 9 | echo "npm run build BEGIN ..." 10 | npm run build 11 | echo "npm run build SUCCESS ..." -------------------------------------------------------------------------------- /public/admin/assets/css/welcome.fcc466af.css: -------------------------------------------------------------------------------- 1 | .welcome-container{width:100%;height:100%;background:#fff;padding:20px}.welcome-container .info-text{text-align:left;margin:0;margin-left:35px}.welcome-container .info-text.msg{color:#888}.welcome-container .change-log{margin-top:30px;width:600px}.welcome-container .change-log .timeline{margin-top:50px} -------------------------------------------------------------------------------- /ormconfig.js.example: -------------------------------------------------------------------------------- 1 | const SOURCE_PATH = process.env.NODE_ENV === 'production' ? 'dist' : 'src' 2 | 3 | module.exports = { 4 | "type": "mysql", 5 | "host": "localhost", 6 | "port": 3306, 7 | "username": "", 8 | "password": "", 9 | "database": "", 10 | "entities": [`${SOURCE_PATH}/**/**.entity{.ts,.js}`], 11 | "synchronize": false 12 | } -------------------------------------------------------------------------------- /public/admin/assets/css/user-manage.ea123a64.css: -------------------------------------------------------------------------------- 1 | .user-manage-container[data-v-1739e42d]{width:calc(100% - 250px);height:100%;padding:20px;text-align:left}.user-manage-container .title[data-v-1739e42d]{text-align:left;font-size:16px;margin:0}.user-manage-container .btns[data-v-1739e42d]{width:calc(100% - 40px);text-align:right}.user-manage-container .content[data-v-1739e42d]{margin-top:30px} -------------------------------------------------------------------------------- /public/admin/assets/js/about.a9d4b32d.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["about"],{f820:function(n,e,t){"use strict";t.r(e);var a=function(){var n=this,e=n.$createElement,t=n._self._c||e;return t("span",[n._v("1")])},c=[],s={},u=s,o=t("2877"),r=Object(o["a"])(u,a,c,!1,null,"3a30297c",null);e["default"]=r.exports}}]); 2 | //# sourceMappingURL=about.a9d4b32d.js.map -------------------------------------------------------------------------------- /jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": [ 3 | "ts", 4 | "tsx", 5 | "js", 6 | "json" 7 | ], 8 | "transform": { 9 | "\\.ts$": "ts-jest" 10 | }, 11 | "testRegex": "/src/.*\\.(test|spec|e2e-spec).(ts|tsx|js)$", 12 | "collectCoverageFrom" : ["src/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"], 13 | "coverageReporters": ["json", "lcov"] 14 | } -------------------------------------------------------------------------------- /pm2.config.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [{ 3 | "name": "ai-apijson-node", 4 | "script": "./dist/http/main.js", 5 | "log_date_format": "YYYY-MM-DD HH:mm:ss", 6 | "instances" : 1, 7 | "instance_var": "INSTANCE_ID", 8 | "exec_mode": "cluster", 9 | "min_uptime": "60s", 10 | "max_memory_restart": "600M", 11 | "max_restarts": 2, 12 | "env": { 13 | "NODE_ENV": "test" 14 | } 15 | }] 16 | } -------------------------------------------------------------------------------- /src/http/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { AppController } from './app.controller' 2 | import { config } from '../entities/index' 3 | 4 | describe('AppController', () => { 5 | let appController: AppController 6 | 7 | beforeEach(() => { 8 | appController = new AppController() 9 | }) 10 | 11 | describe('health test', () => { 12 | it('should return string "ok"', async () => { 13 | expect(await appController.root()).toBe('ok') 14 | }) 15 | }) 16 | 17 | }) -------------------------------------------------------------------------------- /pm2.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [{ 3 | "name": "ai-apijson-node", 4 | "script": "./dist/http/main.js", 5 | "log_date_format": "YYYY-MM-DD HH:mm:ss", 6 | "instances" : 4, 7 | "instance_var": "INSTANCE_ID", 8 | "exec_mode": "cluster", 9 | "min_uptime": "60s", 10 | "max_memory_restart": "600M", 11 | "max_restarts": 2, 12 | "env": { 13 | "NODE_ENV": "production" 14 | }, 15 | "env_production": { 16 | "NODE_ENV": "production" 17 | } 18 | }] 19 | } 20 | -------------------------------------------------------------------------------- /public/admin/assets/css/helper.58fcf0db.css: -------------------------------------------------------------------------------- 1 | .helper-container[data-v-102b3d8b]{width:calc(100% - 250px);height:100%;padding:20px;text-align:left}.helper-container .title[data-v-102b3d8b]{text-align:left;font-size:16px;margin:0}.helper-container .btns[data-v-102b3d8b]{width:calc(100% - 40px);text-align:right}.helper-container .content[data-v-102b3d8b]{margin-top:30px;width:calc(100% - 40px)}.helper-container .content .form[data-v-102b3d8b]{width:800px}.helper-container .content .submit-btn[data-v-102b3d8b]{margin-left:320px} -------------------------------------------------------------------------------- /public/admin/assets/css/reset-psd.a1f420a5.css: -------------------------------------------------------------------------------- 1 | .user-manage-container[data-v-08c4d06c]{width:calc(100% - 250px);height:100%;padding:20px;text-align:left}.user-manage-container .title[data-v-08c4d06c]{text-align:left;font-size:16px;margin:0}.user-manage-container .btns[data-v-08c4d06c]{width:calc(100% - 40px);text-align:right}.user-manage-container .content[data-v-08c4d06c]{margin-top:30px;width:calc(100% - 40px)}.user-manage-container .content .form[data-v-08c4d06c]{width:400px}.user-manage-container .content .submit-btn[data-v-08c4d06c]{margin-left:120px} -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": false, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "lib": ["es2017"], 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es6", 12 | "sourceMap": true, 13 | "allowJs": true, 14 | "outDir": "./dist" 15 | }, 16 | "include": [ 17 | "src/**/*" 18 | ], 19 | "exclude": [ 20 | "node_modules", 21 | "**/*.spec.ts" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /src/http/configEntities/user.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' 2 | 3 | @Entity('user') 4 | export class ApiJsonUserEntity { 5 | @PrimaryGeneratedColumn() 6 | id: number = 0 7 | 8 | @Column({length: 20}) 9 | username: string = '' 10 | 11 | @Column() 12 | role: string = 'user' 13 | 14 | @Column() 15 | realName: string 16 | 17 | @Column() 18 | password: string 19 | 20 | @Column() 21 | createdAt: Date 22 | 23 | @Column() 24 | updatedAt: Date 25 | 26 | @Column() 27 | isDeleted: number = 0 28 | } -------------------------------------------------------------------------------- /src/http/configEntities/config.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' 2 | 3 | /** 4 | * v1 config table 5 | */ 6 | @Entity('config') 7 | export class ApiJsonConfigEntity { 8 | @PrimaryGeneratedColumn() 9 | id: number 10 | 11 | @Column() 12 | name: string 13 | 14 | @Column() 15 | appSign: string 16 | 17 | @Column() 18 | authType: string 19 | 20 | @Column() 21 | tableRight: string 22 | 23 | @Column() 24 | createdAt: Date 25 | 26 | @Column() 27 | updatedAt: Date 28 | 29 | @Column() 30 | operator: string 31 | } -------------------------------------------------------------------------------- /src/helper/resHandler.ts: -------------------------------------------------------------------------------- 1 | export const HTTP_CODE = { 2 | SUCCESS: 0, 3 | WARNING: 10000 4 | } 5 | 6 | interface successFeedback { 7 | code: number, 8 | msg: string, 9 | data: any 10 | } 11 | 12 | export function successHandler( 13 | data: any = {}, 14 | msg: string = 'success' 15 | ): successFeedback { 16 | return baseHandler(HTTP_CODE.SUCCESS, data, msg) 17 | } 18 | 19 | export function baseHandler ( 20 | code: number, 21 | data: any = {}, 22 | msg: string = 'success' 23 | ): successFeedback { 24 | return { 25 | code, 26 | data, 27 | msg 28 | } 29 | } -------------------------------------------------------------------------------- /src/http/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Get, Controller, Render, Req, Inject, Request } from '@nestjs/common' 2 | import { config } from '../entities' 3 | 4 | @Controller() 5 | export class AppController { 6 | /** 7 | * 健康检查接口 8 | */ 9 | @Get('/test') 10 | async root(): Promise { 11 | return 'ok' 12 | } 13 | 14 | @Get('/caniuse') 15 | getEntity(@Req() req?): object { 16 | return config 17 | } 18 | 19 | @Get('/old') 20 | @Render('admin') 21 | home() { 22 | return {} 23 | } 24 | 25 | @Get('/') 26 | @Render('admin-v2') 27 | renderV2() { 28 | return {} 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /public/admin-v2/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/http/configEntities/db-config.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' 2 | 3 | /** 4 | * v2 db配置管理 5 | */ 6 | @Entity('apijson_db_config') 7 | export class ApiJsonDBConfigEntity { 8 | @PrimaryGeneratedColumn() 9 | id: number 10 | 11 | @Column() 12 | name: string 13 | 14 | @Column() 15 | config: string 16 | 17 | @Column() 18 | key: string 19 | 20 | @Column() 21 | valid: number 22 | 23 | @Column() 24 | created_at: Date 25 | 26 | @Column() 27 | updated_at: Date 28 | 29 | @Column() 30 | operator: string 31 | 32 | @Column() 33 | is_deleted: number 34 | } -------------------------------------------------------------------------------- /src/http/configEntities/role.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' 2 | 3 | @Entity('roles') 4 | export class ApiJsonRoleAliasEntity { 5 | @PrimaryGeneratedColumn() 6 | id: number 7 | 8 | /** 9 | * "single" | "multiple" 10 | */ 11 | @Column() 12 | role_type: string 13 | 14 | @Column() 15 | get_fields: string 16 | 17 | @Column() 18 | update_fields: string 19 | 20 | @Column() 21 | status: number 22 | 23 | @Column() 24 | createdAt: Date 25 | 26 | @Column() 27 | updatedAt: Date 28 | 29 | @Column() 30 | operator: string 31 | 32 | @Column() 33 | is_deleted: number 34 | } -------------------------------------------------------------------------------- /src/http/middlewares/user-auth.middleware.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NestMiddleware } from '@nestjs/common' 2 | import { Request, Response } from 'express' 3 | import { ReqHasLoginId } from '../json/json.controller' 4 | import { baseHandler } from '../../helper' 5 | 6 | export type ReqWithSession = { 7 | session?: any 8 | } 9 | 10 | @Injectable() 11 | export class UserAuthMiddleware implements NestMiddleware { 12 | async use(req: Request & ReqHasLoginId & ReqWithSession, res: Response, next: Function) { 13 | if (req.session.userInfo) { 14 | next() 15 | } else { 16 | res.send(baseHandler(403, { 17 | redirect: '/' 18 | }, 'not login')) 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/entities/LocalUser.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from "typeorm" 2 | 3 | @Entity('user') 4 | export class LocalUserEntity { 5 | @PrimaryGeneratedColumn() 6 | id: number 7 | 8 | @Column() 9 | user: string 10 | } 11 | 12 | export const LocalUser = { 13 | "column": [ 14 | { 15 | "desc": "", 16 | "key": "id", 17 | "type": "number", 18 | "isPrimary": true 19 | }, 20 | { 21 | "desc": "", 22 | "key": "user", 23 | "type": "string", 24 | "isPrimary": false 25 | }, 26 | ], 27 | "primary": "id", 28 | "uuid": "", 29 | "uniqueKey": "id", 30 | "db": "default", 31 | "desc": "用户表" 32 | } 33 | -------------------------------------------------------------------------------- /src/core/generatorEntities/index.ts: -------------------------------------------------------------------------------- 1 | // [ 2 | // { 3 | // id: 1, 4 | // name: '3.26medicine', 5 | // key: 'medicine', 6 | // dev: '192.168.3.26>>>3305>>>medicine>>>root>>>realdoctor', 7 | // prod: '192.168.3.26>>>3305>>>medicine>>>root>>>realdoctor', 8 | // } 9 | // ] 10 | 11 | const makeImportSyntax = list => { 12 | return list.map(item => `import { ${item.filename}Entity, ${item.filename} } from './${item.filename}.entity' 13 | `).join('') 14 | } 15 | 16 | 17 | export const genEntityIndex = (list) => { 18 | return `${makeImportSyntax(list)} 19 | 20 | const config = { 21 | ${list.map(item => ` ${item.filename}`).join(`, 22 | `)} 23 | } 24 | 25 | export { 26 | config, 27 | ${list.map(item => ` ${item.filename}Entity`).join(`, 28 | `)} 29 | }` 30 | } -------------------------------------------------------------------------------- /src/http/configEntities/role-config.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' 2 | 3 | /** 4 | * v2 role-config 角色配置表 5 | */ 6 | @Entity('apijson_role_config') 7 | export class ApiJsonRoleConfigEntity { 8 | @PrimaryGeneratedColumn() 9 | id: number 10 | 11 | /** 12 | * name 某个权限集合的名字 13 | */ 14 | @Column() 15 | name: string 16 | 17 | @Column() 18 | desc: string 19 | 20 | @Column() 21 | table: string 22 | 23 | /** 24 | * table_right_list table_right集合 id[] 25 | */ 26 | @Column() 27 | table_right_list: string 28 | 29 | @Column() 30 | created_at: Date 31 | 32 | @Column() 33 | updated_at: Date 34 | 35 | @Column() 36 | operator: string 37 | 38 | @Column() 39 | is_deleted: number 40 | } -------------------------------------------------------------------------------- /src/http/configEntities/custom-mid.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' 2 | 3 | /** 4 | * v2 custom-mid 自定义逻辑表 5 | */ 6 | @Entity('apijson_custom_mid') 7 | export class ApiJsonCustomMidEntity { 8 | @PrimaryGeneratedColumn() 9 | id: number 10 | 11 | @Column() 12 | name: string 13 | 14 | @Column() 15 | desc: string 16 | 17 | /** 18 | * type req, res, ... 19 | */ 20 | @Column() 21 | type: string 22 | 23 | @Column() 24 | function: string 25 | 26 | /** 27 | * checked 0-未审核 1-审核通过 28 | */ 29 | @Column() 30 | checked: number 31 | 32 | @Column() 33 | created_at: Date 34 | 35 | @Column() 36 | updated_at: Date 37 | 38 | @Column() 39 | operator: string 40 | 41 | @Column() 42 | is_deleted: number 43 | } -------------------------------------------------------------------------------- /src/http/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common' 2 | import { TypeOrmModule } from '@nestjs/typeorm' 3 | import { ApiJsonUserEntity } from '../configEntities/user.entity' 4 | import { ApiJsonUserController } from './user.controller' 5 | import { ApiJsonUserService } from './user.service' 6 | import { UserAuthMiddleware } from '../middlewares/user-auth.middleware' 7 | 8 | 9 | @Module({ 10 | imports: [TypeOrmModule.forFeature([ApiJsonUserEntity], 'apijsonDB')], 11 | providers: [ApiJsonUserService], 12 | controllers: [ 13 | ApiJsonUserController 14 | ], 15 | exports: [] 16 | }) 17 | export class ApiJsonUserModule implements NestModule { 18 | public configure(consumer: MiddlewareConsumer) { 19 | consumer.apply(UserAuthMiddleware).exclude( 20 | '/common/login' 21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /public/admin/assets/css/config-manage.a617972b.css: -------------------------------------------------------------------------------- 1 | .config-manage-page-container{padding:20px;-webkit-box-sizing:border-box;box-sizing:border-box}.config-manage-page-container .page-title{text-align:left;margin:0;margin-bottom:20px}.config-manage-page-container .btns{text-align:right}.config-manage-page-container .cards{margin-top:20px;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.config-manage-page-container .cards .box-card{width:calc(50% - 20px);margin-bottom:20px}.config-manage-page-container .cards .box-card:nth-child(odd){margin-right:20px}.config-manage-page-container .cards .box-card .text.item .line{width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.config-manage-page-container .cards .box-card .text.item .line .title{margin-right:10px;color:#999} -------------------------------------------------------------------------------- /src/http/configEntities/index.ts: -------------------------------------------------------------------------------- 1 | import { ApiJsonAppConfigEntity } from './app-config.entity' 2 | import { ApiJsonConfigEntity } from './config.entity' 3 | import { ApiJsonCustomMidEntity } from './custom-mid.entity' 4 | import { ApiJsonRoleConfigEntity } from './role-config.entity' 5 | import { ApiJsonRoleAliasEntity } from './role.entity' 6 | import { ApiJsonTableRightEntity } from './table-right.entity' 7 | import { ApiJsonUserEntity } from './user.entity' 8 | import { ApiJsonTableConfigEntity } from './table-config.entity' 9 | import { ApiJsonDBConfigEntity } from './db-config.entity' 10 | 11 | export { 12 | ApiJsonAppConfigEntity, 13 | ApiJsonConfigEntity, 14 | ApiJsonCustomMidEntity, 15 | ApiJsonRoleConfigEntity, 16 | ApiJsonRoleAliasEntity, 17 | ApiJsonTableRightEntity, 18 | ApiJsonUserEntity, 19 | ApiJsonTableConfigEntity, 20 | ApiJsonDBConfigEntity, 21 | } -------------------------------------------------------------------------------- /src/http/configEntities/app-config.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' 2 | 3 | /** 4 | * v2 app-config table 应用管理 5 | */ 6 | @Entity('apijson_application_config') 7 | export class ApiJsonAppConfigEntity { 8 | @PrimaryGeneratedColumn() 9 | id: number 10 | 11 | @Column() 12 | name: string 13 | 14 | @Column() 15 | app_code: string 16 | 17 | /** 18 | * req middleware[] 19 | */ 20 | @Column() 21 | req_middleware_list: string 22 | 23 | /** 24 | * res middleware[] 25 | */ 26 | @Column() 27 | res_middleware_list: string 28 | 29 | /** 30 | * app_roles role[] 31 | */ 32 | @Column() 33 | app_roles: string 34 | 35 | @Column() 36 | created_at: Date 37 | 38 | @Column() 39 | updated_at: Date 40 | 41 | @Column() 42 | operator: string 43 | 44 | @Column() 45 | is_deleted: number 46 | } -------------------------------------------------------------------------------- /src/helper/helper.spec.ts.bak: -------------------------------------------------------------------------------- 1 | import * as helper from './index' 2 | 3 | describe('helperTest', () => { 4 | describe('HTTP_CODE valid', () => { 5 | it('should be number', () => { 6 | Object.keys(helper.HTTP_CODE).forEach(key => { 7 | expect(typeof helper.HTTP_CODE[key] === 'number').toBe(true) 8 | }) 9 | }) 10 | }) 11 | 12 | describe('baseHandler', () => { 13 | it('should be normal response', () => { 14 | expect(helper.baseHandler(0, {}, 'test msg')).toMatchObject({ 15 | code: 0, 16 | data: {}, 17 | msg: 'test msg' 18 | }) 19 | }) 20 | }) 21 | 22 | describe('successHandler', () => { 23 | it('should be normal response', () => { 24 | expect(helper.successHandler({}, 'success msg')).toMatchObject({ 25 | code: helper.HTTP_CODE.SUCCESS, 26 | data: {}, 27 | msg: 'success msg' 28 | }) 29 | }) 30 | }) 31 | }) -------------------------------------------------------------------------------- /src/http/configEntities/table-config.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' 2 | 3 | /** 4 | * v2 app-config 表配置管理 5 | */ 6 | @Entity('apijson_table_config') 7 | export class ApiJsonTableConfigEntity { 8 | @PrimaryGeneratedColumn() 9 | id: number 10 | 11 | @Column() 12 | filename: string 13 | 14 | @Column() 15 | name: string 16 | 17 | @Column() 18 | table_name: string 19 | 20 | @Column() 21 | db: string 22 | 23 | @Column() 24 | sql: string 25 | 26 | @Column() 27 | columns: string 28 | 29 | @Column() 30 | primary_key: string 31 | 32 | @Column() 33 | uuid: string 34 | 35 | @Column() 36 | unique_key: string 37 | 38 | @Column() 39 | status: number 40 | 41 | @Column() 42 | created_at: Date 43 | 44 | @Column() 45 | updated_at: Date 46 | 47 | @Column() 48 | operator: string 49 | 50 | @Column() 51 | is_deleted: number 52 | } -------------------------------------------------------------------------------- /public/admin-v2/static/js/6.0dd1aff9.chunk.js: -------------------------------------------------------------------------------- 1 | (this["webpackJsonpadmin-v2"]=this["webpackJsonpadmin-v2"]||[]).push([[6],{756:function(e,t,n){"use strict";n.r(t),n.d(t,"conf",(function(){return s})),n.d(t,"language",(function(){return o}));var s={comments:{lineComment:"#"}},o={defaultToken:"keyword",ignoreCase:!0,tokenPostfix:".azcli",str:/[^#\s]/,tokenizer:{root:[{include:"@comment"},[/\s-+@str*\s*/,{cases:{"@eos":{token:"key.identifier",next:"@popall"},"@default":{token:"key.identifier",next:"@type"}}}],[/^-+@str*\s*/,{cases:{"@eos":{token:"key.identifier",next:"@popall"},"@default":{token:"key.identifier",next:"@type"}}}]],type:[{include:"@comment"},[/-+@str*\s*/,{cases:{"@eos":{token:"key.identifier",next:"@popall"},"@default":"key.identifier"}}],[/@str+\s*/,{cases:{"@eos":{token:"string",next:"@popall"},"@default":"string"}}]],comment:[[/#.*$/,{cases:{"@eos":{token:"comment",next:"@popall"}}}]]}}}}]); 2 | //# sourceMappingURL=6.0dd1aff9.chunk.js.map -------------------------------------------------------------------------------- /compile/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "type node: `type node`" 3 | 4 | PM2_COMMAND=$1 5 | SERVER_PORT=9771 6 | 7 | # 安装pm2 logrotate依赖 8 | pm2 describe pm2-logrotate > /dev/null 9 | if [ $? -ne 0 ]; then 10 | echo 'install pm2-logrotate' 11 | pm2 install pm2-logrotate 12 | pm2 set pm2-logrotate:compress true 13 | else 14 | echo 'pm2-logrotate has running' 15 | pm2 set pm2-logrotate:compress true 16 | fi 17 | 18 | start () { 19 | export PORT=$SERVER_PORT 20 | pm2 start ./pm2.config.json 21 | } 22 | 23 | stop () { 24 | pm2 delete ai-apijson-node 25 | } 26 | 27 | restart () { 28 | pm2 describe server > /dev/null 29 | if [ $? -eq 0 ]; then 30 | pm2 reload ai-apijson-node 31 | else 32 | start 33 | fi 34 | } 35 | 36 | case $PM2_COMMAND in 37 | start) 38 | start 39 | ;; 40 | stop) 41 | stop 42 | ;; 43 | restart) 44 | restart 45 | ;; 46 | *) 47 | ;; 48 | esac 49 | 50 | sleep 2 51 | pm2 status 52 | -------------------------------------------------------------------------------- /src/http/configEntities/table-right.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' 2 | 3 | /** 4 | * v2 table-right 单表权限表 5 | */ 6 | @Entity('apijson_table_right') 7 | export class ApiJsonTableRightEntity { 8 | @PrimaryGeneratedColumn() 9 | id: number 10 | 11 | /** 12 | * name 某个权限集合的名字 13 | */ 14 | @Column() 15 | name: string 16 | 17 | @Column() 18 | desc: string 19 | 20 | /** 21 | * table 关联table的名字 22 | */ 23 | @Column() 24 | table: string 25 | 26 | /** 27 | * get fields string[] 28 | */ 29 | @Column() 30 | get_fields: string 31 | 32 | /** 33 | * update fields string[] 34 | */ 35 | @Column() 36 | update_fields: string 37 | 38 | /** 39 | * add_right 0-不允许 1-允许 40 | */ 41 | @Column() 42 | add_right: number 43 | 44 | /** 45 | * del_right 0-不允许 1-允许 46 | */ 47 | @Column() 48 | del_right: number 49 | 50 | @Column() 51 | created_at: Date 52 | 53 | @Column() 54 | updated_at: Date 55 | 56 | @Column() 57 | operator: string 58 | 59 | @Column() 60 | is_deleted: number 61 | } -------------------------------------------------------------------------------- /public/admin-v2/static/js/18.91b39f27.chunk.js: -------------------------------------------------------------------------------- 1 | (this["webpackJsonpadmin-v2"]=this["webpackJsonpadmin-v2"]||[]).push([[18],{768:function(e,n,s){"use strict";s.r(n),s.d(n,"conf",(function(){return t})),s.d(n,"language",(function(){return i}));var t={comments:{lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},i={defaultToken:"",tokenPostfix:".ini",escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/^\[[^\]]*\]/,"metatag"],[/(^\w+)(\s*)(\=)/,["key","","delimiter"]],{include:"@whitespace"},[/\d+/,"number"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/"/,"string",'@string."'],[/'/,"string","@string.'"]],whitespace:[[/[ \t\r\n]+/,""],[/^\s*[#;].*$/,"comment"]],string:[[/[^\\"']+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/["']/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}]]}}}}]); 2 | //# sourceMappingURL=18.91b39f27.chunk.js.map -------------------------------------------------------------------------------- /src/http/user/user.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable} from '@nestjs/common' 2 | import { InjectRepository } from '@nestjs/typeorm' 3 | import { Repository, getConnection } from 'typeorm' 4 | import { ApiJsonUserEntity } from '../configEntities/user.entity' 5 | 6 | @Injectable() 7 | export class ApiJsonUserService { 8 | @InjectRepository(ApiJsonUserEntity, 'apijsonDB') 9 | private readonly ApiJsonUserRepository: Repository 10 | 11 | async findOne(options: any = {}): Promise { 12 | return this.ApiJsonUserRepository.findOne(options) 13 | } 14 | 15 | async find(options: any = {}): Promise { 16 | return this.ApiJsonUserRepository.find(options) 17 | } 18 | 19 | async count(options: any = {}): Promise { 20 | return this.ApiJsonUserRepository.count(options) 21 | } 22 | 23 | async update(id, options: any = {}): Promise { 24 | return this.ApiJsonUserRepository.update(id, options) 25 | } 26 | 27 | async delete(options: any = {}): Promise { 28 | return this.ApiJsonUserRepository.delete(options) 29 | } 30 | 31 | async insert(options: any = {}): Promise { 32 | return this.ApiJsonUserRepository.insert(options) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/http/dbConfig.ts: -------------------------------------------------------------------------------- 1 | 2 | import { TypeOrmModule } from '@nestjs/typeorm' 3 | import { 4 | ApiJsonConfigEntity, 5 | ApiJsonUserEntity, 6 | ApiJsonAppConfigEntity, 7 | ApiJsonCustomMidEntity, 8 | ApiJsonRoleAliasEntity, 9 | ApiJsonRoleConfigEntity, 10 | ApiJsonTableRightEntity, 11 | ApiJsonTableConfigEntity, 12 | ApiJsonDBConfigEntity, 13 | } from './configEntities' 14 | import { resolve } from 'path' 15 | 16 | export const dbConfig = [ 17 | TypeOrmModule.forRoot({ 18 | name: 'apijsonDB', 19 | type: 'sqlite', 20 | database: resolve(__dirname, '../../ConfigDatabase.db'), 21 | entities: [ 22 | ApiJsonUserEntity, ApiJsonConfigEntity, 23 | 24 | ApiJsonAppConfigEntity, ApiJsonCustomMidEntity, ApiJsonRoleAliasEntity, 25 | ApiJsonRoleConfigEntity, ApiJsonTableRightEntity, 26 | ApiJsonTableConfigEntity,ApiJsonDBConfigEntity 27 | ], 28 | synchronize: false 29 | }), 30 | TypeOrmModule.forRoot({ 31 | name: 'default', 32 | type: 'sqlite', 33 | database: resolve(__dirname, '../../Test.db'), 34 | entities: [ `${ [ 'development' ].indexOf(process.env.NODE_ENV) > -1 ? 'src' : 'dist' }/**/**.entity{.ts,.js}` ], 35 | synchronize: false 36 | }) 37 | ] 38 | -------------------------------------------------------------------------------- /public/admin/assets/css/home.4ab99e99.css: -------------------------------------------------------------------------------- 1 | .full-page{width:100vw;height:100vh;overflow-y:auto}.full-page .nav-bar .nav-title{width:250px;height:60px;background:#ff2828;font-size:24px;line-height:60px;text-align:center;font-weight:700;color:#fff;cursor:pointer}.full-page .nav-bar .el-menu-vertical{width:250px;height:calc(100vh - 60px);text-align:left}.full-page .main-page{height:calc(100vh - 60px);top:60px;overflow-y:auto}.full-page .main-page,.full-page .status-bar{width:calc(100vw - 250px);position:fixed;left:250px}.full-page .status-bar{top:0;background:#ff2828;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.full-page .status-bar,.full-page .status-bar .user-info{height:60px;display:-webkit-box;display:-ms-flexbox;display:flex}.full-page .status-bar .user-info{width:280px;line-height:60px;-ms-flex-pack:distribute;justify-content:space-around;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.full-page .status-bar .user-info .user{width:150px;text-overflow:ellipsis;overflow:hidden;height:60px;text-align:right;color:#fff}.full-page .status-bar .user-info .btn{width:80px;height:30px;font-size:14px;color:#fff;font-weight:700;cursor:pointer;background:#5c1818;border:3px solid #fff;line-height:30px;border-radius:10px} -------------------------------------------------------------------------------- /src/http/app-config/app-config.module.ts: -------------------------------------------------------------------------------- 1 | import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common' 2 | import { TypeOrmModule } from '@nestjs/typeorm' 3 | import { 4 | ApiJsonAppConfigEntity, 5 | ApiJsonRoleConfigEntity, 6 | ApiJsonCustomMidEntity, 7 | ApiJsonTableRightEntity, 8 | ApiJsonTableConfigEntity, 9 | ApiJsonDBConfigEntity, 10 | } from '../configEntities' 11 | import { ApiJsonAppConfigController } from './app-config.controller' 12 | import { ApiJsonAppConfigService } from './app-config.service' 13 | import { UserAuthMiddleware } from '../middlewares/user-auth.middleware' 14 | 15 | 16 | @Module({ 17 | imports: [ 18 | TypeOrmModule.forFeature([ 19 | ApiJsonAppConfigEntity, 20 | ApiJsonRoleConfigEntity, 21 | ApiJsonCustomMidEntity, 22 | ApiJsonTableRightEntity, 23 | ApiJsonTableConfigEntity, 24 | ApiJsonDBConfigEntity, 25 | ], 'apijsonDB'), 26 | ], 27 | providers: [ApiJsonAppConfigService], 28 | controllers: [ 29 | ApiJsonAppConfigController 30 | ], 31 | exports: [ApiJsonAppConfigService] 32 | }) 33 | export class ApiJsonAppConfigModule implements NestModule { 34 | public configure(consumer: MiddlewareConsumer) { 35 | consumer.apply(UserAuthMiddleware).exclude( 36 | '/common/login' 37 | ) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/core/sql-parser/create-parser.ts: -------------------------------------------------------------------------------- 1 | interface TableStructure { 2 | name: string 3 | desc: string 4 | fields: FieldStructure[] 5 | } 6 | 7 | interface FieldStructure { 8 | fieldName: string 9 | type: number | Date | string | boolean 10 | desc: string 11 | length?: number 12 | required?: boolean 13 | autoIncrement?: boolean 14 | } 15 | 16 | interface SQLParserOptions { 17 | primary?: string 18 | uniqueKey?: string 19 | // if `uuid` equals `uniqueKey` 20 | // primary is a never-return field 21 | // instead, `uuid` is return for column updating or getting 22 | uuid?: string 23 | } 24 | 25 | // let inputString = `CREATE TABLE \`screen_map_data\` ( 26 | // \`id\` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id', 27 | // \`city_name\` varchar(20) DEFAULT NULL COMMENT '城市名称', 28 | // \`hospital_name\` varchar(32) DEFAULT NULL COMMENT '落地医院名称', 29 | // \`gmt_created\` datetime NOT NULL COMMENT '创建时间', 30 | // \`gmt_modified\` datetime NOT NULL COMMENT '修改时间', 31 | // PRIMARY KEY (\`id\`) 32 | // ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='AI大屏-地图信息表';` 33 | 34 | export function createSQLParser ( 35 | sqlString: string, 36 | options: SQLParserOptions = {}, 37 | type: 'mysql' | 'sqlite' = 'mysql' 38 | ): TableStructure | undefined { 39 | 40 | return undefined 41 | } -------------------------------------------------------------------------------- /public/admin-v2/service-worker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to your Workbox-powered service worker! 3 | * 4 | * You'll need to register this file in your web app and you should 5 | * disable HTTP caching for this file too. 6 | * See https://goo.gl/nhQhGp 7 | * 8 | * The rest of the code is auto-generated. Please don't update this file 9 | * directly; instead, make changes to your Workbox build configuration 10 | * and re-run your build process. 11 | * See https://goo.gl/2aRDsh 12 | */ 13 | 14 | importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"); 15 | 16 | importScripts( 17 | "/admin-v2/precache-manifest.a9765a37657dcaf1f9d6af30e4bb0de7.js" 18 | ); 19 | 20 | self.addEventListener('message', (event) => { 21 | if (event.data && event.data.type === 'SKIP_WAITING') { 22 | self.skipWaiting(); 23 | } 24 | }); 25 | 26 | workbox.core.clientsClaim(); 27 | 28 | /** 29 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to 30 | * requests for URLs in the manifest. 31 | * See https://goo.gl/S9QRab 32 | */ 33 | self.__precacheManifest = [].concat(self.__precacheManifest || []); 34 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); 35 | 36 | workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/admin-v2/index.html"), { 37 | 38 | blacklist: [/^\/_/,/\/[^\/?]+\.[^\/]+$/], 39 | }); 40 | -------------------------------------------------------------------------------- /src/http/json/json.module.ts: -------------------------------------------------------------------------------- 1 | import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common' 2 | import { TypeOrmModule } from '@nestjs/typeorm' 3 | import { JsonService } from './json.service' 4 | import { JsonController } from './json.controller' 5 | import * as entity from '../../entities' 6 | import { ApiJsonConfigEntity } from '../configEntities/config.entity' 7 | import { ApiJsonAppConfigService } from '../app-config/app-config.service' 8 | import { ApiJsonAppConfigEntity, ApiJsonRoleConfigEntity, ApiJsonTableRightEntity, ApiJsonCustomMidEntity, ApiJsonTableConfigEntity, ApiJsonDBConfigEntity } from '../configEntities' 9 | 10 | const { config, ...allEntity } = entity 11 | const entities = Object.keys(allEntity).map(key => entity[key]) 12 | 13 | @Module({ 14 | imports: [ 15 | TypeOrmModule.forFeature(entities, 'default'), 16 | TypeOrmModule.forFeature([ 17 | ApiJsonConfigEntity, 18 | ApiJsonAppConfigEntity, 19 | ApiJsonRoleConfigEntity, 20 | ApiJsonTableRightEntity, 21 | ApiJsonCustomMidEntity, 22 | ApiJsonTableConfigEntity, 23 | ApiJsonDBConfigEntity, 24 | ], 'apijsonDB'), 25 | ], 26 | providers: [JsonService, ApiJsonAppConfigService], 27 | controllers: [ 28 | JsonController 29 | ], 30 | exports: [] 31 | }) 32 | export class JsonModule implements NestModule { 33 | public configure(consumer: MiddlewareConsumer) { 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/http/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common' 2 | import { AppController } from './app.controller' 3 | import { Connection } from 'typeorm' 4 | import { JsonModule } from './json/json.module' 5 | import { AuthMiddleware } from './middlewares/auth.middleware' 6 | import { JsonController } from './json/json.controller' 7 | import { ApiJsonUserModule } from './user/user.module' 8 | import { ApiJsonAppConfigModule } from './app-config/app-config.module' 9 | import { dbConfig } from './dbConfig' 10 | 11 | @Module({ 12 | imports: [ 13 | ...dbConfig, 14 | JsonModule, 15 | ApiJsonUserModule, 16 | ApiJsonAppConfigModule, 17 | ], 18 | controllers: [ 19 | AppController, 20 | ], 21 | providers: [ 22 | ], 23 | }) 24 | // export class ApplicationModule {} 25 | export class ApplicationModule implements NestModule { 26 | constructor( 27 | private readonly connection: Connection, 28 | // private readonly routeAliasService: ApiJsonRouteAliasService 29 | ) { 30 | // this.cacheRoutes() 31 | } 32 | 33 | configure(consumer: MiddlewareConsumer): void { 34 | consumer.apply(AuthMiddleware).forRoutes( 35 | JsonController 36 | ) 37 | } 38 | 39 | // async cacheRoutes() { 40 | // const routeAliasList = await this.routeAliasService.find() 41 | // // todo Map route alias to memory cache 42 | // // console.log('==>', routeAliasList) 43 | // } 44 | } 45 | -------------------------------------------------------------------------------- /public/admin-v2/static/js/11.0057f626.chunk.js: -------------------------------------------------------------------------------- 1 | (this["webpackJsonpadmin-v2"]=this["webpackJsonpadmin-v2"]||[]).push([[11],{761:function(t,r,e){"use strict";e.r(r),e.d(r,"conf",(function(){return s})),e.d(r,"language",(function(){return n}));var s={brackets:[],autoClosingPairs:[],surroundingPairs:[]},n={keywords:[],typeKeywords:[],tokenPostfix:".csp",operators:[],symbols:/[=> ### A ApiJson Server build with Nest.js and TypeORM 4 | 5 | ---------- 6 | 7 | # 启动项目 8 | 9 | 10 | 1.安装依赖 11 | 12 | ```shell 13 | $ npm install 14 | ``` 15 | 16 | 国内安装不上可以使用cnpm 17 | 18 | 2.启动项目 19 | 20 | ```shell 21 | $ npm run dev # 开发 22 | 23 | $ npm run start # 正式 24 | ``` 25 | 26 | 27 | 3. 默认端口9771 打开 http://localhost:9771/ 28 | 29 | 默认账号 admin/admin 30 | 31 | 32 | 4.测试接口 33 | 34 | ```shell 35 | curl --location --request POST 'localhost:9771/api/get?app-sign=local-admin' \ 36 | --header 'Content-Type: application/json' \ 37 | --data-raw '{ 38 | "list[]": { 39 | "LocalUser": {} 40 | } 41 | }' 42 | ``` 43 | app-sign也可放在header里 44 | 45 | ```shell 46 | curl --location --request POST 'localhost:9771/api/get' \ 47 | --header 'Content-Type: application/json' \ 48 | --header 'app-sign: local-admin' \ 49 | --data-raw '{ 50 | "list[]": { 51 | "LocalUser": {} 52 | } 53 | }' 54 | ``` 55 | 56 | 57 | ![](./doc/img/2.png) 58 | 59 | 因为设置了校验中间件 随机返回403 60 | 61 | 正常返回结果如下 62 | 63 | ![](./doc/img/3.png) 64 | 65 | 66 | 5.配置其他数据库 67 | 68 | - 权限管理>数据库管理>添加数据库 69 | 70 | - `测试连接` 71 | 72 | - `添加表` 73 | 74 | - 点击 `写入配置` 75 | 76 | 6.配置表权限 77 | 78 | - 权限管理>表管理>配置表权限角色 79 | 80 | - `部署所有表配置` 81 | 82 | - `热重启` (热重启是针对pm2启动的,如果是npm run dev启动的可自行重启) 83 | 84 | 7.配置角色 85 | 86 | - 角色包含的是上述中的表的权限的集合 87 | 88 | 8.配置应用 89 | 90 | - 应用管理>新增应用 91 | 92 | - 应用可以配置多个角色,自定义逻辑 93 | 94 | 9.其他文档, 请点击`文档` 95 | 96 | 10.数据统计可点击左上角`APIJSON`,统计最近7天数据 97 | 98 | ![]('./doc/img/4.png) -------------------------------------------------------------------------------- /src/http/middlewares/MiddlewareQueue.ts/index.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import Axios from 'axios' 3 | import sha1hex = require('sha1-hex') 4 | import { HttpStatus } from "@nestjs/common"; 5 | 6 | class MiddlewareQueue { 7 | private taskList: string[] 8 | private req: any 9 | private res: any 10 | private next: any 11 | 12 | constructor ( 13 | middlewareList: string[], 14 | req: Request, 15 | res: Response, 16 | next: Function 17 | ) { 18 | this.taskList = middlewareList 19 | this.next = next 20 | this.req = req 21 | this.res = res 22 | } 23 | 24 | execTaskList () { 25 | if (!this.taskList.length) { 26 | this.next() 27 | return 28 | } 29 | const task = this.taskList.shift() 30 | new Promise((resolve, reject) => { 31 | try { 32 | (async (req, res, next, tool) => { 33 | eval(`( 34 | ${task} 35 | )(req, res, next, tool)`) 36 | })(this.req, this.res, resolve, { 37 | Axios, 38 | sha1hex, 39 | // ... 40 | // some other tool instance here 41 | }) 42 | } catch (e) { 43 | console.log(e) 44 | this.res.statusCode = HttpStatus.INTERNAL_SERVER_ERROR 45 | this.res.send('出错了') 46 | reject(e) 47 | } 48 | }).then(() => { 49 | if (this.taskList.length) { 50 | this.execTaskList() 51 | } else { 52 | this.next() 53 | } 54 | }) 55 | } 56 | } 57 | 58 | export default MiddlewareQueue -------------------------------------------------------------------------------- /ab-test.txt: -------------------------------------------------------------------------------- 1 | This is ApacheBench, Version 2.3 <$Revision: 1826891 $> 2 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 3 | Licensed to The Apache Software Foundation, http://www.apache.org/ 4 | 5 | Benchmarking localhost (be patient) 6 | 7 | 8 | Server Software: 9 | Server Hostname: localhost 10 | Server Port: 3000 11 | 12 | Document Path: /api/get 13 | Document Length: 220 bytes 14 | 15 | Concurrency Level: 200 16 | Time taken for tests: 5.798 seconds 17 | Complete requests: 10000 18 | Failed requests: 0 19 | Total transferred: 4650000 bytes 20 | Total body sent: 2650000 21 | HTML transferred: 2200000 bytes 22 | Requests per second: 1724.65 [#/sec] (mean) 23 | Time per request: 115.966 [ms] (mean) 24 | Time per request: 0.580 [ms] (mean, across all concurrent requests) 25 | Transfer rate: 783.17 [Kbytes/sec] received 26 | 446.32 kb/s sent 27 | 1229.49 kb/s total 28 | 29 | Connection Times (ms) 30 | min mean[+/-sd] median max 31 | Connect: 0 0 0.8 0 9 32 | Processing: 18 115 24.8 108 201 33 | Waiting: 9 114 24.8 108 201 34 | Total: 18 115 24.7 108 201 35 | 36 | Percentage of the requests served within a certain time (ms) 37 | 50% 108 38 | 66% 121 39 | 75% 129 40 | 80% 135 41 | 90% 150 42 | 95% 162 43 | 98% 181 44 | 99% 187 45 | 100% 201 (longest request) 46 | -------------------------------------------------------------------------------- /src/core/index.ts: -------------------------------------------------------------------------------- 1 | // parser, handler here for json body transfer to ORM executor 2 | import { RequestParser, JobInterface, StatusEnum } from './traverse' 3 | 4 | export interface jsonBodyParserReturn { 5 | queue: JobInterface[], 6 | errors: string[], 7 | originalJson: any, 8 | RequestParser: RequestParser 9 | } 10 | 11 | const POOL_SIZE: number = 100 // for current-limiting 12 | const RequestParserPool: RequestParser[] = [] 13 | 14 | export function jsonBodyParser (queryBody: object): jsonBodyParserReturn { 15 | if (RequestParserPool.length < POOL_SIZE) { 16 | const RPInstance = new RequestParser(queryBody) 17 | RequestParserPool.push(RPInstance) 18 | return { 19 | queue: RPInstance.queue.slice(), 20 | errors: RPInstance.errors.slice(), 21 | originalJson: queryBody, 22 | RequestParser: RPInstance 23 | } 24 | } else { 25 | const RPInstance = getFreeRequestParser(RequestParserPool) 26 | if (RPInstance) { 27 | RPInstance.startJob(queryBody) 28 | return { 29 | queue: RPInstance.queue.slice(), 30 | errors: RPInstance.errors.slice(), 31 | originalJson: queryBody, 32 | RequestParser: RPInstance 33 | } 34 | } else { 35 | return null 36 | } 37 | } 38 | } 39 | 40 | function getFreeRequestParser (pool: RequestParser[]): RequestParser | null { 41 | for (let i = 0; i < pool.length; i++) { 42 | if (pool[i].status === StatusEnum.Free) { 43 | pool[i].flushJob() 44 | return pool[i] 45 | } 46 | } 47 | console.error('[error] Cannot get a free RequestParser') 48 | return null 49 | } -------------------------------------------------------------------------------- /public/admin/assets/js/reset-psd.0e3e3ccf.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["reset-psd"],{"3d11":function(t,s,e){"use strict";e.r(s);var o=function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"user-manage-container"},[e("h4",{staticClass:"title"},[t._v("密码修改")]),e("div",{staticClass:"content"},[e("el-form",{staticClass:"form",attrs:{model:t.form}},[e("el-form-item",{attrs:{label:"旧密码","label-width":"120px"}},[e("el-input",{attrs:{type:"password",autocomplete:"off"},model:{value:t.form.oldPassword,callback:function(s){t.$set(t.form,"oldPassword",s)},expression:"form.oldPassword"}})],1),e("el-form-item",{attrs:{label:"新密码","label-width":"120px"}},[e("el-input",{attrs:{type:"password",autocomplete:"off"},model:{value:t.form.password,callback:function(s){t.$set(t.form,"password",s)},expression:"form.password"}})],1)],1),e("el-button",{staticClass:"submit-btn",attrs:{type:"primary"},on:{click:t.handleSubmit}},[t._v("提交")])],1)])},a=[],r={name:"user-manage-page",data:function(){return{form:{}}},methods:{handleSubmit:function(){var t=this;this.form.oldPassword&&this.form.password&&this.$http({url:"/common/user/resetPassword",method:"POST",data:this.form}).then((function(s){console.log(s),0===s.data.code?(t.$message({type:"success",message:"修改成功,请重新登录"}),t.form={},t.$http({url:"/common/logout"}).then((function(){setTimeout((function(){t.$router.push("/")}),1e3)}))):t.$message({type:"error",message:s.data.msg})}))}}},n=r,l=(e("7006"),e("2877")),i=Object(l["a"])(n,o,a,!1,null,"08c4d06c",null);s["default"]=i.exports},7006:function(t,s,e){"use strict";var o=e("7d66"),a=e.n(o);a.a},"7d66":function(t,s,e){}}]); 2 | //# sourceMappingURL=reset-psd.0e3e3ccf.js.map -------------------------------------------------------------------------------- /public/admin-v2/static/js/50.b6d92937.chunk.js: -------------------------------------------------------------------------------- 1 | (this["webpackJsonpadmin-v2"]=this["webpackJsonpadmin-v2"]||[]).push([[50],{800:function(e,t,n){"use strict";n.r(t),n.d(t,"conf",(function(){return a})),n.d(t,"language",(function(){return i}));var a={comments:{blockComment:["\x3c!--","--\x3e"]},brackets:[["<",">"]],autoClosingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],surroundingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}]},i={defaultToken:"",tokenPostfix:".xml",ignoreCase:!0,qualifiedName:/(?:[\w\.\-]+:)?[\w\.\-]+/,tokenizer:{root:[[/[^<&]+/,""],{include:"@whitespace"},[/(<)(@qualifiedName)/,[{token:"delimiter"},{token:"tag",next:"@tag"}]],[/(<\/)(@qualifiedName)(\s*)(>)/,[{token:"delimiter"},{token:"tag"},"",{token:"delimiter"}]],[/(<\?)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/(<\!)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/<\!\[CDATA\[/,{token:"delimiter.cdata",next:"@cdata"}],[/&\w+;/,"string.escape"]],cdata:[[/[^\]]+/,""],[/\]\]>/,{token:"delimiter.cdata",next:"@pop"}],[/\]/,""]],tag:[[/[ \t\r\n]+/,""],[/(@qualifiedName)(\s*=\s*)("[^"]*"|'[^']*')/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">?\/]*|'[^'>?\/]*)(?=[\?\/]\>)/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">]*|'[^'>]*)/,["attribute.name","","attribute.value"]],[/@qualifiedName/,"attribute.name"],[/\?>/,{token:"delimiter",next:"@pop"}],[/(\/)(>)/,[{token:"tag"},{token:"delimiter",next:"@pop"}]],[/>/,{token:"delimiter",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,""],[//,{token:"comment",next:"@pop"}],[//,"comment","@pop"],[//,"comment","@pop"],[/[^-]+/,"comment.content"],[/./,"comment.content"]],otherTag:[[/\/?>/,"delimiter","@pop"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/]],script:[[/type/,"attribute.name","@scriptAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter",next:"@scriptEmbedded",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/(<\/)(script\s*)(>)/,["delimiter","tag",{token:"delimiter",next:"@pop"}]]],scriptAfterType:[[/=/,"delimiter","@scriptAfterTypeEquals"],[/>/,{token:"delimiter",next:"@scriptEmbedded",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptAfterTypeEquals:[[/"([^"]*)"/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/>/,{token:"delimiter",next:"@scriptEmbedded",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptWithCustomType:[[/>/,{token:"delimiter",next:"@scriptEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptEmbedded:[[/<\/script/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}],[/[^<]+/,""]],style:[[/type/,"attribute.name","@styleAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter",next:"@styleEmbedded",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/(<\/)(style\s*)(>)/,["delimiter","tag",{token:"delimiter",next:"@pop"}]]],styleAfterType:[[/=/,"delimiter","@styleAfterTypeEquals"],[/>/,{token:"delimiter",next:"@styleEmbedded",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleAfterTypeEquals:[[/"([^"]*)"/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/>/,{token:"delimiter",next:"@styleEmbedded",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleWithCustomType:[[/>/,{token:"delimiter",next:"@styleEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleEmbedded:[[/<\/style/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}],[/[^<]+/,""]]}}}}]); 2 | //# sourceMappingURL=17.cfc7b5dd.chunk.js.map -------------------------------------------------------------------------------- /public/admin-v2/static/js/32.c7bf58f4.chunk.js: -------------------------------------------------------------------------------- 1 | (this["webpackJsonpadmin-v2"]=this["webpackJsonpadmin-v2"]||[]).push([[32],{783:function(e,t,n){"use strict";n.r(t),n.d(t,"conf",(function(){return o})),n.d(t,"language",(function(){return a}));var o={comments:{lineComment:"//"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:'"',close:'"',notIn:["string","comment"]},{open:"'",close:"'",notIn:["string","comment"]},{open:"{",close:"}",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]}],folding:{offSide:!0}},a={defaultToken:"",tokenPostfix:".pug",ignoreCase:!0,brackets:[{token:"delimiter.curly",open:"{",close:"}"},{token:"delimiter.array",open:"[",close:"]"},{token:"delimiter.parenthesis",open:"(",close:")"}],keywords:["append","block","case","default","doctype","each","else","extends","for","if","in","include","mixin","typeof","unless","var","when"],tags:["a","abbr","acronym","address","area","article","aside","audio","b","base","basefont","bdi","bdo","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","command","datalist","dd","del","details","dfn","div","dl","dt","em","embed","fieldset","figcaption","figure","font","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe","img","input","ins","keygen","kbd","label","li","link","map","mark","menu","meta","meter","nav","noframes","noscript","object","ol","optgroup","option","output","p","param","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","small","source","span","strike","strong","style","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","title","tr","tracks","tt","u","ul","video","wbr"],symbols:/[\+\-\*\%\&\|\!\=\/\.\,\:]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/^(\s*)([a-zA-Z_-][\w-]*)/,{cases:{"$2@tags":{cases:{"@eos":["","tag"],"@default":["",{token:"tag",next:"@tag.$1"}]}},"$2@keywords":["",{token:"keyword.$2"}],"@default":["",""]}}],[/^(\s*)(#[a-zA-Z_-][\w-]*)/,{cases:{"@eos":["","tag.id"],"@default":["",{token:"tag.id",next:"@tag.$1"}]}}],[/^(\s*)(\.[a-zA-Z_-][\w-]*)/,{cases:{"@eos":["","tag.class"],"@default":["",{token:"tag.class",next:"@tag.$1"}]}}],[/^(\s*)(\|.*)$/,""],{include:"@whitespace"},[/[a-zA-Z_$][\w$]*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":""}}],[/[{}()\[\]]/,"@brackets"],[/@symbols/,"delimiter"],[/\d+\.\d+([eE][\-+]?\d+)?/,"number.float"],[/\d+/,"number"],[/"/,"string",'@string."'],[/'/,"string","@string.'"]],tag:[[/(\.)(\s*$)/,[{token:"delimiter",next:"@blockText.$S2."},""]],[/\s+/,{token:"",next:"@simpleText"}],[/#[a-zA-Z_-][\w-]*/,{cases:{"@eos":{token:"tag.id",next:"@pop"},"@default":"tag.id"}}],[/\.[a-zA-Z_-][\w-]*/,{cases:{"@eos":{token:"tag.class",next:"@pop"},"@default":"tag.class"}}],[/\(/,{token:"delimiter.parenthesis",next:"@attributeList"}]],simpleText:[[/[^#]+$/,{token:"",next:"@popall"}],[/[^#]+/,{token:""}],[/(#{)([^}]*)(})/,{cases:{"@eos":["interpolation.delimiter","interpolation",{token:"interpolation.delimiter",next:"@popall"}],"@default":["interpolation.delimiter","interpolation","interpolation.delimiter"]}}],[/#$/,{token:"",next:"@popall"}],[/#/,""]],attributeList:[[/\s+/,""],[/(\w+)(\s*=\s*)("|')/,["attribute.name","delimiter",{token:"attribute.value",next:"@value.$3"}]],[/\w+/,"attribute.name"],[/,/,{cases:{"@eos":{token:"attribute.delimiter",next:"@popall"},"@default":"attribute.delimiter"}}],[/\)$/,{token:"delimiter.parenthesis",next:"@popall"}],[/\)/,{token:"delimiter.parenthesis",next:"@pop"}]],whitespace:[[/^(\s*)(\/\/.*)$/,{token:"comment",next:"@blockText.$1.comment"}],[/[ \t\r\n]+/,""],[//,{token:"comment",next:"@pop"}],[//,"comment.html","@pop"],[/[^-]+/,"comment.content.html"],[/./,"comment.content.html"]],otherTag:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.otherTag"}],[/\/?>/,"delimiter.html","@pop"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/]],script:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.script"}],[/type/,"attribute.name","@scriptAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/(<\/)(script\s*)(>)/,["delimiter.html","tag.html",{token:"delimiter.html",next:"@pop"}]]],scriptAfterType:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.scriptAfterType"}],[/=/,"delimiter","@scriptAfterTypeEquals"],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptAfterTypeEquals:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.scriptAfterTypeEquals"}],[/"([^"]*)"/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptWithCustomType:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.scriptWithCustomType.$S2"}],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptEmbedded:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInEmbeddedState.scriptEmbedded.$S2",nextEmbedded:"@pop"}],[/<\/script/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}]],style:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.style"}],[/type/,"attribute.name","@styleAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/(<\/)(style\s*)(>)/,["delimiter.html","tag.html",{token:"delimiter.html",next:"@pop"}]]],styleAfterType:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.styleAfterType"}],[/=/,"delimiter","@styleAfterTypeEquals"],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleAfterTypeEquals:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.styleAfterTypeEquals"}],[/"([^"]*)"/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleWithCustomType:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.styleWithCustomType.$S2"}],[/>/,{token:"delimiter.html",next:"@styleEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleEmbedded:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInEmbeddedState.styleEmbedded.$S2",nextEmbedded:"@pop"}],[/<\/style/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}]],handlebarsInSimpleState:[[/\{\{\{?/,"delimiter.handlebars"],[/\}\}\}?/,{token:"delimiter.handlebars",switchTo:"@$S2.$S3"}],{include:"handlebarsRoot"}],handlebarsInEmbeddedState:[[/\{\{\{?/,"delimiter.handlebars"],[/\}\}\}?/,{token:"delimiter.handlebars",switchTo:"@$S2.$S3",nextEmbedded:"$S3"}],{include:"handlebarsRoot"}],handlebarsRoot:[[/[#/][^\s}]+/,"keyword.helper.handlebars"],[/else\b/,"keyword.helper.handlebars"],[/[\s]+/],[/[^}]/,"variable.parameter.handlebars"]]}}}}]); 2 | //# sourceMappingURL=16.16eacabc.chunk.js.map -------------------------------------------------------------------------------- /src/core/sql-parser/column-parser.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 将如 field、SUM(field)、SEC_TO_TIME(TIME_TO_SEC(field))、 field as name 此类的sql查询字段,解析出field 3 | * 4 | * @author wupeng1@guahao.com 5 | */ 6 | 7 | enum status { 8 | init = 'init', 9 | identifier = 'identifier', 10 | callExpression = 'callExpression', 11 | end = 'end', 12 | } 13 | 14 | enum keywords { 15 | as = 'keyword_as', 16 | order = 'keyword_order', 17 | by = 'keyword_by', 18 | desc = 'keyword_desc', 19 | asc = 'keyword_asc', 20 | } 21 | 22 | const context = { 23 | '(': 'context_left', 24 | ')': 'context_right', 25 | }; 26 | 27 | class ColumnSQLParser { 28 | static parse (source: string): { 29 | list: any[], 30 | ast: any, 31 | } { 32 | let index = 0; 33 | const list = []; 34 | let partNumber = 0; 35 | while (true) { 36 | const token = ColumnSQLParser.getToken(source, index); 37 | if (token.type === status.end) { 38 | break; 39 | } 40 | if (token.type === context['(']) { 41 | partNumber++; 42 | } else if (token.type === context[')']) { 43 | partNumber--; 44 | } 45 | const type = keywords[token.value.toLowerCase()]; 46 | if (type) { 47 | token.type = type; 48 | } 49 | list.push(token); 50 | index = token.index + 1; 51 | } 52 | if (partNumber !== 0) { 53 | if (partNumber > 0) { 54 | throw 'SyntaxError: Unexpected token )'; 55 | } else { 56 | throw 'SyntaxError: Unexpected token ('; 57 | } 58 | } 59 | const ast = ColumnSQLParser.formatAst(list); 60 | return { 61 | list, 62 | ast, 63 | }; 64 | } 65 | 66 | static formatAst (list: any[], isArg?: boolean): any { 67 | let tree: any = {}; 68 | for (let i = 0, size = list.length; i < size; i++) { 69 | const token = list[i]; 70 | if (token.type === status.identifier) { 71 | if (tree.type) { 72 | throw 'error 0x01'; 73 | } 74 | tree = token; 75 | } else if (token.type === context['(']) { 76 | if (i > 0) { 77 | tree.type = status.callExpression; 78 | } 79 | const result = ColumnSQLParser.formatAst(list.slice(i + 1), true); 80 | tree.argument = result.token; 81 | i = result.i + i + 1; 82 | } else if (token.type === context[')']) { 83 | tree = { 84 | i, 85 | token: tree, 86 | }; 87 | break; 88 | } else if (token.type === keywords.as) { 89 | const nextToken = list[i + 1]; 90 | if (nextToken && nextToken.type === status.identifier) { 91 | const next2Token = list[i + 2]; 92 | if (next2Token) { 93 | throw 'error 0x02'; 94 | } 95 | tree.alias = nextToken; 96 | i++; 97 | } else { 98 | throw 'error 0x03'; 99 | } 100 | } else if (token.type === keywords.order) { 101 | if (isArg) { 102 | const nextToken = list[i + 1]; 103 | if (nextToken && nextToken.type === keywords.by) { 104 | const next2Token = list[i + 2]; 105 | if (next2Token && next2Token.type === status.identifier) { 106 | tree.orderBy = next2Token; 107 | i += 2; 108 | } else { 109 | throw 'error 0x05'; 110 | } 111 | } else { 112 | throw 'error 0x06'; 113 | } 114 | } else { 115 | throw 'error 0x07'; 116 | } 117 | } 118 | } 119 | return tree; 120 | } 121 | 122 | static getToken (source: string, index: number): any { 123 | let type = status.init; 124 | let token = ''; 125 | while (true) { 126 | const value = source[index]; 127 | if (type === status.init) { 128 | if (value === undefined) { 129 | return { 130 | type: status.end, 131 | }; 132 | } else if (context[value]) { 133 | return { 134 | type: context[value], 135 | value, 136 | index, 137 | }; 138 | } else if (/^\w$/.test(value)) { 139 | type = status.identifier; 140 | token += value; 141 | index++; 142 | continue; 143 | } else if (value === ' ') { 144 | index++; 145 | continue; 146 | } else { 147 | throw `SyntaxError: Unexpected identifierat '${ value } at ${ index }'`; 148 | } 149 | } 150 | if (type === status.identifier) { 151 | if (value === undefined) { 152 | return { 153 | type, 154 | value: token, 155 | index: index - 1, 156 | }; 157 | } else if (context[value]) { 158 | return { 159 | type, 160 | value: token, 161 | index: index - 1, 162 | }; 163 | } else if (value === ' ') { 164 | return { 165 | type, 166 | value: token, 167 | index: index - 1, 168 | }; 169 | } else if (/^\w$/.test(value)) { 170 | token += value; 171 | index++; 172 | } else { 173 | throw `SyntaxError: Unexpected identifierat '${ value } at ${ index }'`; 174 | } 175 | } 176 | } 177 | } 178 | 179 | static getField (ast: any): string { 180 | let field; 181 | if (ast) { 182 | switch (ast.type) { 183 | case status.identifier: 184 | field = ast.value; 185 | break; 186 | case status.callExpression: 187 | field = ColumnSQLParser.getField(ast.argument); 188 | break; 189 | default: 190 | field = null; 191 | } 192 | } else { 193 | field = null; 194 | } 195 | return field; 196 | 197 | } 198 | } 199 | 200 | const getField = (source: string): string => { 201 | let result; 202 | try { 203 | const { ast } = ColumnSQLParser.parse(source); 204 | result = ColumnSQLParser.getField(ast); 205 | } catch (e) { 206 | console.error(`${ source } 解析错误!`); 207 | result = null; 208 | } 209 | return result; 210 | }; 211 | 212 | export { 213 | ColumnSQLParser, 214 | getField, 215 | } 216 | --------------------------------------------------------------------------------