├── .gitignore ├── .nvmrc ├── .prettierrc.json ├── .vscode └── settings.json ├── @summer-js ├── cli │ ├── compile.js │ ├── index.js │ └── package.json ├── elasticsearch │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── graphql │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── kafka │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── mongodb │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── node-test │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── redis │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── socket.io │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── summer-test │ ├── jest.config.js │ ├── ormconfig.ts │ ├── package.json │ ├── resource │ │ ├── 1.jpg │ │ ├── index.html │ │ ├── spa │ │ │ ├── index.html │ │ │ └── static.txt │ │ ├── static.txt │ │ └── swagger-res │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── index.html │ │ │ ├── swagger-ui-bundle.js │ │ │ ├── swagger-ui-standalone-preset.js │ │ │ └── swagger-ui.css │ ├── src │ │ ├── compile-test │ │ │ ├── 1.ts │ │ │ ├── 2.ts │ │ │ ├── 3.ts │ │ │ ├── 4.ts │ │ │ ├── 5.ts │ │ │ └── 6.ts │ │ ├── config │ │ │ ├── default.config.ts │ │ │ ├── local.config.ts │ │ │ ├── prod.config.ts │ │ │ └── test.config.ts │ │ ├── controllers │ │ │ ├── AnimalController.ts │ │ │ ├── Book.ts │ │ │ ├── BookController.ts │ │ │ ├── CompressionController.ts │ │ │ ├── ConfigController.ts │ │ │ ├── CookieController.ts │ │ │ ├── GenericInjectController.ts │ │ │ ├── GraphqlController.ts │ │ │ ├── HelloController.ts │ │ │ ├── IOEventController.ts │ │ │ ├── JsController.ts │ │ │ ├── KafkaController.ts │ │ │ ├── LoggerController.ts │ │ │ ├── MangoDBController.ts │ │ │ ├── SerializationController.ts │ │ │ ├── SessionController.ts │ │ │ ├── StreamingController.ts │ │ │ ├── Test6.js │ │ │ ├── TestController.ts │ │ │ ├── TestPet.ts │ │ │ └── TestService.ts │ │ ├── decorators │ │ │ ├── AppVersionController.ts │ │ │ ├── CacheController.ts │ │ │ ├── CityListController.ts │ │ │ ├── ConfigInjectController.ts │ │ │ ├── DownloadController.ts │ │ │ ├── JWTController.ts │ │ │ ├── LoginController.ts │ │ │ └── ResponseCodeController.ts │ │ ├── dto │ │ │ ├── error │ │ │ │ └── AppError.ts │ │ │ ├── request │ │ │ │ ├── Animal.ts │ │ │ │ ├── Dog.ts │ │ │ │ ├── Pet.ts │ │ │ │ ├── hello-request.ts │ │ │ │ └── person-request.ts │ │ │ └── resource │ │ │ │ ├── Paging.ts │ │ │ │ └── person-resource.ts │ │ ├── entity │ │ │ ├── Person.ts │ │ │ ├── Student.ts │ │ │ ├── index.ts │ │ │ └── todo.ts │ │ ├── error │ │ │ └── ErrorHandler.ts │ │ ├── graphql │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── js │ │ │ └── index.js │ │ ├── middleware │ │ │ ├── ErrorMiddleware.ts │ │ │ └── PathMiddleware.ts │ │ ├── migrations │ │ │ └── 1650585292097-migration.ts │ │ ├── service │ │ │ ├── GenericService.ts │ │ │ ├── HelloService.ts │ │ │ ├── InjectService.ts │ │ │ ├── TagService.ts │ │ │ ├── ValidateService.ts │ │ │ └── index.ts │ │ ├── task │ │ │ └── TaskService.ts │ │ ├── test │ │ │ ├── case │ │ │ │ ├── AwsLambda.test.ts │ │ │ │ ├── AwsLambdaColdStart.test.ts │ │ │ │ ├── BuildInDecorator.test.ts │ │ │ │ ├── Compress.test.ts │ │ │ │ ├── ConfigEnv.test.ts │ │ │ │ ├── ControllerPath.test.ts │ │ │ │ ├── ControllerPostBody.test.ts │ │ │ │ ├── Cookie.test.ts │ │ │ │ ├── Cors.test.ts │ │ │ │ ├── CustomDecorator.test.ts │ │ │ │ ├── DtoConvert.test.ts │ │ │ │ ├── GenericInject.test.ts │ │ │ │ ├── GlobalVar.test.ts │ │ │ │ ├── Ioc.test.ts │ │ │ │ ├── Logger.test.ts │ │ │ │ ├── Middleware.test.ts │ │ │ │ ├── ParamsValidation.test.ts │ │ │ │ ├── Redis.test.ts │ │ │ │ ├── RequestHeader.test.ts │ │ │ │ ├── ResponseHeader.test.ts │ │ │ │ ├── Rpc.test.ts │ │ │ │ ├── Serialization.test.ts │ │ │ │ ├── ServiceTag.test.ts │ │ │ │ ├── ServiceTags.test.ts │ │ │ │ ├── StaticServer.test.ts │ │ │ │ ├── Swagger.test.ts │ │ │ │ └── Typeorm.test.ts │ │ │ ├── controller │ │ │ │ ├── BasePathController.ts │ │ │ │ ├── BuildInDecoratorTestController.ts │ │ │ │ ├── ContextController.ts │ │ │ │ ├── CookieTestController.ts │ │ │ │ ├── CorsTestController.ts │ │ │ │ ├── ESController.ts │ │ │ │ ├── FileUploadController.ts │ │ │ │ ├── GenericTypeController.ts │ │ │ │ ├── IocController.ts │ │ │ │ ├── MovieController.ts │ │ │ │ ├── MovieControllerV2.ts │ │ │ │ ├── ParamsValidationController.ts │ │ │ │ ├── RedisController.ts │ │ │ │ ├── RedisSessionController.ts │ │ │ │ ├── ResponseHeaderController.ts │ │ │ │ ├── ServerlessControllers.ts │ │ │ │ └── SwaggerController.ts │ │ │ ├── data │ │ │ │ └── SwaggerData.ts │ │ │ ├── middleware │ │ │ │ └── PathTestMiddleware.ts │ │ │ ├── rpc │ │ │ │ ├── UserRpcController.ts │ │ │ │ ├── client │ │ │ │ │ ├── QueryUser.ts │ │ │ │ │ └── UserRpcClientService.ts │ │ │ │ └── server │ │ │ │ │ └── UserRpcService.ts │ │ │ ├── service │ │ │ │ ├── SwaggerService.ts │ │ │ │ ├── TestService.ts │ │ │ │ ├── TestService2.ts │ │ │ │ └── index.ts │ │ │ └── site-example │ │ │ │ └── MovieController.test.ts │ │ └── with-db │ │ │ ├── DataSource.ts │ │ │ ├── PersonController.ts │ │ │ ├── PersonService.ts │ │ │ ├── TodoController.ts │ │ │ └── TodoService.ts │ └── tsconfig.json ├── summer │ ├── compiler-test.txt │ ├── package-lock.json │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── body-parser.ts │ │ ├── config-handler.ts │ │ ├── cookie.ts │ │ ├── cors.ts │ │ ├── decorators │ │ │ ├── build-in.ts │ │ │ ├── class-method.ts │ │ │ ├── class.ts │ │ │ ├── controller.ts │ │ │ ├── custom.ts │ │ │ ├── error.ts │ │ │ ├── index.ts │ │ │ ├── inject.ts │ │ │ ├── ioc.ts │ │ │ ├── method.ts │ │ │ ├── middleware.ts │ │ │ ├── param.ts │ │ │ ├── property.ts │ │ │ ├── rpc.ts │ │ │ ├── serialize.ts │ │ │ ├── task.ts │ │ │ ├── utility.ts │ │ │ └── validate.ts │ │ ├── error.ts │ │ ├── http-server.ts │ │ ├── index.ts │ │ ├── ioc.ts │ │ ├── logger.ts │ │ ├── middleware.ts │ │ ├── plugin.ts │ │ ├── request-handler.ts │ │ ├── request-mapping.ts │ │ ├── rpc.ts │ │ ├── scheduled-tasks.ts │ │ ├── serverless.ts │ │ ├── session.ts │ │ ├── static-server.ts │ │ ├── summer.ts │ │ ├── utils.ts │ │ └── validate-types.ts │ └── tsconfig.json ├── swagger │ ├── index.html │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── test │ ├── index.ts │ ├── jest-preset.js │ ├── package.json │ ├── setup-jest.js │ └── tsconfig.json └── typeorm │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── CONTRIBUTING.md ├── LICENCE ├── assets └── summer-logo.png ├── create-summer ├── index.js ├── package.json └── templates │ ├── empty │ ├── .prettierrc.json │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── config │ │ │ └── default.config.ts │ │ ├── controller │ │ │ └── AppController.ts │ │ ├── index.ts │ │ ├── service │ │ │ ├── HiService.ts │ │ │ └── index.ts │ │ └── test │ │ │ └── HelloController.test.ts │ └── tsconfig.json │ └── movie │ ├── .gitignore │ ├── .prettierrc.json │ ├── jest.config.js │ ├── package.json │ ├── src │ ├── config │ │ └── default.config.ts │ ├── controller │ │ ├── AppController.ts │ │ └── MovieController.ts │ ├── dto │ │ ├── Paging.ts │ │ └── request │ │ │ ├── index.ts │ │ │ └── movie.ts │ ├── entity │ │ ├── Movie.ts │ │ └── index.ts │ ├── error │ │ └── index.ts │ ├── index.ts │ ├── service │ │ ├── MovieService.ts │ │ └── index.ts │ └── test │ │ ├── component │ │ └── MovieService.test.ts │ │ └── e2e │ │ ├── AppController.test.ts │ │ └── MovieController.test.ts │ └── tsconfig.json ├── docs └── develop.md ├── package-lock.json ├── package.json ├── readme.md └── tools └── update-version.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | build 4 | compile 5 | @summer-js/*/lib 6 | create-summer/**/resource 7 | @summer-js/summer-test/coverage 8 | 9 | ## IDEs 10 | .idea 11 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSameLine": false, 4 | "bracketSpacing": true, 5 | "embeddedLanguageFormatting": "auto", 6 | "insertPragma": false, 7 | "printWidth": 120, 8 | "proseWrap": "preserve", 9 | "quoteProps": "as-needed", 10 | "requirePragma": false, 11 | "semi": false, 12 | "singleQuote": true, 13 | "tabWidth": 2, 14 | "trailingComma": "none", 15 | "useTabs": false, 16 | "vueIndentScriptAndStyle": false 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "Autowired", 4 | "Calidan", 5 | "clazz", 6 | "deepmerge", 7 | "esbuild", 8 | "impt", 9 | "INITING", 10 | "Injectables", 11 | "middlewares", 12 | "notype", 13 | "openapi", 14 | "ormconfig", 15 | "outfile" 16 | ], 17 | "typescript.tsdk": "node_modules/typescript/lib" 18 | } 19 | -------------------------------------------------------------------------------- /@summer-js/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@summer-js/cli", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "type": "module", 6 | "bin": { 7 | "summer": "index.js", 8 | "summer-compile": "compile.js" 9 | }, 10 | "scripts": { 11 | "pre-release": "npm publish --tag rc --access public", 12 | "release": "npm publish --access public" 13 | }, 14 | "dependencies": { 15 | "tree-kill": "^1.2.2", 16 | "chokidar": "^4.0.1", 17 | "ora": "^6.0.1", 18 | "commander": "^9.0.0", 19 | "cross-env": "^7.0.3" 20 | }, 21 | "devDependencies": {}, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/calidan-x/summer.git" 25 | }, 26 | "author": "", 27 | "license": "ISC", 28 | "bugs": { 29 | "url": "https://github.com/calidan-x/summer/issues" 30 | }, 31 | "homepage": "https://summerjs.dev", 32 | "keywords": [ 33 | "node", 34 | "nodejs", 35 | "backend", 36 | "api" 37 | ] 38 | } -------------------------------------------------------------------------------- /@summer-js/elasticsearch/index.ts: -------------------------------------------------------------------------------- 1 | import { Logger, SummerPlugin, addPlugin, addInjectable } from '@summer-js/summer' 2 | import { Client, ClientOptions } from '@elastic/elasticsearch' 3 | 4 | export type ElasticSearchConfig = Record 5 | 6 | class ElasticSearch extends SummerPlugin { 7 | configKey = 'ELASTICSEARCH_CONFIG' 8 | clients = {} 9 | 10 | async init(config) { 11 | addInjectable(ESClient, async (dataSourceName: string) => { 12 | if (!config) { 13 | return null 14 | } 15 | if (Object.keys(config).length === 0) { 16 | return null 17 | } 18 | if (!dataSourceName) { 19 | dataSourceName = Object.keys(config)[0] 20 | } 21 | if (!config[dataSourceName]) { 22 | Logger.error(`No data source name \"${dataSourceName}\" in elastic search config`) 23 | return null 24 | } 25 | if (this.clients[dataSourceName]) { 26 | return this.clients[dataSourceName] 27 | } 28 | 29 | const client = await this.connect(config[dataSourceName], dataSourceName) 30 | this.clients[dataSourceName] = client 31 | return client 32 | }) 33 | } 34 | 35 | async connect(options: ClientOptions, dataSourceName: string) { 36 | const isSummerTesting = process.env.SUMMER_TESTING !== undefined 37 | const esClient = new Client(options) 38 | await esClient 39 | .ping() 40 | .then(() => { 41 | if (!isSummerTesting) { 42 | Logger.info(`Elastic Search(${dataSourceName}) Connected`) 43 | } 44 | }) 45 | .catch((err) => { 46 | Logger.error(err) 47 | }) 48 | return esClient 49 | } 50 | 51 | async destroy() {} 52 | } 53 | 54 | addPlugin(ElasticSearch) 55 | export default ElasticSearch 56 | 57 | // @ts-ignore 58 | export class ESClient extends Client {} 59 | -------------------------------------------------------------------------------- /@summer-js/elasticsearch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@summer-js/elasticsearch", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.js", 7 | "peerDependencies": { 8 | "@elastic/elasticsearch": "^8.x || ^7.x || ^6.x" 9 | }, 10 | "devDependencies": {}, 11 | "scripts": { 12 | "build": "tsc", 13 | "pre-release": "npm publish --tag rc --access public", 14 | "release": "npm publish --access public" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/calidan-x/summer.git" 19 | }, 20 | "author": "Calidan", 21 | "license": "ISC", 22 | "bugs": { 23 | "url": "https://github.com/calidan-x/summer/issues" 24 | }, 25 | "homepage": "https://summerjs.dev", 26 | "keywords": [ 27 | "node", 28 | "nodejs", 29 | "backend", 30 | "api" 31 | ] 32 | } -------------------------------------------------------------------------------- /@summer-js/elasticsearch/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns":true, 5 | "noUnusedParameters":true, 6 | "noUnusedLocals":true, 7 | "strictNullChecks": true, 8 | "types": ["node"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./lib", 14 | "moduleResolution": "Node", 15 | "module": "commonjs", 16 | "declaration": true 17 | }, 18 | "exclude": ["**/*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/graphql/index.ts: -------------------------------------------------------------------------------- 1 | import { ResponseError } from '@summer-js/summer' 2 | import { graphql, parse, GraphQLArgs } from 'graphql' 3 | import { validate } from 'graphql/validation' 4 | 5 | export const handle = async (args: GraphQLArgs) => { 6 | try { 7 | const ast = parse(args.source) 8 | const result = validate(args.schema, ast) 9 | if (result.length > 0) { 10 | throw new ResponseError(400, result) 11 | } 12 | } catch (err: any) { 13 | throw new ResponseError(400, err) 14 | } 15 | 16 | try { 17 | return await graphql(args) 18 | } catch (err) { 19 | throw new ResponseError(400, err) 20 | } 21 | } 22 | 23 | export class GraphQLRequest { 24 | @_PropDeclareType([String]) 25 | query: string 26 | } 27 | -------------------------------------------------------------------------------- /@summer-js/graphql/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@summer-js/graphql", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.js", 7 | "peerDependencies": { 8 | "graphql": "^16.9.0" 9 | }, 10 | "scripts": { 11 | "build": "tsc", 12 | "pre-release": "npm publish --tag rc --access public", 13 | "release": "npm publish --access public" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/calidan-x/summer.git" 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/calidan-x/summer/issues" 23 | }, 24 | "homepage": "https://summerjs.dev" 25 | } -------------------------------------------------------------------------------- /@summer-js/graphql/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns":true, 5 | "noUnusedParameters":true, 6 | "noUnusedLocals":true, 7 | "strictNullChecks": true, 8 | "types": ["node"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./lib", 14 | "moduleResolution": "Node", 15 | "module": "commonjs", 16 | "declaration": true 17 | }, 18 | "exclude": ["**/*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/kafka/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@summer-js/kafka", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.js", 7 | "devDependencies": { 8 | "kafkajs": "^2.2.4" 9 | }, 10 | "scripts": { 11 | "build": "tsc", 12 | "pre-release": "npm publish --tag rc --access public", 13 | "release": "npm publish --access public" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/calidan-x/summer.git" 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/calidan-x/summer/issues" 23 | }, 24 | "homepage": "https://summerjs.dev" 25 | } -------------------------------------------------------------------------------- /@summer-js/kafka/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns":true, 5 | "noUnusedParameters":true, 6 | "noUnusedLocals":true, 7 | "strictNullChecks": true, 8 | "types": ["node"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./lib", 14 | "moduleResolution": "Node", 15 | "module": "commonjs", 16 | "declaration": true 17 | }, 18 | "exclude": ["**/*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/mongodb/index.ts: -------------------------------------------------------------------------------- 1 | import { Logger, SummerPlugin, addPlugin, addInjectable } from '@summer-js/summer' 2 | import { MongoClient, MongoClientOptions, Collection as MongoDBCollection, Document } from 'mongodb' 3 | 4 | export type MongoDBConfig = Record< 5 | string, 6 | { url: string; db: string; collectionNameConvert?: (name: string) => string; options?: MongoClientOptions } 7 | > 8 | // @ts-ignore 9 | export class MongoDBClient extends MongoClient {} 10 | // @ts-ignore 11 | export class Collection< 12 | Collection extends Document | string, 13 | // @ts-ignore 14 | DBName extends string = '', 15 | // @ts-ignore 16 | SourceName extends string = '' 17 | > extends MongoDBCollection {} 18 | 19 | class MongoDB extends SummerPlugin { 20 | configKey = 'MONGODB_CONFIG' 21 | config: MongoDBConfig 22 | mangoClients: Record = {} 23 | 24 | async init(_config) { 25 | if (_config) { 26 | this.config = _config 27 | } 28 | addInjectable(MongoDBClient, async (key = '') => { 29 | return await this.connectAndGetInstance(key) 30 | }) 31 | 32 | addInjectable(Collection, async (collection: any, dbName: string, key = '') => { 33 | if (!collection) { 34 | return null 35 | } 36 | const mongoDbClient = await this.connectAndGetInstance(key) 37 | if (mongoDbClient) { 38 | if (!key && Object.keys(this.config).length > 0) { 39 | key = Object.keys(this.config)[0] 40 | } 41 | const db = mongoDbClient.db(dbName || this.config[key].db) 42 | const name = collection.name || collection 43 | return db.collection( 44 | this.config[key].collectionNameConvert ? this.config[key].collectionNameConvert!(name) : name 45 | ) 46 | } 47 | return null 48 | }) 49 | } 50 | 51 | async connectAndGetInstance(key: string) { 52 | if (this.config) { 53 | const isSummerTesting = process.env.SUMMER_TESTING !== undefined 54 | if (!key && Object.keys(this.config).length > 0) { 55 | key = Object.keys(this.config)[0] 56 | } 57 | let mangoClient = this.mangoClients[key] 58 | if (!mangoClient) { 59 | mangoClient = new MongoClient(this.config[key].url, this.config[key].options) 60 | try { 61 | await mangoClient.connect() 62 | if (!isSummerTesting) { 63 | Logger.info(`Mongo Client(${key}) Connected `) 64 | } 65 | } catch (error) { 66 | Logger.error(error) 67 | } 68 | this.mangoClients[key] = mangoClient 69 | } 70 | return mangoClient 71 | } 72 | return null 73 | } 74 | 75 | async destroy() { 76 | try { 77 | for (const mangoClient of Object.values(this.mangoClients)) { 78 | mangoClient.close() 79 | } 80 | } catch (e) {} 81 | } 82 | } 83 | 84 | addPlugin(MongoDB) 85 | export default MongoDB 86 | -------------------------------------------------------------------------------- /@summer-js/mongodb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@summer-js/mongodb", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.js", 7 | "devDependencies": { 8 | "mongodb": "^6.3.0" 9 | }, 10 | "scripts": { 11 | "build": "tsc", 12 | "pre-release": "npm publish --tag rc --access public", 13 | "release": "npm publish --access public" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/calidan-x/summer.git" 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/calidan-x/summer/issues" 23 | }, 24 | "homepage": "https://summerjs.dev" 25 | } -------------------------------------------------------------------------------- /@summer-js/mongodb/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns":true, 5 | "noUnusedParameters":true, 6 | "noUnusedLocals":true, 7 | "strictNullChecks": true, 8 | "types": ["node"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./lib", 14 | "moduleResolution": "Node", 15 | "module": "commonjs", 16 | "declaration": true 17 | }, 18 | "exclude": ["**/*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/node-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@summer-js/node-test", 3 | "version": "0.1.40", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.js", 7 | "files": [ 8 | "lib" 9 | ], 10 | "dependencies": {}, 11 | "scripts": { 12 | "build": "tsc", 13 | "pre-release": "npm publish --tag rc --access public", 14 | "release": "npm publish --access public" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/calidan-x/summer.git" 19 | }, 20 | "keywords": [ 21 | "node", 22 | "nodejs", 23 | "backend", 24 | "api" 25 | ], 26 | "author": "", 27 | "license": "ISC", 28 | "bugs": { 29 | "url": "https://github.com/calidan-x/summer/issues" 30 | }, 31 | "homepage": "https://summerjs.dev" 32 | } 33 | -------------------------------------------------------------------------------- /@summer-js/node-test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns": true, 5 | "noUnusedParameters": true, 6 | "noUnusedLocals": true, 7 | "strictNullChecks": true, 8 | "types": ["node"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./lib", 14 | "moduleResolution": "Node", 15 | "module": "commonjs", 16 | "declaration": true 17 | }, 18 | "exclude": ["**/*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/redis/index.ts: -------------------------------------------------------------------------------- 1 | import { Logger, SummerPlugin, addPlugin, addInjectable } from '@summer-js/summer' 2 | import IORedis, { RedisOptions } from 'ioredis' 3 | 4 | export type RedisConfig = RedisOptions | string 5 | // @ts-ignore 6 | export class RedisClient extends IORedis {} 7 | 8 | class Redis extends SummerPlugin { 9 | configKey = 'REDIS_CONFIG' 10 | config: RedisOptions 11 | redisClients: Record = {} 12 | 13 | async init(_config) { 14 | if (_config) { 15 | this.config = _config 16 | } 17 | addInjectable(RedisClient, async (key = 'default') => { 18 | return await this.connectAndGetInstance(key) 19 | }) 20 | } 21 | 22 | async connectAndGetInstance(key: string = 'default') { 23 | if (this.config) { 24 | const isSummerTesting = process.env.SUMMER_TESTING !== undefined 25 | let redisClient = this.redisClients[key] 26 | if (!redisClient) { 27 | redisClient = new IORedis(this.config) 28 | this.redisClients[key] = redisClient 29 | await new Promise((resolve) => { 30 | redisClient.on('connect', () => { 31 | if (!isSummerTesting) { 32 | Logger.info(`Redis Client<${key}> Connected `) 33 | } 34 | resolve('') 35 | }) 36 | redisClient.on('error', (message) => { 37 | Logger.error(message) 38 | resolve('') 39 | }) 40 | }) 41 | } 42 | return redisClient 43 | } 44 | return null 45 | } 46 | 47 | async destroy() { 48 | try { 49 | for (const redisClient of Object.values(this.redisClients)) { 50 | redisClient.disconnect() 51 | } 52 | } catch (e) {} 53 | } 54 | } 55 | 56 | addPlugin(Redis) 57 | export default Redis 58 | 59 | /* 60 | interface LockOptions { 61 | maxLockTime?: number 62 | maxRetryTimes?: number 63 | retryDelay?: number 64 | lockKey?: string | ((...params: any) => string) 65 | onError?: (error: TooManyExecutionsError | ExecuteTimeoutError) => void 66 | } 67 | 68 | export class TooManyExecutionsError extends Error {} 69 | export class ExecuteTimeoutError extends Error {} 70 | 71 | export const distributedLock = async ( 72 | executeMode: 'ExecuteOnce' | 'ExecuteOneByOne', 73 | lockKey: string, 74 | invokeMethod: () => any, 75 | lockOptions?: Omit 76 | ) => { 77 | let key = lockKey 78 | if (executeMode === 'ExecuteOnce') { 79 | let maxLockTime = lockOptions?.maxLockTime || 5000 80 | maxLockTime = maxLockTime / 1000 81 | const result = await redisClient.setnx(key, 1) 82 | if (result === 1) { 83 | await redisClient.expire(key, maxLockTime) 84 | return await invokeMethod() 85 | } 86 | } else if (executeMode === 'ExecuteOneByOne') { 87 | let maxLockTime = lockOptions?.maxLockTime || 5000 88 | 89 | const maxRetryTimes = lockOptions?.maxRetryTimes || 10 90 | const retryDelay = lockOptions?.retryDelay || 200 91 | const timeout = setTimeout(() => { 92 | redisClient.del(key) 93 | throw new ExecuteTimeoutError('Error: Execute timeout') 94 | }, maxLockTime) 95 | 96 | for (let i = 0; i < maxRetryTimes; i++) { 97 | const result = await redisClient.setnx(key, 1) 98 | if (result === 1) { 99 | try { 100 | clearTimeout(timeout) 101 | const result = await invokeMethod() 102 | await redisClient.del(key) 103 | return result 104 | } catch (e) { 105 | await redisClient.del(key) 106 | throw e 107 | } 108 | } 109 | await new Promise((resolve) => { 110 | setTimeout(resolve, retryDelay) 111 | }) 112 | } 113 | clearTimeout(timeout) 114 | throw new TooManyExecutionsError('Error: Too many executions') 115 | } 116 | } 117 | 118 | export const DistributedLock = createMethodDecorator( 119 | async (ctx, invokeMethod, executeMode: 'ExecuteOnce' | 'ExecuteOneByOne', lockOptions?: LockOptions) => { 120 | let key = ctx.invocation.className + '-' + ctx.invocation.methodName 121 | if (lockOptions?.lockKey) { 122 | key = 123 | typeof lockOptions?.lockKey === 'function' 124 | ? lockOptions?.lockKey.apply(null, ctx.invocation.params) 125 | : lockOptions?.lockKey 126 | } 127 | try { 128 | return distributedLock(executeMode, key, async () => await invokeMethod(ctx.invocation.params), lockOptions) 129 | } catch (err) { 130 | if (lockOptions?.onError) { 131 | lockOptions?.onError(err) 132 | } else { 133 | throw err 134 | } 135 | } 136 | } 137 | ) 138 | */ 139 | -------------------------------------------------------------------------------- /@summer-js/redis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@summer-js/redis", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.js", 7 | "peerDependencies": { 8 | "ioredis": "^5.x || ^6.x" 9 | }, 10 | "scripts": { 11 | "build": "tsc", 12 | "pre-release": "npm publish --tag rc --access public", 13 | "release": "npm publish --access public" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/calidan-x/summer.git" 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/calidan-x/summer/issues" 23 | }, 24 | "homepage": "https://github.com/calidan-x/summer#readme" 25 | } -------------------------------------------------------------------------------- /@summer-js/redis/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns":true, 5 | "noUnusedParameters":true, 6 | "noUnusedLocals":true, 7 | "strictNullChecks": true, 8 | "types": ["node"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./lib", 14 | "moduleResolution": "Node", 15 | "module": "commonjs", 16 | "declaration": true 17 | }, 18 | "exclude": ["**/*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/socket.io/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@summer-js/socket.io", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.js", 7 | "devDependencies": { 8 | "socket.io": "^4.8.0" 9 | }, 10 | "scripts": { 11 | "build": "tsc", 12 | "pre-release": "npm publish --tag rc --access public", 13 | "release": "npm publish --access public" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/calidan-x/summer.git" 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/calidan-x/summer/issues" 23 | }, 24 | "homepage": "https://github.com/calidan-x/summer#readme" 25 | } -------------------------------------------------------------------------------- /@summer-js/socket.io/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns":true, 5 | "noUnusedParameters":true, 6 | "noUnusedLocals":true, 7 | "strictNullChecks": true, 8 | "types": ["node"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./lib", 14 | "moduleResolution": "Node", 15 | "module": "commonjs", 16 | "declaration": true 17 | }, 18 | "exclude": ["**/*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/summer-test/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@summer-js/test', 3 | testPathIgnorePatterns: ['/src/'] 4 | } 5 | -------------------------------------------------------------------------------- /@summer-js/summer-test/ormconfig.ts: -------------------------------------------------------------------------------- 1 | import { DataSource } from 'typeorm' 2 | import { DBNamingStrategy } from '@summer-js/typeorm' 3 | 4 | export default new DataSource({ 5 | type: 'mysql', 6 | host: 'localhost', 7 | database: 'summer-db', 8 | username: 'root', 9 | password: 'root', 10 | namingStrategy: new DBNamingStrategy(), 11 | entities: ['./src/entity/**/*.ts'] 12 | }) 13 | -------------------------------------------------------------------------------- /@summer-js/summer-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@summer-js/summer-test", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "scripts": { 6 | "serve": "summer serve --env local", 7 | "build-and-serve": "npm run -w @summer-js/summer build && summer serve --env local", 8 | "test": "summer test --env test -- --maxWorkers 5", 9 | "build": "summer build --env local", 10 | "start": "node --enable-source-maps ./build/index.js", 11 | "generate-migration": "typeorm-ts-node-commonjs migration:generate src/migrations/migration -d ormconfig.ts -p" 12 | }, 13 | "homepage": "https://summerjs.dev", 14 | "keywords": [ 15 | "node", 16 | "nodejs", 17 | "backend", 18 | "api" 19 | ], 20 | "author": "", 21 | "license": "ISC", 22 | "dependencies": { 23 | "md5": "^2.3.0", 24 | "cross-env": "^7.0.3", 25 | "mysql2": "^3.11.3", 26 | "ts-node": "^10.7.0", 27 | "jsonwebtoken": "^9.0.0", 28 | "redis": "^4.3.0", 29 | "ioredis": "^5.3.0", 30 | "jest": "^29.7.0", 31 | "ts-jest": "^29.2.5", 32 | "socket.io": "^4.8.0", 33 | "@elastic/elasticsearch": "7.15.0", 34 | "@socket.io/redis-emitter": "^5.1.0", 35 | "@socket.io/redis-adapter": "^8.1.0" 36 | } 37 | } -------------------------------------------------------------------------------- /@summer-js/summer-test/resource/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calidan-x/summer/9e19377aca2b348b1c0fdb2d73f77ff9cf77c5df/@summer-js/summer-test/resource/1.jpg -------------------------------------------------------------------------------- /@summer-js/summer-test/resource/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello 4 | 5 | 6 | -------------------------------------------------------------------------------- /@summer-js/summer-test/resource/spa/index.html: -------------------------------------------------------------------------------- 1 | SPA Test 2 | -------------------------------------------------------------------------------- /@summer-js/summer-test/resource/spa/static.txt: -------------------------------------------------------------------------------- 1 | test -------------------------------------------------------------------------------- /@summer-js/summer-test/resource/static.txt: -------------------------------------------------------------------------------- 1 | test -------------------------------------------------------------------------------- /@summer-js/summer-test/resource/swagger-res/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calidan-x/summer/9e19377aca2b348b1c0fdb2d73f77ff9cf77c5df/@summer-js/summer-test/resource/swagger-res/favicon-16x16.png -------------------------------------------------------------------------------- /@summer-js/summer-test/resource/swagger-res/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calidan-x/summer/9e19377aca2b348b1c0fdb2d73f77ff9cf77c5df/@summer-js/summer-test/resource/swagger-res/favicon-32x32.png -------------------------------------------------------------------------------- /@summer-js/summer-test/src/compile-test/1.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calidan-x/summer/9e19377aca2b348b1c0fdb2d73f77ff9cf77c5df/@summer-js/summer-test/src/compile-test/1.ts -------------------------------------------------------------------------------- /@summer-js/summer-test/src/compile-test/2.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calidan-x/summer/9e19377aca2b348b1c0fdb2d73f77ff9cf77c5df/@summer-js/summer-test/src/compile-test/2.ts -------------------------------------------------------------------------------- /@summer-js/summer-test/src/compile-test/3.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calidan-x/summer/9e19377aca2b348b1c0fdb2d73f77ff9cf77c5df/@summer-js/summer-test/src/compile-test/3.ts -------------------------------------------------------------------------------- /@summer-js/summer-test/src/compile-test/4.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calidan-x/summer/9e19377aca2b348b1c0fdb2d73f77ff9cf77c5df/@summer-js/summer-test/src/compile-test/4.ts -------------------------------------------------------------------------------- /@summer-js/summer-test/src/compile-test/5.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calidan-x/summer/9e19377aca2b348b1c0fdb2d73f77ff9cf77c5df/@summer-js/summer-test/src/compile-test/5.ts -------------------------------------------------------------------------------- /@summer-js/summer-test/src/compile-test/6.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calidan-x/summer/9e19377aca2b348b1c0fdb2d73f77ff9cf77c5df/@summer-js/summer-test/src/compile-test/6.ts -------------------------------------------------------------------------------- /@summer-js/summer-test/src/config/default.config.ts: -------------------------------------------------------------------------------- 1 | export const TEST_CONFIG = { 2 | var1: 'VAR1', 3 | var2: ['A1', 'B2'] 4 | } 5 | 6 | export const POST_CONSTRUCT_CONFIG = { 7 | value: 'init post construct' 8 | } 9 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/config/local.config.ts: -------------------------------------------------------------------------------- 1 | import { ServerConfig, SessionConfig, RpcConfig } from '@summer-js/summer' 2 | import { SwaggerConfig } from '@summer-js/swagger' 3 | import { TypeORMConfig } from '@summer-js/typeorm' 4 | // import { MongoDBConfig } from '@summer-js/mongodb' 5 | import { RedisConfig } from '@summer-js/redis' 6 | import { SocketIOConfig } from '@summer-js/socket.io' 7 | // import { KafkaConfig, logLevel } from 'kafkajs' 8 | // import { ElasticSearchConfig } from '@summer-js/elasticsearch' 9 | 10 | export const TYPEORM_CONFIG: TypeORMConfig = { 11 | DATA_SOURCE: { 12 | type: 'mysql', 13 | host: 'localhost', 14 | database: 'summer-db', 15 | username: 'root', 16 | password: 'root' 17 | } 18 | } 19 | 20 | export const SERVER_CONFIG: ServerConfig = { 21 | port: 8801, 22 | cors: true, 23 | basePath: '/local-service', 24 | static: [ 25 | { 26 | requestPath: '/static', 27 | destPath: 'resource', 28 | indexFiles: ['1.txt'] 29 | }, 30 | { 31 | requestPath: '/spa', 32 | destPath: 'resource/spa', 33 | indexFiles: ['index.html'], 34 | spa: true 35 | } 36 | ], 37 | compression: { 38 | enable: true 39 | } 40 | // clusterMode: true, 41 | // workersNumber: 3 42 | } 43 | 44 | export const SESSION_CONFIG: SessionConfig = { 45 | expireIn: 5 46 | } 47 | 48 | export const SWAGGER_CONFIG: SwaggerConfig = { 49 | docPath: '/swagger-ui', 50 | readTypeORMComment: true, 51 | info: { 52 | title: 'Summer', 53 | // description: 'Last build at: ' + new Date(SUMMER_BUILD_TIMESTAMP), 54 | version: '1.0.0' 55 | }, 56 | 57 | securitySchemes: { 58 | AppAuth: { 59 | type: 'apiKey', 60 | in: 'header', 61 | name: 'Authorization' 62 | } 63 | } 64 | } 65 | 66 | export const RPC_CONFIG: RpcConfig = { 67 | provider: { 68 | accessKey: 'xxxxx' 69 | }, 70 | client: { 71 | LOCAL_RPC: { 72 | url: 'http://localhost:8801/local-service/', 73 | accessKey: 'xxxxx' 74 | } 75 | } 76 | } 77 | 78 | // export const ELASTICSEARCH_CONFIG: ElasticSearchConfig = { 79 | // Default: { node: 'http://localhost:9200' } 80 | // } 81 | 82 | export const REDIS_CONFIG: RedisConfig = { 83 | port: 6379, 84 | host: '127.0.0.1' 85 | } 86 | 87 | export const SOCKET_IO_CONFIG: SocketIOConfig = {} 88 | 89 | // export const MONGODB_CONFIG: MongoDBConfig = { 90 | // DefaultSource: { 91 | // url: 'mongodb://localhost:27017', 92 | // db: 'db', 93 | // collectionNameConvert(name) { 94 | // return name.replace(/([A-Z])/g, '_$1').toLowerCase() 95 | // } 96 | // } 97 | // } 98 | 99 | // export const KAFKA_CONFIG: KafkaConfig = { 100 | // clientId: 'client2', 101 | // brokers: ['127.0.0.1:9092'], 102 | // logLevel: logLevel.ERROR 103 | // } 104 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/config/prod.config.ts: -------------------------------------------------------------------------------- 1 | import { ServerConfig, SessionConfig } from '@summer-js/summer' 2 | import { SwaggerConfig } from '@summer-js/swagger' 3 | // import { MySQLConfig } from '@summer-js/typeorm' 4 | 5 | // export const MYSQL_CONFIG: MySQLConfig = { 6 | // host: 'localhost', 7 | // database: 'summer-db', 8 | // username: 'root', 9 | // password: 'root' 10 | // } 11 | 12 | export const SERVER_CONFIG: ServerConfig = { 13 | port: 8801 14 | } 15 | 16 | export const SESSION_CONFIG: SessionConfig = { 17 | expireIn: 5000 18 | } 19 | 20 | export const SWAGGER_CONFIG: SwaggerConfig = { 21 | docPath: '/swagger', 22 | info: { title: 'Summer', version: '1.0.0' } 23 | } 24 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/config/test.config.ts: -------------------------------------------------------------------------------- 1 | import { RedisConfig } from '@summer-js/redis' 2 | import { SocketIOConfig } from '@summer-js/socket.io' 3 | import { SessionConfig, ServerConfig, RpcConfig } from '@summer-js/summer' 4 | import { SwaggerConfig } from '@summer-js/swagger' 5 | import { TypeORMConfig } from '@summer-js/typeorm' 6 | 7 | export const TYPEORM_CONFIG: TypeORMConfig = { 8 | DATA_SOURCE: { 9 | type: 'mysql', 10 | host: 'localhost', 11 | database: 'summer-db', 12 | username: 'root', 13 | password: 'root' 14 | } 15 | } 16 | 17 | export const TEST_CONFIG = { 18 | var1: 'VAR1Change', 19 | var3: 'VAR3' 20 | } 21 | 22 | export const SESSION_CONFIG: SessionConfig = { 23 | expireIn: 5 24 | } 25 | 26 | export const SERVER_CONFIG: ServerConfig = { 27 | static: [{ requestPath: '/static', destPath: 'resource', indexFiles: ['index.html'] }], 28 | cors: true, 29 | port: 8802, 30 | compression: { 31 | enable: true 32 | } 33 | } 34 | 35 | export const RPC_CONFIG: RpcConfig = { 36 | provider: { 37 | accessKey: 'xxxxx' 38 | }, 39 | client: { 40 | LOCAL_RPC: { 41 | url: 'http://localhost:8802', 42 | accessKey: 'xxxxx' 43 | } 44 | } 45 | } 46 | 47 | export const SWAGGER_CONFIG: SwaggerConfig = { 48 | docPath: '/swagger-ui', 49 | readTypeORMComment: true, 50 | info: { title: 'Summer', version: '1.0.0' }, 51 | securitySchemes: { 52 | AppAuth: { 53 | type: 'apiKey', 54 | in: 'header', 55 | name: 'Authorization' 56 | } 57 | } 58 | } 59 | 60 | export const MySQL = { 61 | host: 'localhost' 62 | } 63 | 64 | export const SOCKET_IO_CONFIG: SocketIOConfig = {} 65 | 66 | export const REDIS_CONFIG: RedisConfig = { 67 | port: 6379, 68 | host: '127.0.0.1' 69 | } 70 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/AnimalController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Post, Body } from '@summer-js/summer' 2 | 3 | class Animal { 4 | name: string 5 | weight: number 6 | } 7 | 8 | class Dog extends Animal { 9 | noseLength: number 10 | eyesColor?: 'blue' | 'brown' = 'blue' 11 | } 12 | 13 | @Controller 14 | export class AnimalController { 15 | @Post('/dogs') 16 | add(@Body dog: Dog) { 17 | console.log(typeof dog, dog) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/Book.ts: -------------------------------------------------------------------------------- 1 | enum Process { 2 | 文字 = 1, 3 | 文字2 = 11 4 | } 5 | 6 | enum Process2 { 7 | 'a' = 'aa', 8 | 'b' = 'bb' 9 | } 10 | 11 | export class Book { 12 | name: string 13 | process2: Process2 14 | process: Process 15 | } 16 | 17 | export const getBook = () => { 18 | return new Book() 19 | } 20 | 21 | export const getBook2 = () => { 22 | return [new Book(), new Book()] 23 | } 24 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/BookController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, fillData, Get, PathParam } from '@summer-js/summer' 2 | 3 | class Person { 4 | id: number 5 | name: string 6 | age: int 7 | } 8 | 9 | class Book { 10 | id: number 11 | title: string 12 | } 13 | 14 | class BookResource { 15 | id: number 16 | title: string 17 | author: Person 18 | } 19 | 20 | @Controller 21 | export class BookController { 22 | @Get('/books/:id') 23 | list(@PathParam id: number) { 24 | const bookResource: BookResource = new BookResource() 25 | const book: Book = new Book() 26 | book.id = id 27 | fillData(bookResource, book) 28 | bookResource.author = new Person() 29 | return bookResource 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/CompressionController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@summer-js/summer' 2 | 3 | @Controller 4 | export class CompressionController { 5 | @Get('/text1') 6 | get() { 7 | return 'hello Summer' 8 | } 9 | 10 | @Get('/text2') 11 | getCompressed() { 12 | return `Out too the been like hard off. Improve enquire welcome own beloved matters her. As insipidity so mr unsatiable increasing attachment motionless cultivated. Addition mr husbands unpacked occasion he oh. Is unsatiable if projecting boisterous insensible. It recommend be resolving pretended middleton. 13 | 14 | Dwelling and speedily ignorant any steepest. Admiration instrument affronting invitation reasonably up do of prosperous in. Shy saw declared age debating ecstatic man. Call in so want pure rank am dear were. Remarkably to continuing in surrounded diminution on. In unfeeling existence objection immediate repulsive on he in. Imprudence comparison uncommonly me he difficulty diminution resolution. Likewise proposal differed scarcely dwelling as on raillery. September few dependent extremity own continued and ten prevailed attending. Early to weeks we could. 15 | 16 | Old unsatiable our now but considered travelling impression. In excuse hardly summer in basket misery. By rent an part need. At wrong of of water those linen. Needed oppose seemed how all. Very mrs shed shew gave you. Oh shutters do removing reserved wandered an. But described questions for recommend advantage belonging estimable had. Pianoforte reasonable as so am inhabiting. Chatty design remark and his abroad figure but its. 17 | 18 | Boy desirous families prepared gay reserved add ecstatic say. Replied joy age visitor nothing cottage. Mrs door paid led loud sure easy read. Hastily at perhaps as neither or ye fertile tedious visitor. Use fine bed none call busy dull when. Quiet ought match my right by table means. Principles up do in me favourable affronting. Twenty mother denied effect we to do on. 19 | 20 | Extremely we promotion remainder eagerness enjoyment an. Ham her demands removal brought minuter raising invited gay. Contented consisted continual curiosity contained get sex. Forth child dried in in aware do. You had met they song how feel lain evil near. Small she avoid six yet table china. And bed make say been then dine mrs. To household rapturous fulfilled attempted on so. 21 | 22 | Questions explained agreeable preferred strangers too him her son. Set put shyness offices his females him distant. Improve has message besides shy himself cheered however how son. Quick judge other leave ask first chief her. Indeed or remark always silent seemed narrow be. Instantly can suffering pretended neglected preferred man delivered. Perhaps fertile brandon do imagine to cordial cottage. 23 | 24 | Prepared is me marianne pleasure likewise debating. Wonder an unable except better stairs do ye admire. His and eat secure sex called esteem praise. So moreover as speedily differed branched ignorant. Tall are her knew poor now does then. Procured to contempt oh he raptures amounted occasion. One boy assure income spirit lovers set. 25 | 26 | Out believe has request not how comfort evident. Up delight cousins we feeling minutes. Genius has looked end piqued spring. Down has rose feel find man. Learning day desirous informed expenses material returned six the. She enabled invited exposed him another. Reasonably conviction solicitude me mr at discretion reasonable. Age out full gate bed day lose. 27 | 28 | Arrived totally in as between private. Favour of so as on pretty though elinor direct. Reasonable estimating be alteration we themselves entreaties me of reasonably. Direct wished so be expect polite valley. Whose asked stand it sense no spoil to. Prudent you too his conduct feeling limited and. Side he lose paid as hope so face upon be. Goodness did suitable learning put. 29 | 30 | Ferrars all spirits his imagine effects amongst neither. It bachelor cheerful of mistaken. Tore has sons put upon wife use bred seen. Its dissimilar invitation ten has discretion unreserved. Had you him humoured jointure ask expenses learning. Blush on in jokes sense do do. Brother hundred he assured reached on up no. On am nearer missed lovers. To it mother extent temper figure better. 31 | ` 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/ConfigController.ts: -------------------------------------------------------------------------------- 1 | import { Config, Controller, Get } from '@summer-js/summer' 2 | 3 | @Controller 4 | export class ConfigController { 5 | @Config('MY_CONFIG') 6 | myConfig 7 | 8 | @Get('/config') 9 | add() { 10 | console.log(this.myConfig) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/CookieController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Post, Cookie } from '@summer-js/summer' 2 | 3 | @Controller 4 | export class CookieController { 5 | @Post('/cookie') 6 | add() { 7 | Cookie.set('my-cookie', 'val', { httpOnly: true }) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/GenericInjectController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Service } from '@summer-js/summer' 2 | import { GenericService } from '@/service/GenericService' 3 | 4 | export interface Class extends Function { 5 | new (...args: any[]): any 6 | } 7 | 8 | class Dog { 9 | name: string 10 | } 11 | 12 | class Cat { 13 | name: string 14 | } 15 | 16 | @Service 17 | export class AnimalService { 18 | animal: AnimalType 19 | 20 | constructor(Type: any) { 21 | this.animal = new Type() 22 | } 23 | } 24 | 25 | export class GType {} 26 | 27 | @Controller 28 | export class GenericInjectController { 29 | genericService: GenericService 30 | dogService: AnimalService 31 | catService: AnimalService 32 | 33 | @Get('/generic-inject-test') 34 | testGenericService() { 35 | return this.genericService.t instanceof GType 36 | } 37 | 38 | @Get('/generic-inject-test2') 39 | testGenericService2() { 40 | return this.dogService.animal instanceof Dog && this.catService.animal instanceof Cat 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/GraphqlController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Post, Body } from '@summer-js/summer' 2 | import { handle, GraphQLRequest } from '@summer-js/graphql' 3 | import { schema } from '../graphql' 4 | 5 | @Controller 6 | export class GraphqlController { 7 | @Post('/graphql') 8 | graphql(@Body req: GraphQLRequest) { 9 | var rootValue = { 10 | users: (_query: any) => { 11 | // data can read from DB here 12 | return [ 13 | { 14 | id: 1, 15 | name: 'Tom', 16 | age: 123 17 | } 18 | ] 19 | }, 20 | books: () => ['Book1', 'Book2', 'Book3'], 21 | createMessage({ input }) { 22 | input.id = 1 23 | return input 24 | } 25 | } 26 | return handle({ source: req.query, schema, rootValue }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/HelloController.ts: -------------------------------------------------------------------------------- 1 | import { Body, Controller, EnvConfig, Get, Post } from '@summer-js/summer' 2 | import { HelloService } from '../service' 3 | 4 | export class A { 5 | a?: 'a' | 'b' | 'c' 6 | } 7 | @Controller('/v1') 8 | export class HelloController { 9 | serverConfig: EnvConfig<'SERVER_CONFIG'> 10 | 11 | helloService: HelloService 12 | 13 | anyTypeProp: any[] 14 | neverTypeProp: never[] 15 | 16 | @Get 17 | hello() { 18 | return 'Hello Summer!' 19 | } 20 | 21 | @Get('/null') 22 | null() { 23 | return null 24 | } 25 | 26 | @Get('^/v2/hello') 27 | hello2() { 28 | return 'Hello Summer!' 29 | } 30 | 31 | @Get('/service-inject') 32 | helloFromService() { 33 | return this.helloService.sayHello() 34 | } 35 | 36 | @Get('/service-inject2') 37 | helloFromService2() { 38 | return this.helloService.getInfo2() 39 | } 40 | 41 | @Get('/import-inject-test') 42 | testInjectFromHelloService() { 43 | return this.helloService.getInfo() 44 | } 45 | 46 | @Post('/ttt') 47 | ttt(@Body a: A) { 48 | return a 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/IOEventController.ts: -------------------------------------------------------------------------------- 1 | import { SocketIOController, On, IO } from '@summer-js/socket.io' 2 | import { Emitter } from '@socket.io/redis-emitter' 3 | // import { createAdapter } from '@socket.io/redis-adapter' 4 | import { Socket } from 'socket.io' 5 | import { RedisClient } from '@summer-js/redis' 6 | // import { PostConstruct } from '@summer-js/summer' 7 | 8 | class Dog { 9 | name: string 10 | } 11 | 12 | @SocketIOController 13 | export class IOEventController { 14 | io: IO 15 | redisClient: RedisClient 16 | emitter: Emitter 17 | 18 | // @PostConstruct 19 | // init() { 20 | // const pubClient = this.redisClient 21 | // const subClient = this.redisClient.duplicate() 22 | // this.io.adapter(createAdapter(pubClient, subClient)) 23 | // this.emitter = new Emitter(this.redisClient) 24 | // } 25 | 26 | @On 27 | connection(socket: Socket) { 28 | // console.log(socket.request.headers) 29 | socket.send('欢迎光临') 30 | } 31 | 32 | @On 33 | message(socket: Socket, data: Dog, data2: number, _callback?: (data: any) => void) { 34 | console.log('Dog', data) 35 | console.log('number', data2) 36 | // callback({ status: 'OK' }) 37 | socket.emit('message', { hi: '你好' }) 38 | } 39 | 40 | @On('ev t') 41 | event2(_socket: Socket, data: Dog[]) { 42 | console.log(data) 43 | } 44 | 45 | @On 46 | disconnecting(socket: Socket) { 47 | console.log(socket.data) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/JsController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@summer-js/summer' 2 | import { sum } from '../js/index' 3 | 4 | @Controller 5 | export class JsController { 6 | @Get('/js-test') 7 | add() { 8 | return sum(2, 2) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/KafkaController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Logger } from '@summer-js/summer' 2 | import { Producer, Consume, EachMessagePayload } from '@summer-js/kafka' 3 | 4 | @Controller('/v1/kafka') 5 | export class KafkaController { 6 | producer: Producer 7 | // consumer: Consumer<'test-group-1'> 8 | 9 | // @PostConstruct 10 | // async init() { 11 | // if (!this.consumer) { 12 | // return 13 | // } 14 | // await this.consumer.subscribe({ topic: 'test-topic', fromBeginning: true }) 15 | // await this.consumer.run({ 16 | // eachMessage: async ({ topic, partition, message }) => { 17 | // console.log('topic', topic) 18 | // console.log('partition', partition) 19 | // if (message.value) { 20 | // console.log({ 21 | // value: message.value.toString() 22 | // }) 23 | // } 24 | // } 25 | // }) 26 | // } 27 | 28 | @Get('/send') 29 | async test() { 30 | await this.producer.send({ 31 | topic: 'test-topic', 32 | messages: [{ value: 'Hello KafkaJS user! ' + Date.now() }] 33 | }) 34 | } 35 | 36 | @Consume({ topic: 'test-topic', groupId: 'test-group-1' }) 37 | doSomething({ message }: EachMessagePayload) { 38 | Logger.debug(message.value?.toString()) 39 | } 40 | } 41 | //// 42 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/LoggerController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Logger } from '@summer-js/summer' 2 | 3 | @Controller 4 | export class LoggerController { 5 | @Get('/log') 6 | add() { 7 | Logger.log('警告') 8 | Logger.info('信息') 9 | Logger.error('错误') 10 | Logger.warn('警告') 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/MangoDBController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@summer-js/summer' 2 | import { Collection } from '@summer-js/mongodb' 3 | 4 | class User { 5 | name: string 6 | age: number 7 | } 8 | 9 | @Controller('/v1/mongo') 10 | export class MangoDBController { 11 | user: Collection 12 | 13 | @Get 14 | test() { 15 | this.user.insertMany([{ name: 'tom', age: 123 }]) 16 | this.user.createIndex({ b: 1 }) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/SerializationController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Ignore, Serialize } from '@summer-js/summer' 2 | 3 | enum Direction { 4 | UP = 0, 5 | DOWN = 1, 6 | LEFT = 2, 7 | RIGHT = 3 8 | } 9 | enum Country { 10 | 'China' = '中国', 11 | 'US' = '美国' 12 | } 13 | 14 | class Resource { 15 | direction: Direction 16 | country: Country 17 | } 18 | 19 | class ExtendResource extends Resource { 20 | count: number 21 | } 22 | 23 | class ObjectClass { 24 | str: string 25 | direction: Direction 26 | } 27 | 28 | class D { 29 | d: Date 30 | } 31 | 32 | type T = 'AA' | 'BB' 33 | 34 | class A { 35 | a: T 36 | b: T 37 | } 38 | 39 | enum Gender { 40 | Male, 41 | Female 42 | } 43 | 44 | export class User { 45 | name: string 46 | @Serialize((value: string) => { 47 | return value.replace(/.+/g, '*****') 48 | }) 49 | password: string 50 | gender: Gender 51 | 52 | @Serialize((value: string) => { 53 | if (value === undefined) { 54 | return 'nick' 55 | } 56 | return value 57 | }) 58 | nickName 59 | 60 | @Ignore 61 | salary: number 62 | 63 | @Ignore 64 | city: string 65 | 66 | @Ignore 67 | country: string 68 | 69 | @Serialize((_, obj: User) => { 70 | return obj.city + ', ' + obj.country 71 | }) 72 | address: string 73 | } 74 | 75 | @Controller 76 | export class SerializationController { 77 | @Get('/serialize') 78 | async serialize() { 79 | const r = new Resource() 80 | r.direction = Direction.LEFT 81 | r.country = Country.China 82 | return [r, r] 83 | } 84 | 85 | @Get('/class-extends-serialize') 86 | async extendsSerialize() { 87 | const r = new ExtendResource() 88 | r.direction = Direction.LEFT 89 | r.count = 123 90 | return r 91 | } 92 | 93 | @Get('/object-serialize') 94 | async objectSerialize() { 95 | return { str: 'str', direction: 2 } as ObjectClass 96 | } 97 | 98 | @Get('/not-date') 99 | async dateSerialize() { 100 | return { d: 'not date' } as any as D 101 | } 102 | 103 | @Get('/union-type') 104 | async unionTypeSerialize() { 105 | const a = new A() 106 | a.a = 'AA' 107 | a.b = 'BB' 108 | return a 109 | } 110 | 111 | @Get('/prop-serialize') 112 | async propSerialize() { 113 | const user = new User() 114 | user.name = 'John' 115 | user.password = 'my-password' 116 | user.gender = Gender.Male 117 | user.salary = 10000 118 | user.city = 'New York' 119 | user.country = 'US' 120 | return user 121 | } 122 | 123 | @Get('/assign-serialize') 124 | async assignSerialize() { 125 | const obj = new ObjectClass() 126 | const user = new User() 127 | obj.direction = Direction.LEFT 128 | user.name = 'John' 129 | user.password = 'my-password' 130 | user.gender = Gender.Male 131 | user.salary = 10000 132 | user.city = 'New York' 133 | user.country = 'US' 134 | obj['user'] = user 135 | return obj 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/SessionController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Session } from '@summer-js/summer' 2 | 3 | @Controller('/session') 4 | export class SessionController { 5 | @Get() 6 | session() { 7 | return Session.get('session')! 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/StreamingController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, StreamingData } from '@summer-js/summer' 2 | import fs from 'fs' 3 | 4 | @Controller 5 | export class StreamingDataController { 6 | @Get('/play-video') 7 | async playVideo() { 8 | return new StreamingData('video.mp4') 9 | } 10 | 11 | @Get('/download-streaming-file') 12 | async download() { 13 | return new StreamingData('tmp.pdf', { downloadFileName: 'intro.pdf' }) 14 | } 15 | 16 | @Get('/download-streaming-data') 17 | async downloadData() { 18 | const readStream = fs.createReadStream('report.csv') 19 | return new StreamingData(readStream, { downloadFileName: 'report.csv' }) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/Test6.js: -------------------------------------------------------------------------------- 1 | export const t = 3 2 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/TestController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Patch } from '@summer-js/summer' 2 | 3 | import { t } from './Test6' 4 | import { PetService } from './TestService' 5 | 6 | @Controller('/test') 7 | export class TestController { 8 | petService: PetService 9 | @Patch('/pet') 10 | test() { 11 | return this.petService.getPet() 12 | } 13 | 14 | @Get('/a') 15 | a() { 16 | console.log(t) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/TestPet.ts: -------------------------------------------------------------------------------- 1 | export class Pet { 2 | name: string 3 | } 4 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/controllers/TestService.ts: -------------------------------------------------------------------------------- 1 | import { Service } from '@summer-js/summer' 2 | import { Pet } from './TestPet' 3 | 4 | @Service 5 | export class PetService { 6 | getPet() { 7 | return new Pet() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/decorators/AppVersionController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, createParamDecorator, Get } from '@summer-js/summer' 2 | 3 | export const AppVersion = createParamDecorator((ctx) => { 4 | return ctx.request.headers['app-version'] 5 | }) 6 | 7 | @Controller 8 | export class AppVersionController { 9 | @Get('/app/version') 10 | async version(@AppVersion version) { 11 | return version 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/decorators/CacheController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, createMethodDecorator, Get, PathParam, Service } from '@summer-js/summer' 2 | import md5 from 'md5' 3 | 4 | const CACHE = {} 5 | export const Cache = createMethodDecorator(async (ctx, invokeMethod, cacheTime: number) => { 6 | const callParamHash = md5(JSON.stringify(ctx.invocation)) 7 | if (CACHE[callParamHash] === undefined) { 8 | CACHE[callParamHash] = await invokeMethod(ctx.invocation.params) 9 | // Logger.info('Cache ' + CACHE[callParamHash] + ' in ' + cacheTime + 's') 10 | if (cacheTime) { 11 | setTimeout(() => { 12 | CACHE[callParamHash] = undefined 13 | // Logger.info('Clear Cache') 14 | }, cacheTime) 15 | } 16 | } 17 | return CACHE[callParamHash] 18 | }) 19 | 20 | @Service 21 | export class CacheService { 22 | @Cache 23 | async cache(id) { 24 | return id + ':' + Date.now() 25 | } 26 | } 27 | 28 | @Controller 29 | export class CacheController { 30 | cacheService: CacheService 31 | 32 | @Get('/cache/:id') 33 | @Cache(1000) 34 | async api(@PathParam id: number) { 35 | return id + ':' + Date.now() 36 | } 37 | 38 | @Get('/cache2/:id') 39 | async api2(@PathParam id) { 40 | return this.cacheService.cache(id) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/decorators/CityListController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, createPropertyDecorator, Get } from '@summer-js/summer' 2 | 3 | export const CityList = createPropertyDecorator(() => { 4 | return ['Shanghai', 'Tokyo', 'New York City'] 5 | }) 6 | 7 | @Controller 8 | export class CityListController { 9 | @CityList 10 | cityList 11 | 12 | @Get('/cities') 13 | list() { 14 | return this.cityList 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/decorators/ConfigInjectController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, createPropertyDecorator, Get } from '@summer-js/summer' 2 | 3 | export const MySQLConfig = createPropertyDecorator((config) => { 4 | return config['MySQL'] 5 | }) 6 | 7 | @Controller 8 | export class ConfigInjectController { 9 | @MySQLConfig 10 | mysqlConfig 11 | 12 | @Get('/mysql-host') 13 | host() { 14 | return this.mysqlConfig.host 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/decorators/DownloadController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, createMethodDecorator, Get } from '@summer-js/summer' 2 | 3 | export const DownLoadFile = createMethodDecorator(async (ctx, invokeMethod, fileName: string) => { 4 | ctx.response.headers['content-type'] = 'application/octet-stream' 5 | ctx.response.headers['Content-Disposition'] = `attachment; filename="${fileName}"` 6 | return await invokeMethod(ctx.invocation.params) 7 | }) 8 | 9 | @Controller 10 | export class DownloadController { 11 | @DownLoadFile('hello.txt') 12 | @Get('/download') 13 | download() { 14 | return 'Hello Summer' 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/decorators/JWTController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, createParamDecorator, Get } from '@summer-js/summer' 2 | import jwt from 'jsonwebtoken' 3 | 4 | export const Uid = createParamDecorator((ctx) => { 5 | const token = ctx.request.headers['authentication'] 6 | try { 7 | const decoded = jwt.verify(token, 'xxxxxxxx') 8 | return decoded.uid 9 | } catch (e) {} 10 | return null 11 | }) 12 | 13 | @Controller 14 | export class JWTController { 15 | @Get('/userinfo') 16 | userinfo(@Uid uid: number) { 17 | return uid 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/decorators/LoginController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, createClassAndMethodDecorator, Get, Put } from '@summer-js/summer' 2 | import jwt from 'jsonwebtoken' 3 | 4 | export const RequireLogin = createClassAndMethodDecorator(async (ctx, invokeMethod) => { 5 | const token = ctx.request.headers['authentication'] 6 | try { 7 | jwt.verify(token, 'xxxxxxxx') 8 | return await invokeMethod(ctx.invocation.params) 9 | } catch (e) { 10 | ctx.response.statusCode = 401 11 | ctx.response.body = 'Unauthorized' 12 | } 13 | }) 14 | 15 | @Controller 16 | @RequireLogin 17 | export class LoginController { 18 | @Get('/me') 19 | info() {} 20 | 21 | @Put('/me') 22 | update() {} 23 | } 24 | 25 | @Controller 26 | export class LoginController2 { 27 | @Get('/users/:id') 28 | userInfo() {} 29 | 30 | // highlight-next-line 31 | @RequireLogin 32 | @Put('/userinfo') 33 | update() {} 34 | } 35 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/decorators/ResponseCodeController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, createMethodDecorator, Get } from '@summer-js/summer' 2 | 3 | export const ResponseCode = createMethodDecorator(async (ctx, invokeMethod, code: number) => { 4 | ctx.response.statusCode = code 5 | return await invokeMethod(ctx.invocation.params) 6 | }) 7 | 8 | @Controller 9 | export class ResponseCodeController { 10 | @ResponseCode(404) 11 | @Get('/dog') 12 | userInfo() { 13 | return 'dog not exist' 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/dto/error/AppError.ts: -------------------------------------------------------------------------------- 1 | class AppError extends Error {} 2 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/dto/request/Animal.ts: -------------------------------------------------------------------------------- 1 | enum Gender { 2 | Male = 1, 3 | Female = 2 4 | } 5 | 6 | export class Animal { 7 | id: int; 8 | name: string; 9 | gender: Gender; 10 | } 11 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/dto/request/Dog.ts: -------------------------------------------------------------------------------- 1 | import { Animal } from './Animal' 2 | 3 | enum Color { 4 | 'Blue' = 'blue', 5 | 'Black' = 'black', 6 | 'Brown' = 'brown' 7 | } 8 | 9 | class Toy { 10 | id: int 11 | name: string 12 | } 13 | 14 | export class Dog extends Animal { 15 | eyeColor: Color 16 | tailLength: number 17 | toys: Toy[] 18 | legLength?: number 19 | 20 | bark() { 21 | console.log('wang wang!') 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/dto/request/Pet.ts: -------------------------------------------------------------------------------- 1 | export class Pet { 2 | name: string; 3 | gender: number; 4 | } 5 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/dto/request/hello-request.ts: -------------------------------------------------------------------------------- 1 | import { MaxLen } from '@summer-js/summer' 2 | import { Pet } from './Pet' 3 | 4 | export enum Direction { 5 | Up = 1, 6 | Down, 7 | Left, 8 | Right 9 | } 10 | 11 | export enum Direction2 { 12 | Up = 'hi', 13 | Down = 'go', 14 | Left = 'we', 15 | Right = 'hill' 16 | } 17 | 18 | class Base { 19 | id: number 20 | } 21 | 22 | export class HelloRequest extends Base { 23 | @MaxLen(5) 24 | message: string[] 25 | 26 | direction: Direction 27 | 28 | pet: Pet[] 29 | } 30 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/dto/request/person-request.ts: -------------------------------------------------------------------------------- 1 | import { PropDoc } from '@summer-js/swagger' 2 | 3 | export class PersonRequest { 4 | firstName: string 5 | lastName: string 6 | } 7 | 8 | export class PersonSearchRequest { 9 | @PropDoc('query first name', 'John') 10 | firstName: string 11 | 12 | lastName: string 13 | } 14 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/dto/resource/Paging.ts: -------------------------------------------------------------------------------- 1 | import { PropDoc } from '@summer-js/swagger' 2 | 3 | export class Paging { 4 | constructor(props: { data: T[]; pageNumber: number; pageSize: number; total: number }) { 5 | for (const k in props) { 6 | this[k] = props[k] 7 | } 8 | } 9 | data: T[] 10 | pageNumber: number 11 | pageSize: number 12 | total: number 13 | } 14 | 15 | export class Obj { 16 | @PropDoc('A', 'A Value') 17 | a: string 18 | @PropDoc('B', 1) 19 | b: int 20 | } 21 | 22 | export class ExtendObj extends Obj { 23 | c: string 24 | d: number 25 | } 26 | 27 | export class AnotherObj { 28 | field1: string 29 | field2: number 30 | } 31 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/dto/resource/person-resource.ts: -------------------------------------------------------------------------------- 1 | export class PersonResource { 2 | id: number 3 | name: string 4 | lastName: string 5 | } 6 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/entity/Person.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Index, PrimaryGeneratedColumn, Column } from 'typeorm' 2 | 3 | @Entity() 4 | @Index() 5 | export class Person { 6 | @PrimaryGeneratedColumn() 7 | id: number 8 | 9 | @Column() 10 | firstName: string 11 | 12 | @Column() 13 | lastName: string 14 | 15 | @Column() 16 | isActive: boolean = true 17 | } 18 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/entity/Student.ts: -------------------------------------------------------------------------------- 1 | // import { Entity } from 'typeorm' 2 | // import { VarChar } from '@summer-js/typeorm' 3 | 4 | // @Entity() 5 | export class Student { 6 | // Id 7 | // @PrimaryKey({ autoIncrement: true }) 8 | // id: Int 9 | /* 姓名 */ 10 | // name: VarChar<255> = 'Tom' 11 | // // 年龄 12 | // age: SmallInt 13 | // // 班级 14 | // class?: VarChar<22> 15 | // // 身高 16 | // height: Float<10, 5> 17 | // // 是会员 18 | // isMember: Boolean 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/entity/index.ts: -------------------------------------------------------------------------------- 1 | export { Person } from './Person' 2 | export { Todo } from './Todo' 3 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/entity/todo.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' 2 | 3 | @Entity() 4 | export class Todo { 5 | @PrimaryGeneratedColumn() 6 | id: number 7 | 8 | @Column({ comment: '内容' }) 9 | content: string 10 | 11 | @Column() 12 | isDone: boolean 13 | } 14 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/error/ErrorHandler.ts: -------------------------------------------------------------------------------- 1 | import { OtherErrors, E, Logger, NotFoundError, ValidationError } from '@summer-js/summer' 2 | 3 | // @ErrorHandler 4 | export class HandleError { 5 | @E(NotFoundError) 6 | notFound() { 7 | return { statusCode: 404, body: 'Page Not Found ~' } 8 | } 9 | 10 | @E(ValidationError) 11 | validateFail(err: ValidationError) { 12 | return { statusCode: 404, body: err.body.errors } 13 | } 14 | 15 | @E(OtherErrors) 16 | default(err) { 17 | Logger.error(err) 18 | return { statusCode: 500, body: 'Some Error Happen' } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/graphql/index.ts: -------------------------------------------------------------------------------- 1 | import { buildSchema } from 'graphql' 2 | 3 | export const schema = buildSchema(` 4 | type Query { 5 | users(id: Int): [User] 6 | books: [String] 7 | } 8 | 9 | type User { 10 | id: Int 11 | name: String 12 | age: Int 13 | } 14 | 15 | input MessageInput { 16 | content: String 17 | author: String 18 | } 19 | 20 | type Message { 21 | id: ID! 22 | content: String 23 | author: String 24 | } 25 | 26 | type Mutation { 27 | createMessage(input: MessageInput): Message 28 | } 29 | 30 | `) 31 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/index.ts: -------------------------------------------------------------------------------- 1 | import { summerStart, handler, Logger, replaceEnvConfigValue } from '@summer-js/summer' 2 | import { getDataSource } from '@summer-js/typeorm' 3 | 4 | export { handler } 5 | 6 | const runMigrations = async () => { 7 | const output = await getDataSource('DATA_SOURCE').runMigrations() 8 | output.forEach((m) => { 9 | Logger.info('Run migration: ' + m.name) 10 | }) 11 | } 12 | 13 | summerStart({ 14 | async init() { 15 | replaceEnvConfigValue((value) => { 16 | return value 17 | }) 18 | }, 19 | async before() { 20 | if (SUMMER_ENV !== 'prod') { 21 | await runMigrations() 22 | } 23 | }, 24 | after() {} 25 | }) 26 | 27 | // traceRequest((info) => { 28 | // console.log('-----') 29 | // console.log(info.context) 30 | // }) 31 | 32 | // codeCheck({ 33 | // controller() {}, 34 | // api() {} 35 | // }) 36 | 37 | // 检查文件存放位置 38 | // 检查API命名 39 | // 检查类命名 40 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/js/index.js: -------------------------------------------------------------------------------- 1 | export const sum = (a, b) => a + b + 4 2 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/middleware/ErrorMiddleware.ts: -------------------------------------------------------------------------------- 1 | import { Middleware, Context } from '@summer-js/summer' 2 | 3 | @Middleware({ 4 | order: 0 5 | }) 6 | export class ErrorMiddleware { 7 | async process(_ctx: Context, next: any) { 8 | try { 9 | await next() 10 | } catch (err) { 11 | // if (err instanceof ValidationError) { 12 | // err.body = { code: 10000, message: 'Validation Errors', errors: err.body.errors } 13 | // } 14 | throw err 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/middleware/PathMiddleware.ts: -------------------------------------------------------------------------------- 1 | import { Middleware, Context } from '@summer-js/summer' 2 | import { getApiDoc } from '@summer-js/swagger' 3 | 4 | @Middleware({ order: 1 }) 5 | export class PathMiddleware { 6 | async process(ctx: Context, next: any) { 7 | const apiInfo = getApiDoc(ctx) 8 | if (apiInfo) { 9 | // console.log(apiInfo.apiGroup.name + ' => ' + apiInfo.api.summary) 10 | } 11 | await next() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/migrations/1650585292097-migration.ts: -------------------------------------------------------------------------------- 1 | import { MigrationInterface, QueryRunner } from 'typeorm' 2 | 3 | export class migration1650585292097 implements MigrationInterface { 4 | name = 'migration1650585292097' 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(` 8 | CREATE TABLE \`todo\` ( 9 | \`id\` int NOT NULL AUTO_INCREMENT, 10 | \`content\` varchar(255) NOT NULL, 11 | \`is_done\` tinyint NOT NULL, 12 | PRIMARY KEY (\`id\`) 13 | ) ENGINE = InnoDB 14 | `) 15 | } 16 | 17 | public async down(queryRunner: QueryRunner): Promise { 18 | await queryRunner.query(` 19 | DROP TABLE \`todo\` 20 | `) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/service/GenericService.ts: -------------------------------------------------------------------------------- 1 | import { Service } from '@summer-js/summer' 2 | 3 | @Service 4 | export class GenericService { 5 | t: T 6 | k: K 7 | 8 | constructor(typeT: any, typeK: any) { 9 | if (typeT) { 10 | this.t = new typeT() 11 | } 12 | if (typeK) { 13 | this.k = new typeK() 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/service/HelloService.ts: -------------------------------------------------------------------------------- 1 | import { Service } from '@summer-js/summer' 2 | import { InjectService } from './' 3 | 4 | @Service 5 | export class HelloService { 6 | injectService: InjectService 7 | 8 | sayHello() { 9 | console.log('Hello') 10 | } 11 | 12 | getInfo() { 13 | return this.injectService.getInfo() 14 | } 15 | 16 | getInfo2() { 17 | return this.injectService.getInfo2() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/service/InjectService.ts: -------------------------------------------------------------------------------- 1 | import { Service } from '@summer-js/summer' 2 | 3 | class Info { 4 | msg: string 5 | } 6 | 7 | @Service 8 | export class InjectService { 9 | getInfo() { 10 | return 'info' 11 | } 12 | getInfo2() { 13 | return [new Info()] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/service/TagService.ts: -------------------------------------------------------------------------------- 1 | import { Service } from '@summer-js/summer' 2 | 3 | @Service({ tags: ['pet'] }) 4 | export class CatService {} 5 | 6 | @Service({ tags: ['pet'] }) 7 | export class DogService {} 8 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/service/ValidateService.ts: -------------------------------------------------------------------------------- 1 | import { Service } from '@summer-js/summer' 2 | 3 | @Service 4 | export class ValidateService { 5 | sss: number 6 | hihi() {} 7 | } 8 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/service/index.ts: -------------------------------------------------------------------------------- 1 | export * from './HelloService' 2 | export * from './InjectService' 3 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/task/TaskService.ts: -------------------------------------------------------------------------------- 1 | import { Scheduled, Service } from '@summer-js/summer' 2 | 3 | @Service 4 | export class TaskService { 5 | @Scheduled({ fixedRate: 2000 }) 6 | print() { 7 | // console.log('Scheduled', new Date()) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/AwsLambda.test.ts: -------------------------------------------------------------------------------- 1 | import '@summer-js/test' 2 | 3 | describe('Test AWB Lambda', () => { 4 | test('should AWB Lambda work', async () => { 5 | process.env.AWS_LAMBDA_FUNCTION_VERSION = '1' 6 | await import('../../index').then(async (module) => { 7 | await new Promise((resolve) => { 8 | setTimeout(() => { 9 | resolve('') 10 | }, 200) 11 | }) 12 | 13 | let result = await module.handler({ 14 | path: '/serverless/hello', 15 | httpMethod: 'GET', 16 | headers: {}, 17 | queryStringParameters: {}, 18 | body: '' 19 | }) 20 | 21 | expect(result!.body).toBe('Hello Serverless') 22 | 23 | result = await module.handler({ 24 | path: '/serverless/inject', 25 | httpMethod: 'GET', 26 | headers: {}, 27 | queryStringParameters: {}, 28 | body: '' 29 | }) 30 | 31 | expect(result!.body).toBe('Test Injection') 32 | }) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/AwsLambdaColdStart.test.ts: -------------------------------------------------------------------------------- 1 | import '@summer-js/test' 2 | 3 | describe('Test AWB Lambda', () => { 4 | beforeAll(async () => { 5 | process.env.AWS_LAMBDA_FUNCTION_VERSION = '1' 6 | }) 7 | 8 | test('should AWB Lambda work', async () => { 9 | await import('../../index').then(async (module) => { 10 | let result = await module.handler({ 11 | path: '/serverless/inject', 12 | httpMethod: 'GET', 13 | headers: {}, 14 | queryStringParameters: {}, 15 | body: '' 16 | }) 17 | 18 | expect(result!.body).toBe('Test Injection') 19 | 20 | result = await module.handler({ 21 | path: '/serverless/hello', 22 | httpMethod: 'GET', 23 | headers: {}, 24 | queryStringParameters: {}, 25 | body: '' 26 | }) 27 | 28 | expect(result!.body).toBe('Hello Serverless') 29 | 30 | result = await module.handler({ 31 | path: '/serverless/db-data', 32 | httpMethod: 'GET', 33 | headers: {}, 34 | queryStringParameters: {}, 35 | body: '' 36 | }) 37 | 38 | expect(result!.statusCode).toBe(200) 39 | }) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/Compress.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | 3 | describe('Test gzip response', () => { 4 | test('should return uncompressed data', async () => { 5 | const res = await request.get('/text1') 6 | expect(res.headers['Content-Encoding']).toBe(undefined) 7 | }) 8 | 9 | test('should return compressed data', async () => { 10 | const res = await request.get('/text2') 11 | expect(res.headers['Content-Encoding']).toBe('gzip') 12 | // expect(res.body.length).toBe(1644) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/ConfigEnv.test.ts: -------------------------------------------------------------------------------- 1 | import { getEnvConfig } from '@summer-js/summer' 2 | import '@summer-js/test' 3 | 4 | describe('Config Test', () => { 5 | test('test getConfig', async () => { 6 | const config = getEnvConfig('TEST_CONFIG') 7 | expect(config).toStrictEqual({ 8 | var1: 'VAR1Change', 9 | var2: ['A1', 'B2'], 10 | var3: 'VAR3' 11 | }) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/ControllerPath.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | 3 | describe('Test ControllerPath', () => { 4 | test('should response hello v2', async () => { 5 | const response = await request.get('/v2/hello') 6 | expect(response.rawBody).toBe('Hello Summer!') 7 | expect(response.body).toBe('Hello Summer!') 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/ControllerPostBody.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | 3 | const typeVal = (value) => { 4 | return { type: typeof value, value } 5 | } 6 | 7 | const testRequestParam = async (requestValue: string, resultValue: any) => { 8 | const response = await request.post('/request-body-value', requestValue) 9 | expect(response.body).toStrictEqual(typeVal(resultValue)) 10 | } 11 | 12 | const testErrorRequestParam = async (requestValue: string, errorMessage: any) => { 13 | const response = await request.post('/request-body-value', requestValue) 14 | if (response.statusCode === 500) { 15 | response.print() 16 | } 17 | expect(response.rawBody).toContain(errorMessage) 18 | } 19 | 20 | describe('Controller Object Convert Test', () => { 21 | test('test object convert', async () => { 22 | await testErrorRequestParam( 23 | `{ 24 | "id":1, 25 | "name":"Max", 26 | "tailLength":33.12, 27 | "gender":"Male", 28 | "toys":[ 29 | { 30 | "id":1, 31 | "name":123 32 | }, 33 | { 34 | "id":1, 35 | "name":"Little Monkey" 36 | } 37 | ], 38 | "eyeColor":"Brown" 39 | 40 | }`, 41 | 'is not a string' 42 | ) 43 | 44 | await testRequestParam( 45 | `{ 46 | "id":1, 47 | "name":"Max", 48 | "gender":"Male", 49 | "tailLength":33.12, 50 | "toys":[ 51 | { 52 | "id":1, 53 | "name":"Little Elephant" 54 | }, 55 | { 56 | "id":1, 57 | "name":"Little Monkey" 58 | } 59 | ], 60 | "eyeColor":"Brown" 61 | 62 | }`, 63 | { 64 | eyeColor: 'Brown', 65 | tailLength: 33.12, 66 | toys: [ 67 | { 68 | id: 1, 69 | name: 'Little Elephant' 70 | }, 71 | { 72 | id: 1, 73 | name: 'Little Monkey' 74 | } 75 | ], 76 | id: 1, 77 | name: 'Max', 78 | gender: 'Male' 79 | } 80 | ) 81 | }) 82 | }) 83 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/Cookie.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | 3 | describe('Config Test', () => { 4 | test('test set cookie', async () => { 5 | let result = await request.get('/cookie/set') 6 | expect(result.headers['Set-Cookie'][1]).toEqual('TestCookie=Value') 7 | expect(result.headers['Set-Cookie'][2]).toEqual('TestCookie2=Value2') 8 | }) 9 | 10 | test('test clear cookie', async () => { 11 | let result = await request.get('/cookie/clear') 12 | expect(result.headers['Set-Cookie'][1]).toEqual('TestCookie=; Max-Age=0') 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/Cors.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | 3 | describe('Config Test', () => { 4 | test('cors test path options', async () => { 5 | let result = await request.options('/cors') 6 | expect(result.statusCode).toBe(200) 7 | expect(result.headers['Access-Control-Allow-Origin']).toBe('*') 8 | expect(result.headers['Access-Control-Allow-Methods']).toBe('GET,POST,PUT,DELETE,PATCH,OPTIONS') 9 | expect(result.headers['Access-Control-Allow-Headers']).toBe('*') 10 | expect(result.headers['Access-Control-Allow-Credentials']).toBe('true') 11 | }) 12 | 13 | test('cors test path method', async () => { 14 | let result = await request.get('/cors') 15 | expect(result.statusCode).toBe(200) 16 | expect(result.headers['Access-Control-Allow-Origin']).toBe('*') 17 | expect(result.headers['Access-Control-Allow-Methods']).toBe('GET,POST,PUT,DELETE,PATCH,OPTIONS') 18 | expect(result.headers['Access-Control-Allow-Headers']).toBe('*') 19 | expect(result.headers['Access-Control-Allow-Credentials']).toBe('true') 20 | }) 21 | 22 | test('cors test options origin', async () => { 23 | let result = await request.options('/cors', { headers: { origin: 'https://example.com' } }) 24 | expect(result.statusCode).toBe(200) 25 | expect(result.headers['Access-Control-Allow-Origin']).toBe('https://example.com') 26 | }) 27 | 28 | test('cors test method origin', async () => { 29 | let result = await request.get('/cors', {}, { headers: { origin: 'https://example.com' } }) 30 | expect(result.statusCode).toBe(200) 31 | expect(result.headers['Access-Control-Allow-Origin']).toBe('https://example.com') 32 | }) 33 | 34 | test('chrome preflight request header', async () => { 35 | let result = await request.get( 36 | '/cors', 37 | {}, 38 | { headers: { origin: 'https://example.com', 'access-control-request-headers': 'content-type' } } 39 | ) 40 | expect(result.statusCode).toBe(200) 41 | expect(result.headers['Access-Control-Allow-Origin']).toBe('https://example.com') 42 | expect(result.headers['Access-Control-Allow-Headers']).toBe('content-type') 43 | }) 44 | 45 | test('cors test path not exist', async () => { 46 | let result = await request.options('/cors-not-exist') 47 | expect(result.statusCode).toBe(200) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/CustomDecorator.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | import jwt from 'jsonwebtoken' 3 | 4 | describe('Config Test', () => { 5 | test('test createParamDecorator', async () => { 6 | const result = await request.get('/app/version', {}, { headers: { 'app-version': '1.0.0' } }) 7 | expect(result.statusCode).toBe(200) 8 | expect(result.body).toBe('1.0.0') 9 | }) 10 | 11 | test('test createMethodDecorator', async () => { 12 | let result = await request.get('/cache/1') 13 | expect(result.statusCode).toBe(200) 14 | let firstResult = result.body 15 | result = await request.get('/cache/1') 16 | expect(result.body).toBe(firstResult) 17 | 18 | await new Promise((resolve) => { 19 | setTimeout(async () => { 20 | result = await request.get('/cache/1') 21 | expect(result.body !== firstResult).toBe(true) 22 | resolve('') 23 | }, 1200) 24 | }) 25 | 26 | result = await request.get('/cache2/2') 27 | expect(result.statusCode).toBe(200) 28 | firstResult = result.body 29 | result = await request.get('/cache2/2') 30 | expect(result.body).toBe(firstResult) 31 | }) 32 | 33 | test('test createPropertyDecorator', async () => { 34 | let result = await request.get('/cities') 35 | expect(result.body).toStrictEqual(['Shanghai', 'Tokyo', 'New York City']) 36 | 37 | result = await request.get('/mysql-host') 38 | expect(result.body).toBe('localhost') 39 | }) 40 | 41 | test('test download', async () => { 42 | let result = await request.get('/download') 43 | expect(result.body).toBe('Hello Summer') 44 | expect(result.headers['Content-Type']).toBe('application/octet-stream') 45 | expect(result.headers['Content-Disposition']).toBe(`attachment; filename="hello.txt"`) 46 | }) 47 | 48 | test('test auth', async () => { 49 | let result = await request.get('/userinfo') 50 | expect(result.rawBody).toContain("'uid' is required") 51 | 52 | var token = jwt.sign({ uid: 1, name: 'Tom' }, 'xxxxxxxx') 53 | result = await request.get('/userinfo', {}, { headers: { authentication: token } }) 54 | expect(result.body).toBe('1') 55 | 56 | result = await request.get('/me') 57 | expect(result.statusCode).toBe(401) 58 | 59 | request.setHeaders({ authentication: token }) 60 | result = await request.put('/me') 61 | expect(result.statusCode).toBe(200) 62 | request.setHeaders({}) 63 | }) 64 | 65 | test('test response code', async () => { 66 | let result = await request.get('/dog') 67 | expect(result.statusCode).toBe(404) 68 | }) 69 | }) 70 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/DtoConvert.test.ts: -------------------------------------------------------------------------------- 1 | import { convertData, fillData } from '@summer-js/summer' 2 | import '@summer-js/test' 3 | 4 | class AddUserRequest { 5 | name: string 6 | age: number 7 | moreInfo: string 8 | } 9 | 10 | class User { 11 | id: number 12 | name: string 13 | age: number | null 14 | } 15 | 16 | class UserResource { 17 | name: string 18 | age: number | null 19 | moreInfo: string 20 | } 21 | 22 | describe('DTO convert Test', () => { 23 | test('test convertData', async () => { 24 | const addUserRequest: AddUserRequest = new AddUserRequest() 25 | addUserRequest.name = 'John' 26 | addUserRequest.age = 12 27 | addUserRequest.moreInfo = 'moreInfo' 28 | 29 | const user = convertData(addUserRequest, User) 30 | 31 | expect(user).toEqual({ name: 'John', age: 12 }) 32 | }) 33 | 34 | test('test fillData', async () => { 35 | const user = new User() 36 | user.name = 'John' 37 | user.age = 12 38 | 39 | const userResource = new UserResource() 40 | fillData(userResource, user) 41 | 42 | userResource.moreInfo = 'moreInfo' 43 | 44 | expect(userResource).toEqual({ name: 'John', age: 12, moreInfo: 'moreInfo' }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/GenericInject.test.ts: -------------------------------------------------------------------------------- 1 | import { getInjectable } from '@summer-js/summer' 2 | import { request } from '@summer-js/test' 3 | import { GType } from '../../controllers/GenericInjectController' 4 | import { GenericService } from '../../service/GenericService' 5 | 6 | describe('Test Loc', () => { 7 | test('should inject works', async () => { 8 | let res = await request.get('/generic-inject-test') 9 | expect(res.body).toBe('true') 10 | res = await request.get('/generic-inject-test2') 11 | expect(res.body).toBe('true') 12 | }) 13 | 14 | test('should getInjectable works', async () => { 15 | const genericService = getInjectable(GenericService, [GType, GType]) 16 | if (genericService) { 17 | expect(genericService.t instanceof GType).toBe(true) 18 | expect(genericService.k instanceof GType).toBe(true) 19 | } 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/GlobalVar.test.ts: -------------------------------------------------------------------------------- 1 | import '@summer-js/test' 2 | 3 | const env = SUMMER_ENV 4 | 5 | describe('Test Global Var', () => { 6 | test('env', async () => { 7 | expect(env).toBe('test') 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/Ioc.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | 3 | describe('Test Loc', () => { 4 | test('should @PostConstruct works', async () => { 5 | const res = await request.get('/ioc') 6 | expect(res.body).toBe('init post construct') 7 | }) 8 | 9 | test('should service injection works', async () => { 10 | const res = await request.get('/ioc/service-injection') 11 | expect(res.body).toBe('Hi from TestService') 12 | }) 13 | 14 | test('should middleware injection works', async () => { 15 | const res = await request.get('/middleware-injection') 16 | expect(res.body).toBe('Hi from TestService') 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/Logger.test.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@summer-js/summer' 2 | import '@summer-js/test' 3 | 4 | beforeAll(() => { 5 | const log = (...args) => { 6 | process.stdout.write(args.join(' ') + '\n') 7 | } 8 | console.log = log 9 | console.warn = log 10 | console.info = log 11 | console.debug = log 12 | console.error = log 13 | }) 14 | 15 | describe('Test Logger', () => { 16 | test('should log all', async () => { 17 | jest.spyOn(console, 'log') 18 | jest.spyOn(console, 'warn') 19 | jest.spyOn(console, 'info') 20 | jest.spyOn(console, 'debug') 21 | jest.spyOn(console, 'error') 22 | Logger.enableTypes = ['Info', 'Warn', 'Log', 'Error', 'Debug'] 23 | Logger.log('') 24 | Logger.warn('') 25 | Logger.info('') 26 | Logger.error('') 27 | Logger.debug('') 28 | expect((console.log as any).mock.calls.length).toBe(1) 29 | expect((console.warn as any).mock.calls.length).toBe(1) 30 | expect((console.info as any).mock.calls.length).toBe(1) 31 | expect((console.debug as any).mock.calls.length).toBe(1) 32 | expect((console.error as any).mock.calls.length).toBe(1) 33 | jest.clearAllMocks() 34 | }) 35 | 36 | test('should log noting', async () => { 37 | jest.spyOn(console, 'log') 38 | Logger.enableTypes = [] 39 | Logger.log('') 40 | Logger.warn('') 41 | Logger.info('') 42 | Logger.error('') 43 | Logger.debug('') 44 | expect((console.log as any).mock.calls.length).toBe(0) 45 | jest.clearAllMocks() 46 | }) 47 | 48 | test('should log error', async () => { 49 | jest.spyOn(console, 'log') 50 | Logger.enableTypes = ['Error'] 51 | Logger.log('') 52 | Logger.warn('') 53 | Logger.info('') 54 | Logger.error('') 55 | Logger.debug('') 56 | expect((console.log as any).mock.calls.length).toBe(0) 57 | expect((console.error as any).mock.calls.length).toBe(1) 58 | jest.clearAllMocks() 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/Middleware.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | 3 | describe('Config Test', () => { 4 | test('test middleware string return', async () => { 5 | let result = await request.get('/middleware') 6 | expect(result.body).toEqual('middleware works') 7 | expect(result.headers['Content-Type']).toBe('text/html; charset=utf-8') 8 | }) 9 | 10 | test('test middleware object return', async () => { 11 | let result = await request.get('/middleware-object') 12 | expect(result.body).toStrictEqual({ msg: 'middleware works' }) 13 | expect(result.headers['Content-Type']).toBe('application/json; charset=utf-8') 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/Redis.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | 3 | describe('Test Redis', () => { 4 | test('should set/get redis data', async () => { 5 | let res = await request.get('/redis/set') 6 | expect(res.statusCode).toBe(200) 7 | res = await request.get('/redis/get') 8 | expect(res.body).toBe('1') 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/RequestHeader.test.ts: -------------------------------------------------------------------------------- 1 | import { Request } from '@summer-js/test' 2 | 3 | describe('Test Movie Controller', () => { 4 | // init header 5 | // highlight-next-line 6 | const request = new Request({ headers: { Authorization: 'Bearer xxxxxxxxxxxx' } }) 7 | 8 | test('should return movie list', async () => { 9 | const response = await request.get('/movies') 10 | expect(response.statusCode).toEqual(200) 11 | 12 | // change header 13 | // highlight-next-line 14 | request.setHeaders({ Authorization: 'Bearer xxxxxxxxxxxx' }) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/ResponseHeader.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | 3 | describe('ResponseHeader Test', () => { 4 | test('test string return', async () => { 5 | let result = await request.get('/res-headers/string-return') 6 | expect(result.body).toEqual('string') 7 | expect(result.headers['Content-Type']).toBe('text/html; charset=utf-8') 8 | }) 9 | 10 | test('test object return', async () => { 11 | let result = await request.get('/res-headers/object-return') 12 | expect(result.body).toStrictEqual({ a: 'a', b: 'b' }) 13 | expect(result.headers['Content-Type']).toBe('application/json; charset=utf-8') 14 | }) 15 | 16 | test('test modify header', async () => { 17 | let result = await request.get('/res-headers/modify-return') 18 | expect(result.body).toStrictEqual({ a: 'a', b: 'b' }) 19 | expect(result.headers['Content-Type']).toBe('text/plain') 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/Rpc.test.ts: -------------------------------------------------------------------------------- 1 | import { getInjectable } from '@summer-js/summer' 2 | import { request } from '@summer-js/test' 3 | import http from 'http' 4 | import { UserRpcClientService } from '../rpc/client/UserRpcClientService' 5 | 6 | describe('Test Rpc', () => { 7 | let server 8 | beforeAll(async () => { 9 | server = http 10 | .createServer(function (_req, res) { 11 | res.writeHead(200, { 'Content-Type': 'text/plain' }) 12 | res.write('{"id":99,"name":"John"}') 13 | res.end() 14 | }) 15 | .listen(8802) 16 | }) 17 | 18 | afterAll(async () => { 19 | server.close() 20 | }) 21 | 22 | test('should return right value', async () => { 23 | const userRpcClientService = getInjectable(UserRpcClientService) 24 | if (userRpcClientService) { 25 | const user = await userRpcClientService.getUser(99, { name: 'test' }) 26 | expect(user.id).toBe(99) 27 | expect(user.name).toBe('John') 28 | } 29 | }) 30 | 31 | test('should send right value', async () => { 32 | const result = await request.post( 33 | '/', 34 | { class: 'UserRpcService', method: 'getUser', data: [99] }, 35 | { 36 | headers: { 'summer-rpc-access-key': 'xxxxx' } 37 | } 38 | ) 39 | const user = result.body 40 | expect(user.id).toBe(99) 41 | expect(user.name).toBe('John') 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/Serialization.test.ts: -------------------------------------------------------------------------------- 1 | import { Serialize, serialize } from '@summer-js/summer' 2 | import { request } from '@summer-js/test' 3 | 4 | export class Dog { 5 | name: string 6 | age: number 7 | @Serialize((_, obj: Dog) => { 8 | if (obj.age > 10) { 9 | return 'old' 10 | } 11 | return 'young' 12 | }) 13 | ageType: string 14 | } 15 | 16 | describe('Test Serialization', () => { 17 | test('test serialization function', async () => { 18 | const dog = new Dog() 19 | dog.name = 'Max' 20 | dog.age = 3 21 | expect(serialize(dog)).toEqual({ 22 | name: 'Max', 23 | age: 3, 24 | ageType: 'young' 25 | }) 26 | 27 | dog.name = 'Max' 28 | dog.age = 12 29 | expect(serialize(dog)).toEqual({ 30 | name: 'Max', 31 | age: 12, 32 | ageType: 'old' 33 | }) 34 | }) 35 | 36 | test('test serialization', async () => { 37 | let res = await request.get('/serialize') 38 | expect(res.body).toStrictEqual([ 39 | { 40 | direction: 'LEFT', 41 | country: 'China' 42 | }, 43 | { 44 | direction: 'LEFT', 45 | country: 'China' 46 | } 47 | ]) 48 | 49 | res = await request.get('/class-extends-serialize') 50 | expect(res.body).toStrictEqual({ 51 | direction: 'LEFT', 52 | count: 123 53 | }) 54 | 55 | res = await request.get('/object-serialize') 56 | expect(res.body).toStrictEqual({ 57 | str: 'str', 58 | direction: 'LEFT' 59 | }) 60 | 61 | res = await request.get('/not-date') 62 | expect(res.body).toStrictEqual({ 63 | d: 'not date' 64 | }) 65 | 66 | res = await request.get('/union-type') 67 | expect(res.body).toStrictEqual({ 68 | a: 'AA', 69 | b: 'BB' 70 | }) 71 | 72 | res = await request.get('/prop-serialize') 73 | expect(res.body).toStrictEqual({ 74 | name: 'John', 75 | password: '*****', 76 | gender: 'Male', 77 | nickName: 'nick', 78 | address: 'New York, US' 79 | }) 80 | 81 | res = await request.get('/assign-serialize') 82 | expect(res.body).toStrictEqual({ 83 | direction: 'LEFT', 84 | user: { 85 | name: 'John', 86 | password: '*****', 87 | gender: 'Male', 88 | nickName: 'nick', 89 | address: 'New York, US' 90 | } 91 | }) 92 | }) 93 | }) 94 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/ServiceTag.test.ts: -------------------------------------------------------------------------------- 1 | import { getInjectablesByTags } from '@summer-js/summer' 2 | 3 | describe('Test service Tags', () => { 4 | test('should return services', async () => { 5 | const res = getInjectablesByTags(['pet']) 6 | expect(res.length).toBe(2) 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/ServiceTags.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | 3 | describe('Test service Tags', () => { 4 | test('should return uncompressed data', async () => { 5 | const res = await request.get('/text1') 6 | expect(res.headers['Content-Encoding']).toBe(undefined) 7 | }) 8 | 9 | test('should return compressed data', async () => { 10 | const res = await request.get('/text2') 11 | expect(res.headers['Content-Encoding']).toBe('gzip') 12 | // expect(res.body.length).toBe(1644) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/StaticServer.test.ts: -------------------------------------------------------------------------------- 1 | import { handleStaticRequest } from '@summer-js/summer/lib/static-server' 2 | import '@summer-js/test' 3 | 4 | describe('Config Test', () => { 5 | test('test static server', async () => { 6 | let result = handleStaticRequest('/static/static.txt') 7 | expect(result!.filePath).toEqual('./resource/static.txt') 8 | 9 | result = handleStaticRequest('/404.txt') 10 | expect(result).toEqual(null) 11 | 12 | result = handleStaticRequest('/static/404.txt') 13 | expect(result!.code).toEqual(404) 14 | 15 | result = handleStaticRequest('/static') 16 | expect(result!.code).toEqual(301) 17 | expect(result!.headers).toStrictEqual({ Location: '/static/', 'Cache-Control': 'no-store' }) 18 | 19 | result = handleStaticRequest('/static/') 20 | expect(result!.code).toEqual(200) 21 | expect(result!.filePath).toEqual('./resource/index.html') 22 | 23 | result = handleStaticRequest('/static/../src/index.ts') 24 | expect(result).toEqual(null) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/Swagger.test.ts: -------------------------------------------------------------------------------- 1 | import { getEnvConfig } from '@summer-js/summer' 2 | import { request } from '@summer-js/test' 3 | import { data } from '../data/SwaggerData' 4 | 5 | describe('Swagger Test', () => { 6 | test('test swagger doc', async () => { 7 | const config = getEnvConfig() 8 | const result = await request.get( 9 | (config.SERVER_CONFIG.basePath || '') + config.SWAGGER_CONFIG.docPath + 'swagger-docs.json' 10 | ) 11 | expect(result.body).toStrictEqual(data) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/case/Typeorm.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | import fs from 'fs' 3 | 4 | describe('Test TypeORM', () => { 5 | test('should collect Entity and Migration', async () => { 6 | const indexContent = fs.readFileSync(__dirname + '/../../index.js', { encoding: 'utf-8' }) 7 | expect(indexContent.indexOf('./entity/Person') > 0).toEqual(true) 8 | expect(indexContent.indexOf('/migrations/1650585292097-migration') > 0).toEqual(true) 9 | }) 10 | 11 | test('should read Database', async () => { 12 | const res = await request.get('/todos') 13 | expect(res.body).toEqual([]) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/BasePathController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@summer-js/summer' 2 | 3 | @Controller('/base-path') 4 | export class BasePathController { 5 | @Get('/') 6 | get() { 7 | return 'BasePath works' 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/BuildInDecoratorTestController.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Delete, 4 | Get, 5 | PathParam, 6 | Post, 7 | Put, 8 | Patch, 9 | Query, 10 | Request, 11 | Queries, 12 | Header, 13 | Context, 14 | Body, 15 | RequestPath, 16 | Session, 17 | Cookie 18 | } from '@summer-js/summer' 19 | 20 | export class Obj { 21 | a: number 22 | b: number 23 | } 24 | 25 | export class Q { 26 | ids: number[] 27 | obj: Obj 28 | } 29 | 30 | @Controller('/build-in-decorator') 31 | export class BuildInDecoratorTestController { 32 | @Get('/get') 33 | get() { 34 | return 'Get Done' 35 | } 36 | 37 | @Post('/post') 38 | post() { 39 | return 'Post Done' 40 | } 41 | 42 | @Put('/put') 43 | put() { 44 | return 'Put Done' 45 | } 46 | 47 | @Patch('/patch') 48 | patch() { 49 | return 'Patch Done' 50 | } 51 | 52 | @Delete('/delete') 53 | delete() { 54 | return 'Delete Done' 55 | } 56 | 57 | @Request('/request') 58 | request() { 59 | return 'Request Done' 60 | } 61 | 62 | @Get('/path-param/:id') 63 | pathParam(@PathParam id: number) { 64 | return 'PathParam' + id 65 | } 66 | 67 | @Get('/path-param2/:Id') 68 | pathParam2(@PathParam('Id') id: number) { 69 | return 'PathParam' + id 70 | } 71 | 72 | @Get('/query') 73 | query(@Query id: number) { 74 | return 'Query' + id 75 | } 76 | 77 | @Get('/query2') 78 | query2(@Query('Id') id: number) { 79 | return 'Query' + id 80 | } 81 | 82 | @Get('/query3') 83 | query3(@Query('Id') id = 100) { 84 | return 'Query' + id 85 | } 86 | 87 | @Get('/queries') 88 | queries(@Queries queries: Q) { 89 | return 'Query' + JSON.stringify(queries) 90 | } 91 | 92 | @Get('/header') 93 | header(@Header id: string) { 94 | return 'Header' + id 95 | } 96 | 97 | @Get('/header2') 98 | header2(@Header('Id') id: string) { 99 | return 'Header' + id 100 | } 101 | 102 | @Get('/ctx') 103 | ctx(@Context context: Context) { 104 | return 'Ctx' + context.request.path 105 | } 106 | 107 | @Post('/body') 108 | body(@Body body: string) { 109 | return 'Body' + body 110 | } 111 | 112 | @Get('/request-path') 113 | requestPath(@RequestPath requestPath: string) { 114 | return 'RequestPath' + requestPath 115 | } 116 | 117 | @Get('/cookie') 118 | cookie() { 119 | return 'Cookie' + Cookie.get('id')! 120 | } 121 | 122 | @Get('/cookie2') 123 | cookie2() { 124 | return 'Cookie' + Cookie.get('Id') 125 | } 126 | 127 | @Post('/session') 128 | async addSession() { 129 | await Session.set('id', 100) 130 | } 131 | 132 | @Get('/session') 133 | async session() { 134 | return 'Session' + (await Session.get('id')) 135 | } 136 | 137 | @Get('/session-all') 138 | async sessionAll() { 139 | return JSON.stringify(await Session.getAll()) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/ContextController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, getContext } from '@summer-js/summer' 2 | 3 | @Controller 4 | export class ContextController { 5 | @Get('/context') 6 | context() { 7 | const context = getContext()! 8 | // highlight-next-line 9 | context.response.headers['content-type'] = 'text/plain' 10 | 11 | // although the controller return 'Hello Summer', set context response content has a higher priority 12 | // highlight-next-line 13 | context.response.body = 'hi Summer' 14 | return 'hello winter' 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/CookieTestController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Cookie } from '@summer-js/summer' 2 | 3 | @Controller('/cookie') 4 | export class CookieTestController { 5 | @Get('/set') 6 | set() { 7 | Cookie.set('TestCookie', 'Value') 8 | Cookie.set('TestCookie2', 'Value2') 9 | } 10 | 11 | @Get('/clear') 12 | clear() { 13 | Cookie.clear('TestCookie') 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/CorsTestController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@summer-js/summer' 2 | 3 | @Controller('/cors') 4 | export class CorsTestController { 5 | @Get 6 | cors() { 7 | return '' 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/ESController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@summer-js/summer' 2 | import { ESClient } from '@summer-js/elasticsearch' 3 | 4 | @Controller('/es') 5 | export class ESController { 6 | esClient: ESClient 7 | esClient2: ESClient<'Default'> 8 | 9 | @Get('/search') 10 | async run() { 11 | await this.esClient.index({ 12 | index: 'game-of-thrones', 13 | document: { 14 | character: 'Ned Stark', 15 | quote: 'Winter is coming.' 16 | } 17 | }) 18 | 19 | await this.esClient.index({ 20 | index: 'game-of-thrones', 21 | document: { 22 | character: 'Daenerys Targaryen', 23 | quote: 'I am the blood of the dragon.' 24 | } 25 | }) 26 | 27 | await this.esClient.index({ 28 | index: 'game-of-thrones', 29 | document: { 30 | character: 'Tyrion Lannister', 31 | quote: 'A mind needs books like a sword needs a whetstone.' 32 | } 33 | }) 34 | 35 | // here we are forcing an index refresh, otherwise 36 | await this.esClient.indices.refresh({ index: 'game-of-thrones' }) 37 | 38 | // Let's search! 39 | const result = await this.esClient.search({ 40 | index: 'game-of-thrones', 41 | query: { 42 | match: { quote: 'winter' } 43 | } 44 | }) 45 | 46 | return result.hits.hits 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/FileUploadController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Post, Body, File } from '@summer-js/summer' 2 | import { ApiDoc, ApiDocGroup } from '@summer-js/swagger' 3 | 4 | class Request { 5 | field1: string 6 | field2?: int 7 | file: File 8 | } 9 | 10 | @Controller('/upload') 11 | @ApiDocGroup('上传相关接口') 12 | export class FileUploadController { 13 | @ApiDoc('上传文件') 14 | @Post 15 | uploadFile(@Body req: Request) { 16 | console.log('body', req) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/GenericTypeController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Body, Post } from '@summer-js/summer' 2 | import { ApiDocGroup } from '@summer-js/swagger' 3 | 4 | enum Direction { 5 | Up, 6 | Down 7 | } 8 | 9 | class Obj { 10 | a: number 11 | b: string 12 | } 13 | 14 | class G { 15 | a: T 16 | b: string 17 | d: Date 18 | } 19 | 20 | class Request { 21 | int: int 22 | dir!: Direction[] 23 | intArr: int[] 24 | field1: T 25 | field2: K 26 | obj: L 27 | date: P 28 | g: G 29 | z: Z 30 | x: G 31 | w: W[] 32 | o: O[] 33 | } 34 | 35 | @ApiDocGroup('Generic') 36 | @Controller('/generic-type') 37 | export class GenericTypeController { 38 | @Post 39 | async genericRequest(@Body req: Request, number, Obj, string>) { 40 | return req 41 | } 42 | 43 | @Get('/mixed-object-return') 44 | async mixedObjectReturn() { 45 | const g = new G() 46 | g.a = 123 47 | g.b = 'sss' 48 | g.d = new Date(2022, 12, 12) 49 | return { hello: 'World', g } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/IocController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, EnvConfig, Get, PostConstruct } from '@summer-js/summer' 2 | import { TestService2 } from '@/test/service' 3 | 4 | @Controller('/ioc') 5 | export class LocController { 6 | config: EnvConfig<'POST_CONSTRUCT_CONFIG'> 7 | 8 | testService2: TestService2 9 | 10 | str: string 11 | 12 | @PostConstruct 13 | init() { 14 | this.str = this.config.value 15 | } 16 | 17 | @Get 18 | testStr() { 19 | return this.str 20 | } 21 | 22 | @Get('/service-injection') 23 | testServiceInjection() { 24 | return this.testService2.hiFromTestService1() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/MovieController.ts: -------------------------------------------------------------------------------- 1 | import { Body, Controller, Get, Query, PathParam, Post } from '@summer-js/summer' 2 | import { ApiDoc, ApiDocGroup, PropDoc } from '@summer-js/swagger' 3 | 4 | class AddMovieRequest { 5 | name: string 6 | year: string 7 | } 8 | 9 | export class Movie { 10 | id: number 11 | 12 | @PropDoc('Name of the movie', '2022') 13 | name: string 14 | 15 | @PropDoc('Movie Release Year', '2022') 16 | year: string 17 | } 18 | 19 | @Controller 20 | @ApiDocGroup('Movie Apis') 21 | export class MovieController { 22 | @ApiDoc('Get movies') 23 | @Get('/movies') 24 | list(@Query search?: string) { 25 | search 26 | const movies: Movie[] = [ 27 | { id: 1, name: 'Titanic', year: '1997' }, 28 | { id: 2, name: 'CODA', year: '2021' } 29 | ] 30 | return movies 31 | } 32 | 33 | @ApiDoc('Get movie detail') 34 | @Get('/movies/:id') 35 | detail(@PathParam id: int) { 36 | console.log(id) 37 | const movies: Movie = { id, name: 'Titanic', year: '1997' } 38 | return movies 39 | } 40 | 41 | @ApiDoc('Add new movie') 42 | @Post('/movies') 43 | add(@Body body: AddMovieRequest) { 44 | body 45 | const movies: Movie = { id: 1, name: 'Titanic', year: '1997' } 46 | return movies 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/MovieControllerV2.ts: -------------------------------------------------------------------------------- 1 | import { Body, Controller, Get, Query, PathParam, Post } from '@summer-js/summer' 2 | import { ApiDoc, ApiDocGroup, PropDoc } from '@summer-js/swagger' 3 | 4 | class AddMovieRequest { 5 | name: string 6 | year: string 7 | } 8 | 9 | export class Movie { 10 | id: number 11 | 12 | @PropDoc('Name of the movie', '2022') 13 | name: string 14 | 15 | @PropDoc('Movie Release Year', '2022') 16 | year: string 17 | } 18 | 19 | @Controller('/v2') 20 | @ApiDocGroup('Movie Apis') 21 | export class MovieController { 22 | @ApiDoc('Get movies') 23 | @Get('/movies') 24 | // @ts-ignore 25 | list(@Query search?: string) { 26 | const movies: Movie[] = [ 27 | { id: 1, name: 'Titanic', year: '1997' }, 28 | { id: 2, name: 'CODA', year: '2021' } 29 | ] 30 | return movies 31 | } 32 | 33 | @ApiDoc('Get movie detail') 34 | @Get('/movies/:id') 35 | // @ts-ignore 36 | detail(@PathParam id: string) { 37 | const movies: Movie = { id: 1, name: 'Titanic', year: '1997' } 38 | return movies 39 | } 40 | 41 | @ApiDoc('Add new movie') 42 | @Post('/movies') 43 | // @ts-ignore 44 | add(@Body body: AddMovieRequest) { 45 | const movies: Movie = { id: 1, name: 'Titanic', year: '1997' } 46 | return movies 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/RedisController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@summer-js/summer' 2 | import { RedisClient } from '@summer-js/redis' 3 | 4 | @Controller('/redis') 5 | export class RedisController { 6 | redisClient: RedisClient 7 | redisClient2: RedisClient<'client2'> 8 | 9 | @Get('/test') 10 | async test() { 11 | this.redisClient.set('key', 'value') 12 | this.redisClient2.set('key2', 'value2') 13 | } 14 | 15 | @Get('/set') 16 | async set() { 17 | await this.redisClient.set('id', 1) 18 | await this.redisClient.expire('id', 3) 19 | } 20 | 21 | @Get('/get') 22 | async get() { 23 | return (await this.redisClient2.get('id')) || '' 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/RedisSessionController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Session } from '@summer-js/summer' 2 | import { RedisClient } from '@summer-js/redis' 3 | 4 | @Controller('/redis-session') 5 | export class RedisSessionController { 6 | redisClient: RedisClient 7 | 8 | // @PostConstruct 9 | // init() { 10 | // Session.setStorage({ 11 | // save: async (sessionId, key, value) => { 12 | // const sessionData = JSON.parse((await this.redisClient.get(sessionId)) || '{}') 13 | // sessionData[key] = value 14 | // await this.redisClient.set(sessionId, JSON.stringify(sessionData)) 15 | // }, 16 | // load: async (sessionId) => { 17 | // const sessionData = await this.redisClient.get(sessionId) 18 | // if (sessionData) { 19 | // return JSON.parse(sessionData) 20 | // } 21 | // return {} 22 | // }, 23 | // clear: async (sessionId) => { 24 | // await this.redisClient.del(sessionId) 25 | // }, 26 | // expire: async (sessionId, expireIn) => { 27 | // await this.redisClient.expire(sessionId, expireIn) 28 | // } 29 | // }) 30 | // } 31 | 32 | @Get('/set-session') 33 | async setSession() { 34 | await Session.set('id', 1) 35 | } 36 | 37 | @Get('/get-session') 38 | async getSession() { 39 | return await Session.get('id') 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/ResponseHeaderController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Context } from '@summer-js/summer' 2 | 3 | @Controller('/res-headers') 4 | export class CorsTestController { 5 | @Get('/string-return') 6 | stringReturn() { 7 | return 'string' 8 | } 9 | 10 | @Get('/object-return') 11 | objectReturn() { 12 | return { a: 'a', b: 'b' } 13 | } 14 | 15 | @Get('/modify-return') 16 | headerModifyReturn(@Context context: Context) { 17 | context.response.headers['Content-Type'] = 'text/plain' 18 | return { a: 'a', b: 'b' } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/controller/ServerlessControllers.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, createPropertyDecorator } from '@summer-js/summer' 2 | import { MovieController } from './MovieController' 3 | 4 | const PropInject = createPropertyDecorator(async () => { 5 | return new Promise((resolve) => { 6 | setTimeout(() => { 7 | resolve('Test Injection') 8 | }, 100) 9 | }) 10 | }) 11 | 12 | @Controller('/serverless') 13 | export class ServerlessController { 14 | @PropInject 15 | prop: string 16 | 17 | movieController: MovieController 18 | 19 | @Get('/hello') 20 | serverless() { 21 | return 'Hello Serverless' 22 | } 23 | 24 | @Get('/inject') 25 | inject() { 26 | return this.prop 27 | } 28 | 29 | @Get('/db-data') 30 | async dbData() { 31 | return await this.movieController.list() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/middleware/PathTestMiddleware.ts: -------------------------------------------------------------------------------- 1 | import { Middleware, Context } from '@summer-js/summer' 2 | import { TestService2 } from '../service' 3 | 4 | @Middleware({ order: 0 }) 5 | export class PathTestMiddleware { 6 | testService2: TestService2 7 | 8 | async process(ctx: Context, next: any) { 9 | if (ctx.request.path === '/middleware') { 10 | ctx.response.body = 'middleware works' 11 | } else if (ctx.request.path === '/middleware-object') { 12 | ctx.response.body = { msg: 'middleware works' } 13 | } else if (ctx.request.path === '/middleware-injection') { 14 | ctx.response.body = this.testService2.hiFromTestService1() 15 | } else { 16 | await next() 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/rpc/UserRpcController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@summer-js/summer' 2 | import { UserRpcClientService } from './client/UserRpcClientService' 3 | 4 | @Controller 5 | export class UserRpcController { 6 | userRpcClientService: UserRpcClientService 7 | 8 | @Get('/rpc/user') 9 | async getUser() { 10 | return await this.userRpcClientService.getUser(99, { name: '' }) 11 | } 12 | 13 | @Get('/rpc/users') 14 | async getUsers() { 15 | return await this.userRpcClientService.getUsers() 16 | } 17 | 18 | @Get('/rpc/error') 19 | async error() { 20 | return await this.userRpcClientService.error() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/rpc/client/QueryUser.ts: -------------------------------------------------------------------------------- 1 | export class QueryUser { 2 | name: string 3 | } 4 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/rpc/client/UserRpcClientService.ts: -------------------------------------------------------------------------------- 1 | import { RpcClient } from '@summer-js/summer' 2 | import { QueryUser } from './QueryUser' 3 | 4 | export class User { 5 | id: number 6 | name: string 7 | } 8 | 9 | @RpcClient('LOCAL_RPC', 'UserRpcService') 10 | export class UserRpcClientService { 11 | getUser: (id: number, queryUser: QueryUser) => Promise 12 | getUsers: () => Promise 13 | error: () => Promise 14 | } 15 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/rpc/server/UserRpcService.ts: -------------------------------------------------------------------------------- 1 | import { RpcProvider } from '@summer-js/summer' 2 | 3 | @RpcProvider 4 | export class UserRpcService { 5 | getUser(id: number) { 6 | return { id, name: 'John' } 7 | } 8 | getUsers() { 9 | return [ 10 | { id: 1, name: 'John' }, 11 | { id: 2, name: 'Tom' } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/service/SwaggerService.ts: -------------------------------------------------------------------------------- 1 | import { Service } from '@summer-js/summer' 2 | import { Paging, Obj, AnotherObj } from '../../dto/resource/Paging' 3 | 4 | @Service 5 | export class SwaggerService { 6 | getData() { 7 | return {} as Paging 8 | } 9 | 10 | getPagingData() { 11 | return new Paging({ data: [new AnotherObj()], total: 1, pageNumber: 1, pageSize: 2 }) 12 | } 13 | 14 | getMixType() { 15 | const a = 123 16 | if (a === 123) { 17 | return new Obj() 18 | } 19 | return new AnotherObj() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/service/TestService.ts: -------------------------------------------------------------------------------- 1 | import { Service } from '@summer-js/summer' 2 | 3 | @Service 4 | export class TestService { 5 | sayHi() { 6 | return 'Hi from TestService' 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/service/TestService2.ts: -------------------------------------------------------------------------------- 1 | import { Service } from '@summer-js/summer' 2 | import { TestService } from './' 3 | 4 | @Service 5 | export class TestService2 { 6 | testService: TestService 7 | 8 | hiFromTestService1() { 9 | return this.testService.sayHi() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/service/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TestService2' 2 | export * from './TestService' 3 | export * from './SwaggerService' 4 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/test/site-example/MovieController.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | 3 | describe('Test Movie Controller', () => { 4 | test('should return movie list', async () => { 5 | const response = await request.get('/movies') 6 | expect(response.statusCode).toEqual(200) 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/with-db/DataSource.ts: -------------------------------------------------------------------------------- 1 | import { EntityTarget, ObjectLiteral } from 'typeorm' 2 | import { getDataSource } from '@summer-js/typeorm' 3 | 4 | export const getRepository = (entity: EntityTarget) => { 5 | // if (process.env.SUMMER_ENV === 'prod') { 6 | // return {} as any 7 | // } 8 | return getDataSource('DATA_SOURCE').getRepository(entity) 9 | } 10 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/with-db/PersonController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, PathParam, Post, Body, convertData, ResponseError, Query, Queries } from '@summer-js/summer' 2 | import { ApiDoc, ApiDocGroup } from '@summer-js/swagger' 3 | import { PersonRequest, PersonSearchRequest } from '../dto/request/person-request' 4 | import { Person } from '../entity' 5 | import { PersonService } from './PersonService' 6 | 7 | @Controller('/persons') 8 | @ApiDocGroup('Person相关服务') 9 | export class PersonController { 10 | personService: PersonService 11 | 12 | @Get 13 | @ApiDoc('获取用户列表', { 14 | errors: [ 15 | new ResponseError(500, '错误'), 16 | new ResponseError(400, { message: '错误1' }), 17 | new ResponseError(400, { message: '错误2' }), 18 | { statusCode: 400, description: '请求错误', example: '错误3' }, 19 | { statusCode: 400, example: { message: '错误5' } } 20 | ] 21 | }) 22 | async personList(@Query pageIndex?: string) { 23 | pageIndex 24 | const persons = await this.personService.getPersons() 25 | return persons! 26 | } 27 | 28 | @Post 29 | async addPerson(@Body personRequest: PersonRequest) { 30 | const persons = await this.personService.savePerson(convertData(personRequest, Person)) 31 | return persons! 32 | } 33 | 34 | @Get('/:id') 35 | async personInfo(@PathParam id: number) { 36 | const person = await this.personService.getPersonInfo(id) 37 | return person! 38 | } 39 | 40 | @Get('/search') 41 | async search(@Queries queries: PersonSearchRequest) { 42 | return 'search ' + JSON.stringify(queries) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/with-db/PersonService.ts: -------------------------------------------------------------------------------- 1 | import { Service } from '@summer-js/summer' 2 | import { getRepository } from './DataSource' 3 | 4 | import { Person } from '../entity' 5 | 6 | @Service 7 | export class PersonService { 8 | personRepository = getRepository(Person) 9 | 10 | async getPersons() { 11 | const person = await this.personRepository.find() 12 | return person 13 | } 14 | 15 | async getPersonInfo(id: number) { 16 | const person = await this.personRepository.findOneBy({ id }) 17 | return person 18 | } 19 | 20 | async savePerson(person: Person) { 21 | await this.personRepository.save(person) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/with-db/TodoController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@summer-js/summer' 2 | import { ApiDocGroup } from '@summer-js/swagger' 3 | 4 | import { TodoService } from './TodoService' 5 | 6 | @Controller 7 | @ApiDocGroup('Todo Apis') 8 | export class TodoController { 9 | todoService: TodoService 10 | 11 | @Get('/todos') 12 | list() { 13 | return this.todoService.getTodos() 14 | } 15 | 16 | // @Post('/todos') 17 | // add(@Body _body: string) { 18 | // return this.todoService.addTransaction() 19 | // } 20 | } 21 | -------------------------------------------------------------------------------- /@summer-js/summer-test/src/with-db/TodoService.ts: -------------------------------------------------------------------------------- 1 | import { Service } from '@summer-js/summer' 2 | import { Repository, Transaction } from '@summer-js/typeorm' 3 | 4 | import { Todo } from '../entity/Todo' 5 | 6 | @Service 7 | export class TodoService { 8 | todoRepository: Repository 9 | 10 | async getTodos() { 11 | return await this.todoRepository.find() 12 | } 13 | 14 | async addTodo() { 15 | const todo = new Todo() 16 | return this.todoRepository.save(todo) 17 | } 18 | 19 | @Transaction 20 | async test() { 21 | return 'test' 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /@summer-js/summer-test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns": true, 5 | "noUnusedParameters": true, 6 | "noUnusedLocals": true, 7 | "strictNullChecks": true, 8 | "types": ["node", "jest"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./compile", 14 | "lib": ["es6"], 15 | "moduleResolution": "Node", 16 | "module": "commonjs", 17 | "allowJs": true, 18 | "paths": { 19 | "@/*": ["./src/*"] 20 | } 21 | }, 22 | "include": ["./src/**/*"] 23 | } 24 | -------------------------------------------------------------------------------- /@summer-js/summer/compiler-test.txt: -------------------------------------------------------------------------------- 1 | init compile 2 | init error 3 | init compile interrupt 4 | 5 | add a ts file 6 | add a js file 7 | 8 | update a ts file 9 | update a js file 10 | update error 11 | 12 | delete a ts file 13 | delete a js file 14 | 15 | return not same -------------------------------------------------------------------------------- /@summer-js/summer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@summer-js/summer", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.js", 7 | "files": [ 8 | "lib" 9 | ], 10 | "scripts": { 11 | "clear": "rm -rdf ./lib", 12 | "build": "tsc", 13 | "pre-release": "npm publish --tag rc --access public", 14 | "release": "npm publish --access public" 15 | }, 16 | "author": "Calidan", 17 | "license": "ISC", 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/calidan-x/summer" 21 | }, 22 | "homepage": "https://summerjs.dev", 23 | "keywords": [ 24 | "node", 25 | "nodejs", 26 | "backend", 27 | "api" 28 | ], 29 | "dependencies": { 30 | "busboy": "^1.6.0", 31 | "cookie": "^1.0.1", 32 | "deepmerge": "^4.3.1", 33 | "esbuild": "^0.25.5", 34 | "mime": "^3.0.0", 35 | "path-to-regexp": "^8.2.0", 36 | "reflect-metadata": "0.1.13", 37 | "ts-morph": "^25.0.1", 38 | "typescript": "^5.8.2", 39 | "node-cron": "3.0.2" 40 | }, 41 | "devDependencies": { 42 | "@types/node": "^22.9.3" 43 | } 44 | } -------------------------------------------------------------------------------- /@summer-js/summer/readme.md: -------------------------------------------------------------------------------- 1 | # 🔆 Summer 2 | 3 | Efficient NodeJs Backend Framework 4 | 5 | Official Website [https://summerjs.dev](https://summerjs.dev) 6 | 7 | Summer aims to provide a secure, high-quality, easy fast enterprise backend framework 8 | -------------------------------------------------------------------------------- /@summer-js/summer/src/body-parser.ts: -------------------------------------------------------------------------------- 1 | import busboy from 'busboy' 2 | import { Readable } from 'stream' 3 | import { randomFillSync } from 'crypto' 4 | import fs from 'fs' 5 | import os from 'os' 6 | import path from 'path' 7 | 8 | const random = (() => { 9 | const buf = Buffer.alloc(16) 10 | return () => randomFillSync(buf).toString('hex') 11 | })() 12 | 13 | export const parseBody = (req: Readable, method: string, headers): Promise => { 14 | return new Promise((resolve) => { 15 | if ( 16 | method === 'POST' && 17 | (headers['content-type'] === 'application/x-www-form-urlencoded' || 18 | (headers['content-type'] || '').startsWith('multipart/form-data')) 19 | ) { 20 | let parseEnd = false 21 | const bodyData: Record = {} 22 | const files = {} 23 | const bb = busboy({ headers, limits: { fileSize: 20971520 } }) 24 | bb.on('file', (name, file, info) => { 25 | const saveTo = path.join(os.tmpdir(), `upload-${random()}`) 26 | const steam = file.pipe(fs.createWriteStream(saveTo)) 27 | files[name] = { ...info, tmpPath: saveTo } 28 | steam.on('finish', () => { 29 | if (parseEnd) { 30 | resolve({ ...bodyData, ...files }) 31 | } 32 | }) 33 | }) 34 | bb.on('field', (name, value) => { 35 | bodyData[name] = value 36 | }) 37 | bb.on('close', () => { 38 | parseEnd = true 39 | if (Object.keys(files).length === 0) { 40 | resolve({ ...bodyData, ...files }) 41 | } 42 | }) 43 | req.pipe(bb) 44 | } else { 45 | let bodyData: any[] = [] 46 | req.on('data', (chunk) => { 47 | bodyData.push(chunk) 48 | }) 49 | req.on('end', async () => { 50 | resolve(Buffer.concat(bodyData).toString()) 51 | }) 52 | } 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /@summer-js/summer/src/config-handler.ts: -------------------------------------------------------------------------------- 1 | import merge from 'deepmerge' 2 | 3 | let _envConfig: any = null 4 | 5 | export const getEnvConfig = (key?: string): T => { 6 | if (_envConfig) { 7 | return key ? _envConfig[key] : _envConfig 8 | } 9 | 10 | const defaultConfig = global['$$_DEFAULT_CONFIG'] || {} 11 | const envConfig = global['$$_ENV_CONFIG'] || {} 12 | const finalConfig: any = merge(defaultConfig, envConfig) 13 | 14 | _envConfig = finalConfig 15 | return key ? finalConfig[key] : finalConfig 16 | } 17 | 18 | const loopKeyValue = (config: any, updateFunc: (value: any) => any) => { 19 | if (Array.isArray(config)) { 20 | config.forEach((cfg) => { 21 | loopKeyValue(cfg, updateFunc) 22 | }) 23 | } else if (typeof config === 'object') { 24 | Object.entries(config).forEach(([key, value]) => { 25 | if (typeof value === 'function') { 26 | } else if (typeof value !== 'object') { 27 | config[key] = updateFunc(value) 28 | } else { 29 | loopKeyValue(value, updateFunc) 30 | } 31 | }) 32 | } 33 | } 34 | 35 | export const replaceEnvConfigValue = (updateFunc: (value: any) => any) => { 36 | loopKeyValue(_envConfig, updateFunc) 37 | } 38 | 39 | /** 40 | * @deprecated Please use getEnvConfig() instead 41 | */ 42 | export const getConfig = getEnvConfig 43 | 44 | // @ts-ignore 45 | export type EnvConfig = T 46 | -------------------------------------------------------------------------------- /@summer-js/summer/src/cookie.ts: -------------------------------------------------------------------------------- 1 | import { SerializeOptions, parse, serialize } from 'cookie' 2 | import { Context, getContext } from '.' 3 | 4 | export interface CookieItem { 5 | name: string 6 | value: string 7 | options?: SerializeOptions 8 | } 9 | 10 | const CookieItems = Symbol('Set-Cookies') 11 | 12 | export const parseCookie = (ctx: Context) => { 13 | if (ctx.request.headers!.cookie) { 14 | ctx.cookies = parse(ctx.request.headers!.cookie) || {} 15 | } 16 | } 17 | 18 | export const assembleCookie = (ctx: Context) => { 19 | if (ctx[CookieItems] && ctx[CookieItems].length > 0) { 20 | ctx.response.headers['Set-Cookie'] = ctx[CookieItems].map((rc) => serialize(rc.name, rc.value, rc.options)) 21 | } 22 | } 23 | 24 | export const Cookie = { 25 | get(name: string) { 26 | const context = getContext() 27 | if (context && context.cookies) { 28 | return context.cookies[name] 29 | } 30 | return undefined 31 | }, 32 | getAll() { 33 | const context = getContext() 34 | if (context && context.cookies) { 35 | return context.cookies 36 | } 37 | return undefined 38 | }, 39 | set(name: string, value: string, options?: SerializeOptions) { 40 | const context = getContext() 41 | if (context) { 42 | context.cookies = context.cookies || {} 43 | context.cookies[name] = value 44 | context[CookieItems] = context[CookieItems] || [] 45 | context[CookieItems].push({ name, value, options }) 46 | } 47 | }, 48 | clear(name: string, options?: SerializeOptions) { 49 | const context = getContext() 50 | if (context) { 51 | context[CookieItems] = context[CookieItems] || [] 52 | context[CookieItems].push({ 53 | name, 54 | value: '', 55 | options: { maxAge: 0, ...options } 56 | }) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /@summer-js/summer/src/cors.ts: -------------------------------------------------------------------------------- 1 | import { getEnvConfig } from './config-handler' 2 | import { Context } from '.' 3 | 4 | const corsHeader = (context: Context) => ({ 5 | 'Access-Control-Allow-Origin': context.request.headers['origin'] || '*', 6 | 'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,PATCH,OPTIONS', 7 | 'Access-Control-Allow-Headers': context.request.headers['access-control-request-headers'] || '*', 8 | 'Access-Control-Allow-Credentials': 'true', 9 | 'Access-Control-Max-Age': '86400' 10 | }) 11 | 12 | export const handleCors = (ctx: Context) => { 13 | if (getEnvConfig('SERVER_CONFIG').cors) { 14 | if (ctx.request.method === 'OPTIONS') { 15 | ctx.response = { 16 | statusCode: 200, 17 | body: '', 18 | headers: corsHeader(ctx) 19 | } 20 | return true 21 | } else { 22 | Object.assign(ctx.response.headers, corsHeader(ctx)) 23 | } 24 | } 25 | return false 26 | } 27 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/build-in.ts: -------------------------------------------------------------------------------- 1 | import { Context } from '../request-handler' 2 | import { restfulMethodDecorator } from './method' 3 | import { createParamDecorator } from './param' 4 | import { createPropertyDecorator } from './property' 5 | 6 | // method 7 | export const Get = restfulMethodDecorator('GET') 8 | export const Post = restfulMethodDecorator('POST') 9 | export const Put = restfulMethodDecorator('PUT') 10 | export const Patch = restfulMethodDecorator('PATCH') 11 | export const Delete = restfulMethodDecorator('DELETE') 12 | export const Request = restfulMethodDecorator('REQUEST') 13 | 14 | // params 15 | /** 16 | * @deprecated Please use '@Context' 17 | */ 18 | export const Ctx = createParamDecorator((ctx: Context) => ctx) 19 | 20 | export const _bodyConvertFunc = (ctx: Context) => { 21 | if (ctx.request.headers['Content-Type'] === 'application/json') { 22 | return JSON.parse(ctx.request.body!) 23 | } 24 | return ctx.request.body 25 | } 26 | export const Body = createParamDecorator(_bodyConvertFunc) 27 | 28 | export const _queriesConvertFunc = (ctx: Context) => ctx.request.queries 29 | export const Queries = createParamDecorator(_queriesConvertFunc) 30 | 31 | export const _queryConvertFunc = (ctx: Context, paramName: string, name: string) => 32 | ctx.request.queries[name || paramName] 33 | export const Query = createParamDecorator(_queryConvertFunc) 34 | 35 | export const _pathParamConvertFunc = (ctx: Context, paramName: string, name: string) => 36 | ctx.request.pathParams[name || paramName] 37 | export const PathParam = createParamDecorator(_pathParamConvertFunc) 38 | 39 | export const _headerConvertFunc = (ctx: Context, paramName: string, name: string) => 40 | ctx.request.headers[name || paramName] 41 | export const Header = createParamDecorator(_headerConvertFunc) 42 | 43 | export const RequestPath = createParamDecorator((ctx: Context) => ctx.request.path) 44 | 45 | // property 46 | ;(global as any)._EnvConfig = createPropertyDecorator(async (config, _propertyName, configKey?: string) => { 47 | return configKey ? config[configKey] : config 48 | }) 49 | 50 | /** 51 | * @deprecated Please use EnvConfig<'Name',Type = any> 52 | */ 53 | export const Config = (global as any)._EnvConfig 54 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/class-method.ts: -------------------------------------------------------------------------------- 1 | import { asyncLocalStorage, checkValidationError } from '../request-handler' 2 | import { OmitFirstAndSecondArg, ContextInvocation as Context } from './utility' 3 | 4 | type DecoratorMethodType = (ctx: Context, invokeMethod: (args: any[]) => Promise, ...args: any[]) => void 5 | 6 | export interface ClassMethodDecoratorType { 7 | (...name: Parameters>): any 8 | (target: Object, propertyKey: string, descriptor: PropertyDescriptor): void 9 | (constructor: Function): void 10 | } 11 | 12 | const generateClassMethodDecorator = 13 | (decoratorCall: any, ...args: any[]) => 14 | (target: Object | Function, propertyKey: string | null = null, descriptor: PropertyDescriptor | null = null) => { 15 | if (propertyKey && descriptor) { 16 | const originalFunc = descriptor.value 17 | descriptor.value = async function (...arg) { 18 | const context = asyncLocalStorage.getStore() || ({} as Context) 19 | context.invocation = { 20 | className: target.constructor.name, 21 | methodName: propertyKey, 22 | params: arg || [] 23 | } 24 | const ret = await decoratorCall( 25 | context, 26 | async (mArgs) => { 27 | checkValidationError(originalFunc, context) 28 | return await originalFunc.apply(this, mArgs) 29 | }, 30 | ...args 31 | ) 32 | return ret 33 | } 34 | } else { 35 | const constructor = target as Function 36 | Object.getOwnPropertyNames(constructor.prototype).forEach((name) => { 37 | if (typeof constructor.prototype[name] === 'function' && name !== 'constructor') { 38 | const descriptor = Object.getOwnPropertyDescriptor(constructor.prototype, name)! 39 | const originalFunc = descriptor.value 40 | descriptor.value = async function (...arg) { 41 | const context = asyncLocalStorage.getStore() || ({} as Context) 42 | context.invocation = { 43 | className: constructor.name, 44 | methodName: name, 45 | params: arg || [] 46 | } 47 | const ret = await decoratorCall( 48 | context, 49 | async (mArgs) => { 50 | checkValidationError(originalFunc, context) 51 | return await originalFunc.apply(this, mArgs) 52 | }, 53 | ...args 54 | ) 55 | return ret 56 | } 57 | Object.defineProperty(constructor.prototype, name, descriptor) 58 | } 59 | }) 60 | } 61 | } 62 | 63 | export const createClassAndMethodDecorator = 64 | (paramMethod: T): ClassMethodDecoratorType => 65 | // @ts-ignore 66 | (...dArgs: any[]) => { 67 | if (dArgs.length === 1 && dArgs[0].toString().startsWith('class ')) { 68 | generateClassMethodDecorator(paramMethod)(dArgs[0]) 69 | return 70 | } else if (dArgs.length === 3 && dArgs[0].constructor?.toString().startsWith('class ')) { 71 | generateClassMethodDecorator(paramMethod)(dArgs[0], dArgs[1], dArgs[2]) 72 | return 73 | } 74 | return generateClassMethodDecorator(paramMethod, ...dArgs) 75 | } 76 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/class.ts: -------------------------------------------------------------------------------- 1 | import { asyncLocalStorage, Context, checkValidationError } from '../request-handler' 2 | import { OmitFirstAndSecondArg } from './utility' 3 | 4 | const generateClassDecorator = 5 | (decoratorCall: any, ...args: any[]) => 6 | (constructor: Function) => { 7 | Object.getOwnPropertyNames(constructor.prototype).forEach((name) => { 8 | if (typeof constructor.prototype[name] === 'function' && name !== 'constructor') { 9 | const descriptor = Object.getOwnPropertyDescriptor(constructor.prototype, name)! 10 | const originalFunc = descriptor.value 11 | descriptor.value = async function (...arg) { 12 | const context = asyncLocalStorage.getStore() || ({} as Context) 13 | context.invocation = { 14 | className: constructor.name, 15 | methodName: name, 16 | params: arg || [] 17 | } 18 | const ret = await decoratorCall( 19 | context, 20 | async (mArgs) => { 21 | checkValidationError(originalFunc, context) 22 | return await originalFunc.apply(this, mArgs) 23 | }, 24 | ...args 25 | ) 26 | return ret 27 | } 28 | Object.defineProperty(constructor.prototype, name, descriptor) 29 | } 30 | }) 31 | } 32 | 33 | type DecoratorMethodType = (ctx: Context, invokeMethod: (args: any[]) => Promise, ...args: any[]) => void 34 | 35 | export interface MethodDecoratorType { 36 | (...name: Parameters>): ClassDecorator 37 | (constructor: Function): void 38 | } 39 | 40 | export const createClassDecorator = 41 | (paramMethod: T): MethodDecoratorType => 42 | // @ts-ignore 43 | (...dArgs) => { 44 | if (dArgs.length === 1 && dArgs[0].toString().startsWith('class ')) { 45 | generateClassDecorator(paramMethod)(dArgs[0]) 46 | return 47 | } 48 | return generateClassDecorator(paramMethod, ...dArgs) 49 | } 50 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/controller.ts: -------------------------------------------------------------------------------- 1 | import { IocContainer } from '../ioc' 2 | import { requestMappingAssembler } from '../request-mapping' 3 | 4 | interface ControllerDecoratorType { 5 | (path?: string): ClassDecorator 6 | (target: Object): void 7 | } 8 | 9 | // @ts-ignore 10 | export const Controller: ControllerDecoratorType = (...args: any[]) => { 11 | if (args.length == 0) { 12 | args[0] = '' 13 | } 14 | if (typeof args[0] === 'string') { 15 | return (controllerClass: any) => { 16 | IocContainer.pendingIocClass(controllerClass) 17 | requestMappingAssembler.addControllerRoute(controllerClass.name, args[0]) 18 | requestMappingAssembler.nextController() 19 | } 20 | } else { 21 | IocContainer.pendingIocClass(args[0]) 22 | requestMappingAssembler.addControllerRoute(args[0].name, '') 23 | requestMappingAssembler.nextController() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/custom.ts: -------------------------------------------------------------------------------- 1 | import { createParamDecorator } from './param' 2 | import { createMethodDecorator } from './method' 3 | import { createClassDecorator } from './class' 4 | import { createClassAndMethodDecorator } from './class-method' 5 | import { createPropertyDecorator } from './property' 6 | 7 | export { 8 | createMethodDecorator, 9 | createClassDecorator, 10 | createClassAndMethodDecorator, 11 | createPropertyDecorator, 12 | createParamDecorator 13 | } 14 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/error.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '../logger' 2 | import { errorHandle } from '../error' 3 | import { IocContainer } from '../ioc' 4 | 5 | interface ErrorHandlerType { 6 | (): ClassDecorator 7 | (target: Object): void 8 | } 9 | 10 | // @ts-ignore 11 | export const ErrorHandler: ErrorHandlerType = (...args) => { 12 | const instanceHandler = (target) => { 13 | if (!errorHandle.errorHandlerClass) { 14 | errorHandle.errorHandlerClass = target 15 | IocContainer.pendingIocClass(target) 16 | } else { 17 | Logger.error('Duplicated ErrorHandler') 18 | process.exit() 19 | } 20 | } 21 | 22 | if (args.length == 0) { 23 | return (target: any) => { 24 | instanceHandler(target) 25 | } 26 | } else { 27 | instanceHandler(args[0]) 28 | } 29 | } 30 | 31 | export const E = (err: any): MethodDecorator => { 32 | return (_target: any, method: string) => { 33 | errorHandle.errorMap.push({ type: err, method: method }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/index.ts: -------------------------------------------------------------------------------- 1 | export * from './controller' 2 | export * from './custom' 3 | export * from './inject' 4 | export * from './validate' 5 | export * from './middleware' 6 | export * from './rpc' 7 | export * from './build-in' 8 | export * from './ioc' 9 | export * from './error' 10 | export * from './task' 11 | export * from './serialize' 12 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/inject.ts: -------------------------------------------------------------------------------- 1 | import { IocContainer } from '../ioc' 2 | 3 | interface InjectableDecoratorType { 4 | (options?: { tags: string[] }): ClassDecorator 5 | (target: any): void 6 | } 7 | 8 | // @ts-ignore 9 | export const Injectable: InjectableDecoratorType = (...args) => { 10 | if (args.length === 0) { 11 | return (clazz: any) => { 12 | IocContainer.pendingIocClass(clazz) 13 | } 14 | } else { 15 | if (args[0].tags) { 16 | return (clazz: any) => { 17 | clazz.prototype.__$tags__ = args[0].tags 18 | IocContainer.pendingIocClass(clazz) 19 | } 20 | } else { 21 | IocContainer.pendingIocClass(args[0]) 22 | } 23 | } 24 | } 25 | 26 | export const Service = Injectable 27 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/ioc.ts: -------------------------------------------------------------------------------- 1 | interface MethodDecoratorType { 2 | (): MethodDecorator 3 | (target: Object, propertyKey: string, descriptor: PropertyDescriptor): void 4 | } 5 | 6 | // @ts-ignore 7 | export const PostConstruct: MethodDecoratorType = (...args) => { 8 | if (args.length == 0) { 9 | return (target: any, propertyKey: string, _descriptor: PropertyDescriptor) => { 10 | target.$_postConstruct = propertyKey 11 | } 12 | } else { 13 | args[0].$_postConstruct = args[1] 14 | return null 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/method.ts: -------------------------------------------------------------------------------- 1 | import { asyncLocalStorage, checkValidationError } from '../request-handler' 2 | import { requestMappingAssembler } from '../request-mapping' 3 | import { ContextInvocation as Context, OmitFirstAndSecondArg } from './utility' 4 | 5 | export interface ControllerMethodDecoratorType { 6 | (path?: string): MethodDecorator 7 | (target: Object, propertyKey: string, descriptor: PropertyDescriptor): void 8 | } 9 | 10 | export const restfulMethodDecorator = 11 | (httpMethod: string): ControllerMethodDecoratorType => 12 | //@ts-ignore 13 | (...dArgs): MethodDecorator => { 14 | if (dArgs.length <= 1) { 15 | return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { 16 | requestMappingAssembler.addMethodRoute(dArgs[0] || '', httpMethod, propertyKey, target.constructor) 17 | requestMappingAssembler.addMethodDescriptor(descriptor) 18 | } 19 | } else { 20 | requestMappingAssembler.addMethodRoute('', httpMethod, dArgs[1], dArgs[0].constructor) 21 | requestMappingAssembler.addMethodDescriptor(dArgs[2]) 22 | } 23 | } 24 | 25 | const generateMethodDecorator = 26 | (paramMethod: any, ...args: any[]) => 27 | (target: Object, propertyKey: string, descriptor: PropertyDescriptor) => { 28 | const originalFunc = descriptor.value 29 | descriptor.value = async function (...arg) { 30 | const context = asyncLocalStorage.getStore() || ({} as Context) 31 | context.invocation = { 32 | className: target.constructor.name, 33 | methodName: propertyKey, 34 | params: arg || [] 35 | } 36 | 37 | const ret = await paramMethod( 38 | context, 39 | async (mArgs) => { 40 | checkValidationError(originalFunc, context) 41 | return await originalFunc.apply(this, mArgs) 42 | }, 43 | ...args 44 | ) 45 | return ret 46 | } 47 | } 48 | 49 | type DecoratorMethodType = (ctx: Context, invokeMethod: (args: any[]) => Promise, ...args: any[]) => void 50 | 51 | export interface MethodDecoratorType { 52 | (...name: Parameters>): MethodDecorator 53 | (target: Object, propertyKey: string, descriptor: PropertyDescriptor): void 54 | } 55 | 56 | export const createMethodDecorator = 57 | (paramMethod: T): MethodDecoratorType => 58 | //@ts-ignore 59 | (...dArgs: any[]) => { 60 | if (dArgs.length === 3 && dArgs[0].constructor?.toString().startsWith('class ')) { 61 | generateMethodDecorator(paramMethod)(dArgs[0], dArgs[1], dArgs[2]) 62 | return 63 | } 64 | return generateMethodDecorator(paramMethod, ...dArgs) 65 | } 66 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/middleware.ts: -------------------------------------------------------------------------------- 1 | import { IocContainer } from '../ioc' 2 | import { middlewareAssembler } from '../middleware' 3 | 4 | export interface MiddlewareOptions { 5 | order: number 6 | } 7 | export const Middleware = (middlewareOptions: MiddlewareOptions) => (target: any) => { 8 | middlewareAssembler.add(target, middlewareOptions) 9 | IocContainer.pendingIocClass(target) 10 | } 11 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/param.ts: -------------------------------------------------------------------------------- 1 | import { Context } from '../request-handler' 2 | import { requestMappingAssembler } from '../request-mapping' 3 | import { OmitFirstAndSecondArg } from './utility' 4 | 5 | // not working any more, esbuild will change param name 6 | // const getArgName = (func, argIndex: number) => { 7 | // var args = func.toString().match(/.*?\(([^)]*)\)/)[1] 8 | // return args.split(',').map(function (arg) { 9 | // return arg 10 | // .split('=')[0] 11 | // .replace(/\/\*.*\*\//g, '') 12 | // .trim() 13 | // })[argIndex] 14 | // } 15 | 16 | // const getArgName = (target: any, method: string, argIndex: number) => { 17 | // let parameterNames: any[] = Reflect.getOwnMetadata('DeclareNames', target, method) || [] 18 | // return parameterNames[argIndex] 19 | // } 20 | 21 | // const getArgType = (target: Object, propertyKey: string, parameterIndex: number) => { 22 | // const types = Reflect.getMetadata('design:paramtypes', target, propertyKey) 23 | // return types[parameterIndex] 24 | // } 25 | 26 | // export const getArgDeclareType = (target: Object, propertyKey: string, parameterIndex: number) => { 27 | // const types = Reflect.getOwnMetadata('DeclareTypes', target, propertyKey) 28 | // if (!types) { 29 | // return undefined 30 | // } 31 | // return types[parameterIndex] 32 | // } 33 | 34 | const generateParamDecorator = 35 | (paramMethod: any, ...args: any[]) => 36 | (_target: Object, _propertyKey: string, parameterIndex: number) => { 37 | // const declareType = getArgDeclareType(target, propertyKey, parameterIndex) 38 | if (!args) { 39 | args = [] 40 | } 41 | // args.splice(0, 0, getArgName(target, propertyKey, parameterIndex)) 42 | requestMappingAssembler.addParam(paramMethod, args, undefined, parameterIndex) 43 | } 44 | 45 | type DecoratorMethodType = (ctx: Context, ...args: any[]) => any 46 | 47 | export const createParamDecorator = 48 | (paramMethod: T): ParamDecoratorType => 49 | //@ts-ignore 50 | (...dArgs: any[]) => { 51 | if (dArgs.length === 3 && dArgs[0].constructor?.toString().startsWith('class ')) { 52 | generateParamDecorator(paramMethod)(dArgs[0], dArgs[1], dArgs[2]) 53 | return 54 | } 55 | return generateParamDecorator(paramMethod, ...dArgs) 56 | } 57 | 58 | export interface ParamDecoratorType { 59 | (...name: Parameters>): ParameterDecorator 60 | (target: Object, propertyKey: string, parameterIndex: number): void 61 | } 62 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/property.ts: -------------------------------------------------------------------------------- 1 | import { IocContainer } from '../ioc' 2 | import { OmitFirstAndSecondArg } from './utility' 3 | 4 | const generatePropertyDecorator = 5 | (decoratorMethod: any, ...args: any[]) => 6 | (target: Object, propertyKey: string) => { 7 | IocContainer.pendingAssign(target, propertyKey, decoratorMethod, args) 8 | } 9 | 10 | type DecoratorMethodType = (config: any, propertyName: string, ...args: any[]) => any 11 | 12 | export interface PropertyDecoratorType { 13 | (...name: Parameters>): PropertyDecorator 14 | (target: Object, propertyKey: string): void 15 | } 16 | 17 | export const createPropertyDecorator = 18 | (paramMethod: T): PropertyDecoratorType => 19 | //@ts-ignore 20 | (...dArgs: any[]) => { 21 | if (dArgs.length === 3 && dArgs[0].constructor?.toString().startsWith('class ') && dArgs[2] === undefined) { 22 | generatePropertyDecorator(paramMethod)(dArgs[0], dArgs[1]) 23 | return undefined 24 | } 25 | return generatePropertyDecorator(paramMethod, ...dArgs) 26 | } 27 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/rpc.ts: -------------------------------------------------------------------------------- 1 | import { IocContainer } from '../ioc' 2 | import { Logger } from './../logger' 3 | import { getEnvConfig } from './../config-handler' 4 | 5 | import { rpc } from '../rpc' 6 | 7 | interface RpcProviderDecoratorType { 8 | (): ClassDecorator 9 | (target: any): void 10 | } 11 | 12 | // @ts-ignore 13 | export const RpcProvider: RpcProviderDecoratorType = (...args) => { 14 | if (args.length === 0) { 15 | return (target: any) => { 16 | rpc.addRpcClass(target) 17 | } 18 | } else { 19 | rpc.addRpcClass(args[0]) 20 | } 21 | } 22 | 23 | export const RpcClient = (source: string, targetClass?: string) => { 24 | return (target: any) => { 25 | IocContainer.pendingIocClass(target) 26 | Object.getOwnPropertyNames(target.prototype).forEach((method) => { 27 | if (typeof target.prototype[method] === 'function' && method !== 'constructor') { 28 | target.prototype[method] = async (...args) => { 29 | const config = getEnvConfig('RPC_CONFIG') 30 | if (!config) { 31 | Logger.error('RPC_CONFIG is missing in config') 32 | return 33 | } 34 | const [type, declareType] = Reflect.getMetadata('ReturnDeclareType', target.prototype, method) 35 | if (config.client[source]) { 36 | const result = await rpc.rpcRequest( 37 | config.client[source], 38 | { 39 | class: targetClass || target.name, 40 | method: method, 41 | data: args 42 | }, 43 | type, 44 | declareType, 45 | this 46 | ) 47 | return result 48 | } else { 49 | Logger.error(source + ' not found in RPC_CONFIG.client') 50 | return 51 | } 52 | } 53 | } 54 | }) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/serialize.ts: -------------------------------------------------------------------------------- 1 | export const Serialize = (serializeFunction: (value: any, obj?: any) => any): PropertyDecorator => { 2 | return (target: any, key: string) => { 3 | Reflect.defineMetadata('Serialize', serializeFunction, target, key) 4 | } 5 | } 6 | 7 | export const Ignore = Serialize(() => undefined) 8 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/task.ts: -------------------------------------------------------------------------------- 1 | import { scheduledTask } from '../scheduled-tasks' 2 | 3 | export const Scheduled = ( 4 | cronOrFixedRate: { cron: string; timeZone?: string } | { fixedRate: number } 5 | ): MethodDecorator => { 6 | return (target: any, methodName: string) => { 7 | scheduledTask.add({ class: target.constructor, methodName, cronOrFixedRate }) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/utility.ts: -------------------------------------------------------------------------------- 1 | import { Context } from '../request-handler' 2 | 3 | export type OmitFirstArg = F extends (x: any, ...args: infer P) => infer R ? (...args: P) => R : never 4 | export type OmitFirstAndSecondArg = F extends (a: any, b: any, ...args: infer P) => infer R 5 | ? (...args: P) => R 6 | : never 7 | type WithRequired = T & { [P in K]-?: T[P] } 8 | export type ContextInvocation = WithRequired 9 | -------------------------------------------------------------------------------- /@summer-js/summer/src/decorators/validate.ts: -------------------------------------------------------------------------------- 1 | const defineMetaValue = (arg, validateKey, validateValue) => { 2 | const target = arg[0] 3 | const property = arg[1] 4 | const index = arg[2] 5 | if (index === undefined) { 6 | Reflect.defineMetadata(validateKey, validateValue, target, property) 7 | } 8 | // param 9 | else if (arg.length === 3) { 10 | const paramMaxValues = Reflect.getOwnMetadata(validateKey, target, property) || [] 11 | paramMaxValues[index] = validateValue 12 | Reflect.defineMetadata(validateKey, paramMaxValues, target, property) 13 | } 14 | } 15 | 16 | export type ValidateMessage = string | ((value?: any) => string) 17 | 18 | interface ValidateDecoratorType { 19 | (validateMessage?: ValidateMessage): PropertyDecorator 20 | (target: any, propertyKey: string): void 21 | } 22 | 23 | export const Max = 24 | (max: number, validateMessage?: ValidateMessage) => 25 | (...arg) => { 26 | defineMetaValue(arg, 'max', max) 27 | if (validateMessage) { 28 | defineMetaValue(arg, 'message[max]', validateMessage) 29 | } 30 | } 31 | 32 | export const Min = 33 | (min: number, validateMessage?: ValidateMessage) => 34 | (...arg) => { 35 | defineMetaValue(arg, 'min', min) 36 | if (validateMessage) { 37 | defineMetaValue(arg, 'message[min]', validateMessage) 38 | } 39 | } 40 | 41 | export function Len(length: number, validateMessage?: ValidateMessage): PropertyDecorator 42 | export function Len(minLength: number, maxLength: number, validateMessage?: ValidateMessage): PropertyDecorator 43 | export function Len(...args) { 44 | return (...arg) => { 45 | if (args.length === 1 || (args.length === 2 && typeof args[1] !== 'number')) { 46 | defineMetaValue(arg, 'len', args[0]) 47 | if (args[1]) { 48 | defineMetaValue(arg, 'message[len]', args[1]) 49 | } 50 | } else { 51 | defineMetaValue(arg, 'lenRange', [args[0], args[1]]) 52 | if (args[2]) { 53 | defineMetaValue(arg, 'message[lenRange]', args[2]) 54 | } 55 | } 56 | } 57 | } 58 | 59 | export const MaxLen = 60 | (maxLength: number, validateMessage?: ValidateMessage) => 61 | (...arg) => { 62 | defineMetaValue(arg, 'maxLen', maxLength) 63 | if (validateMessage) { 64 | defineMetaValue(arg, 'message[maxLen]', validateMessage) 65 | } 66 | } 67 | 68 | export const MinLen = 69 | (minLength: number, validateMessage?: ValidateMessage) => 70 | (...arg) => { 71 | defineMetaValue(arg, 'minLen', minLength) 72 | if (validateMessage) { 73 | defineMetaValue(arg, 'message[minLen]', validateMessage) 74 | } 75 | } 76 | // @ts-ignore 77 | const Optional: ValidateDecoratorType = (...args) => { 78 | if (args.length === 0) { 79 | return (...arg) => defineMetaValue(arg, 'optional', true) 80 | } else { 81 | defineMetaValue(args, 'optional', true) 82 | } 83 | } 84 | ;(global as any)._Optional = Optional 85 | 86 | // @ts-ignore 87 | const NotBlank: ValidateDecoratorType = (...args) => { 88 | if (args.length === 0) { 89 | return (...arg) => defineMetaValue(arg, 'notBlank', true) 90 | } else { 91 | defineMetaValue(args, 'notBlank', true) 92 | } 93 | } 94 | ;(global as any)._NotBlank = NotBlank 95 | 96 | export const Pattern = 97 | (regExp: RegExp, validateMessage?: ValidateMessage) => 98 | (...arg) => { 99 | defineMetaValue(arg, 'pattern', regExp) 100 | if (validateMessage) { 101 | defineMetaValue(arg, 'message[pattern]', validateMessage) 102 | } 103 | } 104 | 105 | // @ts-ignore 106 | export const Email: ValidateDecoratorType = (...args) => { 107 | if (args.length <= 1) { 108 | return (...arg) => { 109 | defineMetaValue(arg, 'email', true) 110 | if (args[0]) { 111 | defineMetaValue(arg, 'message[email]', args[0]) 112 | } 113 | } 114 | } else { 115 | defineMetaValue(args, 'email', true) 116 | } 117 | } 118 | 119 | export const Validate = 120 | (validateFunction: (value: any) => boolean | string) => 121 | (...arg) => 122 | defineMetaValue(arg, 'validate', validateFunction) 123 | -------------------------------------------------------------------------------- /@summer-js/summer/src/error.ts: -------------------------------------------------------------------------------- 1 | import { getInjectable } from './ioc' 2 | import { Logger } from './logger' 3 | 4 | export class ResponseError extends Error { 5 | constructor(public statusCode: number, public body: any = '') { 6 | super() 7 | this.name = 'ResponseError' 8 | } 9 | } 10 | 11 | export class ValidationError extends Error { 12 | constructor( 13 | public statusCode: number, 14 | public body: { message: string; errors: any[] } = { message: '', errors: [] } 15 | ) { 16 | super() 17 | this.name = 'ValidationError' 18 | } 19 | } 20 | 21 | export class NotFoundError extends Error { 22 | constructor(public statusCode: number, public body: { message: string } = { message: '' }) { 23 | super() 24 | this.name = 'NotFoundError' 25 | } 26 | } 27 | 28 | export class OtherErrors extends Error {} 29 | 30 | export const errorHandle: { errorHandlerClass?: any; errorMap: { type: any; method: string }[] } = { 31 | errorHandlerClass: null, 32 | errorMap: [] 33 | } 34 | 35 | const unhandledError = (err) => { 36 | let handled = false 37 | if (errorHandle.errorHandlerClass) { 38 | const errorHandler = getInjectable(errorHandle.errorHandlerClass) 39 | if (errorHandler) { 40 | const errType = errorHandle.errorMap.find((e) => err instanceof e.type) 41 | if (errType) { 42 | errorHandler[errType.method](err) 43 | handled = true 44 | } else { 45 | const otherErrorsType = errorHandle.errorMap.find((e) => e.type === OtherErrors) 46 | if (otherErrorsType) { 47 | errorHandler[otherErrorsType.method](err) 48 | handled = true 49 | } 50 | } 51 | } 52 | } 53 | return handled 54 | } 55 | 56 | process.on('unhandledRejection', (reason) => { 57 | if (!(reason instanceof Error && unhandledError(reason))) { 58 | Logger.error('Unhandled Rejection', reason) 59 | } 60 | }) 61 | 62 | process.on('uncaughtException', (err) => { 63 | if (!(err instanceof Error && unhandledError(err))) { 64 | Logger.error('Uncaught Exception', err) 65 | } 66 | }) 67 | -------------------------------------------------------------------------------- /@summer-js/summer/src/index.ts: -------------------------------------------------------------------------------- 1 | export { summerStart, summerDestroy, waitForStart, addPlugin, pluginCollection } from './summer' 2 | export * from './decorators' 3 | export * from './utils' 4 | export { requestHandler, Context, getContext, StreamingData, traceRequest } from './request-handler' 5 | export * from './error' 6 | export { getInjectable, getService, getServicesByTags, addInjectable, getInjectablesByTags } from './ioc' 7 | export { Logger } from './logger' 8 | export { SessionConfig, Session } from './session' 9 | export { RpcConfig } from './rpc' 10 | export { ServerConfig } from './http-server' 11 | export { getConfig, getEnvConfig, EnvConfig, replaceEnvConfigValue } from './config-handler' 12 | export { handler } from './serverless' 13 | export { Cookie } from './cookie' 14 | export { File } from './validate-types' 15 | export { SummerPlugin } from './plugin' 16 | 17 | declare global { 18 | type int = number 19 | const _Int: any 20 | const _EnvConfig: any 21 | const _PropDeclareType: any 22 | const _ParamDeclareType: any 23 | const _ReturnDeclareType: any 24 | const _Optional: any 25 | const _NotBlank: any 26 | const ClassCollect: any 27 | const SUMMER_ENV: string 28 | const SUMMER_BUILD_TIMESTAMP: number 29 | const SUMMER_VERSION: string 30 | const SERVICE_NAME: string 31 | const SERVICE_VERSION: string 32 | } 33 | 34 | ;(global as any)._Int = class _Int {} 35 | ;(global as any)._PropDeclareType = (declareType: any) => (target: Object, propertyKey: string | symbol) => { 36 | Reflect.defineMetadata(propertyKey, propertyKey, target) 37 | declareType = declareType || [] 38 | declareType[2] = declareType[2] || [] 39 | Reflect.defineMetadata('DeclareType', declareType, target, propertyKey) 40 | } 41 | ;(global as any)._ParamDeclareType = 42 | (declareType: any, name: string) => (target: Object, propertyKey: string | symbol, index: number) => { 43 | let existingParameterTypes: any[] = Reflect.getOwnMetadata('DeclareTypes', target, propertyKey) || [] 44 | declareType = declareType || [] 45 | declareType[2] = declareType[2] || [] 46 | existingParameterTypes[index] = declareType 47 | Reflect.defineMetadata('DeclareTypes', existingParameterTypes, target, propertyKey) 48 | 49 | let existingParameterNames: any[] = Reflect.getOwnMetadata('DeclareNames', target, propertyKey) || [] 50 | existingParameterNames[index] = name 51 | Reflect.defineMetadata('DeclareNames', existingParameterNames, target, propertyKey) 52 | } 53 | ;(global as any)._ReturnDeclareType = (declareType: any) => (target: Object, propertyKey: string | symbol) => { 54 | declareType = declareType || [] 55 | declareType[2] = declareType[2] || [] 56 | Reflect.defineMetadata('ReturnDeclareType', declareType, target, propertyKey) 57 | } 58 | -------------------------------------------------------------------------------- /@summer-js/summer/src/logger.ts: -------------------------------------------------------------------------------- 1 | const addZero = (num: number) => (num < 10 ? '0' + num : num + '') 2 | 3 | const timePrefix = () => { 4 | const now = new Date() 5 | if (timeOffset) { 6 | now.setMinutes(now.getMinutes() + timeOffset) 7 | } 8 | const date = now.getFullYear() + '-' + addZero(now.getMonth() + 1) + '-' + addZero(now.getDate()) 9 | const time = addZero(now.getHours()) + ':' + addZero(now.getMinutes()) + ':' + addZero(now.getSeconds()) 10 | return `[${date} ${time}]` 11 | } 12 | 13 | type LogType = ('Log' | 'Warn' | 'Error' | 'Info' | 'Debug')[] 14 | const isTerminal = process.env.TERM !== undefined 15 | const print = (color: string, type: string, method: string, message?: any, ...optionalParams: any[]) => { 16 | if (!isTerminal) { 17 | console[method](timePrefix(), type, message, ...optionalParams) 18 | } else { 19 | console[method](`\x1b[${color}${timePrefix()}`, type, message, ...optionalParams, '\x1b[0m') 20 | } 21 | } 22 | 23 | let timeOffset = 0 24 | let sLogType: LogType = ['Log', 'Warn', 'Error', 'Info', 'Debug'] 25 | export const Logger = { 26 | get enableTypes() { 27 | return sLogType 28 | }, 29 | set enableTypes(enable: LogType) { 30 | sLogType = enable 31 | }, 32 | setTimeZone(timeZone: number) { 33 | timeOffset = timeZone * 60 + new Date().getTimezoneOffset() 34 | }, 35 | info(message?: any, ...optionalParams: any[]) { 36 | if (sLogType.includes('Info')) { 37 | print('32m', '[INFO]', 'info', message, ...optionalParams) 38 | } 39 | }, 40 | error(message?: any, ...optionalParams: any[]) { 41 | if (sLogType.includes('Error')) { 42 | print('31m', '[ERROR]', 'error', message, ...optionalParams) 43 | } 44 | }, 45 | warn(message?: any, ...optionalParams: any[]) { 46 | if (sLogType.includes('Warn')) { 47 | print('33m', '[WARN]', 'warn', message, ...optionalParams) 48 | } 49 | }, 50 | log(message?: any, ...optionalParams: any[]) { 51 | if (sLogType.includes('Log')) { 52 | console.log(timePrefix(), '[LOG]', message, ...optionalParams) 53 | } 54 | }, 55 | debug(message?: any, ...optionalParams: any[]) { 56 | if (sLogType.includes('Debug')) { 57 | print('36m', '[DEBUG]', 'debug', message, ...optionalParams) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /@summer-js/summer/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { MiddlewareOptions } from './decorators/middleware' 2 | import { getInjectable } from './ioc' 3 | 4 | export const middlewares: any[] = [] 5 | export const middlewareAssembler = { 6 | middlewareClasses: [], 7 | add(middleware, options: MiddlewareOptions) { 8 | middleware.options = options 9 | this.middlewareClasses.push(middleware) 10 | this.middlewareClasses.sort((a, b) => a.options.order - b.options.order) 11 | }, 12 | init() { 13 | this.middlewareClasses.forEach((clz) => { 14 | middlewares.push(getInjectable(clz)) 15 | }) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /@summer-js/summer/src/plugin.ts: -------------------------------------------------------------------------------- 1 | import { ClassDeclaration } from 'ts-morph' 2 | 3 | export class SummerPlugin { 4 | configKey: string 5 | //@ts-ignore 6 | compile(clazz: ClassDeclaration, modifyActions: (() => void)[]) {} 7 | //@ts-ignore 8 | postCompile(isFirstCompile?: boolean) {} 9 | //@ts-ignore 10 | init(config: any) {} 11 | //@ts-ignore 12 | postInit(config: any) {} 13 | destroy() {} 14 | 15 | #classCollection: any[] = [] 16 | collectClass(clazz: ClassDeclaration, collectName: string, modifyActions: (() => void)[]) { 17 | modifyActions.push(() => { 18 | clazz.getDecorators().forEach((d) => { 19 | if (d.getName() === 'ClassCollect') { 20 | d.remove() 21 | } 22 | }) 23 | clazz.addDecorator({ name: 'ClassCollect', arguments: ["'" + collectName + "'"] }) 24 | clazz.getChildren()[0].replaceWithText( 25 | clazz 26 | .getChildren()[0] 27 | .getText() 28 | .replace(/\n[^\n]*@ClassCollect/g, ' @ClassCollect') 29 | ) 30 | }) 31 | this.#classCollection.push(clazz) 32 | } 33 | getClassCollection() { 34 | return this.#classCollection 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /@summer-js/summer/src/request-mapping.ts: -------------------------------------------------------------------------------- 1 | import { pathToRegexp } from 'path-to-regexp' 2 | import { Class } from './ioc' 3 | import { Logger } from './logger' 4 | 5 | export const requestMapping = {} 6 | export const requestMappingAssembler = { 7 | params: [], 8 | controllerMethodDescriptors: [], 9 | controllerRequestMapping: {}, 10 | nextController() { 11 | this.controllerMethodDescriptors = [] 12 | this.params = [] 13 | }, 14 | addParam(paramMethod: (ctx: any) => any, paramValues: any[], declareType: any, index: number) { 15 | this.params[index] = { 16 | paramValues, 17 | paramMethod, 18 | declareType, 19 | index 20 | } 21 | }, 22 | addMethodRoute(path: string, httpMethod: string, callMethod: string, controllerClass: Class) { 23 | if (this.controllerRequestMapping[path] && this.controllerRequestMapping[path][httpMethod]) { 24 | Logger.error( 25 | `Duplicated request routes: ${httpMethod} ${path || '/'} in ${controllerClass.name}.${callMethod}() and ${ 26 | this.controllerRequestMapping[path][httpMethod]['controllerName'] 27 | }.${this.controllerRequestMapping[path][httpMethod]['callMethod']}()` 28 | ) 29 | process.exit() 30 | } 31 | this.controllerRequestMapping[path] = { 32 | ...this.controllerRequestMapping[path], 33 | [httpMethod]: { 34 | callMethod, 35 | params: this.params, 36 | controllerClass, 37 | controllerName: controllerClass.name 38 | } 39 | } 40 | this.params = [] 41 | }, 42 | addMethodDescriptor(descriptor: PropertyDescriptor) { 43 | this.controllerMethodDescriptors.push(descriptor) 44 | }, 45 | addControllerRoute(controllerName: string, controllerPath: string) { 46 | Object.keys(this.controllerRequestMapping).forEach((path) => { 47 | const fullPath = path.startsWith('^') ? path.replace(/\^/g, '') : controllerPath + path 48 | const { regexp, keys } = pathToRegexp(fullPath) 49 | 50 | let pathMappingData = requestMapping[fullPath] || {} 51 | ;['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'REQUEST'].forEach((reqMethod) => { 52 | if (pathMappingData[reqMethod] && this.controllerRequestMapping[path][reqMethod]) { 53 | Logger.error( 54 | `Duplicated request routes: ${reqMethod} ${fullPath || '/'} in ${JSON.stringify([ 55 | pathMappingData[reqMethod].controllerName, 56 | controllerName 57 | ])}` 58 | ) 59 | process.exit() 60 | } 61 | }) 62 | 63 | requestMapping[fullPath] = { 64 | ...pathMappingData, 65 | ...this.controllerRequestMapping[path], 66 | pathRegExp: regexp, 67 | pathKeys: keys.map((key) => key.name) 68 | } 69 | }) 70 | this.controllerRequestMapping = {} 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /@summer-js/summer/src/rpc.ts: -------------------------------------------------------------------------------- 1 | import https from 'https' 2 | import http from 'http' 3 | import { URL } from 'url' 4 | 5 | import { validateAndConvertType } from './validate-types' 6 | import { IocContainer } from './ioc' 7 | import { Logger } from './logger' 8 | 9 | interface RpcServerConfig { 10 | url: string 11 | accessKey: string 12 | } 13 | 14 | const httpPost = (url: string, body, headers) => { 15 | const urlObject = new URL(url) 16 | return new Promise<{ statusCode: number; body: string }>((resolve, reject) => { 17 | const client = urlObject.protocol === 'https' ? https : http 18 | const req = client.request( 19 | { 20 | method: 'POST', 21 | hostname: urlObject.hostname, 22 | path: urlObject.pathname, 23 | port: urlObject.port, 24 | headers 25 | }, 26 | 27 | (res) => { 28 | let chunks = '' 29 | res.on('data', (data) => (chunks += data)) 30 | res.on('end', () => { 31 | const resBody = JSON.parse(chunks) 32 | resolve({ statusCode: res.statusCode!, body: resBody }) 33 | }) 34 | } 35 | ) 36 | req.on('error', reject) 37 | req.write(JSON.stringify(body)) 38 | req.end() 39 | }) 40 | } 41 | 42 | export const rpc = { 43 | rpcClass: [], 44 | rpcInstance: {}, 45 | addRpcClass(clazz: any) { 46 | this.rpcClass.push(clazz) 47 | IocContainer.pendingIocClass(clazz) 48 | }, 49 | resolveRpc() { 50 | this.rpcClass.forEach((rc) => { 51 | this.addRpcInstance(new rc()) 52 | }) 53 | }, 54 | addRpcInstance(instance: object) { 55 | const className = instance.constructor.name 56 | if (this.rpcInstance[className]) { 57 | Logger.error('Duplicated ClassName: ' + className) 58 | process.exit() 59 | } 60 | this.rpcInstance[className] = instance 61 | }, 62 | async call(className: string, method: string, args: any[]) { 63 | if (this.rpcInstance[className]) { 64 | if (this.rpcInstance[className][method]) { 65 | return await this.rpcInstance[className][method](...args) 66 | } else { 67 | throw new Error(className + '.' + method + ' not exist') 68 | } 69 | } else { 70 | throw new Error('No PRCProvider named: ' + className) 71 | } 72 | }, 73 | async rpcRequest(rpcConfig: RpcServerConfig, postData: any, _type, declareType, instance) { 74 | let responseBody = '' 75 | try { 76 | const res = await httpPost(rpcConfig.url, postData, { 'summer-rpc-access-key': rpcConfig.accessKey }) 77 | if (res.statusCode === 404) { 78 | throw new Error('RPC Fail: Remote url not found ' + rpcConfig.url) 79 | } 80 | responseBody = res.body 81 | } catch (e) { 82 | Logger.error('RPC Fail:') 83 | throw e 84 | } 85 | const allErrors = [] 86 | responseBody = validateAndConvertType(declareType, '', responseBody, allErrors, '', -1, instance) 87 | if (allErrors.length) { 88 | throw new Error(JSON.stringify(allErrors)) 89 | } 90 | return responseBody 91 | } 92 | } 93 | 94 | export type RpcConfig = { 95 | provider?: { accessKey: string } 96 | client?: Record 97 | } 98 | -------------------------------------------------------------------------------- /@summer-js/summer/src/scheduled-tasks.ts: -------------------------------------------------------------------------------- 1 | const cron = require('node-cron') 2 | import { getInjectable } from './ioc' 3 | import { Logger } from './logger' 4 | 5 | interface ScheduledTask { 6 | class: any 7 | methodName: string 8 | cronOrFixedRate: { cron: string; timeZone?: string } | { fixedRate: number } 9 | } 10 | 11 | export const scheduledTask = { 12 | scheduledTasks: [], 13 | cornTasks: [], 14 | fixedRateTasks: [], 15 | add(scheduledTask: ScheduledTask) { 16 | this.scheduledTasks.push(scheduledTask) 17 | }, 18 | start() { 19 | this.scheduledTasks.forEach((st: ScheduledTask) => { 20 | const task = () => { 21 | const inc = getInjectable(st.class) 22 | if (inc) { 23 | inc[st.methodName]() 24 | } else { 25 | Logger.error( 26 | 'Unable to run scheduled task. ' + 27 | st.class.name + 28 | ' is not an injectable class, please add @Injectable/@Service to fixed.' 29 | ) 30 | } 31 | } 32 | if (st.cronOrFixedRate['cron']) { 33 | const scheduledTask = cron.schedule(st.cronOrFixedRate['cron'], task, { 34 | scheduled: true, 35 | timezone: st.cronOrFixedRate['timeZone'] 36 | }) 37 | this.cornTasks.push(scheduledTask) 38 | } else if (st.cronOrFixedRate['fixedRate']) { 39 | this.fixedRateTasks.push(setInterval(task, st.cronOrFixedRate['fixedRate'])) 40 | } 41 | }) 42 | }, 43 | stop() { 44 | this.cornTasks.forEach((cornTask) => { 45 | cornTask.stop() 46 | }) 47 | this.fixedRateTasks.forEach((fixedRateTask) => { 48 | clearInterval(fixedRateTask) 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /@summer-js/summer/src/static-server.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import mine = require('mime') 3 | import path = require('path') 4 | 5 | import { getEnvConfig } from './config-handler' 6 | import { ServerConfig } from './http-server' 7 | 8 | interface StaticResult { 9 | code: number 10 | headers: any 11 | filePath?: string 12 | } 13 | 14 | export const handleStaticRequest = (requestPath: string): StaticResult | null => { 15 | const serverConfig: ServerConfig = getEnvConfig('SERVER_CONFIG') 16 | if (serverConfig.static) { 17 | for (const staticConfig of serverConfig.static) { 18 | let { requestPath: requestPathRoot, destPath: destPathRoot, indexFiles } = staticConfig 19 | requestPathRoot = requestPathRoot.replace(/\/$/, '') 20 | let requestFile = '.' + requestPath 21 | const targetPath = destPathRoot 22 | if (!path.resolve(requestFile).startsWith(path.resolve(requestPathRoot.replace(/^\//, '')))) { 23 | continue 24 | } 25 | 26 | if (requestPath.startsWith(requestPathRoot + '/') || requestPath === requestPathRoot) { 27 | requestFile = requestFile.replace(requestPathRoot.replace(/^\//, ''), targetPath) 28 | if (targetPath.startsWith('/')) { 29 | requestFile = requestFile.replace('./', '') 30 | } 31 | 32 | if (fs.existsSync(requestFile) && !fs.lstatSync(requestFile).isDirectory()) { 33 | } else if (fs.existsSync(requestFile) && fs.lstatSync(requestFile).isDirectory()) { 34 | if (!requestFile.endsWith('/')) { 35 | return { 36 | code: 301, 37 | headers: { Location: (serverConfig.basePath || '') + requestPath + '/', 'Cache-Control': 'no-store' } 38 | } 39 | } else if (indexFiles) { 40 | let foundFile = false 41 | for (const file of indexFiles) { 42 | if (fs.existsSync(requestFile + file)) { 43 | requestFile = requestFile + file 44 | foundFile = true 45 | break 46 | } 47 | } 48 | if (!foundFile) { 49 | requestFile = '' 50 | } 51 | } 52 | } else { 53 | requestFile = '' 54 | } 55 | 56 | // spa 57 | if (staticConfig.spa && !requestFile && path.extname(requestPath) === '') { 58 | let foundFile = false 59 | for (const file of indexFiles || []) { 60 | if (fs.existsSync(targetPath + '/' + file)) { 61 | requestFile = targetPath + '/' + file 62 | foundFile = true 63 | break 64 | } 65 | } 66 | if (!foundFile) { 67 | requestFile = '' 68 | } 69 | } 70 | 71 | if (requestFile) { 72 | const mineType = mine.getType(path.extname(requestFile).replace('.', '')) 73 | const headers = { 'Cache-Control': 'max-age=3600' } 74 | if (mineType) { 75 | headers['Content-Type'] = mineType 76 | } 77 | return { code: 200, headers, filePath: requestFile } 78 | } else { 79 | return { code: 404, headers: {} } 80 | } 81 | } 82 | } 83 | } 84 | return null 85 | } 86 | -------------------------------------------------------------------------------- /@summer-js/summer/src/utils.ts: -------------------------------------------------------------------------------- 1 | export const fillData = (instance: T, fillData: Partial & Omit, keyof T>) => { 2 | for (const k in fillData) { 3 | let propType = Reflect.getMetadata('DeclareType', instance as any, k) 4 | if (propType) { 5 | instance[k] = fillData[k] 6 | } 7 | } 8 | } 9 | 10 | interface Type extends Function { 11 | new (...args: any[]): T 12 | } 13 | 14 | export const convertData = (data: Partial & Omit, keyof T>, clazz: Type): T => { 15 | const instance = new clazz() 16 | for (const k in data) { 17 | let propType = Reflect.getMetadata('DeclareType', instance as any, k) 18 | if (propType) { 19 | instance[k] = data[k] 20 | } 21 | } 22 | return instance 23 | } 24 | 25 | export const serialize = (obj: T, declareType: any[] = []): T => { 26 | let [d0, , d2] = declareType || [] 27 | if (typeof d0 === 'function' && d0.name === '') { 28 | d0 = d0() 29 | } 30 | 31 | if (typeof obj !== 'object') { 32 | if (typeof d0 === 'object' && !Array.isArray(d0)) { 33 | if (d0[obj] && typeof obj === 'number') { 34 | obj = d0[obj] 35 | } else if (typeof d0[obj] !== 'number') { 36 | for (const enumKey in d0) { 37 | if (d0[enumKey] === obj) { 38 | obj = enumKey as any 39 | } 40 | } 41 | } 42 | } 43 | } else if (Array.isArray(obj)) { 44 | obj = (obj || []).map((item) => serialize(item, [d0, undefined, d2])) as any 45 | } else { 46 | const t = { ...obj } 47 | for (const key in obj) { 48 | let declareType = 49 | Reflect.getMetadata('DeclareType', obj, key) || 50 | (d0 ? Reflect.getMetadata('DeclareType', d0.prototype, key) : []) || 51 | [] 52 | if (typeof declareType[0] === 'number') { 53 | if (d2) { 54 | const d1Type = declareType[1] 55 | declareType = d2[declareType[0]] || [] 56 | if (d1Type) { 57 | declareType[1] = d1Type 58 | } 59 | } else { 60 | declareType = [] 61 | } 62 | } 63 | if (declareType[2]) { 64 | declareType[2].forEach((d, inx) => { 65 | if (typeof d[0] === 'number') { 66 | declareType[2][inx] = d2[d[0]] 67 | } 68 | }) 69 | } 70 | const serializeFunc = Reflect.getMetadata('Serialize', obj, key) 71 | obj[key] = serializeFunc ? serializeFunc(obj[key], t) : serialize(obj[key], declareType) 72 | } 73 | } 74 | 75 | return obj 76 | } 77 | -------------------------------------------------------------------------------- /@summer-js/summer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns": true, 5 | "noUnusedParameters": true, 6 | "noUnusedLocals": true, 7 | "strictNullChecks": true, 8 | "types": ["node"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./lib", 14 | "moduleResolution": "Node", 15 | "module": "commonjs", 16 | "declaration": true 17 | }, 18 | "exclude": ["**/*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/swagger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@summer-js/swagger", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.js", 7 | "files": [ 8 | "lib", 9 | "index.html" 10 | ], 11 | "dependencies": { 12 | "swagger-ui-dist": "^5.1.3" 13 | }, 14 | "scripts": { 15 | "build": "tsc", 16 | "pre-release": "npm publish --tag rc --access public", 17 | "release": "npm publish --access public" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/calidan-x/summer.git" 22 | }, 23 | "keywords": [ 24 | "node", 25 | "nodejs", 26 | "backend", 27 | "api" 28 | ], 29 | "author": "", 30 | "license": "ISC", 31 | "bugs": { 32 | "url": "https://github.com/calidan-x/summer/issues" 33 | }, 34 | "homepage": "https://summerjs.dev" 35 | } -------------------------------------------------------------------------------- /@summer-js/swagger/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns":true, 5 | "noUnusedParameters":true, 6 | "noUnusedLocals":true, 7 | "strictNullChecks": true, 8 | "types": ["node"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./lib", 14 | "moduleResolution": "Node", 15 | "module": "commonjs", 16 | "declaration": true 17 | }, 18 | "exclude": ["**/*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/test/jest-preset.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | setupFilesAfterEnv: ['@summer-js/test/setup-jest.js'] 5 | } 6 | 7 | module.exports = config 8 | -------------------------------------------------------------------------------- /@summer-js/test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@summer-js/test", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "module": "lib/index.js", 7 | "files": [ 8 | "lib", 9 | "jest-preset.js", 10 | "setup-jest.js" 11 | ], 12 | "dependencies": { 13 | "@types/jest": "^29.5.13", 14 | "ts-jest": "^29.2.5", 15 | "jest": "^29.7.0" 16 | }, 17 | "scripts": { 18 | "build": "tsc", 19 | "pre-release": "npm publish --tag rc --access public", 20 | "release": "npm publish --access public" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/calidan-x/summer.git" 25 | }, 26 | "keywords": [ 27 | "node", 28 | "nodejs", 29 | "backend", 30 | "api" 31 | ], 32 | "author": "", 33 | "license": "ISC", 34 | "bugs": { 35 | "url": "https://github.com/calidan-x/summer/issues" 36 | }, 37 | "homepage": "https://summerjs.dev" 38 | } -------------------------------------------------------------------------------- /@summer-js/test/setup-jest.js: -------------------------------------------------------------------------------- 1 | require('./lib/index.js') 2 | -------------------------------------------------------------------------------- /@summer-js/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns":true, 5 | "noUnusedParameters":true, 6 | "noUnusedLocals":true, 7 | "strictNullChecks": true, 8 | "types": ["node","jest"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./lib", 14 | "moduleResolution": "Node", 15 | "module": "commonjs", 16 | "declaration": true 17 | }, 18 | "exclude": ["**/*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /@summer-js/typeorm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@summer-js/typeorm", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "files": [ 7 | "lib" 8 | ], 9 | "scripts": { 10 | "build": "tsc", 11 | "pre-release": "npm publish --tag rc --access public", 12 | "release": "npm publish --access public" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/calidan-x/summer.git" 17 | }, 18 | "keywords": [ 19 | "node", 20 | "nodejs", 21 | "backend", 22 | "api" 23 | ], 24 | "author": "", 25 | "license": "ISC", 26 | "bugs": { 27 | "url": "https://github.com/calidan-x/summer/issues" 28 | }, 29 | "homepage": "https://summerjs.dev", 30 | "dependencies": { 31 | "typeorm": "^0.3.20" 32 | } 33 | } -------------------------------------------------------------------------------- /@summer-js/typeorm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns":true, 5 | "noUnusedParameters":true, 6 | "noUnusedLocals":true, 7 | "strictNullChecks": true, 8 | "types": ["node"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./lib", 14 | "moduleResolution": "Node", 15 | "module": "commonjs", 16 | "declaration": true 17 | }, 18 | "exclude": ["**/*.d.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | ## Contributing Guide 3 | 4 | Hi! We are really excited that you are interested in contributing to summer. Before submitting your contribution, please make sure to take a moment and read through the following guide: 5 | 6 | ### Ready to start 7 | 8 | We welcome everyone to join the construction of the project. 9 | As a pre requirement, you need have a preliminary understanding of `NodeJS`, if you don't know `NodeJS` 10 | this is a good [learn document for NodeJS](https://nodejs.org/en/docs). 11 | And you can refer to [GitHub's help documentation](https://help.github.com/en/github/using-git) if you don't know the basic operation of Git 12 | 13 | 14 | This project maintains monorepo by npm v8 workspace 15 | 16 | To develop and test the core packages: 17 | 18 | 1. [Fork this repository](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) to your own account and then clone it. 19 | 2. Create a new branch for your changes: `git checkout -b {BRANCH_NAME}`. 20 | 3. Run `npm i -ws` in the root folder to install all dependencies. 21 | 4. cd `@summer-js/summer-test/` and run `npm run serve` to start 22 | 23 | **Create Feature** 24 | 25 | 1. Get into `@summer-js/summer`. 26 | 2. Coding... 27 | 3. Running `npm run build`. 28 | 4. Get into `@summer-js/summer-test/`. 29 | 5. Coding... 30 | 6. Running `npm run serve`. 31 | 32 | **Create testcase** 33 | 34 | 1. If you are creating a new Characteristics, the testcase is required. 35 | 2. If you only fix some bug, please note update test case. 36 | 3. Please into `@summer-js/summer-test/` & Running `npm run test` before submit. 37 | 38 | ### Q & A 39 | 40 | > How can I update remote origin ? 41 | 42 | - refer to [here](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes). 43 | 44 | > How to choose the target branch of PR ? 45 | 46 | - Make sure PRs are make sure to `dev` branch. 47 | 48 | 49 | ### Get stuck 50 | 51 | - Create new issue to tell us: [create issue](https://github.com/calidan-x/summer/issues). -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 @summer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/summer-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calidan-x/summer/9e19377aca2b348b1c0fdb2d73f77ff9cf77c5df/assets/summer-logo.png -------------------------------------------------------------------------------- /create-summer/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // @ts-check 4 | const fs = require('fs') 5 | const path = require('path') 6 | const prompts = require('prompts') 7 | 8 | const copyRecursiveSync = function (src, dest) { 9 | const exists = fs.existsSync(src) 10 | const isDirectory = exists && fs.statSync(src).isDirectory() 11 | if (isDirectory) { 12 | fs.mkdirSync(dest) 13 | fs.readdirSync(src).forEach((childItemName) => { 14 | copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName)) 15 | }) 16 | } else { 17 | fs.copyFileSync(src, dest) 18 | } 19 | } 20 | 21 | ;(async () => { 22 | let response = await prompts({ 23 | type: 'text', 24 | name: 'value', 25 | message: 'Project Name:', 26 | validate: (value) => (!value ? 'Please enter a name' : true) 27 | }) 28 | 29 | const projectName = response.value 30 | if (!projectName) { 31 | return 32 | } 33 | 34 | response = await prompts({ 35 | type: 'select', 36 | name: 'value', 37 | message: 'Pick a template', 38 | choices: [ 39 | { title: 'Empty Project', value: 'empty' }, 40 | { 41 | title: 'Rest API Project', 42 | description: 'A movie rest api project with TypeORM and Swagger', 43 | value: 'movie' 44 | } 45 | ], 46 | initial: 0 47 | }) 48 | const templateName = response.value 49 | 50 | if (fs.existsSync(projectName)) { 51 | console.log(projectName + ' exists, exit') 52 | return 53 | } 54 | 55 | copyRecursiveSync(path.join(__dirname, `templates/${templateName}`), projectName) 56 | 57 | fs.writeFileSync(projectName + '/.gitignore', ['.DS_Store', 'node_modules', 'build', 'compile'].join('\n')) 58 | const packageJson = JSON.parse(fs.readFileSync(projectName + '/package.json', { encoding: 'utf8' })) 59 | packageJson.name = projectName 60 | fs.writeFileSync(projectName + '/package.json', JSON.stringify(packageJson, null, 4)) 61 | 62 | console.log('Project Created! Now run:') 63 | console.log('') 64 | console.log(`$ cd ${projectName}`) 65 | console.log(`$ npm install`) 66 | console.log(`$ npm run serve`) 67 | console.log('') 68 | })() 69 | -------------------------------------------------------------------------------- /create-summer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-summer", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "main": "index.js", 6 | "bin": { 7 | "create-summer": "index.js" 8 | }, 9 | "scripts": { 10 | "release": "npm publish --access public" 11 | }, 12 | "dependencies": { 13 | "prompts": "^2.4.2" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/calidan-x/summer.git" 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/calidan-x/summer/issues" 23 | }, 24 | "homepage": "https://github.com/calidan-x/summer#readme" 25 | } -------------------------------------------------------------------------------- /create-summer/templates/empty/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSameLine": false, 4 | "bracketSpacing": true, 5 | "embeddedLanguageFormatting": "auto", 6 | "insertPragma": false, 7 | "printWidth": 120, 8 | "proseWrap": "preserve", 9 | "quoteProps": "as-needed", 10 | "requirePragma": false, 11 | "semi": false, 12 | "singleQuote": true, 13 | "tabWidth": 2, 14 | "trailingComma": "none", 15 | "useTabs": false, 16 | "vueIndentScriptAndStyle": false 17 | } 18 | -------------------------------------------------------------------------------- /create-summer/templates/empty/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@summer-js/test', 3 | testPathIgnorePatterns: ['/src/'] 4 | } 5 | -------------------------------------------------------------------------------- /create-summer/templates/empty/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "summer-project", 3 | "version": "1.0.0", 4 | "description": "", 5 | "keywords": [], 6 | "scripts": { 7 | "serve": "summer serve --env local", 8 | "test": "summer test --env test -- --maxWorkers 5", 9 | "build": "summer build --env prod", 10 | "start": "node --enable-source-maps ./build/index.js" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@summer-js/summer": "0.1.52-rc.2", 16 | "@summer-js/test": "0.1.52-rc.2", 17 | "@summer-js/cli": "0.1.52-rc.2" 18 | } 19 | } -------------------------------------------------------------------------------- /create-summer/templates/empty/src/config/default.config.ts: -------------------------------------------------------------------------------- 1 | import { ServerConfig } from '@summer-js/summer' 2 | 3 | export const SERVER_CONFIG: ServerConfig = { 4 | port: 8801 5 | } 6 | -------------------------------------------------------------------------------- /create-summer/templates/empty/src/controller/AppController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@summer-js/summer' 2 | import { HiService } from '@/service' 3 | 4 | @Controller 5 | export class AppController { 6 | hiService: HiService 7 | 8 | @Get 9 | hello() { 10 | return 'Hello Summer!' 11 | } 12 | 13 | @Get('/hi') 14 | hi() { 15 | return this.hiService.sayHi() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /create-summer/templates/empty/src/index.ts: -------------------------------------------------------------------------------- 1 | import { summerStart, handler } from '@summer-js/summer' 2 | export { handler } 3 | 4 | summerStart({ 5 | async before(_config) {}, 6 | async after(_config) {} 7 | }) 8 | -------------------------------------------------------------------------------- /create-summer/templates/empty/src/service/HiService.ts: -------------------------------------------------------------------------------- 1 | import { Service } from '@summer-js/summer' 2 | 3 | @Service 4 | export class HiService { 5 | sayHi() { 6 | return 'Hi Summer!' 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /create-summer/templates/empty/src/service/index.ts: -------------------------------------------------------------------------------- 1 | export * from './HiService' 2 | -------------------------------------------------------------------------------- /create-summer/templates/empty/src/test/HelloController.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | 3 | describe('Test HelloController', () => { 4 | test('should response hello', async () => { 5 | const response = await request.get('/') 6 | expect(response.body).toBe('Hello Summer!') 7 | }) 8 | 9 | test('should response hi', async () => { 10 | const response = await request.get('/hi') 11 | expect(response.body).toBe('Hi Summer!') 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /create-summer/templates/empty/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns": true, 5 | "noUnusedParameters": true, 6 | "noUnusedLocals": true, 7 | "strictNullChecks": true, 8 | "types": ["node", "jest"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./compile", 14 | "allowJs": true, 15 | "moduleResolution": "Node", 16 | "module": "commonjs", 17 | "paths": { 18 | "@/*": ["./src/*"] 19 | } 20 | }, 21 | "include": ["./src/**/*"] 22 | } 23 | -------------------------------------------------------------------------------- /create-summer/templates/movie/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | build 4 | compile -------------------------------------------------------------------------------- /create-summer/templates/movie/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSameLine": false, 4 | "bracketSpacing": true, 5 | "embeddedLanguageFormatting": "auto", 6 | "insertPragma": false, 7 | "printWidth": 120, 8 | "proseWrap": "preserve", 9 | "quoteProps": "as-needed", 10 | "requirePragma": false, 11 | "semi": false, 12 | "singleQuote": true, 13 | "tabWidth": 2, 14 | "trailingComma": "none", 15 | "useTabs": false, 16 | "vueIndentScriptAndStyle": false 17 | } 18 | -------------------------------------------------------------------------------- /create-summer/templates/movie/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@summer-js/test', 3 | testPathIgnorePatterns: ['/src/'] 4 | } 5 | -------------------------------------------------------------------------------- /create-summer/templates/movie/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "summer-movie", 3 | "version": "1.0.0", 4 | "description": "", 5 | "keywords": [], 6 | "scripts": { 7 | "serve": "summer serve --env local", 8 | "test": "summer test --env test -- --maxWorkers 5", 9 | "build": "summer build --env prod", 10 | "start": "node --enable-source-maps ./build/index.js" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@summer-js/cli": "0.1.52-rc.2", 16 | "@summer-js/summer": "0.1.52-rc.2", 17 | "@summer-js/swagger": "0.1.52-rc.2", 18 | "@summer-js/test": "0.1.52-rc.2", 19 | "@summer-js/typeorm": "0.1.52-rc.2", 20 | "mysql2": "^3.11.3" 21 | } 22 | } -------------------------------------------------------------------------------- /create-summer/templates/movie/src/config/default.config.ts: -------------------------------------------------------------------------------- 1 | import { ServerConfig } from '@summer-js/summer' 2 | import { TypeORMConfig } from '@summer-js/typeorm' 3 | import { SwaggerConfig } from '@summer-js/swagger' 4 | 5 | export const SERVER_CONFIG: ServerConfig = { 6 | port: 8801 7 | } 8 | 9 | export const TYPEORM_CONFIG: TypeORMConfig = { 10 | MOVIE_DB: { 11 | type: 'mysql', 12 | host: 'localhost', 13 | database: 'movie-db', 14 | username: 'root', 15 | password: 'root' 16 | } 17 | } 18 | 19 | export const SWAGGER_CONFIG: SwaggerConfig = { 20 | docPath: '/swagger', 21 | info: { title: 'Movie', version: '1.0.0' }, 22 | readTypeORMComment: true, 23 | securitySchemes: { 24 | AppAuth: { 25 | type: 'apiKey', 26 | in: 'header', 27 | name: 'Authorization' 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/controller/AppController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@summer-js/summer' 2 | 3 | @Controller 4 | export class AppController { 5 | @Get 6 | hello() { 7 | return 'Hello Summer!' 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/controller/MovieController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Post, Put, Delete, PathParam, Body, Query } from '@summer-js/summer' 2 | import { ApiDoc, ApiDocGroup } from '@summer-js/swagger' 3 | 4 | import { Paging } from '@/dto/Paging' 5 | import { MovieRequest } from '@/dto/request/movie' 6 | import { ResError } from '@/error' 7 | import { MovieService } from '@/service' 8 | 9 | @Controller('/v1/movies') 10 | @ApiDocGroup('Movie APIs') 11 | export class MovieController { 12 | movieService: MovieService 13 | 14 | @Get 15 | @ApiDoc('fetch movie list') 16 | async list(@Query pageNumber: int, @Query pageSize: int) { 17 | const { movies, total } = await this.movieService.getMovies(pageNumber, pageSize) 18 | return new Paging({ data: movies, total, pageNumber, pageSize }) 19 | } 20 | 21 | @Get('/:id') 22 | @ApiDoc('get a movie detail', { 23 | errors: [ResError.MOVIE_NOT_FOUND, ResError.FETCH_MOVIE_LIST_FAIL] 24 | }) 25 | async detail(@PathParam id: int) { 26 | return await this.movieService.getMovie(id) 27 | } 28 | 29 | @Post 30 | @ApiDoc('add a new movie', { security: [{ AppAuth: [] }] }) 31 | async add(@Body req: MovieRequest) { 32 | return await this.movieService.addMovie(req) 33 | } 34 | 35 | @Put('/:id') 36 | @ApiDoc('update a movie') 37 | async update(@PathParam id: int, @Body req: MovieRequest) { 38 | return await this.movieService.updateMovie(id, req) 39 | } 40 | 41 | @Delete('/:id') 42 | @ApiDoc('delete a movie', { deprecated: true }) 43 | async delete(@PathParam id: int) { 44 | await this.movieService.deleteMovie(id) 45 | } 46 | 47 | @Delete('^/v2/movies/:id') 48 | @ApiDoc('delete a movie') 49 | async deleteV2(@PathParam id: int) { 50 | await this.movieService.deleteMovie(id) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/dto/Paging.ts: -------------------------------------------------------------------------------- 1 | export class Paging { 2 | constructor(props: { data: T[]; pageNumber: number; pageSize: number; total: number }) { 3 | Object.assign(this, props) 4 | } 5 | data: T[] 6 | pageNumber: number 7 | pageSize: number 8 | total: number 9 | } 10 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/dto/request/index.ts: -------------------------------------------------------------------------------- 1 | export * from './movie' 2 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/dto/request/movie.ts: -------------------------------------------------------------------------------- 1 | export class MovieRequest { 2 | name: string 3 | year: string 4 | } 5 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/entity/Movie.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' 2 | 3 | @Entity() 4 | export class Movie { 5 | @PrimaryGeneratedColumn() 6 | id: number 7 | 8 | @Column({ comment: 'Movie Name' }) 9 | name: string 10 | 11 | @Column({ comment: 'Movie Year' }) 12 | year: string 13 | } 14 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/entity/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Movie' 2 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/error/index.ts: -------------------------------------------------------------------------------- 1 | import { ResponseError } from '@summer-js/summer' 2 | 3 | const E = (statusCode: number, message: string) => { 4 | return new ResponseError(statusCode, { message }) 5 | } 6 | 7 | export const ResError = { 8 | // Movie Errors 9 | FETCH_MOVIE_LIST_FAIL: E(400, 'Fetch movie list fail'), 10 | MOVIE_NOT_FOUND: E(404, 'Movie not exist') 11 | } 12 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/index.ts: -------------------------------------------------------------------------------- 1 | import { summerStart, handler } from '@summer-js/summer' 2 | import { getDataSource } from '@summer-js/typeorm' 3 | export { handler } 4 | 5 | summerStart({ 6 | async before(_config) { 7 | // You need to create database first 8 | // SQL: CREATE DATABASE `movie-db` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci 9 | const movieDataSource = getDataSource('MOVIE_DB') 10 | await movieDataSource.synchronize() 11 | }, 12 | async after(_config) {} 13 | }) 14 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/service/MovieService.ts: -------------------------------------------------------------------------------- 1 | import { convertData, Service } from '@summer-js/summer' 2 | import { Repository } from '@summer-js/typeorm' 3 | 4 | import { MovieRequest } from '../dto/request' 5 | import { Movie } from '../entity' 6 | import { ResError } from '../error' 7 | 8 | @Service 9 | export class MovieService { 10 | movieRepository: Repository 11 | 12 | async addMovie(addMovieRequest: MovieRequest) { 13 | const movie = convertData(addMovieRequest, Movie) 14 | return await this.movieRepository.save(movie) 15 | } 16 | 17 | async getMovies(pageNumber: number, pageSize: number) { 18 | const [movies, total] = await this.movieRepository.findAndCount({ 19 | take: pageSize, 20 | skip: (pageNumber - 1) * pageSize 21 | }) 22 | return { movies, total } 23 | } 24 | 25 | async getMovie(id: number) { 26 | const movie = await this.movieRepository.findOneBy({ id }) 27 | if (!movie) { 28 | throw ResError.MOVIE_NOT_FOUND 29 | } 30 | return movie 31 | } 32 | 33 | async updateMovie(id, movieReq: MovieRequest) { 34 | const movie = convertData(movieReq, Movie) 35 | movie.id = id 36 | return await this.movieRepository.save(movie) 37 | } 38 | 39 | async deleteMovie(id: number) { 40 | return await this.movieRepository.delete({ id }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/service/index.ts: -------------------------------------------------------------------------------- 1 | export * from './MovieService' 2 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/test/component/MovieService.test.ts: -------------------------------------------------------------------------------- 1 | import { getInjectable } from '@summer-js/summer' 2 | import '@summer-js/test' 3 | import { Movie } from '../../entity/Movie' 4 | import { MovieService } from '../../service/MovieService' 5 | 6 | describe('Test MovieService', () => { 7 | let movieService: MovieService 8 | 9 | beforeAll(async () => { 10 | movieService = getInjectable(MovieService) 11 | }) 12 | 13 | test('should get movie detail', async () => { 14 | const testMovie: Movie = { id: 1, name: 'The Godfather', year: '1972' } 15 | movieService.movieRepository.findOneBy = jest.fn().mockReturnValue(Promise.resolve(testMovie)) 16 | expect(await movieService.getMovie(1)).toStrictEqual(testMovie) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/test/e2e/AppController.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | 3 | describe('Test AppController', () => { 4 | test('should response hello', async () => { 5 | const response = await request.get('/') 6 | expect(response.rawBody).toBe('Hello Summer!') 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /create-summer/templates/movie/src/test/e2e/MovieController.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@summer-js/test' 2 | import { getDataSource } from '@summer-js/typeorm' 3 | 4 | import { Movie } from '../../entity/Movie' 5 | 6 | describe('Test MovieController', () => { 7 | beforeAll(async () => { 8 | // clear all movies at the beginning 9 | await getDataSource('MOVIE_DB').getRepository(Movie).clear() 10 | }) 11 | 12 | test('test add movie', async () => { 13 | const response = await request.post('/v1/movies', { name: 'Titanic', year: '1997' }) 14 | expect(response.statusCode).toBe(200) 15 | }) 16 | 17 | test('test get movie', async () => { 18 | const response = await request.get('/v1/movies/1') 19 | expect(response.body).toStrictEqual({ id: 1, name: 'Titanic', year: '1997' }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /create-summer/templates/movie/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "noImplicitReturns": true, 5 | "noUnusedParameters": true, 6 | "noUnusedLocals": true, 7 | "strictNullChecks": true, 8 | "types": ["node", "jest"], 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "sourceMap": true, 12 | "esModuleInterop": true, 13 | "outDir": "./compile", 14 | "moduleResolution": "Node", 15 | "allowJs": true, 16 | "module": "commonjs", 17 | "paths": { 18 | "@/*": ["./src/*"] 19 | } 20 | }, 21 | "include": ["./src/**/*"] 22 | } 23 | -------------------------------------------------------------------------------- /docs/develop.md: -------------------------------------------------------------------------------- 1 | ### How to start 2 | 3 | This project maintain monorepo by npm v8 workspace 4 | 5 | run `npm i -ws` to install all dependencies 6 | 7 | 8 | 9 | ### Packages 10 | 11 | There are 7 npm projects in this repo 12 | 13 | 1. **create-summer** 14 | project create command `npm create summer` 15 | 16 | 2. **@summer-js/cli** 17 | compiler and local develop listener 18 | 19 | 3. **@summer-js/summer** 20 | 21 | core code 22 | 23 | 4. **@summer-js/summer-test** (not pulish to npmjs) 24 | 25 | test project for testing summer core code 26 | 27 | 5. **@summer-js/swagger** 28 | 29 | swagger plugin 30 | 31 | 6. **@summer-js/test** 32 | 33 | project testing library 34 | 35 | 7. **@summer-js/typeorm** 36 | 37 | typeorm plugin 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "summer-packages", 3 | "version": "0.1.52-rc.2", 4 | "description": "", 5 | "scripts": { 6 | "install-all": "npm --ws install", 7 | "audit-all": "npm --ws audit", 8 | "build-all": "npm -w @summer-js/summer run build && npm -w @summer-js/typeorm run build && npm -w @summer-js/test run build && npm -w @summer-js/swagger run build && npm -w @summer-js/elasticsearch run build && npm -w @summer-js/redis run build && npm -w @summer-js/socket.io run build && npm -w @summer-js/graphql run build && npm -w @summer-js/mongodb run build && npm -w @summer-js/kafka run build", 9 | "test-all": "npm -w @summer-js/summer-test run test", 10 | "publish-all": "npm -w @summer-js/cli run release && npm -w @summer-js/summer run release && npm -w @summer-js/test run release && npm -w @summer-js/swagger run release && npm -w @summer-js/typeorm run release && npm -w @summer-js/elasticsearch run release && npm -w @summer-js/redis run release && npm -w @summer-js/socket.io run release && npm -w @summer-js/graphql run release && npm -w @summer-js/mongodb run release && npm -w @summer-js/kafka run release && npm -w create-summer run release", 11 | "pre-publish-all": "npm -w @summer-js/cli run pre-release && npm -w @summer-js/summer run pre-release && npm -w @summer-js/test run pre-release && npm -w @summer-js/swagger run pre-release && npm -w @summer-js/typeorm run pre-release && npm -w @summer-js/elasticsearch run pre-release && npm -w @summer-js/redis run pre-release && npm -w @summer-js/socket.io run pre-release && npm -w @summer-js/graphql run pre-release && npm -w @summer-js/mongodb run pre-release && npm -w @summer-js/kafka run pre-release" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/calidan-x/summer.git" 16 | }, 17 | "author": "Calidan", 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/calidan-x/summer/issues" 21 | }, 22 | "homepage": "https://github.com/calidan-x/summer#readme", 23 | "workspaces": [ 24 | "create-summer", 25 | "@summer-js/summer", 26 | "@summer-js/typeorm", 27 | "@summer-js/summer-test", 28 | "@summer-js/cli", 29 | "@summer-js/test", 30 | "@summer-js/node-test", 31 | "@summer-js/swagger", 32 | "@summer-js/elasticsearch", 33 | "@summer-js/redis", 34 | "@summer-js/socket.io", 35 | "@summer-js/graphql", 36 | "@summer-js/mongodb", 37 | "@summer-js/kafka" 38 | ] 39 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 | 6 | 7 |

Summer.js

8 | 9 |

10 | 11 | 12 |

13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 |

23 | 24 | ## Why SummerJS 25 | 26 | End-to-end typesafe APIs made easy. 27 | 28 | 29 | 30 | ## Quickstart 31 | 32 | Visit https://summerjs.dev/ to get started with summer.js. 33 | 34 | ## Documentation 35 | 36 | Visit [https://summerjs.dev/docs/intro](https://summerjs.dev/docs/intro) to view the full documentation. 37 | 38 | ## Contributing 39 | 40 | Please see our [contributing.md](./CONTRIBUTING.md). 41 | 42 | 43 | ## Thanks to contributors 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /tools/update-version.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | const packageFiles = [ 4 | './package.json', 5 | './@summer-js/cli/package.json', 6 | './@summer-js/summer/package.json', 7 | './@summer-js/summer-test/package.json', 8 | './@summer-js/swagger/package.json', 9 | './@summer-js/test/package.json', 10 | './@summer-js/typeorm/package.json', 11 | './@summer-js/elasticsearch/package.json', 12 | './@summer-js/redis/package.json', 13 | './@summer-js/socket.io/package.json', 14 | './@summer-js/graphql/package.json', 15 | './@summer-js/mongodb/package.json', 16 | './@summer-js/kafka/package.json' 17 | ] 18 | 19 | const templateFiles = ['./create-summer/templates/empty/package.json', './create-summer/templates/movie/package.json'] 20 | 21 | const newVersion = process.argv[2] 22 | 23 | if (!newVersion) { 24 | console.error('Error: Missing Version') 25 | process.exit() 26 | } 27 | 28 | // if (newVersion.split('.').length !== 3) { 29 | // console.error('Error: Version must like 1.0.0') 30 | // process.exit() 31 | // } 32 | 33 | packageFiles.forEach((f) => { 34 | const content = fs.readFileSync(f, { encoding: 'utf-8' }) 35 | const json = JSON.parse(content) 36 | json.version = newVersion 37 | fs.writeFileSync(f, JSON.stringify(json, null, '\t')) 38 | }) 39 | 40 | templateFiles.forEach((f) => { 41 | const content = fs.readFileSync(f, { encoding: 'utf-8' }) 42 | const json = JSON.parse(content) 43 | json.dependencies['@summer-js/summer'] = newVersion 44 | json.dependencies['@summer-js/test'] = newVersion 45 | json.dependencies['@summer-js/cli'] = newVersion 46 | if (json.dependencies['@summer-js/swagger']) { 47 | json.dependencies['@summer-js/swagger'] = newVersion 48 | } 49 | if (json.dependencies['@summer-js/typeorm']) { 50 | json.dependencies['@summer-js/typeorm'] = newVersion 51 | } 52 | fs.writeFileSync(f, JSON.stringify(json, null, '\t')) 53 | }) 54 | 55 | const createSummerPackage = './create-summer/package.json' 56 | const content = fs.readFileSync(createSummerPackage, { encoding: 'utf-8' }) 57 | const json = JSON.parse(content) 58 | json.version = newVersion 59 | fs.writeFileSync(createSummerPackage, JSON.stringify(json, null, '\t')) 60 | --------------------------------------------------------------------------------