├── .editorconfig ├── .gitignore ├── .prettierrc ├── .vscode ├── launch.json └── settings.json ├── Dockerfile ├── README.md ├── build └── BuildCover.js ├── config ├── ConfigDB.ts ├── ConfigDefault.ts ├── ConfigDev.ts └── ConfigProd.ts ├── docker-compose.yml ├── docs ├── 1567861669077.jpg └── 1569804144167.jpg ├── index.d.ts ├── jest.json ├── nodemon.json ├── nuxt.config.ts ├── package.json ├── src ├── client │ ├── assets │ │ ├── README.md │ │ └── style │ │ │ └── App.styl │ ├── common │ │ ├── CommonCart.ts │ │ └── CommonCookie.ts │ ├── components │ │ ├── ComponentHeader.vue │ │ ├── ComponentLoading.vue │ │ ├── ComponentLogo.vue │ │ ├── ComponentTwitter.vue │ │ └── README.md │ ├── core │ │ ├── consts │ │ │ └── ConstHttp.ts │ │ ├── interfaces │ │ │ ├── IView.ts │ │ │ └── IVo.ts │ │ ├── service │ │ │ ├── ServiceBase.ts │ │ │ ├── ServiceConfig.ts │ │ │ └── ServiceTransport.ts │ │ └── views │ │ │ └── ViewBase.ts │ ├── layouts │ │ ├── LayoutDefault.vue │ │ ├── LayoutError.vue │ │ └── README.md │ ├── middleware │ │ ├── MiddlewareMobile.ts │ │ ├── MiddlewareNotAuthenticated.ts │ │ └── README.md │ ├── models │ │ ├── ModelCarts.ts │ │ ├── ModelPost.ts │ │ └── ModelUser.ts │ ├── pages │ │ ├── README.md │ │ ├── auth │ │ │ └── login │ │ │ │ └── index.vue │ │ ├── cart │ │ │ └── index.vue │ │ ├── error │ │ │ ├── 400 │ │ │ │ └── index.vue │ │ │ └── 500 │ │ │ │ └── index.vue │ │ ├── goods │ │ │ └── index.vue │ │ ├── index.vue │ │ └── list │ │ │ └── index.vue │ ├── plugins │ │ ├── PluginAxios.ts │ │ ├── PluginFlexible.js │ │ ├── PluginVant.ts │ │ └── README.md │ ├── routers │ │ └── RoutersClient.ts │ ├── service │ │ ├── ServiceCNode.ts │ │ ├── ServiceLocal.ts │ │ └── ServiceNews.ts │ ├── static │ │ ├── README.md │ │ ├── about.js │ │ ├── body.js │ │ ├── carts.json │ │ ├── defer.js │ │ ├── favicon.ico │ │ ├── friends.json │ │ ├── goods.json │ │ ├── head.js │ │ ├── list.json │ │ ├── robots.txt │ │ └── v.png │ └── store │ │ ├── README.md │ │ ├── index.ts │ │ └── modules │ │ ├── auth.ts │ │ ├── carts.ts │ │ └── root.ts └── server │ ├── AppController.ts │ ├── AppModule.ts │ ├── Main.ts │ ├── common │ ├── core │ │ └── CommonEngineController.ts │ ├── decorators │ │ └── CommonRolesDecorator.ts │ ├── filters │ │ └── CommonHttpExceptionFilter.ts │ ├── guards │ │ └── CommonRolesGuard.ts │ ├── interceptors │ │ ├── CommonExceptionInterceptor.ts │ │ ├── CommonLoggingInterceptor.ts │ │ ├── CommonTimeoutInterceptor.ts │ │ └── CommonTransformInterceptor.ts │ ├── middleware │ │ ├── CommonHttpMiddleware.ts │ │ └── CommonLoggerMiddleware.ts │ ├── nuxt │ │ ├── CommonNuxtFilter.ts │ │ └── CommonNuxtServer.ts │ └── pipes │ │ ├── CommonParseIntPipe.ts │ │ └── CommonValidationPipe.ts │ ├── modules │ ├── cats │ │ ├── CatsController.ts │ │ ├── CatsModule.ts │ │ ├── CatsService.ts │ │ ├── dto │ │ │ └── CatsCreateDto.ts │ │ └── interfaces │ │ │ └── ICats.ts │ ├── db │ │ ├── DBModule.ts │ │ └── DBProviders.ts │ ├── local │ │ ├── LocalController.ts │ │ ├── LocalModule.ts │ │ ├── LocalProviders.ts │ │ ├── LocalService.ts │ │ ├── dto │ │ │ ├── LocalAddCartsDto.ts │ │ │ ├── LocalCardListItemDto.ts │ │ │ ├── LocalCreateCatDto.ts │ │ │ ├── LocalCreateGoodsDto.ts │ │ │ └── LocalLoginDto.ts │ │ ├── interfaces │ │ │ ├── ICart.ts │ │ │ ├── ICat.ts │ │ │ ├── IGoods.ts │ │ │ └── IUser.ts │ │ └── schemas │ │ │ ├── LocalCartSchema.ts │ │ │ ├── LocalCatSchema.ts │ │ │ ├── LocalGoodsSchema.ts │ │ │ └── LocalUserSchema.ts │ └── transport │ │ ├── TransportController.ts │ │ ├── TransportModule.ts │ │ └── TransportService.ts │ └── routers │ └── RoutersServer.ts ├── tsconfig.json ├── tslint.json └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = tab 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | quote_type = single 11 | 12 | [*.md] 13 | max_line_length = off 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # IDE 5 | /.idea 6 | /.awcache 7 | 8 | # misc 9 | npm-debug.log 10 | 11 | # tests 12 | /test 13 | /coverage 14 | /.nyc_output 15 | 16 | # dist 17 | /dist 18 | yarn.lock 19 | .nuxt 20 | .history 21 | config/config.env.ts 22 | config/env.config.ts 23 | yarn-error.log 24 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "bracketSpacing": true, 4 | "jsxBracketSameLine": true, 5 | "tabWidth": 4, 6 | "useTabs": true, 7 | "singleQuote": true, 8 | "overrides": [{ 9 | "files": ["*.json", ".eslintrc", ".tslintrc", ".prettierrc", ".tern-project"], 10 | "options": { 11 | "parser": "json", 12 | "tabWidth": 4 13 | } 14 | }, 15 | { 16 | "files": "*.ts", 17 | "options": { 18 | "parser": "typescript" 19 | } 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Nest.js Debug", 9 | "type": "node", 10 | "request": "launch", 11 | "cwd": "${workspaceFolder}", 12 | "runtimeExecutable": "npm", 13 | "runtimeArgs": ["run-script", "start:dev"], 14 | "port": 9229, 15 | "env": { 16 | "NODE_ENV": "development", 17 | "NodeLogStore": "local" 18 | }, 19 | "sourceMaps": true, 20 | "protocol": "inspector", 21 | "console": "integratedTerminal", 22 | "internalConsoleOptions": "neverOpen", 23 | "autoAttachChildProcesses": true 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.fontSize": 14, 3 | "editor.tabCompletion": "on", 4 | "editor.formatOnPaste": true, 5 | "editor.formatOnSave": true, 6 | "editor.formatOnType": true, 7 | "window.zoomLevel": 0, 8 | "terminal.integrated.fontSize": 14, 9 | "terminal.integrated.fontFamily": "Menlo, Monaco, 'Courier New', monospace", 10 | "files.exclude": { 11 | "_mock": true, 12 | "_nginx": true, 13 | ".history": true, 14 | ".idea": true, 15 | ".nuxt": true, 16 | ".vscode": true, 17 | "**/.DS_Store": true, 18 | "**/.git": true, 19 | "**/.hg": true, 20 | "**/.svn": true, 21 | "**/CVS": true, 22 | "config/config.env.ts": true, 23 | "config/env.config.ts": true, 24 | "coverage": true, 25 | "logs": true, 26 | "package-lock.json": true, 27 | "run": true, 28 | "test": true 29 | }, 30 | "commentTranslate.targetLanguage": "zh-CN" 31 | } 32 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM keymetrics/pm2:latest-alpine 2 | 3 | # Create own api folder 4 | RUN mkdir -p /api 5 | WORKDIR /api 6 | 7 | # Install the dependencies 8 | ENV NPM_CONFIG_LOGLEVEL warn 9 | RUN npm i -g yarn 10 | COPY package.json . 11 | COPY yarn.lock . 12 | RUN yarn 13 | 14 | # Build the API 15 | COPY src src/ 16 | COPY tsconfig.json . 17 | COPY config config/ 18 | COPY build build/ 19 | RUN yarn build 20 | 21 | # Expose the API port 22 | EXPOSE 8088 23 | 24 | # Run 25 | CMD [ "start" ] 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](docs/1567861669077.jpg) 2 | 3 | ## Nuxt.js and Nest.js isomorphism project 4 | 5 |

Nuxt 和 Nest 集成示例工程, UI层使用 Vant框架实现

6 | 7 | ## 1.前置条件 8 | 9 | #### MongoDB 10 | 11 | > 工程中使用到了 MongoDB,需先在本地安装该服务 12 | 13 | [MongoDB 安装教程](https://docs.mongodb.com/manual/installation/) 14 | 15 | > 安装完成后使用以下命令启动: 16 | 17 | ```js 18 | mongod --config mongod配置目录/mongod.conf --dbpath 数据库存放目录 19 | 20 | 例如: 21 | mongod --config /usr/local/etc/mongod.conf --dbpath /usr/local/mongodir 22 | ``` 23 | 24 |
25 | 26 | #### Charles 27 | 28 | > 对接接口时借助 Charles 抓包析工具能提升开发效率和快速定位问题,请注意 Charles 端口是否已设置为 8888(默认值) 29 | 30 | [Charles Web Debugging Proxy](https://www.charlesproxy.com/) 31 | 32 |
33 | 34 | ## 2.开发环境运行 35 | 36 | ```js 37 | npm run client:prod // local start on production mode 38 | 39 | npm run client:dev // local start on development mode 40 | 41 | visit: http://localhost:8088 42 | ``` 43 | 44 | ## 3.生产环境运行 45 | 46 | ```js 47 | npm run build 48 | 49 | and 50 | 51 | cross-env NODE_ENV=production npm run start // server start on production mode 52 | 53 | cross-env NODE_ENV=development npm run start // server start on development mode 54 | ``` 55 | 56 | ## 4.目录结构 57 | 58 | ```js 59 | client ---The client side source code folder, write Vue.js logical 60 | here 61 | |-------- assets 62 | |-------- components 63 | |-------- layouts 64 | |-------- middleware 65 | |-------- pages 66 | |-------- plugins 67 | |-------- static 68 | |-------- store 69 | 70 | server --- The server side(Node.js) logical here 71 | |-------- main.ts 72 | |-------- app.module.ts 73 | |-------- app.controller.ts 74 | |-------- common 75 | |--------- decorators 76 | |--------- filters 77 | |--------- guards 78 | |--------- interceptors 79 | |--------- middleware 80 | |--------- nuxt 81 | |--------- pipes 82 | |-------- routers 83 | |-------- modules 84 | |--------- modeule A.. 85 | |--------- modeule B.. 86 | |--------- modeule C.. 87 | |--------- modeule D.. 88 | ``` 89 | 90 | ## 5.DataBase Proview 91 | 92 | ![db](docs/1569804144167.jpg) 93 |
94 | 95 | ## 6.Frameworks 96 | 97 | > [Vant - Mobile UI Components built on Vue](https://youzan.github.io/vant/#/zh-CN/intro) 98 | 99 | > [Nest.js - A progressive Node.js framework for building efficient, reliable and scalable server-side applications.](https://nestjs.com/) 100 | 101 | > [Nuxt.js - The Vue.js Framework](https://nuxtjs.org/) 102 | 103 | ## 7.Plugins 104 | 105 | > [Nuxt-property-decorator](https://github.com/nuxt-community/nuxt-property-decorator) 106 | 107 | > [Vue class component](https://github.com/vuejs/vue-class-component) 108 | 109 | > [Vuex class](https://github.com/ktsn/vuex-class/) 110 | 111 | > [Nuxt class component](https://github.com/nuxt-community/nuxt-class-component) 112 | -------------------------------------------------------------------------------- /build/BuildCover.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const arguments = process.env.NODE_ENV || 'production'; 5 | let file = ''; 6 | const coverPath = path.resolve(__dirname, '../config/env.config.ts'); 7 | 8 | const replacestr = 'Replace ENV:: '; 9 | 10 | switch (arguments) { 11 | case 'development': 12 | file = 13 | `export default { 14 | env: 'development' 15 | };`; 16 | fs.writeFileSync(coverPath, file); 17 | console.log(replacestr, arguments); 18 | break; 19 | 20 | case 'production': 21 | file = `export default { 22 | env: 'production' 23 | };`; 24 | fs.writeFileSync(coverPath, file); 25 | console.log(replacestr, arguments); 26 | break; 27 | 28 | default: 29 | file = `export default { 30 | env: 'production' 31 | };`; 32 | fs.writeFileSync(coverPath, file); 33 | console.log(replacestr, arguments); 34 | break; 35 | } 36 | -------------------------------------------------------------------------------- /config/ConfigDB.ts: -------------------------------------------------------------------------------- 1 | import { ConfigDefault } from './ConfigDefault'; 2 | 3 | /** 4 | * DB配置 5 | * 6 | * @export 7 | * @class ConfigDB 8 | * @extends {ConfigDefault} 9 | */ 10 | export class ConfigDB extends ConfigDefault { 11 | constructor() { 12 | super(); 13 | } 14 | 15 | /** 16 | * 开发MOGO DB配置 17 | * @description Gets dev enviroment mongo config 18 | * @returns dev enviroment mongo config 19 | */ 20 | private getDevEnviromentMongoConfig(): IMongoDBConfig { 21 | return { 22 | uri: 'localhost', 23 | port: 27017, 24 | db: 'admin', 25 | userName: 'admin', 26 | password: '123456' 27 | }; 28 | } 29 | 30 | /** 31 | * 生产MOGO DB配置 32 | * @description Gets dev enviroment mongo config 33 | * @returns dev enviroment mongo config 34 | */ 35 | private getProdEnviromentMongoConfig(): IMongoDBConfig { 36 | return { 37 | uri: 'localhost', 38 | port: 27017, 39 | db: 'admin', 40 | userName: 'admin', 41 | password: '123456' 42 | }; 43 | } 44 | 45 | /** 46 | * 获取MONGO DB层配置 47 | * @description Gets db config 48 | * @returns db config 49 | */ 50 | public getMongoDbConfig(): IMongoDBConfig { 51 | switch (this.getEnv()) { 52 | // 开发MOGO DB配置 53 | case ConfigDefault.ENV_DEV: 54 | return this.getDevEnviromentMongoConfig(); 55 | // 生产MOGO DB配置 56 | case ConfigDefault.ENV_PROD: 57 | return this.getProdEnviromentMongoConfig(); 58 | // 默认MOGO DB配置 59 | default: 60 | return this.getProdEnviromentMongoConfig(); 61 | } 62 | } 63 | } 64 | 65 | /** 66 | * mongo db 基础配置 67 | * @description Idbconfig 68 | */ 69 | export interface IMongoDBConfig { 70 | uri: string; 71 | port: number; 72 | db: string; 73 | userName: string; 74 | password: string; 75 | } 76 | -------------------------------------------------------------------------------- /config/ConfigDefault.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 基础配置 3 | * 4 | * @export 5 | * @class ConfigDefault 6 | */ 7 | export class ConfigDefault { 8 | 9 | /** 10 | * X real url of config default 11 | */ 12 | public static X_REAL_URL: string = 'x-real-url'; 13 | 14 | /** 15 | * X real base url of config default 16 | */ 17 | public static X_REAL_BASE_URL: string = 'x-real-base-url'; 18 | 19 | /** 20 | * content-type 21 | */ 22 | public static CONTENT_TYPE: string = 'content-type'; 23 | 24 | /** 25 | * Env dev of config default 26 | */ 27 | public static ENV_DEV: string = 'development'; 28 | 29 | /** 30 | * Env prod of config default 31 | */ 32 | public static ENV_PROD: string = 'production'; 33 | 34 | /** 35 | * 默认环境标识 36 | * @description Env of config default 37 | */ 38 | private _env: string = ConfigDefault.ENV_PROD; 39 | 40 | constructor() { 41 | // hole 42 | } 43 | 44 | /** 45 | * 服务器访问路径 46 | * 47 | * @type {string} 48 | * @memberof ConfigDefault 49 | */ 50 | public serverAdderess: string = '127.0.0.1'; 51 | 52 | /** 53 | * 启动端口 54 | * 55 | * @type {number} 56 | * @memberof ConfigDefault 57 | */ 58 | public port: number = 8088; 59 | 60 | /** 61 | * 是否输出所有日志记录 62 | */ 63 | public monitorAllLogs: boolean = false; 64 | 65 | /** 66 | * 本地URL地址 67 | * 68 | * @type {string} 69 | * @memberof ConfigDefault 70 | */ 71 | public localUrl: string = 'http://127.0.0.1'; 72 | 73 | /** 74 | * charles抓包地址 75 | */ 76 | public httpProxyUrl: string = 'http://127.0.0.1'; 77 | 78 | /** 79 | * charles抓包端口 80 | */ 81 | public httpProxyPort: number = 8888; 82 | 83 | /** 84 | * cnode配置 85 | * 86 | * @type {string} 87 | * @memberof ConfigDefault 88 | */ 89 | public cnodeUrl: string = 'https://cnodejs.org'; 90 | 91 | /** 92 | * proxy-agent地址 93 | */ 94 | public getProxyAddress(): string { 95 | return `${this.httpProxyUrl}:${this.httpProxyPort}`; 96 | } 97 | 98 | /** 99 | * API统一前缀 100 | */ 101 | public getGlobalPrefix(): string { 102 | return 'api'; 103 | } 104 | 105 | /** 106 | * 获取当前环境标识 107 | * @description Gets env 108 | * @returns env 109 | */ 110 | public getEnv(): string { 111 | return this._env; 112 | } 113 | 114 | /** 115 | * 更新环境标识 116 | * @description Updates env 117 | * @param env 118 | */ 119 | public updateEnv(env: string): void { 120 | this._env = env; 121 | } 122 | 123 | /** 124 | * 鉴权白名单页面,在此列表内页面将进行登录判断 125 | */ 126 | public authWhiteList(): Array { 127 | return [ 128 | '/cart', 129 | '/goods', 130 | '/' 131 | ]; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /config/ConfigDev.ts: -------------------------------------------------------------------------------- 1 | import { ConfigDefault } from './ConfigDefault'; 2 | 3 | /** 4 | * 开发服配置 5 | * 6 | * @export 7 | * @class ConfigDev 8 | * @extends {ConfigDefault} 9 | */ 10 | export class ConfigDev extends ConfigDefault { 11 | constructor() { 12 | super(); 13 | // console.log('开发服配置::: 启动'); 14 | } 15 | 16 | /** 17 | * 覆盖本地URL地址 18 | * 19 | * @type {string} 20 | * @memberof ConfigDefault 21 | */ 22 | public localUrl: string = 'http://localhost'; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /config/ConfigProd.ts: -------------------------------------------------------------------------------- 1 | import { ConfigDefault } from './ConfigDefault'; 2 | 3 | /** 4 | * 生产服配置 5 | * 6 | * @export 7 | * @class ConfigProd 8 | * @extends {ConfigDefault} 9 | */ 10 | export class ConfigProd extends ConfigDefault { 11 | constructor() { 12 | super(); 13 | // console.log('生产服配置::: 启动'); 14 | } 15 | 16 | /** 17 | * 覆盖本地URL地址 18 | * 19 | * @type {string} 20 | * @memberof ConfigDefault 21 | */ 22 | public localUrl: string = 'http://127.0.0.1'; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | nest: 5 | build: . 6 | ports: 7 | - "3100:3100" 8 | networks: 9 | - docker-nest-typegoose-net 10 | depends_on: 11 | - mongo 12 | mongo: 13 | image: mongo:4.2 14 | ports: 15 | - "27017:27017" 16 | networks: 17 | - docker-nest-typegoose-net 18 | 19 | networks: 20 | docker-nest-typegoose-net: 21 | driver: bridge 22 | -------------------------------------------------------------------------------- /docs/1567861669077.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1166544/nuxt.js-nest.js-isomorphism/37a442a3b09e4566580e08b8027634e153ac7ac2/docs/1567861669077.jpg -------------------------------------------------------------------------------- /docs/1569804144167.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1166544/nuxt.js-nest.js-isomorphism/37a442a3b09e4566580e08b8027634e153ac7ac2/docs/1569804144167.jpg -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | declare module '*.vue' { 4 | const _default: Vue; 5 | export default _default; 6 | } 7 | -------------------------------------------------------------------------------- /jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": [ 3 | "ts", 4 | "tsx", 5 | "js", 6 | "json" 7 | ], 8 | "transform": { 9 | "^.+\\.tsx?$": "ts-jest" 10 | }, 11 | "testRegex": "/src/server/.*\\.(test|spec).(ts|tsx|js)$", 12 | "collectCoverageFrom": ["src/server/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"], 13 | "coverageReporters": ["json", "lcov"] 14 | } 15 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": [], 3 | "exec": "node dist/main.js" 4 | } 5 | -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | import { ConfigDefault } from './config/ConfigDefault'; 2 | import NuxtConfiguration from '@nuxt/config'; 3 | import nodeExternals from 'webpack-node-externals'; 4 | 5 | const isDev: boolean = !(process.env.NODE_ENV === 'production'); 6 | const configDefault: ConfigDefault = new ConfigDefault(); 7 | 8 | const config: NuxtConfiguration = { 9 | mode: 'universal', 10 | 11 | env: { 12 | host: configDefault.serverAdderess, 13 | port: configDefault.port, 14 | baseUrl: process.env.BASE_URL || `http://${configDefault.serverAdderess}:${configDefault.port}` 15 | }, 16 | 17 | srcDir: './src/client/', 18 | dev: isDev, 19 | 20 | /* 21 | ** 默认SEO头部信息 22 | */ 23 | head: { 24 | title: 'Nuxt.js bio project', 25 | meta: [ 26 | { charset: 'utf-8' }, 27 | { name: 'viewport', content: 'width=device-width, initial-scale=1, user-scalable=0' }, 28 | ], 29 | link: [ 30 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } 31 | ] 32 | }, 33 | 34 | /** Render content */ 35 | render: { 36 | ssrLog: 'collapsed' 37 | }, 38 | 39 | /* 40 | ** Customize the progress-bar 41 | */ 42 | loading: '~/components/ComponentLoading.vue', 43 | 44 | /* 45 | ** Global CSS 46 | */ 47 | css: ['~/assets/style/App.styl'], 48 | 49 | /* 50 | ** Plugins to load before mounting the App 51 | */ 52 | plugins: [ 53 | { 54 | src: '~/plugins/PluginVant', 55 | ssr: true 56 | }, 57 | { 58 | src: '~/plugins/PluginFlexible', 59 | ssr: false 60 | }, 61 | { 62 | src: '~/plugins/PluginAxios', 63 | ssr: true 64 | } 65 | ], 66 | 67 | /* 68 | ** Nuxt.js modules 69 | */ 70 | modules: [ 71 | '@nuxtjs/axios' // Doc: https://axios.nuxtjs.org/usage 72 | // '@nuxtjs/proxy' 73 | ], 74 | /* 75 | ** Axios module configuration 76 | */ 77 | axios: { 78 | // See https://github.com/nuxt-community/axios-module#options 79 | proxy: false, 80 | retry: { retries: 3 }, 81 | credentials: true 82 | }, 83 | /* 84 | ** Build configuration 85 | */ 86 | build: { 87 | cache: true, 88 | postcss: { 89 | // 添加插件名称作为键,参数作为值,使用npm或yarn安装它们 90 | plugins: { 91 | 'postcss-pxtorem': { 92 | rootValue: 37.5, 93 | propList: ['*'] 94 | } 95 | } 96 | }, 97 | babel: { 98 | plugins: [ 99 | '@babel/plugin-transform-modules-commonjs', 100 | ['@babel/plugin-proposal-decorators', { legacy: true }], 101 | ['@babel/plugin-proposal-class-properties', { loose: true }] 102 | ] 103 | }, 104 | transpile: [], 105 | plugins: [], 106 | loaders: { 107 | }, 108 | /* 109 | ** You can extend webpack config here 110 | */ 111 | // tslint:disable-next-line:typedef 112 | extend(config, { isClient }) { 113 | if (process.server) { 114 | config.externals = [ 115 | nodeExternals({ 116 | whitelist: [/^vant/] 117 | }) 118 | ]; 119 | } 120 | 121 | if (isClient) { 122 | config.devtool = '#source-map'; 123 | } 124 | } 125 | }, 126 | 127 | router: { 128 | middleware: [ 129 | 'MiddlewareMobile', 130 | 'MiddlewareNotAuthenticated' 131 | ] 132 | } 133 | }; 134 | 135 | export default config; 136 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxtjs-nestjs-isomorphism", 3 | "version": "1.0.0", 4 | "description": "A project for nuxtjs and nestjs integrated.", 5 | "author": "James Liauw", 6 | "license": "MIT", 7 | "pre-commit": [ 8 | "lint" 9 | ], 10 | "scripts": { 11 | "format": "prettier --write \"**/*.ts\"", 12 | "start": "npm run cover && cross-env NODE_ENV=production node dist/src/server/main.js", 13 | "start:dev": "npm run lint && cross-env NODE_ENV=development npm run cover && webpack --config webpack.config.js", 14 | "start:prod": "npm run lint && cross-env NODE_ENV=production npm run cover && webpack --config webpack.config.js", 15 | "build": "npm run lint && cross-env NODE_ENV=production npm run clean && nuxt build && tsc", 16 | "lint": "tslint -p tsconfig.json -c tslint.json", 17 | "lintfix": "tslint -p tsconfig.json --fix", 18 | "clean": "rimraf dist", 19 | "client": "npm run start:dev", 20 | "client:dev": "npm run start:dev", 21 | "client:prod": "npm run start:prod", 22 | "serve": "npm run start:dev", 23 | "prestart:prod": "npm run build", 24 | "test": "jest --config=jest.json", 25 | "cover": "node build/BuildCover.js", 26 | "test:watch": "jest --watch --config=jest.json", 27 | "test:coverage": "jest --config=jest.json --coverage --coverageDirectory=coverage" 28 | }, 29 | "dependencies": { 30 | "@nestjs/common": "6.5.2", 31 | "@nestjs/core": "6.5.2", 32 | "@nestjs/microservices": "5.7.4", 33 | "@nestjs/mongoose": "6.1.2", 34 | "@nestjs/platform-express": "6.6.3", 35 | "@nestjs/testing": "6.5.2", 36 | "@nestjs/websockets": "5.7.4", 37 | "@nuxtjs/axios": "5.4.1", 38 | "@nuxtjs/proxy": "1.3.3", 39 | "@types/jasmine": "3.3.16", 40 | "@types/mocha": "5.2.7", 41 | "@types/mongoose": "5.5.16", 42 | "class-transformer": "0.2.3", 43 | "class-validator": "0.9.1", 44 | "cookieparser": "0.1.0", 45 | "cross-env": "5.2.0", 46 | "js-cookie": "2.2.1", 47 | "md5": "2.2.1", 48 | "mongoose": "5.6.11", 49 | "nuxt": "2.8.1", 50 | "pre-commit": "1.2.2", 51 | "proxy-agent": "3.1.0", 52 | "querystring": "0.2.0", 53 | "reflect-metadata": "0.1.13", 54 | "rimraf": "2.6.3", 55 | "rxjs": "6.5.2", 56 | "ts-loader": "5.3.3", 57 | "tslint-config-alloy": "0.2.1", 58 | "typescript": "3.5.3", 59 | "uuid": "3.3.3", 60 | "vant": "2.1.2", 61 | "vuex-class": "0.3.1" 62 | }, 63 | "devDependencies": { 64 | "@babel/plugin-transform-modules-commonjs": "7.2.0", 65 | "@nuxt/config": "2.4.5", 66 | "@nuxt/typescript": "2.8.1", 67 | "@types/jest": "24.0.15", 68 | "@types/node": "10.14.0", 69 | "jest": "24.8.0", 70 | "nodemon": "1.18.9", 71 | "nuxt-property-decorator": "2.1.3", 72 | "postcss-pxtorem": "4.0.1", 73 | "prettier": "1.15.3", 74 | "stylus": "0.54.5", 75 | "stylus-loader": "3.0.2", 76 | "supertest": "4.0.2", 77 | "ts-jest": "24.0.2", 78 | "ts-node": "8.3.0", 79 | "tsconfig-paths": "3.7.0", 80 | "tslint": "5.13.1", 81 | "tslint-config-prettier": "1.18.0", 82 | "tslint-plugin-prettier": "2.0.1", 83 | "vuex-class-component": "1.4.3", 84 | "webpack-cli": "3.2.3", 85 | "webpack-shell-plugin": "0.5.0" 86 | }, 87 | "browserslist": [ 88 | "Android >= 4.0", 89 | "iOS >= 7" 90 | ] 91 | } 92 | -------------------------------------------------------------------------------- /src/client/assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). 8 | -------------------------------------------------------------------------------- /src/client/assets/style/App.styl: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | html, body, div, span, applet, object, iframe, 6 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 7 | a, abbr, acronym, address, big, cite, code, 8 | del, dfn, em, img, ins, kbd, q, s, samp, 9 | small, strike, strong, sub, sup, tt, var, 10 | b, u, i, center, 11 | dl, dt, dd, ol, ul, li, 12 | fieldset, form, label, legend, 13 | table, caption, tbody, tfoot, thead, tr, th, td, 14 | article, aside, canvas, details, embed, 15 | figure, figcaption, footer, header, hgroup, 16 | menu, nav, output, ruby, section, summary, 17 | time, mark, audio, video { 18 | margin: 0; 19 | padding: 0; 20 | border: 0; 21 | font-size: 100%; 22 | font: inherit; 23 | vertical-align: baseline; 24 | } 25 | /* HTML5 display-role reset for older browsers */ 26 | article, aside, details, figcaption, figure, 27 | footer, header, hgroup, menu, nav, section { 28 | display: block; 29 | } 30 | body { 31 | line-height: 1; 32 | } 33 | ol, ul { 34 | list-style: none; 35 | } 36 | blockquote, q { 37 | quotes: none; 38 | } 39 | blockquote:before, blockquote:after, 40 | q:before, q:after { 41 | content: ''; 42 | content: none; 43 | } 44 | table { 45 | border-collapse: collapse; 46 | border-spacing: 0; 47 | } 48 | 49 | body { 50 | font-size: 14px; 51 | background: #f6f6f6; 52 | } -------------------------------------------------------------------------------- /src/client/common/CommonCart.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 购物车公用逻辑 3 | */ 4 | class CommonCart { 5 | constructor() { 6 | // hole 7 | } 8 | 9 | /** 更新用户已添加入购物车数量 */ 10 | public getUpdatedCartsList( 11 | list: Array, 12 | sourceData: Array 13 | ): Array { 14 | for (let indexList: number = 0; indexList < list.length; indexList++) { 15 | const element: any = list[indexList]; 16 | 17 | for (let index: number = 0; index < sourceData.length; index++) { 18 | const item: any = sourceData[index]; 19 | 20 | if (element._id === item.id) { 21 | element.num = item.num; 22 | break; 23 | } 24 | } 25 | } 26 | 27 | return list; 28 | } 29 | 30 | /** 格式价格 */ 31 | public formatPrice(price: number = 1): string { 32 | return (price / 100).toFixed(2); 33 | } 34 | } 35 | 36 | export default new CommonCart(); 37 | -------------------------------------------------------------------------------- /src/client/common/CommonCookie.ts: -------------------------------------------------------------------------------- 1 | import Cookie from 'js-cookie'; 2 | 3 | /** 4 | * 转换COOKIE为对象 5 | * @param cookie 6 | */ 7 | export function parseCookie(cookie: string): Object { 8 | const output: Object = {}; 9 | cookie.split(/\s*;\s*/).forEach((pairCopy: any): any => { 10 | let pair: any = pairCopy; 11 | pair = pair.split(/\s*=\s*/); 12 | output[pair[0]] = pair.splice(1).join('='); 13 | }); 14 | 15 | return output; 16 | } 17 | 18 | /** 19 | * 服务端时生成自定义axios头部token 20 | * @param config 21 | */ 22 | export function setCustomHeaderBySSR(app: any, axios: any): any { 23 | 24 | let accessToken: string = ''; 25 | // 检测cookie是否存在 26 | if (app && app.context && app.context.req && app.context.req.headers) { 27 | // 抓取cookie内认证值 28 | const cookie: any = parseCookie(app.context.req.headers.cookie || ''); 29 | 30 | if (cookie && cookie.auth) { 31 | let authData: any; 32 | try { 33 | authData = JSON.parse(decodeURIComponent(cookie.auth)); 34 | } catch (error) { 35 | console.log(error); 36 | } 37 | accessToken = authData.accessToken; 38 | } 39 | } 40 | 41 | // 设置自定义头部token 42 | axios.setToken(accessToken); 43 | 44 | return accessToken; 45 | } 46 | 47 | /** 48 | * 客户端时生成自定义axios头部token 49 | * @param config 50 | */ 51 | export function setCustomHeaderByCSR(axios: any): any { 52 | 53 | let accessToken: string = ''; 54 | const cookie: any = Cookie.get('auth'); 55 | // 检测cookie是否存在 56 | if (cookie) { 57 | let authData: any; 58 | try { 59 | authData = JSON.parse(decodeURIComponent(cookie)); 60 | if (authData) { 61 | accessToken = authData.accessToken; 62 | } 63 | } catch (error) { 64 | console.log(error); 65 | } 66 | } 67 | 68 | // 设置自定义头部token 69 | axios.setToken(accessToken); 70 | 71 | return accessToken; 72 | } 73 | 74 | /** 75 | * 客户端更新axios头部token 76 | * @param config 77 | */ 78 | export function updateCustomHeaderByCSR(accessToken: string, axios: any): any { 79 | // 设置自定义头部token 80 | axios.setToken(accessToken); 81 | } 82 | 83 | /** 84 | * 获取用户信息 85 | * @param config 86 | */ 87 | export function getUser(req: any, app: any): any { 88 | // 抓取cookie 89 | let authData: any = {}; 90 | if (process.server) { 91 | // ssr 92 | const cookieValue: string = req.headers.cookie; 93 | const cookie: any = parseCookie(cookieValue || ''); 94 | 95 | if (cookie) { 96 | try { 97 | authData = JSON.parse(decodeURIComponent(cookie.auth)); 98 | } catch (error) { 99 | console.log(error); 100 | } 101 | } 102 | } else { 103 | // csr 104 | authData = app.store.state.modules.auth.auth; 105 | 106 | if (!authData || !authData._id) { 107 | const cookie: any = Cookie.get('auth'); 108 | if (cookie) { 109 | try { 110 | authData = JSON.parse(decodeURIComponent(cookie)); 111 | } catch (error) { 112 | console.log(error); 113 | } 114 | } 115 | } 116 | } 117 | 118 | return authData; 119 | } 120 | -------------------------------------------------------------------------------- /src/client/components/ComponentHeader.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 24 | 25 | 30 | -------------------------------------------------------------------------------- /src/client/components/ComponentLoading.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 27 | 28 | 42 | 43 | -------------------------------------------------------------------------------- /src/client/components/ComponentLogo.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | 20 | 90 | -------------------------------------------------------------------------------- /src/client/components/ComponentTwitter.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /src/client/components/README.md: -------------------------------------------------------------------------------- 1 | # COMPONENTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | The components directory contains your Vue.js Components. 6 | 7 | _Nuxt.js doesn't supercharge these components._ 8 | -------------------------------------------------------------------------------- /src/client/core/consts/ConstHttp.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 网络状态常量 3 | * @description Http const 4 | */ 5 | export class HttpConst { 6 | public static STATUS_200: number = 200; 7 | public static STATUS_401: number = 401; 8 | public static STATUS_403: number = 403; 9 | } 10 | -------------------------------------------------------------------------------- /src/client/core/interfaces/IView.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 基础视图结构接口 3 | * 4 | * @export 5 | * @interface IView 6 | */ 7 | export interface IView { 8 | 9 | /** 10 | * SEO页面标题 11 | * 12 | * @type {string} 13 | * @memberof IView 14 | */ 15 | title: string; 16 | 17 | /** 18 | * SEO页面描述 19 | * 20 | * @type {string} 21 | * @memberof IView 22 | */ 23 | description: string; 24 | 25 | /** 26 | * SEO页面关键字 27 | * 28 | * @type {string} 29 | * @memberof IView 30 | */ 31 | keywords: string; 32 | } 33 | -------------------------------------------------------------------------------- /src/client/core/interfaces/IVo.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 基础VO结构 3 | * 4 | * @export 5 | * @interface IVO 6 | */ 7 | export interface IVO { 8 | /** 9 | * 更新数据 10 | * 11 | * @param {*} val 12 | * @memberof IVO 13 | */ 14 | update(val: any): void; 15 | } 16 | -------------------------------------------------------------------------------- /src/client/core/service/ServiceBase.ts: -------------------------------------------------------------------------------- 1 | import { IVO } from '~/core/interfaces/IVo'; 2 | import IUser from '~/models/ModelUser'; 3 | import configService from '~/core/service/ServiceConfig'; 4 | import { setCustomHeaderByCSR, updateCustomHeaderByCSR } from '~/common/CommonCookie'; 5 | 6 | /** 7 | * 基础配置接口 8 | * 9 | * @export 10 | * @interface IBaseOption 11 | */ 12 | export interface IBaseOption { 13 | /** 基础URL */ 14 | baseUrl: string; 15 | 16 | /** 是否本地调用 */ 17 | isLocalHost: boolean; 18 | } 19 | 20 | /** 21 | * 基础配置类型 22 | * 23 | * @export 24 | * @class BaseOption 25 | * @implements {IBaseOption} 26 | */ 27 | export class BaseOption implements IBaseOption, IVO { 28 | constructor() { 29 | // hole 30 | } 31 | 32 | /** 33 | * 调用URL地址 34 | * 35 | * @type {string} 36 | * @memberof BaseOption 37 | */ 38 | public baseUrl: string; 39 | 40 | /** 41 | * 是否为本地周用 42 | * 43 | * @type {boolean} 44 | * @memberof BaseOption 45 | */ 46 | public isLocalHost: boolean = false; 47 | 48 | /** 49 | * 更新数据 50 | * 51 | * @param {*} val 52 | * @memberof BaseOption 53 | */ 54 | public update(val: any): void { 55 | // hole 56 | } 57 | } 58 | 59 | 60 | /** 61 | * 基础接口调用类 62 | * 63 | * @export 64 | * @class BaseService 65 | */ 66 | export class BaseService { 67 | [x: string]: any; 68 | protected apiServiceInstance: any; 69 | protected options: IBaseOption; 70 | 71 | constructor(optios: IBaseOption) { 72 | this.options = optios; 73 | } 74 | 75 | protected get apiService(): any { 76 | if (!this.apiServiceInstance) { 77 | this.apiServiceInstance = configService.getAxios(); 78 | } 79 | 80 | // 客户端渲染时 81 | if (process.client) { 82 | setCustomHeaderByCSR(this.apiServiceInstance); 83 | } 84 | 85 | return this.apiServiceInstance; 86 | } 87 | 88 | /** 89 | * 更新拦截信息,将token设入请求头部 90 | * @param loginData 91 | */ 92 | public updateIntercept(loginData: IUser): void { 93 | updateCustomHeaderByCSR(loginData.accessToken, this.apiServiceInstance); 94 | } 95 | 96 | /** 97 | * 更新配置,加上目标URL前缀 98 | * 99 | * @protected 100 | * @memberof BaseService 101 | */ 102 | protected updateConfig(url: string): string { 103 | // 本地调用 104 | if (this.options.isLocalHost) { 105 | return url; 106 | } 107 | 108 | // 非本地调用 109 | const baseUrl: string = this.options.baseUrl || ''; 110 | const resultUrl: string = `${baseUrl}${url}`; 111 | // console.log(resultUrl); 112 | 113 | return resultUrl; 114 | } 115 | 116 | /** 117 | * Request请求 118 | * 119 | * @protected 120 | * @param {*} config 121 | * @returns {void} 122 | * @memberof BaseService 123 | */ 124 | protected request(config: any): void { 125 | return this.apiService.request(config); 126 | } 127 | 128 | /** 129 | * 处理GET 130 | * 131 | * @param {string} url 接口地址 132 | * @returns {Promise} 133 | * @memberof BaseService 134 | */ 135 | public async get(url: string, config: any = {}): Promise { 136 | return await this.apiService.get(this.updateConfig(url), config); 137 | } 138 | 139 | /** 140 | * 处理POST 141 | * 142 | * @param {string} url 接口地址 143 | * @param {*} data 请求参数 144 | * @param {*} [config={}] 配置信息 145 | * @returns {Promise} 146 | * @memberof BaseService 147 | */ 148 | public async post(url: string, data: any, config: any = {}): Promise { 149 | return this.apiService.post(this.updateConfig(url), data, config); 150 | } 151 | 152 | /** 153 | * 处理DELETE 154 | * 155 | * @param {string} url 接口地址 156 | * @param {*} [config={}] 配置信息 157 | * @returns {Promise} 158 | * @memberof BaseService 159 | */ 160 | public async delete(url: string, config: any = {}): Promise { 161 | return this.apiService.delete(this.updateConfig(url), config); 162 | } 163 | 164 | /** 165 | * 处理HEAD 166 | * 167 | * @param {string} url 接口地址 168 | * @param {*} [config={}] 配置信息 169 | * @returns {Promise} 170 | * @memberof BaseService 171 | */ 172 | public async head(url: string, config: any = {}): Promise { 173 | return this.apiService.head(this.updateConfig(url), config); 174 | } 175 | 176 | /** 177 | * 处理PUT 178 | * 179 | * @param {string} url 接口地址 180 | * @param {*} data 请求参数 181 | * @param {*} [config={}] 配置信息 182 | * @returns {Promise} 183 | * @memberof BaseService 184 | */ 185 | public async put(url: string, data: any, config: any = {}): Promise { 186 | return this.apiService.put(this.updateConfig(url), data, config); 187 | } 188 | 189 | /** 190 | * 处理PATCH 191 | * 192 | * @param {string} url 接口地址 193 | * @param {*} data 请求参数 194 | * @param {*} [config={}] 配置信息 195 | * @returns {Promise} 196 | * @memberof BaseService 197 | */ 198 | public async patch(url: string, data: any, config: any = {}): Promise { 199 | return this.apiService.patch(this.updateConfig(url), data, config); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/client/core/service/ServiceConfig.ts: -------------------------------------------------------------------------------- 1 | import { ConfigDefault } from '../../../../config/ConfigDefault'; 2 | import { ConfigDev } from '../../../../config/ConfigDev'; 3 | import { ConfigProd } from '../../../../config/ConfigProd'; 4 | import ConfigEnv from '../../../../config/env.config'; 5 | 6 | /** 7 | * 配置服务 8 | * 9 | * @class ConfigService 10 | */ 11 | class ConfigService { 12 | private static instance: ConfigService; 13 | private axios: any; 14 | 15 | /** 16 | * 配置服务单例 17 | * 18 | * @static 19 | * @returns {ConfigService} 20 | * @memberof ConfigService 21 | */ 22 | public static getInstance(): ConfigService { 23 | if (!ConfigService.instance) { 24 | ConfigService.instance = new ConfigService(); 25 | } 26 | 27 | return ConfigService.instance; 28 | } 29 | 30 | /** 31 | * 配置数据 32 | * 33 | * @private 34 | * @type {ConfigDefault} 35 | * @memberof ConfigService 36 | */ 37 | private config: ConfigDefault; 38 | 39 | constructor() { 40 | switch (ConfigEnv.env) { 41 | case ConfigDefault.ENV_DEV: 42 | this.config = new ConfigDev(); 43 | break; 44 | case ConfigDefault.ENV_PROD: 45 | this.config = new ConfigProd(); 46 | break; 47 | default: 48 | this.config = new ConfigProd(); 49 | break; 50 | } 51 | 52 | // 将环境标识写入配置中 53 | this.config.updateEnv(ConfigEnv.env); 54 | } 55 | 56 | /** 57 | * 获取配置数据 58 | * 59 | * @returns {DefaultConfig} 60 | * @memberof ConfigService 61 | */ 62 | public getConfig(): ConfigDefault { 63 | return this.config; 64 | } 65 | 66 | /** 67 | * 获取AXIOS实例 68 | * 69 | * @returns {*} 70 | * @memberof ConfigService 71 | */ 72 | public getAxios(): any { 73 | return this.axios; 74 | } 75 | 76 | /** 77 | * 保存axios实例 78 | * 79 | * @param {*} axios 80 | * @memberof ConfigService 81 | */ 82 | public saveAxios(axios: any): void { 83 | this.axios = axios; 84 | } 85 | } 86 | 87 | export default ConfigService.getInstance(); 88 | -------------------------------------------------------------------------------- /src/client/core/service/ServiceTransport.ts: -------------------------------------------------------------------------------- 1 | import { BaseService, IBaseOption } from './ServiceBase'; 2 | import { ConfigDefault } from '../../../../config/ConfigDefault'; 3 | 4 | /** 5 | * 重写基类,中转服务 6 | * 7 | * @export 8 | * @class TransportService 9 | * @extends {BaseService} 10 | */ 11 | export class TransportService extends BaseService { 12 | constructor(optios: IBaseOption) { 13 | super(optios); 14 | } 15 | 16 | /** 17 | * 更新配置,加上目标URL前缀,如果是中转的话则指定固定地址 18 | * 19 | * @protected 20 | * @memberof BaseService 21 | */ 22 | protected updateConfig(url: string): string { 23 | return '/api/transport/getTransportData'; 24 | } 25 | 26 | /** 27 | * 更新配置,将URL放在HEADER头部 28 | * 29 | * @protected 30 | * @param {string} url 请求URL接口 31 | * @param {*} config 基础配置,将传入AXIOS 32 | * @returns {*} 33 | * @memberof TransportService 34 | */ 35 | protected transportConfig(url: string, config: any): any { 36 | config.headers = {}; 37 | config.headers[ConfigDefault.X_REAL_URL] = url; 38 | config.headers[ConfigDefault.X_REAL_BASE_URL] = this.options.baseUrl || ''; 39 | 40 | return config; 41 | } 42 | 43 | /** 44 | * 处理GET 45 | * 46 | * @param {string} url 接口地址 47 | * @returns {Promise} 48 | * @memberof BaseService 49 | */ 50 | public async get(url: string, config: any = {}): Promise { 51 | return await this.apiService.get(this.updateConfig(url), this.transportConfig(url, config)); 52 | } 53 | 54 | /** 55 | * 处理POST 56 | * 57 | * @param {string} url 接口地址 58 | * @param {*} data 请求参数 59 | * @param {*} [config={}] 配置信息 60 | * @returns {Promise} 61 | * @memberof BaseService 62 | */ 63 | public async post(url: string, data: any, config: any = {}): Promise { 64 | return this.apiService.post(this.updateConfig(url), data, this.transportConfig(url, config)); 65 | } 66 | 67 | /** 68 | * 处理DELETE 69 | * 70 | * @param {string} url 接口地址 71 | * @param {*} [config={}] 配置信息 72 | * @returns {Promise} 73 | * @memberof BaseService 74 | */ 75 | public async delete(url: string, config: any = {}): Promise { 76 | return this.apiService.delete(this.updateConfig(url), this.transportConfig(url, config)); 77 | } 78 | 79 | /** 80 | * 处理HEAD 81 | * 82 | * @param {string} url 接口地址 83 | * @param {*} [config={}] 配置信息 84 | * @returns {Promise} 85 | * @memberof BaseService 86 | */ 87 | public async head(url: string, config: any = {}): Promise { 88 | return this.apiService.head(this.updateConfig(url), this.transportConfig(url, config)); 89 | } 90 | 91 | /** 92 | * 处理PUT 93 | * 94 | * @param {string} url 接口地址 95 | * @param {*} data 请求参数 96 | * @param {*} [config={}] 配置信息 97 | * @returns {Promise} 98 | * @memberof BaseService 99 | */ 100 | public async put(url: string, data: any, config: any = {}): Promise { 101 | return this.apiService.put(this.updateConfig(url), data, this.transportConfig(url, config)); 102 | } 103 | 104 | /** 105 | * 处理PATCH 106 | * 107 | * @param {string} url 接口地址 108 | * @param {*} data 请求参数 109 | * @param {*} [config={}] 配置信息 110 | * @returns {Promise} 111 | * @memberof BaseService 112 | */ 113 | public async patch(url: string, data: any, config: any = {}): Promise { 114 | return this.apiService.patch(this.updateConfig(url), data, this.transportConfig(url, config)); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/client/core/views/ViewBase.ts: -------------------------------------------------------------------------------- 1 | import { Vue } from 'nuxt-property-decorator'; 2 | import { IView } from '~/core/interfaces/IView'; 3 | 4 | /** 5 | * 基础视图类 6 | * 7 | * @export 8 | * @class BaseView 9 | * @extends {Vue} 10 | * @implements {IView} 11 | */ 12 | export class BaseView extends Vue implements IView { 13 | 14 | /** 15 | * SEO Title 16 | * 17 | * @type {string} 18 | * @memberof BaseView 19 | */ 20 | public title: string; 21 | 22 | /** 23 | * SEO Description 24 | * 25 | * @type {string} 26 | * @memberof BaseView 27 | */ 28 | public description: string; 29 | 30 | /** 31 | * SEO Keywords 32 | * 33 | * @type {string} 34 | * @memberof BaseView 35 | */ 36 | public keywords: string; 37 | 38 | public constructor() { 39 | super(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/client/layouts/LayoutDefault.vue: -------------------------------------------------------------------------------- 1 | 6 | 86 | -------------------------------------------------------------------------------- /src/client/layouts/LayoutError.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 28 | 29 | 40 | -------------------------------------------------------------------------------- /src/client/layouts/README.md: -------------------------------------------------------------------------------- 1 | # LAYOUTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Application Layouts. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts). 8 | -------------------------------------------------------------------------------- /src/client/middleware/MiddlewareMobile.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断是否为数移动端,同时输出标记 3 | * 4 | * @export 5 | * @param {*} ctx 6 | */ 7 | export default function (ctx: any): void { 8 | const userAgent: any = ctx.req ? ctx.req.headers['user-agent'] : navigator.userAgent; 9 | ctx.isMobile = /mobile/i.test(userAgent); 10 | } 11 | -------------------------------------------------------------------------------- /src/client/middleware/MiddlewareNotAuthenticated.ts: -------------------------------------------------------------------------------- 1 | import configService from '~/core/service/ServiceConfig'; 2 | import { ConfigDefault } from '../../../config/ConfigDefault'; 3 | import { parseCookie } from '~/common/CommonCookie'; 4 | 5 | const config: ConfigDefault = configService.getConfig(); 6 | const whiteList: Array = config.authWhiteList(); 7 | 8 | /** 9 | * 检测是否在白名单中 10 | * @param url 11 | */ 12 | function checkWheatherInWhiteList(url: string): boolean { 13 | let isIn: boolean = false; 14 | 15 | for (let index: number = 0; index < whiteList.length; index++) { 16 | const element: string = whiteList[index]; 17 | 18 | if (element.indexOf(url) !== -1) { 19 | isIn = true; 20 | break; 21 | } 22 | } 23 | 24 | return isIn; 25 | } 26 | 27 | /** 28 | * 判断是否为未登录 29 | * 30 | * @export 31 | * @param {*} ctx 32 | */ 33 | export default function ({ store, redirect, req }: any): void { 34 | if (process.server) { 35 | // 未登录跳转 36 | const originalUrl: string = req.originalUrl; 37 | const redirectUrl: string = `/auth/login?uri=${originalUrl}`; 38 | if (checkWheatherInWhiteList(originalUrl)) { 39 | // 判断COOKIE是否存在 40 | const cookie: any = parseCookie(req.headers.cookie || ''); 41 | if (store && store.state && store.state.modules && cookie && cookie.auth) { 42 | 43 | if (!store.state.modules.auth || !store.state.modules.auth.auth) { 44 | let authData: Object; 45 | try { 46 | authData = JSON.parse(decodeURIComponent(cookie.auth)); 47 | } catch (error) { 48 | console.log(error); 49 | } 50 | store.state.modules.auth.auth = authData; 51 | } 52 | } 53 | 54 | if (store && store.state && store.state.modules) { 55 | if (!store.state.modules.auth || !store.state.modules.auth.auth) { 56 | return redirect(redirectUrl); 57 | } 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/client/middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /src/client/models/ModelCarts.ts: -------------------------------------------------------------------------------- 1 | import { IVO } from '~/core/interfaces/IVo'; 2 | 3 | /** 4 | * 基础数据结构 5 | * 6 | * @export 7 | * @interface IVO 8 | */ 9 | /** 10 | * 购物车数据结构 11 | * 12 | * @export 13 | * @interface ICarts 14 | */ 15 | export interface ICartsItem extends IVO { 16 | id: string; 17 | title: string; 18 | desc: string; 19 | price: number; 20 | num: number; 21 | thumb: string; 22 | thumbList: Array; 23 | userId: string; 24 | } 25 | 26 | /** 27 | * 购物车VO 28 | * 29 | * @export 30 | * @class CartsVO 31 | * @implements {ICarts} 32 | */ 33 | export class CartsVO implements ICartsItem { 34 | constructor() { 35 | // hole 36 | } 37 | 38 | /** 增加 */ 39 | public static INC_NUM: number = 0; 40 | 41 | /** 减少 */ 42 | public static DEC_NUM: number = 1; 43 | 44 | /** 原值传入 */ 45 | public static FORCE_NUM: number = 2; 46 | 47 | public id: string; 48 | public title: string; 49 | public desc: string; 50 | public price: number; 51 | public num: number; 52 | public thumb: string; 53 | public thumbList: Array = []; 54 | public userId: string; 55 | 56 | /** 57 | * 更新数据源 58 | * 59 | * @param {*} val 60 | * @memberof CartsVO 61 | */ 62 | public update(val: any): void { 63 | if (val) { 64 | this.id = val.id || val._id; 65 | this.title = val.title; 66 | this.desc = val.desc; 67 | this.price = Number(val.price); 68 | this.num = Number(val.num); 69 | this.thumb = val.thumb; 70 | this.userId = val.userId; 71 | 72 | if (val.thumbList && val.thumbList.length) { 73 | for (let index: number = 0; index < val.thumbList.length; index++) { 74 | const element: any = val.thumbList[index]; 75 | this.thumbList.push(element); 76 | } 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/client/models/ModelPost.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Post data structs 3 | * 4 | * @export 5 | * @interface Post 6 | */ 7 | export default interface IPost { 8 | id: number; 9 | title: string; 10 | description: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/client/models/ModelUser.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 用户信息 3 | */ 4 | export default interface IUser { 5 | accessToken: string; 6 | _id: string; 7 | userName: string; 8 | } 9 | -------------------------------------------------------------------------------- /src/client/pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application. 5 | 6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). 7 | -------------------------------------------------------------------------------- /src/client/pages/auth/login/index.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 142 | 143 | 169 | -------------------------------------------------------------------------------- /src/client/pages/cart/index.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 157 | 158 | 188 | -------------------------------------------------------------------------------- /src/client/pages/error/400/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 29 | 30 | 41 | -------------------------------------------------------------------------------- /src/client/pages/error/500/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 29 | 30 | 41 | -------------------------------------------------------------------------------- /src/client/pages/goods/index.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 155 | 156 | 194 | -------------------------------------------------------------------------------- /src/client/pages/index.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 132 | 133 | 177 | -------------------------------------------------------------------------------- /src/client/pages/list/index.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 123 | 124 | 154 | -------------------------------------------------------------------------------- /src/client/plugins/PluginAxios.ts: -------------------------------------------------------------------------------- 1 | import configService from '~/core/service/ServiceConfig'; 2 | import { NuxtAxiosInstance } from '@nuxtjs/axios'; 3 | import { HttpConst } from '~/core/consts/ConstHttp'; 4 | import Cookie from 'js-cookie'; 5 | import { setCustomHeaderBySSR } from '~/common/CommonCookie'; 6 | 7 | /** axios defined */ 8 | export let axios: NuxtAxiosInstance = null; 9 | 10 | export const CODE_400: number = 400; 11 | export const CODE_500: number = 500; 12 | 13 | /** axios instance */ 14 | export default ({ app, redirect }): any => { 15 | axios = app.$axios; 16 | axios.defaults.baseURL = ''; 17 | 18 | configService.saveAxios(axios); 19 | 20 | // Adds header: `Content-Type: application/x-www-form-urlencoded` to only post requests 21 | // axios.setHeader('Content-Type', 'application/x-www-form-urlencoded', [ 22 | // 'post' 23 | // ]); 24 | 25 | // Removes default Content-Type header from `post` scope 26 | // axios.setHeader('Content-Type', false, ['post']); 27 | 28 | // Overrides `Authorization` header with new value 29 | // axios.setToken('456'); 30 | 31 | // Adds header: `Authorization: Bearer 123` to all requests 32 | // axios.setToken('123', 'Bearer'); 33 | 34 | // Adds header: `Authorization: Bearer 123` to only post and delete requests 35 | // axios.setToken('123', 'Bearer', ['post', 'delete']); 36 | 37 | // Removes default Authorization header from `common` scope (all requests) 38 | // axios.setToken(false); 39 | // axios.setHeader('Authorization', getCustomHeader(app)); 40 | 41 | // 服务端使用时设置accessToken 42 | setCustomHeaderBySSR(app, axios); 43 | 44 | // 注册状态回调 45 | axios.onRequest((config: any): any => { 46 | // 设置自定义头部,TOKEN, AUTH等 47 | // Adds header: `Authorization: test` to all requests 48 | // console.log(config.url); 49 | }); 50 | axios.onResponse((response: any): any => { 51 | // console.log('onResponse..', response); 52 | switch (response.status) { 53 | case HttpConst.STATUS_401: 54 | redirect('/auth/login'); 55 | break; 56 | case HttpConst.STATUS_403: 57 | if (process.client) { 58 | Cookie.set('auth', null); 59 | } 60 | redirect('/auth/login'); 61 | break; 62 | } 63 | }); 64 | axios.onError((error: any): any => { 65 | const code: number = Number(error.response && error.response.status); 66 | if (code === CODE_400) { 67 | redirect('/error/400'); 68 | } 69 | if (code === CODE_500) { 70 | redirect('/error/500'); 71 | } 72 | }); 73 | axios.onRequestError((error: any): any => { 74 | console.log('onRequestError...', error); 75 | }); 76 | axios.onResponseError((error: any): any => { 77 | console.log('onResponseError...', error); 78 | }); 79 | }; 80 | -------------------------------------------------------------------------------- /src/client/plugins/PluginFlexible.js: -------------------------------------------------------------------------------- 1 | /** REM 自动转换 */ 2 | (function flexible(window, document) { 3 | var docEl = document.documentElement 4 | var dpr = window.devicePixelRatio || 1 5 | 6 | // adjust body font size 7 | function setBodyFontSize() { 8 | if (document.body) { 9 | document.body.style.fontSize = (12 * dpr) + 'px' 10 | } else { 11 | document.addEventListener('DOMContentLoaded', setBodyFontSize) 12 | } 13 | } 14 | // setBodyFontSize(); 15 | 16 | // set 1rem = viewWidth / 10 17 | function setRemUnit() { 18 | var rem = docEl.clientWidth / 10 19 | docEl.style.fontSize = rem + 'px' 20 | } 21 | 22 | setRemUnit() 23 | 24 | // reset rem unit on page resize 25 | window.addEventListener('resize', setRemUnit) 26 | window.addEventListener('pageshow', function (e) { 27 | if (e.persisted) { 28 | setRemUnit() 29 | } 30 | }) 31 | 32 | // detect 0.5px supports 33 | if (dpr >= 2) { 34 | var fakeBody = document.createElement('body') 35 | var testElement = document.createElement('div') 36 | testElement.style.border = '.5px solid transparent' 37 | fakeBody.appendChild(testElement) 38 | docEl.appendChild(fakeBody) 39 | if (testElement.offsetHeight === 1) { 40 | docEl.classList.add('hairlines') 41 | } 42 | docEl.removeChild(fakeBody) 43 | } 44 | }(window, document)) 45 | -------------------------------------------------------------------------------- /src/client/plugins/PluginVant.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { 3 | Button, 4 | Cell, 5 | CellGroup, 6 | Icon, 7 | Row, 8 | Col, 9 | Dialog, 10 | Toast, 11 | Skeleton, 12 | List, 13 | Tag, 14 | PullRefresh, 15 | NavBar, 16 | Checkbox, 17 | CheckboxGroup, 18 | Card, 19 | SubmitBar, 20 | Swipe, 21 | SwipeItem, 22 | GoodsAction, 23 | GoodsActionIcon, 24 | GoodsActionButton, 25 | Stepper, 26 | Field, 27 | Panel 28 | } from 'vant'; 29 | 30 | import 'vant/lib/index.css'; 31 | 32 | // 注册通用底层组件 33 | Vue.use(Button); 34 | Vue.use(Cell).use(CellGroup); 35 | Vue.use(Icon); 36 | Vue.use(Row).use(Col); 37 | Vue.use(Dialog); 38 | Vue.use(Skeleton); 39 | Vue.use(List); 40 | Vue.use(PullRefresh); 41 | Vue.use(NavBar); 42 | Vue.use(Tag); 43 | Vue.use(Col); 44 | Vue.use(Icon); 45 | Vue.use(Cell); 46 | Vue.use(CellGroup); 47 | Vue.use(Swipe); 48 | Vue.use(SwipeItem); 49 | Vue.use(GoodsAction); 50 | Vue.use(GoodsActionIcon); 51 | Vue.use(GoodsActionButton); 52 | Vue.use(Checkbox); 53 | Vue.use(CheckboxGroup); 54 | Vue.use(Card); 55 | Vue.use(SubmitBar); 56 | Vue.use(Toast); 57 | Vue.use(Stepper); 58 | Vue.use(Field); 59 | Vue.use(Panel); 60 | -------------------------------------------------------------------------------- /src/client/plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). 8 | -------------------------------------------------------------------------------- /src/client/routers/RoutersClient.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 路由常量定义 3 | * 4 | * @export 5 | * @class Routers 6 | */ 7 | export default class Routers { 8 | 9 | /** 10 | * 购物车页 11 | * 12 | * @static 13 | * @type {string} 14 | * @memberof Routers 15 | */ 16 | public static CART_PAGE: string = 'cart'; 17 | 18 | /** 19 | * 物品页 20 | * 21 | * @static 22 | * @type {string} 23 | * @memberof Routers 24 | */ 25 | public static GOODS_PAGE: string = 'goods'; 26 | 27 | /** 28 | * 首页 29 | * 30 | * @static 31 | * @type {string} 32 | * @memberof Routers 33 | */ 34 | public static HOME_PAGE: string = ''; 35 | 36 | /** 37 | * 列表页 38 | * 39 | * @static 40 | * @type {string} 41 | * @memberof Routers 42 | */ 43 | public static LIST_PAGE: string = 'list'; 44 | 45 | /** 46 | * 登录页 47 | * 48 | * @static 49 | * @type {string} 50 | * @memberof Routers 51 | */ 52 | public static LOGIN_PAGE: string = 'auth/login'; 53 | } 54 | -------------------------------------------------------------------------------- /src/client/service/ServiceCNode.ts: -------------------------------------------------------------------------------- 1 | import { BaseOption } from '~/core/service/ServiceBase'; 2 | import configService from '~/core/service/ServiceConfig'; 3 | import { TransportService } from '~/core/service/ServiceTransport'; 4 | 5 | /** 6 | * cn node服务(中转方式,解决跨域问题) 7 | * 8 | * @class CnNodeService 9 | * @extends {TransportService} 10 | */ 11 | class CnNodeService extends TransportService { 12 | constructor() { 13 | const baseOption: BaseOption = new BaseOption(); 14 | baseOption.baseUrl = configService.getConfig().cnodeUrl; 15 | super(baseOption); 16 | } 17 | 18 | /** 19 | * 获取主题列表 20 | * 21 | * @returns {Promise} 22 | * @memberof CnNodeService 23 | */ 24 | public async getTopics(): Promise { 25 | return await this.get('/api/v1/topics'); 26 | } 27 | } 28 | 29 | export default new CnNodeService(); 30 | -------------------------------------------------------------------------------- /src/client/service/ServiceLocal.ts: -------------------------------------------------------------------------------- 1 | import { BaseService, BaseOption } from '~/core/service/ServiceBase'; 2 | import configService from '~/core/service/ServiceConfig'; 3 | import { CartsVO } from '~/models/ModelCarts'; 4 | /** 5 | * 本地 node服务 6 | * 7 | * @class LocalService 8 | * @extends {BaseService} 9 | */ 10 | class LocalService extends BaseService { 11 | constructor() { 12 | const baseOption: BaseOption = new BaseOption(); 13 | baseOption.baseUrl = configService.getConfig().localUrl; 14 | baseOption.isLocalHost = true; 15 | super(baseOption); 16 | } 17 | 18 | /** 19 | * 登录 20 | * @description login 21 | * @param userName 22 | * @param password 23 | * @returns to cart 24 | */ 25 | public async login(userName: string, password: string): Promise { 26 | return await this.post('/api/local/login', { 27 | userName, 28 | password 29 | }); 30 | } 31 | 32 | /** 33 | * 调用接口添加入购物车 34 | * @description Adds to cart 35 | * @param cartsVO 36 | * @param forceType 是否原值存入 37 | * @returns to cart 38 | */ 39 | public async addToCart(cartsVO: CartsVO, forceType: number = 0): Promise { 40 | return await this.post('/api/local/addToCart', { 41 | id: cartsVO.id, 42 | num: cartsVO.num, 43 | forceType, 44 | userId: cartsVO.userId 45 | }); 46 | } 47 | 48 | /** 49 | * 获取已选择商品数据ID列表 50 | * 51 | * @returns {Promise} 52 | * @memberof LocalService 53 | */ 54 | public async getGoodsListData(userId: string): Promise { 55 | return await this.get(`/api/local/getCarts?userId=${userId}`); 56 | } 57 | 58 | /** 59 | * 获取已选择商品列表数据项 60 | * 61 | * @returns {Promise} 62 | * @memberof LocalService 63 | */ 64 | public async getCartsListData(params: Array): Promise { 65 | return await this.post('/api/local/getCartsListData', { list: params }); 66 | } 67 | 68 | /** 69 | * 获取商品页面单个商品数据 70 | * 71 | * @returns {Promise} 72 | * @memberof LocalService 73 | */ 74 | public async getGoodsData(): Promise { 75 | return await this.get('/api/local/getGoods'); 76 | } 77 | 78 | /** 79 | * 获取商品列表数据 80 | * 81 | * @returns {Promise} 82 | * @memberof LocalService 83 | */ 84 | public async getListData(): Promise { 85 | return await this.get('/api/local/getList'); 86 | } 87 | } 88 | 89 | export default new LocalService(); 90 | -------------------------------------------------------------------------------- /src/client/service/ServiceNews.ts: -------------------------------------------------------------------------------- 1 | import { BaseService, BaseOption } from '~/core/service/ServiceBase'; 2 | import configService from '~/core/service/ServiceConfig'; 3 | 4 | /** 5 | * cn node服务(非中转) 6 | * 7 | * @class NewsService 8 | * @extends {BaseService} 9 | */ 10 | class NewsService extends BaseService { 11 | constructor() { 12 | const baseOption: BaseOption = new BaseOption(); 13 | baseOption.baseUrl = configService.getConfig().cnodeUrl; 14 | super(baseOption); 15 | } 16 | 17 | /** 18 | * 获取主题列表 19 | * 20 | * @returns {Promise} 21 | * @memberof CnNodeService 22 | */ 23 | public async getTopics(): Promise { 24 | return await this.get('/api/v1/topics'); 25 | } 26 | } 27 | 28 | export default new NewsService(); 29 | -------------------------------------------------------------------------------- /src/client/static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your static files. 6 | Each file inside this directory is mapped to `/`. 7 | Thus you'd want to delete this README.md before deploying to production. 8 | 9 | Example: `/static/robots.txt` is mapped as `/robots.txt`. 10 | 11 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static). 12 | -------------------------------------------------------------------------------- /src/client/static/about.js: -------------------------------------------------------------------------------- 1 | console.log('about.js loaded!'); 2 | -------------------------------------------------------------------------------- /src/client/static/body.js: -------------------------------------------------------------------------------- 1 | console.log('body.js loaded!'); 2 | -------------------------------------------------------------------------------- /src/client/static/carts.json: -------------------------------------------------------------------------------- 1 | { 2 | "list": [{ 3 | "id": "1", 4 | "title": "进口香蕉", 5 | "desc": "约250g,2根", 6 | "price": 200, 7 | "num": "1", 8 | "thumb": "https://img.yzcdn.cn/public_files/2017/10/24/2f9a36046449dafb8608e99990b3c205.jpeg" 9 | }, { 10 | "id": "2", 11 | "title": "陕西蜜梨", 12 | "desc": "约600g", 13 | "price": 690, 14 | "num": "1", 15 | "thumb": "https://img.yzcdn.cn/public_files/2017/10/24/f6aabd6ac5521195e01e8e89ee9fc63f.jpeg" 16 | }, { 17 | "id": "3", 18 | "title": "美国伽力果", 19 | "desc": "约680g/3个", 20 | "price": 2680, 21 | "num": "1", 22 | "thumb": "https://img.yzcdn.cn/public_files/2017/10/24/320454216bbe9e25c7651e1fa51b31fd.jpeg" 23 | }] 24 | } 25 | -------------------------------------------------------------------------------- /src/client/static/defer.js: -------------------------------------------------------------------------------- 1 | console.log('defer.js loaded!'); 2 | -------------------------------------------------------------------------------- /src/client/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1166544/nuxt.js-nest.js-isomorphism/37a442a3b09e4566580e08b8027634e153ac7ac2/src/client/static/favicon.ico -------------------------------------------------------------------------------- /src/client/static/friends.json: -------------------------------------------------------------------------------- 1 | { 2 | "friends": [ 3 | { 4 | "id": 0, 5 | "name": "Roth Murray" 6 | }, 7 | { 8 | "id": 1, 9 | "name": "Colette Church" 10 | }, 11 | { 12 | "id": 2, 13 | "name": "Washington Best" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/client/static/goods.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "美国伽力果(约680g/3个)", 3 | "price": 2680, 4 | "express": "免邮", 5 | "remain": 19, 6 | "desc": "约680g/3个", 7 | "num": "1", 8 | "thumb": "https://img.yzcdn.cn/public_files/2017/10/24/e5a5a02309a41f9f5def56684808d9ae.jpeg", 9 | "thumbList": ["https://img.yzcdn.cn/public_files/2017/10/24/e5a5a02309a41f9f5def56684808d9ae.jpeg", "https://img.yzcdn.cn/public_files/2017/10/24/1791ba14088f9c2be8c610d0a6cc0f93.jpeg"] 10 | } 11 | -------------------------------------------------------------------------------- /src/client/static/head.js: -------------------------------------------------------------------------------- 1 | console.log('hesad.js loaded!'); 2 | -------------------------------------------------------------------------------- /src/client/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /cart/ 3 | Disallow: /*?* 4 | Disallow: /*.sql 5 | Disallow: /*.js 6 | Disallow: /*.css 7 | Disallow: /*.asmx 8 | -------------------------------------------------------------------------------- /src/client/static/v.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1166544/nuxt.js-nest.js-isomorphism/37a442a3b09e4566580e08b8027634e153ac7ac2/src/client/static/v.png -------------------------------------------------------------------------------- /src/client/store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Vuex Store files. 6 | Vuex Store option is implemented in the Nuxt.js framework. 7 | 8 | Creating a file in this directory automatically activates the option in the framework. 9 | 10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). 11 | -------------------------------------------------------------------------------- /src/client/store/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import { root, RootStore } from './modules/root'; 4 | import { carts, CartsStore } from './modules/carts'; 5 | import { auth, AuthStore } from './modules/auth'; 6 | 7 | Vue.use(Vuex); 8 | 9 | /** store */ 10 | export const store: any = new Vuex.Store({ 11 | modules: { 12 | root, 13 | carts, 14 | auth 15 | } 16 | }); 17 | 18 | /** 注册store实例 */ 19 | Vue.prototype.$vxm = { 20 | root: RootStore.CreateProxy(store, RootStore), 21 | carts: CartsStore.CreateProxy(store, CartsStore), 22 | auth: CartsStore.CreateProxy(store, AuthStore) 23 | }; 24 | 25 | /** 定义接口格式用于TS语法提示 */ 26 | declare module 'vue/types/vue' { 27 | 28 | /** define vue interface */ 29 | // tslint:disable-next-line:interface-name 30 | interface Vue { 31 | $vxm: { 32 | root: RootStore; 33 | carts: CartsStore; 34 | auth: AuthStore; 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/client/store/modules/auth.ts: -------------------------------------------------------------------------------- 1 | import { 2 | action, 3 | Module, 4 | mutation, 5 | VuexModule 6 | } from 'vuex-class-component'; 7 | // import { axios } from '~/plugins/axios.plugins'; 8 | 9 | /** 10 | * RootStore define 11 | * 12 | * @export 13 | * @class RootStore 14 | * @extends {VuexModule} 15 | */ 16 | @Module({ namespacedPath: 'auth/' }) 17 | export class AuthStore extends VuexModule { 18 | private authData: any = null; 19 | 20 | /** 21 | * set auth 22 | * 23 | * @param {*} authData 24 | * @memberof RootStore 25 | */ 26 | @mutation 27 | public setAuth(authData: any): void { 28 | this.authData = authData; 29 | } 30 | 31 | /** 32 | * 获取auth 33 | * 34 | * @readonly 35 | * @type {*} 36 | * @memberof RootStore 37 | */ 38 | public get auth(): any { 39 | return this.authData; 40 | } 41 | 42 | /** 43 | * 获取auth(异步方式) 44 | * 45 | * @returns {Promise} 46 | * @memberof RootStore 47 | */ 48 | @action() 49 | public async getAuth(): Promise { 50 | // const data: any = await axios.get('/friends.json'); 51 | // this.setAuth(data.data.data); 52 | // hole 53 | } 54 | } 55 | 56 | /** AuthStore */ 57 | export const auth: any = AuthStore.ExtractVuexModule(AuthStore); 58 | -------------------------------------------------------------------------------- /src/client/store/modules/carts.ts: -------------------------------------------------------------------------------- 1 | import { 2 | action, 3 | Module, 4 | mutation, 5 | VuexModule 6 | } from 'vuex-class-component'; 7 | import { ICartsItem, CartsVO } from '~/models/ModelCarts'; 8 | // import { axios } from '~/plugins/axios.plugins'; 9 | 10 | /** 11 | * 购物车Store定义 12 | * 13 | * @export 14 | * @class CartsStore 15 | * @extends {VuexModule} 16 | */ 17 | @Module({ namespacedPath: 'carts/' }) 18 | export class CartsStore extends VuexModule { 19 | 20 | /** 21 | * 购物车物品总数 22 | * 23 | * @type {Array} 24 | * @memberof CartsStore 25 | */ 26 | private cartsListSource: Array = []; 27 | 28 | /** 29 | * 获取购物车物品数量 30 | * 31 | * @readonly 32 | * @type {*} 33 | * @memberof CartsStore 34 | */ 35 | public get cartsList(): Array { 36 | return this.cartsListSource; 37 | } 38 | 39 | /** 40 | * 获取购物车列表数据 41 | * 42 | * @readonly 43 | * @type {number} 44 | * @memberof CartsStore 45 | */ 46 | public get cartsNum(): number { 47 | let totalNum: number = 0; 48 | for (let index: number = 0; index < this.cartsListSource.length; index++) { 49 | const element: CartsVO = this.cartsListSource[index]; 50 | totalNum += element.num; 51 | } 52 | 53 | return totalNum; 54 | } 55 | 56 | /** 57 | * 设置购物车物品 58 | * 59 | * @param {*} friends 60 | * @memberof CartsStore 61 | */ 62 | @mutation 63 | public addCarts(item: ICartsItem): void { 64 | this.cartsListSource.push(item); 65 | } 66 | 67 | /** 68 | * 更新购物车列表 69 | * 70 | * @param {Array} cartsList 71 | * @memberof CartsStore 72 | */ 73 | @mutation 74 | public updateCarts(cartsList: Array): void { 75 | this.cartsListSource = cartsList; 76 | } 77 | 78 | /** 79 | * 更新购物车列表内商品数量 80 | * 81 | * @param ICartsItem item 82 | * @memberof CartsStore 83 | */ 84 | @mutation 85 | public updateCartsNum(item: ICartsItem): void { 86 | for (let index: number = 0; index < this.cartsListSource.length; index++) { 87 | const element: CartsVO = this.cartsListSource[index]; 88 | 89 | if (element.id === item.id) { 90 | element.num++; 91 | } 92 | } 93 | } 94 | 95 | /** 96 | * 异步获取购物车物品,例如调用接口 97 | * 98 | * @returns {Promise} 99 | * @memberof CartsStore 100 | */ 101 | @action() 102 | public async getCartsListFromAsync(addedCartsList: Array): Promise { 103 | if (this.cartsNum) { 104 | return ''; 105 | } 106 | 107 | let data: Array; 108 | 109 | if (addedCartsList) { 110 | // 页面渲染时已初始化 111 | if (addedCartsList && addedCartsList.length) { 112 | data = addedCartsList; 113 | } 114 | } 115 | 116 | if (data && data.length) { 117 | while (data.length) { 118 | const item: any = data.shift(); 119 | const cartItem: CartsVO = new CartsVO(); 120 | 121 | cartItem.update(item); 122 | this.addCarts(cartItem); 123 | } 124 | } 125 | 126 | return data; 127 | } 128 | } 129 | 130 | /** CartsStore */ 131 | export const carts: any = CartsStore.ExtractVuexModule(CartsStore); 132 | -------------------------------------------------------------------------------- /src/client/store/modules/root.ts: -------------------------------------------------------------------------------- 1 | import { 2 | action, 3 | Module, 4 | mutation, 5 | VuexModule 6 | } from 'vuex-class-component'; 7 | import { axios } from '~/plugins/PluginAxios'; 8 | 9 | /** 10 | * RootStore define 11 | * 12 | * @export 13 | * @class RootStore 14 | * @extends {VuexModule} 15 | */ 16 | @Module({ namespacedPath: 'root/' }) 17 | export class RootStore extends VuexModule { 18 | public friends: any = []; 19 | 20 | /** 21 | * set friends 22 | * 23 | * @param {*} friends 24 | * @memberof RootStore 25 | */ 26 | @mutation 27 | public setFriends(friends: any): void { 28 | this.friends = friends; 29 | } 30 | 31 | /** 32 | * 获取列表 get 33 | * 34 | * @readonly 35 | * @type {*} 36 | * @memberof RootStore 37 | */ 38 | public get rFriends(): any { 39 | return this.friends; 40 | } 41 | 42 | /** 43 | * 获取列表 44 | * 45 | * @returns {Promise} 46 | * @memberof RootStore 47 | */ 48 | @action() 49 | public async getFriends(): Promise { 50 | console.log('root friends right?'); 51 | const data: any = await axios.get('/friends.json'); 52 | 53 | this.setFriends(data.data.friends); 54 | } 55 | } 56 | 57 | /** RootStore */ 58 | export const root: any = RootStore.ExtractVuexModule(RootStore); 59 | -------------------------------------------------------------------------------- /src/server/AppController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | 3 | /** 4 | * App controller 5 | * 6 | * @export 7 | * @class AppController 8 | */ 9 | @Controller() 10 | export class AppController { 11 | // tslint:disable-next-line:completed-docs 12 | @Get('/test') 13 | public test(): string { 14 | return 'Server OK.'; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/server/AppModule.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { CatsModule } from './modules/cats/CatsModule'; 3 | import { TransportModule } from './modules/transport/TransportModule'; 4 | import { AppController } from './AppController'; 5 | import { LocalModule } from './modules/local/LocalModule'; 6 | 7 | /** 8 | * App Module 9 | * 10 | * @export 11 | * @class ApplicationModule 12 | */ 13 | @Module({ 14 | imports: [CatsModule, TransportModule, LocalModule], 15 | controllers: [AppController] 16 | }) 17 | export class ApplicationModule {} 18 | -------------------------------------------------------------------------------- /src/server/Main.ts: -------------------------------------------------------------------------------- 1 | import { ValidationPipe } from '@nestjs/common'; 2 | import { NestFactory } from '@nestjs/core'; 3 | // tslint:disable-next-line: no-implicit-dependencies 4 | import Consola from 'consola'; 5 | import config from '../../nuxt.config'; 6 | import { ApplicationModule } from './AppModule'; 7 | import NuxtServer from './common/nuxt/CommonNuxtServer'; 8 | import { NuxtFilter } from './common/nuxt/CommonNuxtFilter'; 9 | import configService from '../client/core/service/ServiceConfig'; 10 | import { ConfigDefault } from '../../config/ConfigDefault'; 11 | 12 | // tslint:disable-next-line:completed-docs 13 | declare const module: any; 14 | 15 | /** 16 | * bootstrap 17 | * 18 | */ 19 | // tslint:disable-next-line:typedef 20 | async function bootstrap() { 21 | const { host, port } = config.env; 22 | const serverPort: number = process.env.PORT || port; 23 | 24 | const nuxt: any = await NuxtServer.getInstance().run( 25 | config.dev ? !module.hot._main : true 26 | ); 27 | const server: any = await NestFactory.create(ApplicationModule); 28 | const configer: ConfigDefault = configService.getConfig(); 29 | 30 | server.useGlobalFilters(new NuxtFilter(nuxt)); 31 | server.useGlobalPipes(new ValidationPipe()); 32 | server.setGlobalPrefix(configer.getGlobalPrefix()); 33 | await server.listen(serverPort, host, () => { 34 | Consola.ready({ 35 | message: `Server listening on http://${host}:${serverPort}`, 36 | badge: true 37 | }); 38 | }); 39 | 40 | if (config.dev && module.hot) { 41 | module.hot.accept(); 42 | module.hot.dispose(() => server.close()); 43 | } 44 | } 45 | 46 | bootstrap(); 47 | -------------------------------------------------------------------------------- /src/server/common/core/CommonEngineController.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | 3 | /** 4 | * CONTROLLER基类 5 | */ 6 | export class EngineController { 7 | /** 8 | * 格式化列表返回数据结构 9 | * @param total 总条数 10 | * @param pageSize 每页数 11 | * @param pageNo 当前页 12 | * @param pages 总页数 13 | * @param list 集合数据 14 | * @param {number} [code=HttpStatus.OK] 15 | * @param {string} [message='操作成功'] 16 | * @param {boolean} [success=true] 17 | */ 18 | public encapsulationReturnList( 19 | total: number, 20 | pageSize: number, 21 | pageNo: number, 22 | pages: number, 23 | list: Array, 24 | code: number = HttpStatus.OK, 25 | message: string = '操作成功', 26 | success: boolean = true 27 | ): any { 28 | return { 29 | code, 30 | message, 31 | success, 32 | data: { 33 | total, 34 | pageSize, 35 | pageNo, 36 | pages, 37 | list 38 | } 39 | }; 40 | } 41 | 42 | /** 43 | * 格式化普通对像返回结构 44 | * 45 | * @param {*} data 46 | * @param {number} [code=HttpStatus.OK] 47 | * @param {string} [message='操作成功'] 48 | * @param {boolean} [success=true] 49 | * @returns 50 | * @memberof EngineController 51 | */ 52 | public encapsulationReturnObject( 53 | data: any, 54 | code: number = HttpStatus.OK, 55 | message: string = '操作成功', 56 | success: boolean = true 57 | ): any { 58 | return { 59 | code, 60 | message, 61 | success, 62 | data 63 | }; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/server/common/decorators/CommonRolesDecorator.ts: -------------------------------------------------------------------------------- 1 | import { SetMetadata } from '@nestjs/common'; 2 | 3 | /** Roles const */ 4 | // tslint:disable-next-line: variable-name 5 | export const Roles: any = (...roles: string[]): any => SetMetadata('roles', roles); 6 | -------------------------------------------------------------------------------- /src/server/common/filters/CommonHttpExceptionFilter.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ArgumentsHost, 3 | Catch, 4 | ExceptionFilter, 5 | HttpException 6 | } from '@nestjs/common'; 7 | 8 | /** 9 | * Http exception filter 10 | * 11 | * @export 12 | * @class HttpExceptionFilter 13 | * @implements {ExceptionFilter} 14 | */ 15 | @Catch(HttpException) 16 | export class HttpExceptionFilter implements ExceptionFilter { 17 | 18 | /** 19 | * Catch error 20 | * 21 | * @param {HttpException} exception 22 | * @param {ArgumentsHost} host 23 | * @memberof HttpExceptionFilter 24 | */ 25 | public catch(exception: HttpException, host: ArgumentsHost): void { 26 | const ctx: any = host.switchToHttp(); 27 | const response: any = ctx.getResponse(); 28 | const request: any = ctx.getRequest(); 29 | const statusCode: any = exception.getStatus(); 30 | 31 | response.status(statusCode).json({ 32 | statusCode, 33 | timestamp: new Date().toISOString(), 34 | path: request.url 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/server/common/guards/CommonRolesGuard.ts: -------------------------------------------------------------------------------- 1 | import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; 2 | import { Reflector } from '@nestjs/core'; 3 | 4 | /** 5 | * RolesGurad 6 | * 7 | * @export 8 | * @class RolesGuard 9 | * @implements {CanActivate} 10 | */ 11 | @Injectable() 12 | export class RolesGuard implements CanActivate { 13 | constructor(private readonly reflector: Reflector) { } 14 | 15 | /** 16 | * can active 17 | * 18 | * @param {ExecutionContext} context 19 | * @returns {boolean} 20 | * @memberof RolesGuard 21 | */ 22 | public canActivate(context: ExecutionContext): boolean { 23 | const roles: any = this.reflector.get('roles', context.getHandler()); 24 | if (!roles) { 25 | return true; 26 | } 27 | const request: any = context.switchToHttp().getRequest(); 28 | const user: any = request.user; 29 | const hasRole: any = (): any => 30 | user.roles.some((role) => !!roles.find((item) => item === role)); 31 | 32 | return user && user.roles && hasRole(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/server/common/interceptors/CommonExceptionInterceptor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CallHandler, 3 | ExecutionContext, 4 | HttpException, 5 | HttpStatus, 6 | Injectable, 7 | NestInterceptor 8 | } from '@nestjs/common'; 9 | import { Observable, throwError } from 'rxjs'; 10 | import { catchError } from 'rxjs/operators'; 11 | 12 | /** 13 | * ErrorsInterrecptor 14 | * 15 | * @export 16 | * @class ErrorsInterceptor 17 | * @implements {NestInterceptor} 18 | */ 19 | @Injectable() 20 | export class ErrorsInterceptor implements NestInterceptor { 21 | 22 | /** 23 | * intercept 24 | * 25 | * @param {ExecutionContext} context 26 | * @param {CallHandler} next 27 | * @returns {Observable} 28 | * @memberof ErrorsInterceptor 29 | */ 30 | public intercept(context: ExecutionContext, next: CallHandler): Observable { 31 | return next 32 | .handle() 33 | .pipe( 34 | catchError((err) => 35 | throwError(new HttpException('New message', HttpStatus.BAD_GATEWAY)) 36 | ) 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/server/common/interceptors/CommonLoggingInterceptor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CallHandler, 3 | ExecutionContext, 4 | Injectable, 5 | NestInterceptor 6 | } from '@nestjs/common'; 7 | import { Observable } from 'rxjs'; 8 | import { tap } from 'rxjs/operators'; 9 | 10 | /** 11 | * Logging interceptor 12 | * 13 | * @export 14 | * @class LoggingInterceptor 15 | * @implements {NestInterceptor} 16 | */ 17 | @Injectable() 18 | export class LoggingInterceptor implements NestInterceptor { 19 | 20 | /** 21 | * intercept 22 | * 23 | * @param {ExecutionContext} context 24 | * @param {CallHandler} next 25 | * @returns {Observable} 26 | * @memberof LoggingInterceptor 27 | */ 28 | public intercept(context: ExecutionContext, next: CallHandler): Observable { 29 | console.log('Before...'); 30 | 31 | const now: any = Date.now(); 32 | 33 | return next 34 | .handle() 35 | .pipe(tap(() => console.log(`After... ${Date.now() - now}ms`))); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/server/common/interceptors/CommonTimeoutInterceptor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CallHandler, 3 | ExecutionContext, 4 | Injectable, 5 | NestInterceptor 6 | } from '@nestjs/common'; 7 | import { Observable } from 'rxjs'; 8 | import { timeout } from 'rxjs/operators'; 9 | 10 | /** 11 | * Timeout intercept 12 | * 13 | * @export 14 | * @class TimeoutInterceptor 15 | * @implements {NestInterceptor} 16 | */ 17 | @Injectable() 18 | export class TimeoutInterceptor implements NestInterceptor { 19 | 20 | /** 21 | * intercept 22 | * 23 | * @param {ExecutionContext} context 24 | * @param {CallHandler} next 25 | * @returns {Observable} 26 | * @memberof TimeoutInterceptor 27 | */ 28 | public intercept(context: ExecutionContext, next: CallHandler): Observable { 29 | const timeoutNum: number = 5000; 30 | 31 | return next.handle().pipe(timeout(timeoutNum)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/server/common/interceptors/CommonTransformInterceptor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CallHandler, 3 | ExecutionContext, 4 | Injectable, 5 | NestInterceptor 6 | } from '@nestjs/common'; 7 | import { Observable } from 'rxjs'; 8 | import { map } from 'rxjs/operators'; 9 | 10 | /** 11 | * Response 12 | * 13 | * @export 14 | * @interface Response 15 | * @template T 16 | */ 17 | export interface IResponse { 18 | data: T; 19 | } 20 | 21 | /** 22 | * TransformInterreptor 23 | * 24 | * @export 25 | * @class TransformInterceptor 26 | * @implements {NestInterceptor>} 27 | * @template T 28 | */ 29 | @Injectable() 30 | export class TransformInterceptor implements NestInterceptor> { 31 | 32 | /** 33 | * handle intercept 34 | * 35 | * @param {ExecutionContext} context 36 | * @param {CallHandler} next 37 | * @returns {Observable>} 38 | * @memberof TransformInterceptor 39 | */ 40 | public intercept( 41 | context: ExecutionContext, 42 | next: CallHandler 43 | ): Observable> { 44 | return next.handle().pipe( 45 | map( 46 | (data: any) => ({ data }) 47 | ) 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/server/common/middleware/CommonHttpMiddleware.ts: -------------------------------------------------------------------------------- 1 | import Axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'; 2 | import { defer, Observable } from 'rxjs'; 3 | import ProxyAgent from 'proxy-agent'; 4 | import { ConfigDefault } from '../../../../config/ConfigDefault'; 5 | import configService from '../../../client/core/service/ServiceConfig'; 6 | 7 | /** 8 | * 9 | * http 请求封装基类 10 | * @export 11 | * @class BaseHttpClient 12 | */ 13 | export class BaseHttpClient { 14 | /** 15 | * http代理地址 16 | */ 17 | private proxyUri: string; 18 | 19 | /** 20 | * Axios 实例 21 | */ 22 | private readonly instance: AxiosInstance = Axios; 23 | 24 | constructor() { 25 | // 读取配置 26 | const configer: ConfigDefault = configService.getConfig(); 27 | this.proxyUri = configer.getProxyAddress(); 28 | 29 | // 请求拦截 30 | this.instance.interceptors.request.use( 31 | (request: AxiosRequestConfig) => { 32 | request.headers.startInvokeTime = Date.now(); 33 | // 开发模式则添加代理-charles 34 | if (process.env.NODE_ENV === ConfigDefault.ENV_DEV) { 35 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; 36 | request.httpAgent = new ProxyAgent(this.proxyUri); 37 | request.httpsAgent = new ProxyAgent(this.proxyUri); 38 | } 39 | 40 | return request; 41 | }, 42 | (error: any) => { 43 | // hole 采集上传错误日志 error.response || {} 44 | } 45 | ); 46 | 47 | // 响应拦截 48 | this.instance.interceptors.response.use( 49 | (response: AxiosResponse) => { 50 | // hole 采信响应日志 response 51 | 52 | return response; 53 | }, 54 | (error) => { 55 | // hole 采集响应错误日志 console.log('error.......', error); 56 | // const copyHeaders: any = { ...(error.config.headers || {}) }; 57 | // `Error: ${error.syscall} ${error.errno} ${error.hostname} ${error.port}` 58 | throw error; 59 | } 60 | ); 61 | } 62 | 63 | /** 64 | * request 请求 65 | * @template T 66 | * @param {AxiosRequestConfig} config 67 | * @returns {Observable>} 68 | * @memberof BaseHttpClient 69 | */ 70 | public request( 71 | config: AxiosRequestConfig 72 | ): Observable> { 73 | return defer(() => this.instance.request(config)); 74 | } 75 | 76 | /** 77 | * get 请求 78 | * @template T 79 | * @param {string} url 80 | * @param {AxiosRequestConfig} [config] 81 | * @returns {Observable>} 82 | * @memberof BaseHttpClient 83 | */ 84 | public get( 85 | url: string, 86 | config?: AxiosRequestConfig 87 | ): Observable> { 88 | return defer(() => this.instance.get(url, config)); 89 | } 90 | 91 | /** 92 | * 93 | * delete 94 | * @template T 95 | * @param {string} url 96 | * @param {AxiosRequestConfig} [config] 97 | * @returns {Observable>} 98 | * @memberof BaseHttpClient 99 | */ 100 | public delete( 101 | url: string, 102 | config?: AxiosRequestConfig 103 | ): Observable> { 104 | return defer(() => this.instance.delete(url, config)); 105 | } 106 | 107 | /** 108 | * 109 | * head 110 | * @template T 111 | * @param {string} url 112 | * @param {AxiosRequestConfig} [config] 113 | * @returns {Observable>} 114 | * @memberof BaseHttpClient 115 | */ 116 | public head( 117 | url: string, 118 | config?: AxiosRequestConfig 119 | ): Observable> { 120 | return defer(() => this.instance.head(url, config)); 121 | } 122 | 123 | /** 124 | * 125 | * post 126 | * @template T 127 | * @param {string} url 128 | * @param {*} [data] 129 | * @param {AxiosRequestConfig} [config] 130 | * @returns {Observable>} 131 | * @memberof BaseHttpClient 132 | */ 133 | public post( 134 | url: string, 135 | data?: any, 136 | config?: AxiosRequestConfig 137 | ): Observable> { 138 | return defer(() => this.instance.post(url, data, config)); 139 | } 140 | 141 | /** 142 | * 143 | * put 144 | * @template T 145 | * @param {string} url 146 | * @param {*} [data] 147 | * @param {AxiosRequestConfig} [config] 148 | * @returns {Observable>} 149 | * @memberof BaseHttpClient 150 | */ 151 | public put( 152 | url: string, 153 | data?: any, 154 | config?: AxiosRequestConfig 155 | ): Observable> { 156 | return defer(() => this.instance.put(url, data, config)); 157 | } 158 | 159 | /** 160 | * 161 | * patch 162 | * @template T 163 | * @param {string} url 164 | * @param {*} [data] 165 | * @param {AxiosRequestConfig} [config] 166 | * @returns {Observable>} 167 | * @memberof BaseHttpClient 168 | */ 169 | public patch( 170 | url: string, 171 | data?: any, 172 | config?: AxiosRequestConfig 173 | ): Observable> { 174 | return defer(() => this.instance.patch(url, data, config)); 175 | } 176 | 177 | /** 178 | * 179 | * AxiosInstance 180 | * @readonly 181 | * @type {AxiosInstance} 182 | * @memberof BaseHttpClient 183 | */ 184 | public get axiosRef(): AxiosInstance { 185 | return this.instance; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/server/common/middleware/CommonLoggerMiddleware.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NestMiddleware } from '@nestjs/common'; 2 | 3 | /** 4 | * LoggerMiddleware 5 | * 6 | * @export 7 | * @class LoggerMiddleware 8 | * @implements {NestMiddleware} 9 | */ 10 | @Injectable() 11 | export class LoggerMiddleware implements NestMiddleware { 12 | 13 | /** 14 | * use middleware 15 | * 16 | * @param {*} req 17 | * @param {*} res 18 | * @param {() => void} next 19 | * @memberof LoggerMiddleware 20 | */ 21 | public use(req: any, res: any, next: () => void): void { 22 | console.log('Request...'); 23 | next(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/server/common/nuxt/CommonNuxtFilter.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ArgumentsHost, 3 | Catch, 4 | ExceptionFilter, 5 | HttpException, 6 | HttpStatus 7 | } from '@nestjs/common'; 8 | import { Nuxt } from 'nuxt'; 9 | 10 | /** 11 | * NUXT过滤器 12 | * 13 | * @export 14 | * @class NuxtFilter 15 | * @implements {ExceptionFilter} 16 | */ 17 | @Catch() 18 | export class NuxtFilter implements ExceptionFilter { 19 | private readonly nuxt: Nuxt; 20 | 21 | constructor(nuxt: Nuxt) { 22 | this.nuxt = nuxt; 23 | } 24 | 25 | /** 26 | * catch 27 | * 28 | * @param {HttpException} exception 29 | * @param {ArgumentsHost} host 30 | * @memberof NuxtFilter 31 | */ 32 | public async catch(exception: HttpException, host: ArgumentsHost): Promise { 33 | 34 | try { 35 | if (exception && !exception.hasOwnProperty('getStatus')) { 36 | const response: any = exception.getResponse(); 37 | 38 | if (response.message.indexOf('Cannot GET /') === -1) { 39 | console.log(exception); 40 | } 41 | } 42 | 43 | const ctx: any = host.switchToHttp(); 44 | const res: any = ctx.getResponse(); 45 | const req: any = ctx.getRequest(); 46 | const status: any = exception ? exception.getStatus() : HttpStatus.OK; 47 | 48 | if (status === HttpStatus.NOT_FOUND) { 49 | // console.log('Render========', HttpStatus.NOT_FOUND); 50 | if (!res.headersSent) { 51 | await this.nuxt.render(req, res); 52 | } 53 | } else { 54 | // console.log('Render========', status); 55 | res.status(status).json({ 56 | statusCode: status, 57 | timestamp: new Date().toISOString(), 58 | path: req.url 59 | }); 60 | } 61 | } catch (error) { 62 | console.log(error); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/server/common/nuxt/CommonNuxtServer.ts: -------------------------------------------------------------------------------- 1 | import { Builder } from '@nuxt/builder'; 2 | import { Nuxt } from '@nuxt/core'; 3 | import { BundleBuilder } from '@nuxt/webpack'; 4 | import config from '../../../../nuxt.config'; 5 | 6 | /** 7 | * NUXT服务 8 | * 9 | * @export 10 | * @class NuxtServer 11 | */ 12 | // tslint:disable-next-line: no-default-export 13 | export default class NuxtServer { 14 | private static instance: NuxtServer; 15 | public nuxt: Nuxt; 16 | 17 | /** 18 | * RUN 19 | * 20 | * @param {boolean} [shouldBuild=true] 21 | * @returns {Nuxt} 22 | * @memberof NuxtServer 23 | */ 24 | public async run(shouldBuild: boolean = true): Nuxt { 25 | const nuxt: any = new Nuxt(config); 26 | 27 | // Build only in dev mode 28 | if (config.dev && shouldBuild) { 29 | const builder: any = new Builder(nuxt, BundleBuilder); 30 | const res: any = await builder.build(); 31 | 32 | this.nuxt = res.nuxt; 33 | 34 | return res.nuxt; 35 | } 36 | 37 | if (this.nuxt) { 38 | return this.nuxt; 39 | } 40 | 41 | return nuxt; 42 | } 43 | 44 | /** 45 | * 单例模式 46 | * 47 | * @static 48 | * @returns {NuxtServer} 49 | * @memberof NuxtServer 50 | */ 51 | public static getInstance(): NuxtServer { 52 | if (this.instance) { 53 | return this.instance; 54 | } else { 55 | this.instance = new NuxtServer(); 56 | 57 | return this.instance; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/server/common/pipes/CommonParseIntPipe.ts: -------------------------------------------------------------------------------- 1 | import { BadRequestException } from '@nestjs/common'; 2 | import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common'; 3 | 4 | /** 5 | * Parse init pipe 6 | * 7 | * @export 8 | * @class ParseIntPipe 9 | * @implements {PipeTransform} 10 | */ 11 | @Injectable() 12 | export class ParseIntPipe implements PipeTransform { 13 | 14 | /** 15 | * transform 16 | * 17 | * @param {string} value 18 | * @param {ArgumentMetadata} metadata 19 | * @returns 20 | * @memberof ParseIntPipe 21 | */ 22 | public async transform(value: string, metadata: ArgumentMetadata): Promise { 23 | const val: any = parseInt(value, 10); 24 | if (isNaN(val)) { 25 | throw new BadRequestException('Validation failed'); 26 | } 27 | 28 | return val; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/server/common/pipes/CommonValidationPipe.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ArgumentMetadata, 3 | BadRequestException, 4 | Injectable, 5 | PipeTransform, 6 | Type 7 | } from '@nestjs/common'; 8 | import { plainToClass } from 'class-transformer'; 9 | import { validate } from 'class-validator'; 10 | 11 | /** 12 | * ValidationPipe 13 | * 14 | * @export 15 | * @class ValidationPipe 16 | * @implements {PipeTransform} 17 | */ 18 | @Injectable() 19 | export class ValidationPipe implements PipeTransform { 20 | 21 | /** 22 | * transform 23 | * 24 | * @param {*} value 25 | * @param {ArgumentMetadata} metadata 26 | * @returns 27 | * @memberof ValidationPipe 28 | */ 29 | public async transform(value: any, metadata: ArgumentMetadata): Promise { 30 | const { metatype } = metadata; 31 | if (!metatype || !this.toValidate(metatype)) { 32 | return value; 33 | } 34 | const object: any = plainToClass(metatype, value); 35 | const errors: any = await validate(object); 36 | if (errors.length > 0) { 37 | throw new BadRequestException('Validation failed'); 38 | } 39 | 40 | return value; 41 | } 42 | 43 | /** 44 | * toValidate 45 | * 46 | * @private 47 | * @param {Type} metatype 48 | * @returns {boolean} 49 | * @memberof ValidationPipe 50 | */ 51 | private toValidate(metatype: Type): boolean { 52 | const types: any = [String, Boolean, Number, Array, Object]; 53 | 54 | return !types.find( 55 | (type: any) => { 56 | return metatype === type; 57 | } 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/server/modules/cats/CatsController.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Body, 3 | Controller, 4 | Get, 5 | Param, 6 | Post, 7 | UseGuards, 8 | UseInterceptors 9 | } from '@nestjs/common'; 10 | import { Roles } from '../../common/decorators/CommonRolesDecorator'; 11 | import { RolesGuard } from '../../common/guards/CommonRolesGuard'; 12 | import { LoggingInterceptor } from '../../common/interceptors/CommonLoggingInterceptor'; 13 | import { TransformInterceptor } from '../../common/interceptors/CommonTransformInterceptor'; 14 | import { ParseIntPipe } from '../../common/pipes/CommonParseIntPipe'; 15 | import { Routers } from '../../routers/RoutersServer'; 16 | import { CatsService } from './CatsService'; 17 | import { CreateCatDto } from './dto/CatsCreateDto'; 18 | import { ICat } from './interfaces/ICats'; 19 | 20 | /** 21 | * Cats controller 22 | * 23 | * @export 24 | * @class CatsController 25 | */ 26 | @Controller(Routers.CATS_MODULE_ROUTER) 27 | @UseGuards(RolesGuard) 28 | @UseInterceptors(LoggingInterceptor, TransformInterceptor) 29 | export class CatsController { 30 | constructor(private readonly catsService: CatsService) { } 31 | 32 | /** 33 | * create 34 | * 35 | * @param {CreateCatDto} createCatDto 36 | * @memberof CatsController 37 | */ 38 | @Post() 39 | @Roles('admin') 40 | public async create(@Body() createCatDto: CreateCatDto): Promise { 41 | this.catsService.create(createCatDto); 42 | } 43 | 44 | /** 45 | * Find all 46 | * 47 | * @returns {Promise} 48 | * @memberof CatsController 49 | */ 50 | @Get() 51 | public async findAll(): Promise { 52 | return this.catsService.findAll(); 53 | } 54 | 55 | /** 56 | * Find one 57 | * 58 | * @param {*} id 59 | * @memberof CatsController 60 | */ 61 | @Get(':id') 62 | public findOne( 63 | @Param('id', new ParseIntPipe()) 64 | id: any 65 | ): any { 66 | // logic 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/server/modules/cats/CatsModule.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { CatsController } from './CatsController'; 3 | import { CatsService } from './CatsService'; 4 | 5 | /** 6 | * Cats Module 7 | * 8 | * @export 9 | * @class CatsModule 10 | */ 11 | @Module({ 12 | controllers: [CatsController], 13 | providers: [CatsService] 14 | }) 15 | export class CatsModule { } 16 | -------------------------------------------------------------------------------- /src/server/modules/cats/CatsService.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { ICat } from './interfaces/ICats'; 3 | 4 | /** 5 | * Cats service 6 | * 7 | * @export 8 | * @class CatsService 9 | */ 10 | @Injectable() 11 | export class CatsService { 12 | private readonly cats: ICat[] = []; 13 | 14 | /** 15 | * create 16 | * 17 | * @param {ICat} cat 18 | * @returns {*} 19 | * @memberof CatsService 20 | */ 21 | public create(cat: ICat): any { 22 | this.cats.push(cat); 23 | } 24 | 25 | /** 26 | * Find all 27 | * 28 | * @returns {ICat[]} 29 | * @memberof CatsService 30 | */ 31 | public findAll(): ICat[] { 32 | return this.cats; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/server/modules/cats/dto/CatsCreateDto.ts: -------------------------------------------------------------------------------- 1 | import { IsInt, IsString } from 'class-validator'; 2 | 3 | /** 4 | * Create4 cat dto 5 | * 6 | * @export 7 | * @class CreateCatDto 8 | */ 9 | export class CreateCatDto { 10 | /** 11 | * name field 12 | * 13 | * @type {string} 14 | * @memberof CreateCatDto 15 | */ 16 | @IsString() 17 | public readonly name: string; 18 | 19 | /** 20 | * age field 21 | * 22 | * @type {number} 23 | * @memberof CreateCatDto 24 | */ 25 | @IsInt() 26 | public readonly age: number; 27 | 28 | /** 29 | * bread 30 | * 31 | * @type {string} 32 | * @memberof CreateCatDto 33 | */ 34 | @IsString() 35 | public readonly breed: string; 36 | } 37 | -------------------------------------------------------------------------------- /src/server/modules/cats/interfaces/ICats.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Interface cat 3 | * 4 | * @export 5 | * @interface ICat 6 | */ 7 | export interface ICat { 8 | /** 9 | * name field 10 | * 11 | * @type {string} 12 | * @memberof ICat 13 | */ 14 | readonly name: string; 15 | 16 | /** 17 | * age field 18 | * 19 | * @type {number} 20 | * @memberof ICat 21 | */ 22 | readonly age: number; 23 | 24 | /** 25 | * breed field 26 | * 27 | * @type {string} 28 | * @memberof ICat 29 | */ 30 | readonly breed: string; 31 | } 32 | -------------------------------------------------------------------------------- /src/server/modules/db/DBModule.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { databaseProviders } from './DBProviders'; 3 | 4 | /** 5 | * database module 6 | * @description Module 7 | */ 8 | @Module({ 9 | providers: [...databaseProviders], 10 | exports: [...databaseProviders] 11 | }) 12 | export class DataBaseModule { 13 | // hole 14 | } 15 | -------------------------------------------------------------------------------- /src/server/modules/db/DBProviders.ts: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | import configService from '../../../client/core/service/ServiceConfig'; 3 | import { ConfigDefault } from '../../../../config/ConfigDefault'; 4 | import { ConfigDB, IMongoDBConfig } from '../../../../config/ConfigDB'; 5 | 6 | /** 7 | * data base providers 8 | */ 9 | export const databaseProviders: Array = [ 10 | { 11 | provide: 'DATABASE_CONNECTION', 12 | useFactory: async (): Promise => { 13 | // 获取默认配置 14 | const configer: ConfigDefault = configService.getConfig(); 15 | const configDB: ConfigDB = new ConfigDB(); 16 | 17 | // 更新当前环境配置 18 | configDB.updateEnv(configer.getEnv()); 19 | 20 | // 组装连接字符串 21 | const config: Object = { 22 | autoIndex: false, 23 | useNewUrlParser: true 24 | }; 25 | const configData: IMongoDBConfig = configDB.getMongoDbConfig(); 26 | const configStr: string = `mongodb://${configData.userName}:${configData.password}@${configData.uri}:${configData.port}/${configData.db}`; 27 | const connectResult: any = await mongoose.connect(configStr, config); 28 | 29 | return connectResult; 30 | } 31 | } 32 | ]; 33 | -------------------------------------------------------------------------------- /src/server/modules/local/LocalController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Req, Get, Res, HttpStatus, Post, Body, Query } from '@nestjs/common'; 2 | import { LocalService } from './LocalService'; 3 | import { Routers } from '../../routers/RoutersServer'; 4 | import { CreateCatDto } from './dto/LocalCreateCatDto'; 5 | import { ICat } from './interfaces/ICat'; 6 | import { CreateGoodsDto } from './dto/LocalCreateGoodsDto'; 7 | import { EngineController } from '../../common/core/CommonEngineController'; 8 | import { AddCartsDto } from './dto/LocalAddCartsDto'; 9 | import { CardListItemDto } from './dto/LocalCardListItemDto'; 10 | import { LoginDto } from './dto/LocalLoginDto'; 11 | 12 | /** 13 | * 中转服务CONTROLLER处理器 14 | * 15 | * @export 16 | * @class LocalController 17 | * @extends {} 18 | */ 19 | @Controller(Routers.LOCAL_MODULE_ROUTER) 20 | export class LocalController extends EngineController { 21 | constructor( 22 | private readonly localService: LocalService 23 | ) { 24 | super(); 25 | } 26 | 27 | /** 28 | * create cat 29 | * @description Posts local controller 30 | * @param createCatDto 31 | */ 32 | @Post('login') 33 | public async login(@Res() res: any, @Body() loginDto: LoginDto): Promise { 34 | const returnData: Array = await this.localService.login(loginDto); 35 | 36 | if (returnData.length) { 37 | res.status(HttpStatus.OK).json( 38 | this.encapsulationReturnObject(returnData[0]) 39 | ); 40 | } else { 41 | res.status(HttpStatus.FORBIDDEN).json( 42 | this.encapsulationReturnObject({}, HttpStatus.FORBIDDEN, '用户名或密码不正确') 43 | ); 44 | } 45 | } 46 | 47 | /** 48 | * create cat 49 | * @description Posts local controller 50 | * @param createCatDto 51 | */ 52 | @Post('createCat') 53 | public async create(@Body() createCatDto: CreateCatDto): Promise { 54 | return await this.localService.create(createCatDto); 55 | } 56 | 57 | /** 58 | * find all 59 | * @description Gets local controller 60 | * @returns all 61 | */ 62 | @Get('findAll') 63 | public async findAll(): Promise { 64 | return this.localService.findAll(); 65 | } 66 | 67 | /** 68 | * 获取新闻列表 69 | * 70 | * @param {*} request 71 | * @returns 72 | * @memberof TransportController 73 | */ 74 | @Get('getList') 75 | public async getList(@Res() res: any, @Req() request: any): Promise { 76 | const returnData: any = await this.localService.getList(); 77 | 78 | res.status(HttpStatus.OK).json(returnData.data); 79 | } 80 | 81 | /** 82 | * 获取购物车信息 83 | * @param request 84 | * @returns carts 85 | */ 86 | @Get('getCarts') 87 | public async getCarts(@Res() res: any, @Req() request: any, @Query() cartsParams: any): Promise { 88 | const returnData: any = await this.localService.getCarts(cartsParams.userId); 89 | 90 | res.status(HttpStatus.OK).json(returnData); 91 | } 92 | 93 | /** 94 | * 获取商品信息 95 | * @param request 96 | * @returns goods 97 | */ 98 | @Get('getGoods') 99 | public async getGoods(@Res() res: any, @Req() request: any, @Query() query: any): Promise { 100 | const id: string = query.id || '5d74f3dc40407532cd556949'; 101 | const returnData: any = await this.localService.getGoods(id); 102 | 103 | res.status(HttpStatus.OK).json( 104 | this.encapsulationReturnObject(returnData) 105 | ); 106 | } 107 | 108 | /** 109 | * create goods 110 | * @description Posts local controller 111 | * @param createGoodsDto 112 | */ 113 | @Post('createGoods') 114 | public async createGoods(@Body() createGoodsDto: CreateGoodsDto): Promise { 115 | await this.localService.createGoods(createGoodsDto); 116 | } 117 | 118 | /** 119 | * 添加商品入购物车 120 | * @description Posts local controller 121 | * @param addToCartsDto 122 | */ 123 | @Post('addToCart') 124 | public async addToCart(@Res() res: any, @Body() addToCartsDto: AddCartsDto): Promise { 125 | const returnData: any = await this.localService.addToCart(addToCartsDto); 126 | 127 | res.status(HttpStatus.OK).json( 128 | this.encapsulationReturnObject(returnData) 129 | ); 130 | } 131 | 132 | /** 133 | * 依据ID获取已添加入购物车的列表数据项 134 | * @description Posts local controller 135 | * @param addToCartsDto 136 | */ 137 | @Post('getCartsListData') 138 | public async getCartsListData(@Res() res: any, @Body() cartListItem: CardListItemDto): Promise { 139 | const returnData: any = await this.localService.getCartsListData(cartListItem); 140 | 141 | res.status(HttpStatus.OK).json( 142 | this.encapsulationReturnObject(returnData) 143 | ); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/server/modules/local/LocalModule.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { LocalController } from './LocalController'; 3 | import { LocalService } from './LocalService'; 4 | import { DataBaseModule } from '../db/DBModule'; 5 | import { localProviders } from './LocalProviders'; 6 | 7 | /** 8 | * 本地服务模块 9 | */ 10 | @Module({ 11 | imports: [DataBaseModule], 12 | controllers: [LocalController], 13 | providers: [LocalService, ...localProviders] 14 | }) 15 | export class LocalModule { 16 | constructor() { 17 | // hole 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/server/modules/local/LocalProviders.ts: -------------------------------------------------------------------------------- 1 | import { Connection } from 'mongoose'; 2 | import { CatSchema } from './schemas/LocalCatSchema'; 3 | import { GoodsSchema } from './schemas/LocalGoodsSchema'; 4 | import { CartsSchema } from './schemas/LocalCartSchema'; 5 | import { UserSchema } from './schemas/LocalUserSchema'; 6 | 7 | /** 8 | * model常量 9 | * @description Model 10 | */ 11 | export class MODEL { 12 | public static CAT_MODEL: string = 'CAT_MODEL'; 13 | public static GOODS_MODEL: string = 'GOODS_MODEL'; 14 | public static CARTS_MODEL: string = 'CARTS_MODEL'; 15 | public static USER_MODEL: string = 'USER_MODEL'; 16 | } 17 | 18 | /** 19 | * MODEL定义 20 | */ 21 | export const localProviders: Array = [ 22 | { 23 | provide: MODEL.CAT_MODEL, 24 | useFactory: (connection: Connection): any => connection.model('Cat', CatSchema), 25 | inject: ['DATABASE_CONNECTION'] 26 | }, 27 | { 28 | provide: MODEL.USER_MODEL, 29 | useFactory: (connection: Connection): any => connection.model('User', UserSchema), 30 | inject: ['DATABASE_CONNECTION'] 31 | }, 32 | { 33 | provide: MODEL.GOODS_MODEL, 34 | useFactory: (connection: Connection): any => connection.model('Goods', GoodsSchema), 35 | inject: ['DATABASE_CONNECTION'] 36 | }, 37 | { 38 | provide: MODEL.CARTS_MODEL, 39 | useFactory: (connection: Connection): any => connection.model('Carts', CartsSchema), 40 | inject: ['DATABASE_CONNECTION'] 41 | } 42 | ]; 43 | -------------------------------------------------------------------------------- /src/server/modules/local/LocalService.ts: -------------------------------------------------------------------------------- 1 | import { BaseHttpClient } from '../../common/middleware/CommonHttpMiddleware'; 2 | import { Model } from 'mongoose'; 3 | import { Injectable, Inject } from '@nestjs/common'; 4 | import { ICat } from './interfaces/ICat'; 5 | import { CreateCatDto } from './dto/LocalCreateCatDto'; 6 | import { CreateGoodsDto } from './dto/LocalCreateGoodsDto'; 7 | import { IGoods } from './interfaces/IGoods'; 8 | import { MODEL } from './LocalProviders'; 9 | import { AddCartsDto } from './dto/LocalAddCartsDto'; 10 | import { ICart } from './interfaces/ICart'; 11 | import { CardListItemDto } from './dto/LocalCardListItemDto'; 12 | import { LoginDto } from './dto/LocalLoginDto'; 13 | import uuidV1 from 'uuid/v1'; 14 | import uuidV3 from 'uuid/v3'; 15 | import { IUser } from './interfaces/IUser'; 16 | 17 | /** 18 | * 本地服务 19 | * 20 | * @export 21 | * @class LocalService 22 | */ 23 | @Injectable() 24 | export class LocalService extends BaseHttpClient { 25 | constructor( 26 | @Inject(MODEL.CAT_MODEL) private readonly catModel: Model, 27 | @Inject(MODEL.GOODS_MODEL) private readonly goodsModel: Model, 28 | @Inject(MODEL.CARTS_MODEL) private readonly cartsModel: Model, 29 | @Inject(MODEL.USER_MODEL) private readonly userModel: Model 30 | ) { 31 | super(); 32 | } 33 | 34 | /** 35 | * 添加入购物车 36 | * @description Creates local service 37 | * @param addToCartsDto 38 | * @returns create 39 | */ 40 | public async addToCart(addToCartsDto: AddCartsDto): Promise { 41 | const findResult: Array = await this.cartsModel.find({ id: addToCartsDto.id }).exec(); 42 | let processResult: any; 43 | if (findResult && findResult.length === 0) { 44 | // 插入新的 45 | const addToCarts: any = new this.cartsModel(addToCartsDto); 46 | processResult = await addToCarts.save(); 47 | } else { 48 | 49 | // 更新数量 50 | // 0.增加 1.减少 2.原值存入 51 | let numValue: number; 52 | switch (addToCartsDto.forceType) { 53 | case AddCartsDto.INC_NUM: 54 | // 增加 55 | numValue = Number(findResult[0].num) + 1; 56 | break; 57 | case AddCartsDto.DEC_NUM: 58 | // 减少 59 | numValue = Number(findResult[0].num) - 1; 60 | break; 61 | case AddCartsDto.FORCE_NUM: 62 | // 原值存入 63 | numValue = addToCartsDto.num; 64 | break; 65 | default: 66 | // 增加 67 | numValue = Number(findResult[0].num) + 1; 68 | break; 69 | } 70 | // 执行更新 71 | processResult = this.cartsModel.updateOne({ id: addToCartsDto.id, userId: addToCartsDto.userId }, { num: numValue }); 72 | } 73 | 74 | return processResult; 75 | } 76 | 77 | /** 78 | * login 79 | * @description Creates local service 80 | * @param loginDto 81 | * @returns create 82 | */ 83 | public async login(loginDto: LoginDto): Promise> { 84 | const returnValue: Array = await this.userModel.find({ userName: loginDto.userName }).exec(); 85 | const userList: Array = []; 86 | 87 | if (returnValue.length > 0) { 88 | while (returnValue.length) { 89 | const item: IUser = returnValue.shift(); 90 | let accessToken: string = uuidV3(item.userName, uuidV1()); 91 | 92 | accessToken = accessToken.replace(/-/ig, ''); 93 | userList.push({ 94 | userName: item.userName, 95 | _id: item._id, 96 | accessToken 97 | }); 98 | 99 | // TODO: 存储token到redis或全局对象用于校验 100 | } 101 | } 102 | 103 | return userList; 104 | } 105 | 106 | /** 107 | * create goods 108 | * @description Creates local service 109 | * @param createGoodsDto 110 | * @returns create 111 | */ 112 | public async createGoods(createGoodsDto: CreateGoodsDto): Promise { 113 | const createdGoods: any = new this.goodsModel(createGoodsDto); 114 | 115 | return await createdGoods.save(); 116 | } 117 | 118 | /** 119 | * create cat 120 | * @description Creates local service 121 | * @param createCatDto 122 | * @returns create 123 | */ 124 | public async create(createCatDto: CreateCatDto): Promise { 125 | const createdCat: any = new this.catModel(createCatDto); 126 | 127 | return await createdCat.save(); 128 | } 129 | 130 | /** 131 | * finds all 132 | * @description Finds all 133 | * @returns all 134 | */ 135 | public async findAll(): Promise { 136 | return await this.catModel.find().exec(); 137 | } 138 | 139 | /** 140 | * 获取已选择商品列表数据 141 | * 142 | * @returns {Promise} 143 | * @memberof LocalService 144 | */ 145 | public async getCarts(userId: string): Promise { 146 | const returnData: any = await this.cartsModel.find({ userId }).exec(); 147 | 148 | return returnData; 149 | } 150 | 151 | /** 152 | * 获取商品页面单个商品数据 153 | * 154 | * @returns {Promise} 155 | * @memberof LocalService 156 | */ 157 | public async getGoods(id: string): Promise { 158 | const result: any = await this.goodsModel.findById(id).exec(); 159 | 160 | return result; 161 | } 162 | 163 | /** 164 | * 依据ID获取已添加入购物车的列表数据项 165 | * 166 | * @returns {Promise} 167 | * @memberof LocalService 168 | */ 169 | public async getCartsListData(cartListItem: CardListItemDto): Promise { 170 | const ids: Array = []; 171 | while (cartListItem.list.length) { 172 | const cartItem: AddCartsDto = cartListItem.list.shift(); 173 | ids.push(cartItem.id); 174 | } 175 | 176 | const result: IGoods[] = await this.goodsModel.find().where('_id').in(ids).exec(); 177 | 178 | return result; 179 | } 180 | 181 | /** 182 | * 获取商品列表数据 183 | * 184 | * @returns {Promise} 185 | * @memberof LocalService 186 | */ 187 | public async getList(): Promise { 188 | return await this.get('/list.json').toPromise(); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/server/modules/local/dto/LocalAddCartsDto.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 添加入购物车DTO 3 | * @description Add carts dto 4 | */ 5 | export class AddCartsDto { 6 | 7 | /** 增加 */ 8 | public static INC_NUM: number = 0; 9 | 10 | /** 减少 */ 11 | public static DEC_NUM: number = 1; 12 | 13 | /** 原值传入 */ 14 | public static FORCE_NUM: number = 2; 15 | 16 | public readonly id: string; 17 | public readonly num: number; 18 | public readonly forceType: number; 19 | public readonly userId: string; 20 | } 21 | -------------------------------------------------------------------------------- /src/server/modules/local/dto/LocalCardListItemDto.ts: -------------------------------------------------------------------------------- 1 | import { AddCartsDto } from './LocalAddCartsDto'; 2 | 3 | /** 4 | * 购物车列表ID DTO 5 | * @description Add carts dto 6 | */ 7 | export class CardListItemDto { 8 | public readonly list: Array; 9 | } 10 | -------------------------------------------------------------------------------- /src/server/modules/local/dto/LocalCreateCatDto.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * create cat dto 3 | * @description Create cat dto 4 | */ 5 | export class CreateCatDto { 6 | public readonly name: string; 7 | public readonly age: number; 8 | public readonly bread: string; 9 | } 10 | -------------------------------------------------------------------------------- /src/server/modules/local/dto/LocalCreateGoodsDto.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 产品DTO 3 | * @description Create goods dto 4 | */ 5 | export class CreateGoodsDto { 6 | public readonly title: string; 7 | public readonly price: number; 8 | public readonly express: string; 9 | public readonly remain: number; 10 | public readonly num: string; 11 | public readonly thumb: string; 12 | public readonly thumbList: Array; 13 | } 14 | -------------------------------------------------------------------------------- /src/server/modules/local/dto/LocalLoginDto.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * login dto 3 | */ 4 | export class LoginDto { 5 | public readonly userName: string; 6 | public readonly password: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/server/modules/local/interfaces/ICart.ts: -------------------------------------------------------------------------------- 1 | import { Document } from 'mongoose'; 2 | 3 | /** 4 | * ICart 5 | * @description ICart 6 | */ 7 | export interface ICart extends Document { 8 | id: string; 9 | num: number; 10 | userId: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/server/modules/local/interfaces/ICat.ts: -------------------------------------------------------------------------------- 1 | import { Document } from 'mongoose'; 2 | 3 | /** 4 | * cat 5 | * @description Cat 6 | */ 7 | export interface ICat extends Document { 8 | readonly name: string, 9 | readonly age: number, 10 | readonly bread: string 11 | } 12 | -------------------------------------------------------------------------------- /src/server/modules/local/interfaces/IGoods.ts: -------------------------------------------------------------------------------- 1 | import { Document } from 'mongoose'; 2 | 3 | /** 4 | * 产品数据结构 5 | * @description Igoods 6 | */ 7 | export interface IGoods extends Document { 8 | readonly title: string; 9 | readonly price: number; 10 | readonly express: string; 11 | readonly remain: number; 12 | readonly num: string; 13 | readonly thumb: string; 14 | readonly thumbList: Array; 15 | } 16 | -------------------------------------------------------------------------------- /src/server/modules/local/interfaces/IUser.ts: -------------------------------------------------------------------------------- 1 | import { Document } from 'mongoose'; 2 | 3 | /** 4 | * 用户信息 5 | */ 6 | export interface IUser extends Document { 7 | _id: string; 8 | userName: string; 9 | } 10 | -------------------------------------------------------------------------------- /src/server/modules/local/schemas/LocalCartSchema.ts: -------------------------------------------------------------------------------- 1 | import * as mongoose from 'mongoose'; 2 | 3 | /** 4 | * carts schema 5 | */ 6 | export const CartsSchema: any = new mongoose.Schema({ 7 | id: { type: String, unique: true, required: true }, 8 | num: Number, 9 | userId: { type: String, unique: true, required: true } 10 | }, 11 | { collection: 'isomorphism.carts' } 12 | ); 13 | -------------------------------------------------------------------------------- /src/server/modules/local/schemas/LocalCatSchema.ts: -------------------------------------------------------------------------------- 1 | import * as mongoose from 'mongoose'; 2 | 3 | /** 4 | * cat schema 5 | */ 6 | export const CatSchema: any = new mongoose.Schema({ 7 | name: String, 8 | age: Number, 9 | bread: String 10 | }, { collection: 'isomorphism.cats' }); 11 | -------------------------------------------------------------------------------- /src/server/modules/local/schemas/LocalGoodsSchema.ts: -------------------------------------------------------------------------------- 1 | import * as mongoose from 'mongoose'; 2 | 3 | /** 4 | * Goods Schema 5 | */ 6 | export const GoodsSchema: any = new mongoose.Schema({ 7 | title: String, 8 | price: Number, 9 | express: String, 10 | remain: Number, 11 | num: String, 12 | thumb: String, 13 | thumbList: Array 14 | }, { collection: 'isomorphism.goods' }); 15 | -------------------------------------------------------------------------------- /src/server/modules/local/schemas/LocalUserSchema.ts: -------------------------------------------------------------------------------- 1 | import * as mongoose from 'mongoose'; 2 | 3 | /** 4 | * user schema 5 | */ 6 | export const UserSchema: any = new mongoose.Schema({ 7 | userName: { type: String, unique: true, required: true }, 8 | password: { type: String, unique: true, required: true } 9 | }, 10 | { collection: 'isomorphism.user' } 11 | ); 12 | -------------------------------------------------------------------------------- /src/server/modules/transport/TransportController.ts: -------------------------------------------------------------------------------- 1 | import { All, Controller, HttpCode, Req, HttpStatus } from '@nestjs/common'; 2 | import { TransportService } from './TransportService'; 3 | import { Routers } from '../../routers/RoutersServer'; 4 | 5 | /** 6 | * 中转服务CONTROLLER处理器 7 | * 8 | * @export 9 | * @class TransportController 10 | * @extends {EngineController} 11 | */ 12 | @Controller(Routers.TRANSPORT_MODULE_ROUTER) 13 | export class TransportController { 14 | constructor( 15 | private readonly transportService: TransportService 16 | ) { 17 | // hole 18 | } 19 | 20 | /** 21 | * 中转服务接收 22 | * 23 | * @param {*} request 24 | * @returns 25 | * @memberof TransportController 26 | */ 27 | @HttpCode(HttpStatus.OK) 28 | @All('getTransportData') 29 | public async getTransportData(@Req() request: any): Promise { 30 | 31 | let result: any = await this.transportService.getTransportData(request); 32 | 33 | if (result === undefined) { 34 | result = { 35 | code: HttpStatus.INTERNAL_SERVER_ERROR, 36 | message: 'Internal server error' 37 | }; 38 | 39 | return result; 40 | } else { 41 | return result.data; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/server/modules/transport/TransportModule.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TransportController } from './TransportController'; 3 | import { TransportService } from './TransportService'; 4 | 5 | /** 6 | * 中转模块 7 | */ 8 | @Module({ 9 | imports: [], 10 | controllers: [TransportController], 11 | providers: [TransportService] 12 | }) 13 | export class TransportModule { 14 | constructor() { 15 | // hole 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/server/modules/transport/TransportService.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig } from 'axios'; 2 | import { stringify } from 'querystring'; 3 | import { BaseHttpClient } from '../../common/middleware/CommonHttpMiddleware'; 4 | import { ConfigDefault } from '../../../../config/ConfigDefault'; 5 | 6 | /** 7 | * 中转接口服务 8 | * 9 | * @export 10 | * @class TransportService 11 | */ 12 | export class TransportService extends BaseHttpClient { 13 | constructor() { 14 | super(); 15 | } 16 | 17 | /** 18 | * 中转 19 | */ 20 | public async getTransportData(request: any): Promise { 21 | 22 | const url: string = request.headers[ConfigDefault.X_REAL_URL]; 23 | const baseURL: string = request.headers[ConfigDefault.X_REAL_BASE_URL]; 24 | const contentType: string = request.headers[ConfigDefault.CONTENT_TYPE]; 25 | 26 | let headers: any = {}; 27 | let requestObj: AxiosRequestConfig = { 28 | data: request.body, 29 | params: request.query || request.params, 30 | baseURL, 31 | url, 32 | method: request.method 33 | }; 34 | 35 | if ( 36 | contentType && 37 | contentType === 'application/x-www-form-urlencoded' 38 | ) { 39 | headers[ConfigDefault.CONTENT_TYPE] = contentType; 40 | requestObj.data = stringify(requestObj.data); 41 | } 42 | requestObj.headers = headers; 43 | 44 | return await this.request(requestObj).toPromise(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/server/routers/RoutersServer.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Router list 3 | * 4 | * @export 5 | * @class Routers 6 | */ 7 | export class Routers { 8 | 9 | /** 10 | * cats router /api/cats/....visit 11 | */ 12 | public static CATS_MODULE_ROUTER: string = 'cats'; 13 | 14 | /** 15 | * transport router /api/transport/....visit 16 | */ 17 | public static TRANSPORT_MODULE_ROUTER: string = 'transport'; 18 | 19 | /** 20 | * local router /api/local/....visit 21 | */ 22 | public static LOCAL_MODULE_ROUTER: string = 'local'; 23 | } 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": false, 5 | "removeComments": true, 6 | "noLib": false, 7 | "allowSyntheticDefaultImports": true, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "allowJs": true, 11 | "target": "es6", 12 | "resolveJsonModule": true, 13 | "sourceMap": true, 14 | "outDir": "./dist", 15 | "esModuleInterop": true, 16 | "moduleResolution": "node", 17 | "baseUrl": "./", 18 | "noImplicitAny": false, 19 | "paths": { 20 | "~/*": ["src/client/*"] 21 | }, 22 | "types": ["@types/node", "@nuxt/vue-app", "vuex-class-component/dist"] 23 | }, 24 | "include": ["src/server/**/*", "src/client/**/*"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint-config-alloy" 5 | ], 6 | "linterOptions": { 7 | "exclude": [ 8 | "e2e/*", 9 | "src/server/*-spec.ts", 10 | "e2e/*-spec.ts", 11 | "src/server/nuxt/index.ts", 12 | "nuxt.config.ts", 13 | "config/env.config.ts" 14 | ] 15 | }, 16 | "jsRules": { 17 | "no-unused-expression": true 18 | }, 19 | "rules": { 20 | "ordered-imports": false, 21 | "no-implicit-dependencies": false, 22 | "no-inferrable-types": false, 23 | "typedef": [true, "variable-declaration", "property-declaration", "arrow-call-signature", "call-signature", "parameter", "member-variable-declaration", "array-destructuring"], 24 | // 禁止给类的构造函数的参数添加修饰符 25 | "no-parameter-properties": false, 26 | // 禁止使用 debugger 27 | "no-debugger": true, 28 | // 禁止行尾有空格 29 | "no-trailing-whitespace": true, 30 | // 禁止无用的表达式 31 | "no-unused-expression": true, 32 | // 定义过的变量必须使用 33 | "no-unused-variable": true, 34 | // 变量必须先定义后使用 35 | "no-use-before-declare": true, 36 | // 禁止使用 var 37 | "no-var-keyword": true, 38 | // 必须使用 === 或 !==,禁止使用 == 或 !=,与 null 比较时除外 39 | "triple-equals": true, 40 | // 指定类成员的排序规则 41 | "member-ordering": false, 42 | // 强制方法注释 43 | "completed-docs": [true, "enums", "functions", "methods", "classes", "interfaces", "namespaces", "types"], 44 | // 方法RETURN 前必须有空行 45 | "newline-before-return": true, 46 | // 禁止将 this 赋值给其他变量,除非是解构赋值 47 | "no-this-assignment": [ 48 | false, 49 | { 50 | "allowed-names": [ 51 | "^self$", 52 | "^that$" 53 | ], 54 | "allow-destructuring": true 55 | } 56 | ], 57 | // 必须使用箭头函数,除非是单独的函数声明或是命名函数 58 | "only-arrow-functions": [ 59 | true, 60 | "allow-declarations", 61 | "allow-named-functions" 62 | ], 63 | // 禁止出现空代码块,允许 catch 是空代码块 64 | "no-empty": [ 65 | true, 66 | "allow-empty-catch" 67 | ], 68 | // 禁止无用的类型断言 69 | "no-unnecessary-type-assertion": true, 70 | // 使用 return; 而不是 return undefined; 71 | "return-undefined": true, 72 | // 禁止对 array 使用 for in 循环 73 | "no-for-in-array": true, 74 | "comment-format": [ 75 | true, 76 | "check-space" 77 | ], 78 | // 单行注释格式化规则 79 | // 定义函数时如果用到了覆写,则必须将覆写的函数写到一起 80 | "adjacent-overload-signatures": true, 81 | // 禁止对函数的参数重新赋值 82 | "no-parameter-reassignment": true, 83 | // if 后面必须有 {,除非是单行 if 84 | "curly": [ 85 | true, 86 | "ignore-same-line" 87 | ], 88 | // for in 内部必须有 hasOwnProperty 89 | "forin": true, 90 | // 禁止在分支条件判断中有赋值操作 91 | "no-conditional-assignment": true, 92 | // 禁止使用 new 来生成 String, Number 或 Boolean 93 | "no-construct": true, 94 | // 禁止 super 在一个构造函数中出现两次 95 | "no-duplicate-super": true, 96 | // 禁止在 switch 语句中出现重复测试表达式的 case 97 | "no-duplicate-switch-case": true, 98 | // 禁止出现重复的变量定义或函数参数名 99 | "no-duplicate-variable": [ 100 | true, 101 | "check-parameters" 102 | ], 103 | // 禁止使用 eval 104 | "no-eval": true, 105 | // 禁止对对象字面量进行类型断言(断言成 any 是允许的) 106 | "no-object-literal-type-assertion": false, 107 | // 禁止没必要的 return await 108 | "no-return-await": false, 109 | // 禁止在数组中出现连续的逗号,如 let foo = [,,] 110 | "no-sparse-arrays": true, 111 | // 禁止 throw 字符串,必须 throw 一个 Error 对象 112 | "no-string-throw": true, 113 | // switch 的 case 必须 return 或 break 114 | "no-switch-case-fall-through": true, 115 | // 使用实例的方法时,必须 bind 到实例上 116 | "no-unbound-method": [ 117 | true, 118 | "ignore-static" 119 | ], 120 | // 使用 { ...foo, bar: 1 } 代替 Object.assign({}, foo, { bar: 1 }) 121 | // 前者的类型检查更完善 122 | "prefer-object-spread": true, 123 | // parseInt 必须传入第二个参数 124 | "radix": true, 125 | // 必须使用 isNaN(foo) 而不是 foo === NaN 126 | "use-isnan": true, 127 | // 128 | // 129 | // 可维护性 130 | // 这些规则可以增加代码的可维护性 131 | // 132 | // 禁止函数的循环复杂度超过 20,https://en.wikipedia.org/wiki/Cyclomatic_complexity 133 | "cyclomatic-complexity": [ 134 | true, 135 | 20 136 | ], 137 | // 禁止使用废弃(被标识了 @deprecated)的 API 138 | "deprecation": true, 139 | // 一个缩进必须用四个空格替代 140 | "indent": [ 141 | true, 142 | "tabs", 143 | 2 144 | ], 145 | // 禁止出现重复的 import 146 | "no-duplicate-imports": false, 147 | // 禁止一个文件中出现多个相同的 namespace 148 | "no-mergeable-namespace": true, 149 | // 文件类型必须时 utf-8 150 | "encoding": true, 151 | // import 语句中,关键字之间的间距必须是一个空格 152 | "import-spacing": true, 153 | // 接口可以 implement extend 和 merge 154 | "interface-over-type-literal": true, 155 | // new 后面只必须有一个空格 156 | "new-parens": true, 157 | // 类型断言必须使用 as Type,禁止使用 158 | // 容易被理解为 jsx 159 | "no-angle-bracket-type-assertion": false, 160 | // 禁止连续超过三行空行 161 | "no-consecutive-blank-lines": [ 162 | true, 163 | 3 164 | ], 165 | // 禁止使用特殊空白符(比如全角空格) 166 | "no-irregular-whitespace": true, 167 | // 禁止使用 JSDoc,因为 TypeScirpt 已经包含了大部分功能 168 | "no-redundant-jsdoc": false, 169 | // 禁止使用三斜杠引入类型定义文件 170 | "no-reference-import": true, 171 | // 禁止变量定义时赋值为 undefined 172 | "no-unnecessary-initializer": true, 173 | // 小数必须以 0. 开头,禁止以 . 开头,并且不能以 0 结尾 174 | "number-literal-format": true, 175 | // 必须使用 a = {b} 而不是 a = {b: b} 176 | "object-literal-shorthand": true, 177 | // 变量申明必须每行一个,for 循环的初始条件中除外 178 | "one-variable-per-declaration": [ 179 | true, 180 | "ignore-for-loop" 181 | ], 182 | // if 后的 { 禁止换行 183 | "one-line": true, 184 | // 必须使用单引号,jsx 中必须使用双引号 185 | "quotemark": [ 186 | true, 187 | "single", 188 | "jsx-double", 189 | "avoid-template", 190 | "avoid-escape" 191 | ], 192 | // 行尾必须有分号 193 | "semicolon": [ 194 | true, 195 | "always", 196 | "ignore-interfaces" 197 | ], 198 | // 函数名前必须有空格 199 | "space-before-function-paren": [ 200 | true, 201 | "asyncArrow" 202 | ], 203 | // 括号内首尾禁止有空格 204 | "space-within-parens": [ 205 | true, 206 | 0 207 | ], 208 | // 禁止 finally 内出现 return, continue, break, throw 等 209 | // finally 会比 catch 先执行 210 | "no-unsafe-finally": true, 211 | // 设置成员对象的访问权限(public,private,protect) 212 | "member-access": true, 213 | // 不允许空接口 214 | "no-empty-interface": true, 215 | // 如果for循环中没有使用索引,建议是使用for-of 216 | "prefer-for-of": false, 217 | // 不允许没有Promise的情况下使用await 218 | "await-promise": true, 219 | // 不允许使用arguments.callee 220 | "no-arg": true, 221 | // 不允许使用特殊运算符 &, &=, |, |=, ^, ^=, <<, <<=, >>, >>=, >>>, >>>=, ~ 222 | // "no-bitwise":true, 223 | // 不允许使用console对象 224 | // "no-console":true, 225 | // 只允许在模板字符串中使用${ 226 | "no-invalid-template-strings": true, 227 | // 不允许在class之外使用this 228 | "no-invalid-this": true, 229 | // 不允许使用null,使用undefined代替null,指代空指针对象 230 | "no-null-keyword": false, 231 | // 不允许自动类型转换,如果已设置不允许使用关键字var该设置无效 232 | "restrict-plus-operands": true, 233 | 234 | // 换行符格式 CR/LF可以通用使用在windows和osx 235 | "linebreak-style": [false, "CR/LF"], 236 | // 每个文件中可定义类的个数 237 | "max-classes-per-file": [true, 6], 238 | // 定义每个文件代码行数 239 | "max-file-line-count": [true, 500], 240 | // 定义每行代码数 241 | "max-line-length": [true, 500], 242 | // 禁止使用export default关键字,因为当export对象名称发生变化时,需要修改import中的对象名。https://github.com/palantir/tslint/issues/1182#issue-151780453 243 | "no-default-export": false, 244 | 245 | // 定义对齐风格 246 | "align": [true, "parameters", "statements", "members", "elements"], 247 | // 建议使用T[]方式声明一个数组对象 248 | "array-type": [false, "array"], 249 | // 类名以大驼峰格式命名 250 | "class-name": true, 251 | // interface必须以I开头 252 | "interface-name": [true, "always-prefix"], 253 | // 注释基于jsdoc风格 254 | "jsdoc-format": false, 255 | // 定义变量命名规则 256 | "variable-name": [false, "check-format", 257 | "allow-leading-underscore", 258 | "allow-trailing-underscore", 259 | "ban-keywords" 260 | ] 261 | }, 262 | "rulesDirectory": [] 263 | } 264 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const nodeExternals = require('webpack-node-externals'); 4 | const WebpackShellPlugin = require('webpack-shell-plugin'); 5 | 6 | module.exports = { 7 | entry: ['webpack/hot/poll?1000', './src/server/Main.ts'], 8 | watch: true, 9 | target: 'node', 10 | externals: [ 11 | nodeExternals({ 12 | whitelist: ['webpack/hot/poll?1000'], 13 | }), 14 | ], 15 | module: { 16 | rules: [{ 17 | test: /\.tsx?$/, 18 | use: 'ts-loader', 19 | exclude: /node_modules/, 20 | }, ], 21 | }, 22 | mode: 'development', 23 | resolve: { 24 | extensions: ['.tsx', '.ts', '.js'], 25 | }, 26 | plugins: [ 27 | new webpack.HotModuleReplacementPlugin(), 28 | new WebpackShellPlugin({ 29 | onBuildEnd: ['node dist/main.js'] 30 | }), 31 | ], 32 | output: { 33 | path: `${__dirname}/dist`, 34 | publicPath: '/', 35 | }, 36 | }; 37 | --------------------------------------------------------------------------------