├── .dockerignore ├── .env.example ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── Dockerfile ├── LICENSE ├── README.MD ├── build.sh ├── id_rsa.enc ├── importmap.json ├── server ├── ci-precompile.ts ├── common │ ├── Reflect.ts │ ├── base_controller.ts │ ├── cookis.ts │ ├── json_result.ts │ ├── mail.ts │ ├── mongo.ts │ ├── query-util.ts │ ├── redis.ts │ ├── render.ts │ ├── session.ts │ ├── state.ts │ └── util.ts ├── config.ts ├── controller.ts ├── controllers │ ├── file.ts │ ├── reply.ts │ ├── topic.ts │ └── user.ts ├── deps.ts ├── global.d.ts ├── models │ ├── base.ts │ ├── reply.ts │ ├── topic.ts │ └── user.ts └── server.ts ├── startup.sh └── tsconfig.json /.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .travis.yml 3 | .gitignore 4 | /mysql.log 5 | /package*.json 6 | /yarn.lock 7 | /web -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | MONGODB_URI=mongodb://127.0.0.1:27017 2 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | publish: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | with: 11 | submodules: true 12 | - uses: denolib/setup-deno@v1 13 | with: 14 | deno-version: "v0.38.0" 15 | - name: build web 16 | run: | 17 | ./build.sh 18 | - name: Build Image 19 | env: 20 | DOCKER_USER: ${{ secrets.DOCKER_USER }} 21 | DOCKER_PASS: ${{ secrets.DOCKER_PASS }} 22 | run: | 23 | docker login -u $DOCKER_USER -p $DOCKER_PASS registry.cn-hongkong.aliyuncs.com 24 | docker build ./ -t registry.cn-hongkong.aliyuncs.com/denocn/denocn-website 25 | docker push registry.cn-hongkong.aliyuncs.com/denocn/denocn-website 26 | 27 | deploy: 28 | runs-on: ubuntu-latest 29 | needs: [publish] 30 | steps: 31 | - uses: appleboy/ssh-action@master 32 | with: 33 | host: ${{ secrets.DEPLOY_HOST }} 34 | username: ${{ secrets.DEPLOY_USER }} 35 | password: ${{ secrets.DEPLOY_PASSWORD }} 36 | port: 22 37 | script: ${{ secrets.DEPLOY_SCRIPT }} 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | mysql.log 2 | package-lock.json 3 | .vscode 4 | *node_modules/ 5 | .deno_plugins/ 6 | web/.cache 7 | public 8 | .env 9 | cache 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "web"] 2 | path = web 3 | url = https://github.com/deno-china/denocn-website.git 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maxmcd/deno 2 | MAINTAINER manyuanrong "416828041@qq.com" 3 | 4 | ENV TZ Asia/Shanghai 5 | COPY ./ /data/ 6 | WORKDIR /data/ 7 | RUN touch .env && deno fetch -c tsconfig.json --importmap importmap.json ./server/server.ts 8 | RUN deno run -A -c tsconfig.json --importmap importmap.json ./server/ci-precompile.ts 9 | 10 | EXPOSE 3000 11 | ENTRYPOINT [] 12 | 13 | CMD [ "deno", "run", "--allow-all", "-c", "tsconfig.json", "--importmap", "importmap.json", "./server/server.ts" ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 DenoChina 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 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | ## Deno 中文社区后端部分 2 | 3 | [![Build Status](https://github.com/deno-china/website/workflows/CI/badge.svg)](https://github.com/deno-china/website/actions) 4 | ![GitHub](https://img.shields.io/github/license/deno-china/website.svg) 5 | [![(Deno)](https://img.shields.io/badge/deno-0.20.0-green.svg)](https://deno.land) 6 | [![Website](https://img.shields.io/website/https/denocn.org.svg?up_message=startup)](https://denocn.org) 7 | 8 | ### 项目结构 9 | 10 | ```sh 11 | ├── Dockerfile 12 | ├── LICENSE 13 | ├── READEME.MD 14 | ├── common 15 | │   ├── Reflect.ts 16 | │   ├── base_controller.ts # 基础控制器类,所有Controller都要继承此类 17 | │   ├── cookis.ts # Cookie中间件,实现Cookie操作 18 | │   ├── json_result.ts # 封装返回数据为Json格式的中间件 19 | │   ├── redis.ts # Redis连接模块 20 | │   ├── session.ts # Session机制中间件,实现Session存储在Redis上 21 | │   └── util.ts # 工具方法 22 | ├── config.ts # 网站配置文件 23 | ├── controller.ts # 控制器加载模块,所有controllers下的控制器都在这里导入(Deno等动态引入功能好了之后重构为自动载入) 24 | ├── controllers # 控制器目录 25 | │   ├── home.ts 26 | │   ├── topic.ts 27 | │   └── user.ts 28 | ├── deps.ts # 外部依赖 29 | ├── id_rsa.enc # 加密之后的,用于持续部署访问服务器的Key 30 | ├── models # 数据库模型目录,所有模型都需要在main.ts中引入。 31 | │   ├── main.ts 32 | │   ├── reply.ts 33 | │   ├── topic.ts 34 | │   └── user.ts 35 | ├── server.ts # 应用入口模块 36 | ├── setup.ts # 执行环境初始化时的入口文件 37 | ├── test.ts # TODO 测试用例 38 | └── tsconfig.json # TypeScript参数配置。 39 | ``` 40 | 41 | ### 修改配置 42 | 43 | 配置全局环境变量,或者配置 `.env` 文件。参考 `.env.example` 文件 44 | 45 | ```sh 46 | # 环境变量配置示例。请创建.env文件,将需要修改的项配置到.env文件中 47 | 48 | MYSQL_HOST= #MySQL地址 49 | MYSQL_PORT= #MySQL端口号 50 | MYSQL_USER= #MySQL用户名 51 | MYSQL_PASS= #MySQL数据库密码 52 | MYSQL_DB= #MySQL数据库名 53 | REDIS_PORT= #Reids端口 54 | REDIS_HOST= #Redis服务地址 55 | REDIS_PASS= #Redis密码 56 | GITHUB_SECRET= #GitHub应用Secret 57 | GITHUB_CLIENTID= #Github应用ID 58 | GITHUB_REDIRECT= #Github登录回调地址 59 | ``` 60 | 61 | 62 | ### 启动 63 | ```sh 64 | deno -A -c tsconfig.json server.ts 65 | ``` 66 | 67 | ### 技术栈和用到的库 68 | 69 | 项目采用 `deno` + `oak` + `dso` + `deno-redis` 70 | 71 | https://github.com/manyuanrong/dso 72 | 73 | https://github.com/oakserver/oak 74 | 75 | https://github.com/keroxp/deno-redis -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | rm -rf public 2 | cd web 3 | yarn 4 | yarn build 5 | cp -rf ./dist/ ../public 6 | cd ../ 7 | 8 | deno fetch -c tsconfig.json --importmap importmap.json server/server.ts 9 | -------------------------------------------------------------------------------- /id_rsa.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deno-china/denocn-server/6ff52e3970948635b9a27b1f1c00d0f6a8dac5c5/id_rsa.enc -------------------------------------------------------------------------------- /importmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "std/": "https://deno.land/std@v0.38.0/", 4 | "oak": "https://raw.githubusercontent.com/manyuanrong/oak/fix_deadlock/mod.ts", 5 | "logger": "https://deno.land/std@v0.38.0/log/mod.ts", 6 | "path": "https://deno.land/std@v0.38.0/path/mod.ts", 7 | "fs": "https://deno.land/std@v0.38.0/fs/mod.ts", 8 | "redis": "https://deno.land/x/redis@v0.8.2/redis.ts", 9 | "dso": "https://deno.land/x/dso@0.7.0/mod.ts", 10 | "asserts": "https://deno.land/std@v0.38.0/testing/asserts.ts", 11 | "mongo": "https://deno.land/x/mongo@v0.5.1/mod.ts" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/ci-precompile.ts: -------------------------------------------------------------------------------- 1 | // pre compile for dynamic load files 2 | 3 | import { loadControllers } from "./controller.ts"; 4 | 5 | await loadControllers(); 6 | -------------------------------------------------------------------------------- /server/common/Reflect.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | /*! ***************************************************************************** 3 | Copyright (C) Microsoft. All rights reserved. 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 8 | KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 9 | WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 10 | MERCHANTABLITY OR NON-INFRINGEMENT. 11 | See the Apache Version 2.0 License for specific language governing permissions 12 | and limitations under the License. 13 | ***************************************************************************** */ 14 | namespace Reflect { 15 | // Metadata Proposal 16 | // https://rbuckton.github.io/reflect-metadata/ 17 | 18 | type HashMap = Record; 19 | 20 | interface BufferLike { 21 | [offset: number]: number; 22 | length: number; 23 | } 24 | 25 | type IteratorResult = 26 | | { value: T; done: false } 27 | | { value: never; done: true }; 28 | 29 | interface Iterator { 30 | next(value?: any): IteratorResult; 31 | throw?(value: any): IteratorResult; 32 | return?(value?: T): IteratorResult; 33 | } 34 | 35 | interface Iterable { 36 | "@@iterator"(): Iterator; 37 | } 38 | 39 | interface IterableIterator extends Iterator { 40 | "@@iterator"(): IterableIterator; 41 | } 42 | 43 | interface Map extends Iterable<[K, V]> { 44 | size: number; 45 | has(key: K): boolean; 46 | get(key: K): V; 47 | set(key: K, value?: V): this; 48 | delete(key: K): boolean; 49 | clear(): void; 50 | keys(): IterableIterator; 51 | values(): IterableIterator; 52 | entries(): IterableIterator<[K, V]>; 53 | } 54 | 55 | interface MapConstructor { 56 | new (): Map; 57 | new (): Map; 58 | prototype: Map; 59 | } 60 | 61 | interface Set extends Iterable { 62 | size: number; 63 | has(value: T): boolean; 64 | add(value: T): this; 65 | delete(value: T): boolean; 66 | clear(): void; 67 | keys(): IterableIterator; 68 | values(): IterableIterator; 69 | entries(): IterableIterator<[T, T]>; 70 | } 71 | 72 | interface SetConstructor { 73 | new (): Set; 74 | new (): Set; 75 | prototype: Set; 76 | } 77 | 78 | interface WeakMap { 79 | clear(): void; 80 | delete(key: K): boolean; 81 | get(key: K): V; 82 | has(key: K): boolean; 83 | set(key: K, value?: V): WeakMap; 84 | } 85 | 86 | interface WeakMapConstructor { 87 | new (): WeakMap; 88 | new (): WeakMap; 89 | prototype: WeakMap; 90 | } 91 | 92 | type MemberDecorator = ( 93 | target: Object, 94 | propertyKey: string | symbol, 95 | descriptor?: TypedPropertyDescriptor 96 | ) => TypedPropertyDescriptor | void; 97 | declare const Symbol: { iterator: symbol; toPrimitive: symbol }; 98 | declare const Set: SetConstructor; 99 | declare const WeakMap: WeakMapConstructor; 100 | declare const Map: MapConstructor; 101 | declare const global: any; 102 | // @ts-ignore 103 | declare const crypto: Crypto; 104 | // @ts-ignore 105 | declare const msCrypto: Crypto; 106 | declare const process: any; 107 | 108 | /** 109 | * Applies a set of decorators to a target object. 110 | * @param decorators An array of decorators. 111 | * @param target The target object. 112 | * @returns The result of applying the provided decorators. 113 | * @remarks Decorators are applied in reverse order of their positions in the array. 114 | * @example 115 | * 116 | * class Example { } 117 | * 118 | * // constructor 119 | * Example = Reflect.decorate(decoratorsArray, Example); 120 | * 121 | */ 122 | export declare function decorate( 123 | decorators: ClassDecorator[], 124 | target: Function 125 | ): Function; 126 | 127 | /** 128 | * Applies a set of decorators to a property of a target object. 129 | * @param decorators An array of decorators. 130 | * @param target The target object. 131 | * @param propertyKey The property key to decorate. 132 | * @param attributes A property descriptor. 133 | * @remarks Decorators are applied in reverse order. 134 | * @example 135 | * 136 | * class Example { 137 | * // property declarations are not part of ES6, though they are valid in TypeScript: 138 | * // static staticProperty; 139 | * // property; 140 | * 141 | * static staticMethod() { } 142 | * method() { } 143 | * } 144 | * 145 | * // property (on constructor) 146 | * Reflect.decorate(decoratorsArray, Example, "staticProperty"); 147 | * 148 | * // property (on prototype) 149 | * Reflect.decorate(decoratorsArray, Example.prototype, "property"); 150 | * 151 | * // method (on constructor) 152 | * Object.defineProperty(Example, "staticMethod", 153 | * Reflect.decorate(decoratorsArray, Example, "staticMethod", 154 | * Object.getOwnPropertyDescriptor(Example, "staticMethod"))); 155 | * 156 | * // method (on prototype) 157 | * Object.defineProperty(Example.prototype, "method", 158 | * Reflect.decorate(decoratorsArray, Example.prototype, "method", 159 | * Object.getOwnPropertyDescriptor(Example.prototype, "method"))); 160 | * 161 | */ 162 | export declare function decorate( 163 | decorators: (PropertyDecorator | MethodDecorator)[], 164 | target: any, 165 | propertyKey: string | symbol, 166 | attributes?: PropertyDescriptor | null 167 | ): PropertyDescriptor | undefined; 168 | 169 | /** 170 | * Applies a set of decorators to a property of a target object. 171 | * @param decorators An array of decorators. 172 | * @param target The target object. 173 | * @param propertyKey The property key to decorate. 174 | * @param attributes A property descriptor. 175 | * @remarks Decorators are applied in reverse order. 176 | * @example 177 | * 178 | * class Example { 179 | * // property declarations are not part of ES6, though they are valid in TypeScript: 180 | * // static staticProperty; 181 | * // property; 182 | * 183 | * static staticMethod() { } 184 | * method() { } 185 | * } 186 | * 187 | * // property (on constructor) 188 | * Reflect.decorate(decoratorsArray, Example, "staticProperty"); 189 | * 190 | * // property (on prototype) 191 | * Reflect.decorate(decoratorsArray, Example.prototype, "property"); 192 | * 193 | * // method (on constructor) 194 | * Object.defineProperty(Example, "staticMethod", 195 | * Reflect.decorate(decoratorsArray, Example, "staticMethod", 196 | * Object.getOwnPropertyDescriptor(Example, "staticMethod"))); 197 | * 198 | * // method (on prototype) 199 | * Object.defineProperty(Example.prototype, "method", 200 | * Reflect.decorate(decoratorsArray, Example.prototype, "method", 201 | * Object.getOwnPropertyDescriptor(Example.prototype, "method"))); 202 | * 203 | */ 204 | export declare function decorate( 205 | decorators: (PropertyDecorator | MethodDecorator)[], 206 | target: any, 207 | propertyKey: string | symbol, 208 | attributes: PropertyDescriptor 209 | ): PropertyDescriptor; 210 | 211 | /** 212 | * A default metadata decorator factory that can be used on a class, class member, or parameter. 213 | * @param metadataKey The key for the metadata entry. 214 | * @param metadataValue The value for the metadata entry. 215 | * @returns A decorator function. 216 | * @remarks 217 | * If `metadataKey` is already defined for the target and target key, the 218 | * metadataValue for that key will be overwritten. 219 | * @example 220 | * 221 | * // constructor 222 | * @Reflect.metadata(key, value) 223 | * class Example { 224 | * } 225 | * 226 | * // property (on constructor, TypeScript only) 227 | * class Example { 228 | * @Reflect.metadata(key, value) 229 | * static staticProperty; 230 | * } 231 | * 232 | * // property (on prototype, TypeScript only) 233 | * class Example { 234 | * @Reflect.metadata(key, value) 235 | * property; 236 | * } 237 | * 238 | * // method (on constructor) 239 | * class Example { 240 | * @Reflect.metadata(key, value) 241 | * static staticMethod() { } 242 | * } 243 | * 244 | * // method (on prototype) 245 | * class Example { 246 | * @Reflect.metadata(key, value) 247 | * method() { } 248 | * } 249 | * 250 | */ 251 | export declare function metadata( 252 | metadataKey: any, 253 | metadataValue: any 254 | ): { 255 | (target: Function): void; 256 | (target: any, propertyKey: string | symbol): void; 257 | }; 258 | 259 | /** 260 | * Define a unique metadata entry on the target. 261 | * @param metadataKey A key used to store and retrieve metadata. 262 | * @param metadataValue A value that contains attached metadata. 263 | * @param target The target object on which to define metadata. 264 | * @example 265 | * 266 | * class Example { 267 | * } 268 | * 269 | * // constructor 270 | * Reflect.defineMetadata("custom:annotation", options, Example); 271 | * 272 | * // decorator factory as metadata-producing annotation. 273 | * function MyAnnotation(options): ClassDecorator { 274 | * return target => Reflect.defineMetadata("custom:annotation", options, target); 275 | * } 276 | * 277 | */ 278 | export declare function defineMetadata( 279 | metadataKey: any, 280 | metadataValue: any, 281 | target: any 282 | ): void; 283 | 284 | /** 285 | * Define a unique metadata entry on the target. 286 | * @param metadataKey A key used to store and retrieve metadata. 287 | * @param metadataValue A value that contains attached metadata. 288 | * @param target The target object on which to define metadata. 289 | * @param propertyKey The property key for the target. 290 | * @example 291 | * 292 | * class Example { 293 | * // property declarations are not part of ES6, though they are valid in TypeScript: 294 | * // static staticProperty; 295 | * // property; 296 | * 297 | * static staticMethod(p) { } 298 | * method(p) { } 299 | * } 300 | * 301 | * // property (on constructor) 302 | * Reflect.defineMetadata("custom:annotation", Number, Example, "staticProperty"); 303 | * 304 | * // property (on prototype) 305 | * Reflect.defineMetadata("custom:annotation", Number, Example.prototype, "property"); 306 | * 307 | * // method (on constructor) 308 | * Reflect.defineMetadata("custom:annotation", Number, Example, "staticMethod"); 309 | * 310 | * // method (on prototype) 311 | * Reflect.defineMetadata("custom:annotation", Number, Example.prototype, "method"); 312 | * 313 | * // decorator factory as metadata-producing annotation. 314 | * function MyAnnotation(options): PropertyDecorator { 315 | * return (target, key) => Reflect.defineMetadata("custom:annotation", options, target, key); 316 | * } 317 | * 318 | */ 319 | export declare function defineMetadata( 320 | metadataKey: any, 321 | metadataValue: any, 322 | target: any, 323 | propertyKey: string | symbol 324 | ): void; 325 | 326 | /** 327 | * Gets a value indicating whether the target object or its prototype chain has the provided metadata key defined. 328 | * @param metadataKey A key used to store and retrieve metadata. 329 | * @param target The target object on which the metadata is defined. 330 | * @returns `true` if the metadata key was defined on the target object or its prototype chain; otherwise, `false`. 331 | * @example 332 | * 333 | * class Example { 334 | * } 335 | * 336 | * // constructor 337 | * result = Reflect.hasMetadata("custom:annotation", Example); 338 | * 339 | */ 340 | export declare function hasMetadata(metadataKey: any, target: any): boolean; 341 | 342 | /** 343 | * Gets a value indicating whether the target object or its prototype chain has the provided metadata key defined. 344 | * @param metadataKey A key used to store and retrieve metadata. 345 | * @param target The target object on which the metadata is defined. 346 | * @param propertyKey The property key for the target. 347 | * @returns `true` if the metadata key was defined on the target object or its prototype chain; otherwise, `false`. 348 | * @example 349 | * 350 | * class Example { 351 | * // property declarations are not part of ES6, though they are valid in TypeScript: 352 | * // static staticProperty; 353 | * // property; 354 | * 355 | * static staticMethod(p) { } 356 | * method(p) { } 357 | * } 358 | * 359 | * // property (on constructor) 360 | * result = Reflect.hasMetadata("custom:annotation", Example, "staticProperty"); 361 | * 362 | * // property (on prototype) 363 | * result = Reflect.hasMetadata("custom:annotation", Example.prototype, "property"); 364 | * 365 | * // method (on constructor) 366 | * result = Reflect.hasMetadata("custom:annotation", Example, "staticMethod"); 367 | * 368 | * // method (on prototype) 369 | * result = Reflect.hasMetadata("custom:annotation", Example.prototype, "method"); 370 | * 371 | */ 372 | export declare function hasMetadata( 373 | metadataKey: any, 374 | target: any, 375 | propertyKey: string | symbol 376 | ): boolean; 377 | 378 | /** 379 | * Gets a value indicating whether the target object has the provided metadata key defined. 380 | * @param metadataKey A key used to store and retrieve metadata. 381 | * @param target The target object on which the metadata is defined. 382 | * @returns `true` if the metadata key was defined on the target object; otherwise, `false`. 383 | * @example 384 | * 385 | * class Example { 386 | * } 387 | * 388 | * // constructor 389 | * result = Reflect.hasOwnMetadata("custom:annotation", Example); 390 | * 391 | */ 392 | export declare function hasOwnMetadata( 393 | metadataKey: any, 394 | target: any 395 | ): boolean; 396 | 397 | /** 398 | * Gets a value indicating whether the target object has the provided metadata key defined. 399 | * @param metadataKey A key used to store and retrieve metadata. 400 | * @param target The target object on which the metadata is defined. 401 | * @param propertyKey The property key for the target. 402 | * @returns `true` if the metadata key was defined on the target object; otherwise, `false`. 403 | * @example 404 | * 405 | * class Example { 406 | * // property declarations are not part of ES6, though they are valid in TypeScript: 407 | * // static staticProperty; 408 | * // property; 409 | * 410 | * static staticMethod(p) { } 411 | * method(p) { } 412 | * } 413 | * 414 | * // property (on constructor) 415 | * result = Reflect.hasOwnMetadata("custom:annotation", Example, "staticProperty"); 416 | * 417 | * // property (on prototype) 418 | * result = Reflect.hasOwnMetadata("custom:annotation", Example.prototype, "property"); 419 | * 420 | * // method (on constructor) 421 | * result = Reflect.hasOwnMetadata("custom:annotation", Example, "staticMethod"); 422 | * 423 | * // method (on prototype) 424 | * result = Reflect.hasOwnMetadata("custom:annotation", Example.prototype, "method"); 425 | * 426 | */ 427 | export declare function hasOwnMetadata( 428 | metadataKey: any, 429 | target: any, 430 | propertyKey: string | symbol 431 | ): boolean; 432 | 433 | /** 434 | * Gets the metadata value for the provided metadata key on the target object or its prototype chain. 435 | * @param metadataKey A key used to store and retrieve metadata. 436 | * @param target The target object on which the metadata is defined. 437 | * @returns The metadata value for the metadata key if found; otherwise, `undefined`. 438 | * @example 439 | * 440 | * class Example { 441 | * } 442 | * 443 | * // constructor 444 | * result = Reflect.getMetadata("custom:annotation", Example); 445 | * 446 | */ 447 | export declare function getMetadata(metadataKey: any, target: any): any; 448 | 449 | /** 450 | * Gets the metadata value for the provided metadata key on the target object or its prototype chain. 451 | * @param metadataKey A key used to store and retrieve metadata. 452 | * @param target The target object on which the metadata is defined. 453 | * @param propertyKey The property key for the target. 454 | * @returns The metadata value for the metadata key if found; otherwise, `undefined`. 455 | * @example 456 | * 457 | * class Example { 458 | * // property declarations are not part of ES6, though they are valid in TypeScript: 459 | * // static staticProperty; 460 | * // property; 461 | * 462 | * static staticMethod(p) { } 463 | * method(p) { } 464 | * } 465 | * 466 | * // property (on constructor) 467 | * result = Reflect.getMetadata("custom:annotation", Example, "staticProperty"); 468 | * 469 | * // property (on prototype) 470 | * result = Reflect.getMetadata("custom:annotation", Example.prototype, "property"); 471 | * 472 | * // method (on constructor) 473 | * result = Reflect.getMetadata("custom:annotation", Example, "staticMethod"); 474 | * 475 | * // method (on prototype) 476 | * result = Reflect.getMetadata("custom:annotation", Example.prototype, "method"); 477 | * 478 | */ 479 | export declare function getMetadata( 480 | metadataKey: any, 481 | target: any, 482 | propertyKey: string | symbol 483 | ): any; 484 | 485 | /** 486 | * Gets the metadata value for the provided metadata key on the target object. 487 | * @param metadataKey A key used to store and retrieve metadata. 488 | * @param target The target object on which the metadata is defined. 489 | * @returns The metadata value for the metadata key if found; otherwise, `undefined`. 490 | * @example 491 | * 492 | * class Example { 493 | * } 494 | * 495 | * // constructor 496 | * result = Reflect.getOwnMetadata("custom:annotation", Example); 497 | * 498 | */ 499 | export declare function getOwnMetadata(metadataKey: any, target: any): any; 500 | 501 | /** 502 | * Gets the metadata value for the provided metadata key on the target object. 503 | * @param metadataKey A key used to store and retrieve metadata. 504 | * @param target The target object on which the metadata is defined. 505 | * @param propertyKey The property key for the target. 506 | * @returns The metadata value for the metadata key if found; otherwise, `undefined`. 507 | * @example 508 | * 509 | * class Example { 510 | * // property declarations are not part of ES6, though they are valid in TypeScript: 511 | * // static staticProperty; 512 | * // property; 513 | * 514 | * static staticMethod(p) { } 515 | * method(p) { } 516 | * } 517 | * 518 | * // property (on constructor) 519 | * result = Reflect.getOwnMetadata("custom:annotation", Example, "staticProperty"); 520 | * 521 | * // property (on prototype) 522 | * result = Reflect.getOwnMetadata("custom:annotation", Example.prototype, "property"); 523 | * 524 | * // method (on constructor) 525 | * result = Reflect.getOwnMetadata("custom:annotation", Example, "staticMethod"); 526 | * 527 | * // method (on prototype) 528 | * result = Reflect.getOwnMetadata("custom:annotation", Example.prototype, "method"); 529 | * 530 | */ 531 | export declare function getOwnMetadata( 532 | metadataKey: any, 533 | target: any, 534 | propertyKey: string | symbol 535 | ): any; 536 | 537 | /** 538 | * Gets the metadata keys defined on the target object or its prototype chain. 539 | * @param target The target object on which the metadata is defined. 540 | * @returns An array of unique metadata keys. 541 | * @example 542 | * 543 | * class Example { 544 | * } 545 | * 546 | * // constructor 547 | * result = Reflect.getMetadataKeys(Example); 548 | * 549 | */ 550 | export declare function getMetadataKeys(target: any): any[]; 551 | 552 | /** 553 | * Gets the metadata keys defined on the target object or its prototype chain. 554 | * @param target The target object on which the metadata is defined. 555 | * @param propertyKey The property key for the target. 556 | * @returns An array of unique metadata keys. 557 | * @example 558 | * 559 | * class Example { 560 | * // property declarations are not part of ES6, though they are valid in TypeScript: 561 | * // static staticProperty; 562 | * // property; 563 | * 564 | * static staticMethod(p) { } 565 | * method(p) { } 566 | * } 567 | * 568 | * // property (on constructor) 569 | * result = Reflect.getMetadataKeys(Example, "staticProperty"); 570 | * 571 | * // property (on prototype) 572 | * result = Reflect.getMetadataKeys(Example.prototype, "property"); 573 | * 574 | * // method (on constructor) 575 | * result = Reflect.getMetadataKeys(Example, "staticMethod"); 576 | * 577 | * // method (on prototype) 578 | * result = Reflect.getMetadataKeys(Example.prototype, "method"); 579 | * 580 | */ 581 | export declare function getMetadataKeys( 582 | target: any, 583 | propertyKey: string | symbol 584 | ): any[]; 585 | 586 | /** 587 | * Gets the unique metadata keys defined on the target object. 588 | * @param target The target object on which the metadata is defined. 589 | * @returns An array of unique metadata keys. 590 | * @example 591 | * 592 | * class Example { 593 | * } 594 | * 595 | * // constructor 596 | * result = Reflect.getOwnMetadataKeys(Example); 597 | * 598 | */ 599 | export declare function getOwnMetadataKeys(target: any): any[]; 600 | 601 | /** 602 | * Gets the unique metadata keys defined on the target object. 603 | * @param target The target object on which the metadata is defined. 604 | * @param propertyKey The property key for the target. 605 | * @returns An array of unique metadata keys. 606 | * @example 607 | * 608 | * class Example { 609 | * // property declarations are not part of ES6, though they are valid in TypeScript: 610 | * // static staticProperty; 611 | * // property; 612 | * 613 | * static staticMethod(p) { } 614 | * method(p) { } 615 | * } 616 | * 617 | * // property (on constructor) 618 | * result = Reflect.getOwnMetadataKeys(Example, "staticProperty"); 619 | * 620 | * // property (on prototype) 621 | * result = Reflect.getOwnMetadataKeys(Example.prototype, "property"); 622 | * 623 | * // method (on constructor) 624 | * result = Reflect.getOwnMetadataKeys(Example, "staticMethod"); 625 | * 626 | * // method (on prototype) 627 | * result = Reflect.getOwnMetadataKeys(Example.prototype, "method"); 628 | * 629 | */ 630 | export declare function getOwnMetadataKeys( 631 | target: any, 632 | propertyKey: string | symbol 633 | ): any[]; 634 | 635 | /** 636 | * Deletes the metadata entry from the target object with the provided key. 637 | * @param metadataKey A key used to store and retrieve metadata. 638 | * @param target The target object on which the metadata is defined. 639 | * @returns `true` if the metadata entry was found and deleted; otherwise, false. 640 | * @example 641 | * 642 | * class Example { 643 | * } 644 | * 645 | * // constructor 646 | * result = Reflect.deleteMetadata("custom:annotation", Example); 647 | * 648 | */ 649 | export declare function deleteMetadata( 650 | metadataKey: any, 651 | target: any 652 | ): boolean; 653 | 654 | /** 655 | * Deletes the metadata entry from the target object with the provided key. 656 | * @param metadataKey A key used to store and retrieve metadata. 657 | * @param target The target object on which the metadata is defined. 658 | * @param propertyKey The property key for the target. 659 | * @returns `true` if the metadata entry was found and deleted; otherwise, false. 660 | * @example 661 | * 662 | * class Example { 663 | * // property declarations are not part of ES6, though they are valid in TypeScript: 664 | * // static staticProperty; 665 | * // property; 666 | * 667 | * static staticMethod(p) { } 668 | * method(p) { } 669 | * } 670 | * 671 | * // property (on constructor) 672 | * result = Reflect.deleteMetadata("custom:annotation", Example, "staticProperty"); 673 | * 674 | * // property (on prototype) 675 | * result = Reflect.deleteMetadata("custom:annotation", Example.prototype, "property"); 676 | * 677 | * // method (on constructor) 678 | * result = Reflect.deleteMetadata("custom:annotation", Example, "staticMethod"); 679 | * 680 | * // method (on prototype) 681 | * result = Reflect.deleteMetadata("custom:annotation", Example.prototype, "method"); 682 | * 683 | */ 684 | export declare function deleteMetadata( 685 | metadataKey: any, 686 | target: any, 687 | propertyKey: string | symbol 688 | ): boolean; 689 | 690 | (function( 691 | this: any, 692 | factory: ( 693 | exporter: ( 694 | key: K, 695 | value: typeof Reflect[K] 696 | ) => void 697 | ) => void 698 | ) { 699 | var self; 700 | const root = 701 | typeof global === "object" 702 | ? global 703 | : typeof self === "object" 704 | ? self 705 | : typeof this === "object" 706 | ? this 707 | : Function("return this;")(); 708 | 709 | let exporter = makeExporter(Reflect); 710 | if (typeof root.Reflect === "undefined") { 711 | root.Reflect = Reflect; 712 | } else { 713 | exporter = makeExporter(root.Reflect, exporter); 714 | } 715 | 716 | factory(exporter); 717 | 718 | function makeExporter( 719 | target: typeof Reflect, 720 | previous?: ( 721 | key: K, 722 | value: typeof Reflect[K] 723 | ) => void 724 | ) { 725 | return ( 726 | key: K, 727 | value: typeof Reflect[K] 728 | ) => { 729 | if (typeof target[key] !== "function") { 730 | Object.defineProperty(target, key, { 731 | configurable: true, 732 | writable: true, 733 | value 734 | }); 735 | } 736 | if (previous) previous(key, value); 737 | }; 738 | } 739 | })(function(exporter) { 740 | const hasOwn = Object.prototype.hasOwnProperty; 741 | 742 | // feature test for Symbol support 743 | const supportsSymbol = typeof Symbol === "function"; 744 | const toPrimitiveSymbol = 745 | supportsSymbol && typeof Symbol.toPrimitive !== "undefined" 746 | ? Symbol.toPrimitive 747 | : "@@toPrimitive"; 748 | const iteratorSymbol = 749 | supportsSymbol && typeof Symbol.iterator !== "undefined" 750 | ? Symbol.iterator 751 | : "@@iterator"; 752 | const supportsCreate = typeof Object.create === "function"; // feature test for Object.create support 753 | const supportsProto = { __proto__: [] } instanceof Array; // feature test for __proto__ support 754 | const downLevel = !supportsCreate && !supportsProto; 755 | 756 | const HashMap = { 757 | // create an object in dictionary mode (a.k.a. "slow" mode in v8) 758 | create: supportsCreate 759 | ? () => MakeDictionary(Object.create(null) as HashMap) 760 | : supportsProto 761 | ? () => MakeDictionary({ __proto__: null as any } as HashMap) 762 | : () => MakeDictionary({} as HashMap), 763 | 764 | has: downLevel 765 | ? (map: HashMap, key: string | number | symbol) => 766 | hasOwn.call(map, key) 767 | : (map: HashMap, key: string | number | symbol) => key in map, 768 | 769 | get: downLevel 770 | ? (map: HashMap, key: string | number | symbol): V | undefined => 771 | hasOwn.call(map, key) ? map[key as string | number] : undefined 772 | : (map: HashMap, key: string | number | symbol): V | undefined => 773 | map[key as string | number] 774 | }; 775 | 776 | // Load global or shim versions of Map, Set, and WeakMap 777 | const functionPrototype = Object.getPrototypeOf(Function); 778 | const usePolyfill = 779 | typeof process === "object" && 780 | process.env && 781 | process.env["REFLECT_METADATA_USE_MAP_POLYFILL"] === "true"; 782 | const _Map: typeof Map = 783 | !usePolyfill && 784 | typeof Map === "function" && 785 | typeof Map.prototype.entries === "function" 786 | ? Map 787 | : CreateMapPolyfill(); 788 | const _Set: typeof Set = 789 | !usePolyfill && 790 | typeof Set === "function" && 791 | typeof Set.prototype.entries === "function" 792 | ? Set 793 | : CreateSetPolyfill(); 794 | const _WeakMap: typeof WeakMap = 795 | !usePolyfill && typeof WeakMap === "function" 796 | ? WeakMap 797 | : CreateWeakMapPolyfill(); 798 | 799 | // [[Metadata]] internal slot 800 | // https://rbuckton.github.io/reflect-metadata/#ordinary-object-internal-methods-and-internal-slots 801 | const Metadata = new _WeakMap< 802 | any, 803 | Map> 804 | >(); 805 | 806 | function decorate(decorators: ClassDecorator[], target: Function): Function; 807 | function decorate( 808 | decorators: (PropertyDecorator | MethodDecorator)[], 809 | target: any, 810 | propertyKey: string | symbol, 811 | attributes?: PropertyDescriptor | null 812 | ): PropertyDescriptor | undefined; 813 | function decorate( 814 | decorators: (PropertyDecorator | MethodDecorator)[], 815 | target: any, 816 | propertyKey: string | symbol, 817 | attributes: PropertyDescriptor 818 | ): PropertyDescriptor; 819 | 820 | /** 821 | * Applies a set of decorators to a property of a target object. 822 | * @param decorators An array of decorators. 823 | * @param target The target object. 824 | * @param propertyKey (Optional) The property key to decorate. 825 | * @param attributes (Optional) The property descriptor for the target key. 826 | * @remarks Decorators are applied in reverse order. 827 | * @example 828 | * 829 | * class Example { 830 | * // property declarations are not part of ES6, though they are valid in TypeScript: 831 | * // static staticProperty; 832 | * // property; 833 | * 834 | * constructor(p) { } 835 | * static staticMethod(p) { } 836 | * method(p) { } 837 | * } 838 | * 839 | * // constructor 840 | * Example = Reflect.decorate(decoratorsArray, Example); 841 | * 842 | * // property (on constructor) 843 | * Reflect.decorate(decoratorsArray, Example, "staticProperty"); 844 | * 845 | * // property (on prototype) 846 | * Reflect.decorate(decoratorsArray, Example.prototype, "property"); 847 | * 848 | * // method (on constructor) 849 | * Object.defineProperty(Example, "staticMethod", 850 | * Reflect.decorate(decoratorsArray, Example, "staticMethod", 851 | * Object.getOwnPropertyDescriptor(Example, "staticMethod"))); 852 | * 853 | * // method (on prototype) 854 | * Object.defineProperty(Example.prototype, "method", 855 | * Reflect.decorate(decoratorsArray, Example.prototype, "method", 856 | * Object.getOwnPropertyDescriptor(Example.prototype, "method"))); 857 | * 858 | */ 859 | function decorate( 860 | decorators: (ClassDecorator | MemberDecorator)[], 861 | target: any, 862 | propertyKey?: string | symbol, 863 | attributes?: PropertyDescriptor | null 864 | ): PropertyDescriptor | Function | undefined { 865 | if (!IsUndefined(propertyKey)) { 866 | if (!IsArray(decorators)) throw new TypeError(); 867 | if (!IsObject(target)) throw new TypeError(); 868 | if ( 869 | !IsObject(attributes) && 870 | !IsUndefined(attributes) && 871 | !IsNull(attributes) 872 | ) 873 | throw new TypeError(); 874 | if (IsNull(attributes)) attributes = undefined; 875 | propertyKey = ToPropertyKey(propertyKey); 876 | return DecorateProperty( 877 | decorators, 878 | target, 879 | propertyKey, 880 | attributes 881 | ); 882 | } else { 883 | if (!IsArray(decorators)) throw new TypeError(); 884 | if (!IsConstructor(target)) throw new TypeError(); 885 | return DecorateConstructor( 886 | decorators, 887 | target 888 | ); 889 | } 890 | } 891 | 892 | exporter("decorate", decorate); 893 | 894 | // 4.1.2 Reflect.metadata(metadataKey, metadataValue) 895 | // https://rbuckton.github.io/reflect-metadata/#reflect.metadata 896 | 897 | /** 898 | * A default metadata decorator factory that can be used on a class, class member, or parameter. 899 | * @param metadataKey The key for the metadata entry. 900 | * @param metadataValue The value for the metadata entry. 901 | * @returns A decorator function. 902 | * @remarks 903 | * If `metadataKey` is already defined for the target and target key, the 904 | * metadataValue for that key will be overwritten. 905 | * @example 906 | * 907 | * // constructor 908 | * @Reflect.metadata(key, value) 909 | * class Example { 910 | * } 911 | * 912 | * // property (on constructor, TypeScript only) 913 | * class Example { 914 | * @Reflect.metadata(key, value) 915 | * static staticProperty; 916 | * } 917 | * 918 | * // property (on prototype, TypeScript only) 919 | * class Example { 920 | * @Reflect.metadata(key, value) 921 | * property; 922 | * } 923 | * 924 | * // method (on constructor) 925 | * class Example { 926 | * @Reflect.metadata(key, value) 927 | * static staticMethod() { } 928 | * } 929 | * 930 | * // method (on prototype) 931 | * class Example { 932 | * @Reflect.metadata(key, value) 933 | * method() { } 934 | * } 935 | * 936 | */ 937 | function metadata(metadataKey: any, metadataValue: any) { 938 | function decorator(target: Function): void; 939 | function decorator(target: any, propertyKey: string | symbol): void; 940 | function decorator(target: any, propertyKey?: string | symbol): void { 941 | if (!IsObject(target)) throw new TypeError(); 942 | if (!IsUndefined(propertyKey) && !IsPropertyKey(propertyKey)) 943 | throw new TypeError(); 944 | OrdinaryDefineOwnMetadata( 945 | metadataKey, 946 | metadataValue, 947 | target, 948 | propertyKey 949 | ); 950 | } 951 | return decorator; 952 | } 953 | 954 | exporter("metadata", metadata); 955 | 956 | // 4.1.3 Reflect.defineMetadata(metadataKey, metadataValue, target [, propertyKey]) 957 | // https://rbuckton.github.io/reflect-metadata/#reflect.definemetadata 958 | 959 | function defineMetadata( 960 | metadataKey: any, 961 | metadataValue: any, 962 | target: any 963 | ): void; 964 | function defineMetadata( 965 | metadataKey: any, 966 | metadataValue: any, 967 | target: any, 968 | propertyKey: string | symbol 969 | ): void; 970 | 971 | /** 972 | * Define a unique metadata entry on the target. 973 | * @param metadataKey A key used to store and retrieve metadata. 974 | * @param metadataValue A value that contains attached metadata. 975 | * @param target The target object on which to define metadata. 976 | * @param propertyKey (Optional) The property key for the target. 977 | * @example 978 | * 979 | * class Example { 980 | * // property declarations are not part of ES6, though they are valid in TypeScript: 981 | * // static staticProperty; 982 | * // property; 983 | * 984 | * constructor(p) { } 985 | * static staticMethod(p) { } 986 | * method(p) { } 987 | * } 988 | * 989 | * // constructor 990 | * Reflect.defineMetadata("custom:annotation", options, Example); 991 | * 992 | * // property (on constructor) 993 | * Reflect.defineMetadata("custom:annotation", options, Example, "staticProperty"); 994 | * 995 | * // property (on prototype) 996 | * Reflect.defineMetadata("custom:annotation", options, Example.prototype, "property"); 997 | * 998 | * // method (on constructor) 999 | * Reflect.defineMetadata("custom:annotation", options, Example, "staticMethod"); 1000 | * 1001 | * // method (on prototype) 1002 | * Reflect.defineMetadata("custom:annotation", options, Example.prototype, "method"); 1003 | * 1004 | * // decorator factory as metadata-producing annotation. 1005 | * function MyAnnotation(options): Decorator { 1006 | * return (target, key?) => Reflect.defineMetadata("custom:annotation", options, target, key); 1007 | * } 1008 | * 1009 | */ 1010 | function defineMetadata( 1011 | metadataKey: any, 1012 | metadataValue: any, 1013 | target: any, 1014 | propertyKey?: string | symbol 1015 | ): void { 1016 | if (!IsObject(target)) throw new TypeError(); 1017 | if (!IsUndefined(propertyKey)) propertyKey = ToPropertyKey(propertyKey); 1018 | return OrdinaryDefineOwnMetadata( 1019 | metadataKey, 1020 | metadataValue, 1021 | target, 1022 | propertyKey 1023 | ); 1024 | } 1025 | 1026 | exporter("defineMetadata", defineMetadata); 1027 | 1028 | // 4.1.4 Reflect.hasMetadata(metadataKey, target [, propertyKey]) 1029 | // https://rbuckton.github.io/reflect-metadata/#reflect.hasmetadata 1030 | 1031 | function hasMetadata(metadataKey: any, target: any): boolean; 1032 | function hasMetadata( 1033 | metadataKey: any, 1034 | target: any, 1035 | propertyKey: string | symbol 1036 | ): boolean; 1037 | 1038 | /** 1039 | * Gets a value indicating whether the target object or its prototype chain has the provided metadata key defined. 1040 | * @param metadataKey A key used to store and retrieve metadata. 1041 | * @param target The target object on which the metadata is defined. 1042 | * @param propertyKey (Optional) The property key for the target. 1043 | * @returns `true` if the metadata key was defined on the target object or its prototype chain; otherwise, `false`. 1044 | * @example 1045 | * 1046 | * class Example { 1047 | * // property declarations are not part of ES6, though they are valid in TypeScript: 1048 | * // static staticProperty; 1049 | * // property; 1050 | * 1051 | * constructor(p) { } 1052 | * static staticMethod(p) { } 1053 | * method(p) { } 1054 | * } 1055 | * 1056 | * // constructor 1057 | * result = Reflect.hasMetadata("custom:annotation", Example); 1058 | * 1059 | * // property (on constructor) 1060 | * result = Reflect.hasMetadata("custom:annotation", Example, "staticProperty"); 1061 | * 1062 | * // property (on prototype) 1063 | * result = Reflect.hasMetadata("custom:annotation", Example.prototype, "property"); 1064 | * 1065 | * // method (on constructor) 1066 | * result = Reflect.hasMetadata("custom:annotation", Example, "staticMethod"); 1067 | * 1068 | * // method (on prototype) 1069 | * result = Reflect.hasMetadata("custom:annotation", Example.prototype, "method"); 1070 | * 1071 | */ 1072 | function hasMetadata( 1073 | metadataKey: any, 1074 | target: any, 1075 | propertyKey?: string | symbol 1076 | ): boolean { 1077 | if (!IsObject(target)) throw new TypeError(); 1078 | if (!IsUndefined(propertyKey)) propertyKey = ToPropertyKey(propertyKey); 1079 | return OrdinaryHasMetadata(metadataKey, target, propertyKey); 1080 | } 1081 | 1082 | exporter("hasMetadata", hasMetadata); 1083 | 1084 | // 4.1.5 Reflect.hasOwnMetadata(metadataKey, target [, propertyKey]) 1085 | // https://rbuckton.github.io/reflect-metadata/#reflect-hasownmetadata 1086 | 1087 | function hasOwnMetadata(metadataKey: any, target: any): boolean; 1088 | function hasOwnMetadata( 1089 | metadataKey: any, 1090 | target: any, 1091 | propertyKey: string | symbol 1092 | ): boolean; 1093 | 1094 | /** 1095 | * Gets a value indicating whether the target object has the provided metadata key defined. 1096 | * @param metadataKey A key used to store and retrieve metadata. 1097 | * @param target The target object on which the metadata is defined. 1098 | * @param propertyKey (Optional) The property key for the target. 1099 | * @returns `true` if the metadata key was defined on the target object; otherwise, `false`. 1100 | * @example 1101 | * 1102 | * class Example { 1103 | * // property declarations are not part of ES6, though they are valid in TypeScript: 1104 | * // static staticProperty; 1105 | * // property; 1106 | * 1107 | * constructor(p) { } 1108 | * static staticMethod(p) { } 1109 | * method(p) { } 1110 | * } 1111 | * 1112 | * // constructor 1113 | * result = Reflect.hasOwnMetadata("custom:annotation", Example); 1114 | * 1115 | * // property (on constructor) 1116 | * result = Reflect.hasOwnMetadata("custom:annotation", Example, "staticProperty"); 1117 | * 1118 | * // property (on prototype) 1119 | * result = Reflect.hasOwnMetadata("custom:annotation", Example.prototype, "property"); 1120 | * 1121 | * // method (on constructor) 1122 | * result = Reflect.hasOwnMetadata("custom:annotation", Example, "staticMethod"); 1123 | * 1124 | * // method (on prototype) 1125 | * result = Reflect.hasOwnMetadata("custom:annotation", Example.prototype, "method"); 1126 | * 1127 | */ 1128 | function hasOwnMetadata( 1129 | metadataKey: any, 1130 | target: any, 1131 | propertyKey?: string | symbol 1132 | ): boolean { 1133 | if (!IsObject(target)) throw new TypeError(); 1134 | if (!IsUndefined(propertyKey)) propertyKey = ToPropertyKey(propertyKey); 1135 | return OrdinaryHasOwnMetadata(metadataKey, target, propertyKey); 1136 | } 1137 | 1138 | exporter("hasOwnMetadata", hasOwnMetadata); 1139 | 1140 | // 4.1.6 Reflect.getMetadata(metadataKey, target [, propertyKey]) 1141 | // https://rbuckton.github.io/reflect-metadata/#reflect-getmetadata 1142 | 1143 | function getMetadata(metadataKey: any, target: any): any; 1144 | function getMetadata( 1145 | metadataKey: any, 1146 | target: any, 1147 | propertyKey: string | symbol 1148 | ): any; 1149 | 1150 | /** 1151 | * Gets the metadata value for the provided metadata key on the target object or its prototype chain. 1152 | * @param metadataKey A key used to store and retrieve metadata. 1153 | * @param target The target object on which the metadata is defined. 1154 | * @param propertyKey (Optional) The property key for the target. 1155 | * @returns The metadata value for the metadata key if found; otherwise, `undefined`. 1156 | * @example 1157 | * 1158 | * class Example { 1159 | * // property declarations are not part of ES6, though they are valid in TypeScript: 1160 | * // static staticProperty; 1161 | * // property; 1162 | * 1163 | * constructor(p) { } 1164 | * static staticMethod(p) { } 1165 | * method(p) { } 1166 | * } 1167 | * 1168 | * // constructor 1169 | * result = Reflect.getMetadata("custom:annotation", Example); 1170 | * 1171 | * // property (on constructor) 1172 | * result = Reflect.getMetadata("custom:annotation", Example, "staticProperty"); 1173 | * 1174 | * // property (on prototype) 1175 | * result = Reflect.getMetadata("custom:annotation", Example.prototype, "property"); 1176 | * 1177 | * // method (on constructor) 1178 | * result = Reflect.getMetadata("custom:annotation", Example, "staticMethod"); 1179 | * 1180 | * // method (on prototype) 1181 | * result = Reflect.getMetadata("custom:annotation", Example.prototype, "method"); 1182 | * 1183 | */ 1184 | function getMetadata( 1185 | metadataKey: any, 1186 | target: any, 1187 | propertyKey?: string | symbol 1188 | ): any { 1189 | if (!IsObject(target)) throw new TypeError(); 1190 | if (!IsUndefined(propertyKey)) propertyKey = ToPropertyKey(propertyKey); 1191 | return OrdinaryGetMetadata(metadataKey, target, propertyKey); 1192 | } 1193 | 1194 | exporter("getMetadata", getMetadata); 1195 | 1196 | // 4.1.7 Reflect.getOwnMetadata(metadataKey, target [, propertyKey]) 1197 | // https://rbuckton.github.io/reflect-metadata/#reflect-getownmetadata 1198 | 1199 | function getOwnMetadata(metadataKey: any, target: any): any; 1200 | function getOwnMetadata( 1201 | metadataKey: any, 1202 | target: any, 1203 | propertyKey: string | symbol 1204 | ): any; 1205 | 1206 | /** 1207 | * Gets the metadata value for the provided metadata key on the target object. 1208 | * @param metadataKey A key used to store and retrieve metadata. 1209 | * @param target The target object on which the metadata is defined. 1210 | * @param propertyKey (Optional) The property key for the target. 1211 | * @returns The metadata value for the metadata key if found; otherwise, `undefined`. 1212 | * @example 1213 | * 1214 | * class Example { 1215 | * // property declarations are not part of ES6, though they are valid in TypeScript: 1216 | * // static staticProperty; 1217 | * // property; 1218 | * 1219 | * constructor(p) { } 1220 | * static staticMethod(p) { } 1221 | * method(p) { } 1222 | * } 1223 | * 1224 | * // constructor 1225 | * result = Reflect.getOwnMetadata("custom:annotation", Example); 1226 | * 1227 | * // property (on constructor) 1228 | * result = Reflect.getOwnMetadata("custom:annotation", Example, "staticProperty"); 1229 | * 1230 | * // property (on prototype) 1231 | * result = Reflect.getOwnMetadata("custom:annotation", Example.prototype, "property"); 1232 | * 1233 | * // method (on constructor) 1234 | * result = Reflect.getOwnMetadata("custom:annotation", Example, "staticMethod"); 1235 | * 1236 | * // method (on prototype) 1237 | * result = Reflect.getOwnMetadata("custom:annotation", Example.prototype, "method"); 1238 | * 1239 | */ 1240 | function getOwnMetadata( 1241 | metadataKey: any, 1242 | target: any, 1243 | propertyKey?: string | symbol 1244 | ): any { 1245 | if (!IsObject(target)) throw new TypeError(); 1246 | if (!IsUndefined(propertyKey)) propertyKey = ToPropertyKey(propertyKey); 1247 | return OrdinaryGetOwnMetadata(metadataKey, target, propertyKey); 1248 | } 1249 | 1250 | exporter("getOwnMetadata", getOwnMetadata); 1251 | 1252 | // 4.1.8 Reflect.getMetadataKeys(target [, propertyKey]) 1253 | // https://rbuckton.github.io/reflect-metadata/#reflect-getmetadatakeys 1254 | 1255 | function getMetadataKeys(target: any): any[]; 1256 | function getMetadataKeys(target: any, propertyKey: string | symbol): any[]; 1257 | 1258 | /** 1259 | * Gets the metadata keys defined on the target object or its prototype chain. 1260 | * @param target The target object on which the metadata is defined. 1261 | * @param propertyKey (Optional) The property key for the target. 1262 | * @returns An array of unique metadata keys. 1263 | * @example 1264 | * 1265 | * class Example { 1266 | * // property declarations are not part of ES6, though they are valid in TypeScript: 1267 | * // static staticProperty; 1268 | * // property; 1269 | * 1270 | * constructor(p) { } 1271 | * static staticMethod(p) { } 1272 | * method(p) { } 1273 | * } 1274 | * 1275 | * // constructor 1276 | * result = Reflect.getMetadataKeys(Example); 1277 | * 1278 | * // property (on constructor) 1279 | * result = Reflect.getMetadataKeys(Example, "staticProperty"); 1280 | * 1281 | * // property (on prototype) 1282 | * result = Reflect.getMetadataKeys(Example.prototype, "property"); 1283 | * 1284 | * // method (on constructor) 1285 | * result = Reflect.getMetadataKeys(Example, "staticMethod"); 1286 | * 1287 | * // method (on prototype) 1288 | * result = Reflect.getMetadataKeys(Example.prototype, "method"); 1289 | * 1290 | */ 1291 | function getMetadataKeys( 1292 | target: any, 1293 | propertyKey?: string | symbol 1294 | ): any[] { 1295 | if (!IsObject(target)) throw new TypeError(); 1296 | if (!IsUndefined(propertyKey)) propertyKey = ToPropertyKey(propertyKey); 1297 | return OrdinaryMetadataKeys(target, propertyKey); 1298 | } 1299 | 1300 | exporter("getMetadataKeys", getMetadataKeys); 1301 | 1302 | // 4.1.9 Reflect.getOwnMetadataKeys(target [, propertyKey]) 1303 | // https://rbuckton.github.io/reflect-metadata/#reflect-getownmetadata 1304 | 1305 | function getOwnMetadataKeys(target: any): any[]; 1306 | function getOwnMetadataKeys( 1307 | target: any, 1308 | propertyKey: string | symbol 1309 | ): any[]; 1310 | 1311 | /** 1312 | * Gets the unique metadata keys defined on the target object. 1313 | * @param target The target object on which the metadata is defined. 1314 | * @param propertyKey (Optional) The property key for the target. 1315 | * @returns An array of unique metadata keys. 1316 | * @example 1317 | * 1318 | * class Example { 1319 | * // property declarations are not part of ES6, though they are valid in TypeScript: 1320 | * // static staticProperty; 1321 | * // property; 1322 | * 1323 | * constructor(p) { } 1324 | * static staticMethod(p) { } 1325 | * method(p) { } 1326 | * } 1327 | * 1328 | * // constructor 1329 | * result = Reflect.getOwnMetadataKeys(Example); 1330 | * 1331 | * // property (on constructor) 1332 | * result = Reflect.getOwnMetadataKeys(Example, "staticProperty"); 1333 | * 1334 | * // property (on prototype) 1335 | * result = Reflect.getOwnMetadataKeys(Example.prototype, "property"); 1336 | * 1337 | * // method (on constructor) 1338 | * result = Reflect.getOwnMetadataKeys(Example, "staticMethod"); 1339 | * 1340 | * // method (on prototype) 1341 | * result = Reflect.getOwnMetadataKeys(Example.prototype, "method"); 1342 | * 1343 | */ 1344 | function getOwnMetadataKeys( 1345 | target: any, 1346 | propertyKey?: string | symbol 1347 | ): any[] { 1348 | if (!IsObject(target)) throw new TypeError(); 1349 | if (!IsUndefined(propertyKey)) propertyKey = ToPropertyKey(propertyKey); 1350 | return OrdinaryOwnMetadataKeys(target, propertyKey); 1351 | } 1352 | 1353 | exporter("getOwnMetadataKeys", getOwnMetadataKeys); 1354 | 1355 | // 4.1.10 Reflect.deleteMetadata(metadataKey, target [, propertyKey]) 1356 | // https://rbuckton.github.io/reflect-metadata/#reflect-deletemetadata 1357 | 1358 | function deleteMetadata(metadataKey: any, target: any): boolean; 1359 | function deleteMetadata( 1360 | metadataKey: any, 1361 | target: any, 1362 | propertyKey: string | symbol 1363 | ): boolean; 1364 | 1365 | /** 1366 | * Deletes the metadata entry from the target object with the provided key. 1367 | * @param metadataKey A key used to store and retrieve metadata. 1368 | * @param target The target object on which the metadata is defined. 1369 | * @param propertyKey (Optional) The property key for the target. 1370 | * @returns `true` if the metadata entry was found and deleted; otherwise, false. 1371 | * @example 1372 | * 1373 | * class Example { 1374 | * // property declarations are not part of ES6, though they are valid in TypeScript: 1375 | * // static staticProperty; 1376 | * // property; 1377 | * 1378 | * constructor(p) { } 1379 | * static staticMethod(p) { } 1380 | * method(p) { } 1381 | * } 1382 | * 1383 | * // constructor 1384 | * result = Reflect.deleteMetadata("custom:annotation", Example); 1385 | * 1386 | * // property (on constructor) 1387 | * result = Reflect.deleteMetadata("custom:annotation", Example, "staticProperty"); 1388 | * 1389 | * // property (on prototype) 1390 | * result = Reflect.deleteMetadata("custom:annotation", Example.prototype, "property"); 1391 | * 1392 | * // method (on constructor) 1393 | * result = Reflect.deleteMetadata("custom:annotation", Example, "staticMethod"); 1394 | * 1395 | * // method (on prototype) 1396 | * result = Reflect.deleteMetadata("custom:annotation", Example.prototype, "method"); 1397 | * 1398 | */ 1399 | function deleteMetadata( 1400 | metadataKey: any, 1401 | target: any, 1402 | propertyKey?: string | symbol 1403 | ): boolean { 1404 | if (!IsObject(target)) throw new TypeError(); 1405 | if (!IsUndefined(propertyKey)) propertyKey = ToPropertyKey(propertyKey); 1406 | const metadataMap = GetOrCreateMetadataMap( 1407 | target, 1408 | propertyKey, 1409 | /*Create*/ false 1410 | ); 1411 | if (IsUndefined(metadataMap)) return false; 1412 | if (!metadataMap.delete(metadataKey)) return false; 1413 | if (metadataMap.size > 0) return true; 1414 | const targetMetadata = Metadata.get(target); 1415 | targetMetadata.delete(propertyKey); 1416 | if (targetMetadata.size > 0) return true; 1417 | Metadata.delete(target); 1418 | return true; 1419 | } 1420 | 1421 | exporter("deleteMetadata", deleteMetadata); 1422 | 1423 | function DecorateConstructor( 1424 | decorators: ClassDecorator[], 1425 | target: Function 1426 | ): Function { 1427 | for (let i = decorators.length - 1; i >= 0; --i) { 1428 | const decorator = decorators[i]; 1429 | const decorated = decorator(target); 1430 | if (!IsUndefined(decorated) && !IsNull(decorated)) { 1431 | if (!IsConstructor(decorated)) throw new TypeError(); 1432 | target = decorated; 1433 | } 1434 | } 1435 | return target; 1436 | } 1437 | 1438 | function DecorateProperty( 1439 | decorators: MemberDecorator[], 1440 | target: any, 1441 | propertyKey: string | symbol, 1442 | descriptor: PropertyDescriptor | undefined 1443 | ): PropertyDescriptor | undefined { 1444 | for (let i = decorators.length - 1; i >= 0; --i) { 1445 | const decorator = decorators[i]; 1446 | const decorated = decorator(target, propertyKey, descriptor); 1447 | if (!IsUndefined(decorated) && !IsNull(decorated)) { 1448 | if (!IsObject(decorated)) throw new TypeError(); 1449 | descriptor = decorated; 1450 | } 1451 | } 1452 | return descriptor; 1453 | } 1454 | 1455 | // 2.1.1 GetOrCreateMetadataMap(O, P, Create) 1456 | // https://rbuckton.github.io/reflect-metadata/#getorcreatemetadatamap 1457 | function GetOrCreateMetadataMap( 1458 | O: any, 1459 | P: string | symbol | undefined, 1460 | Create: true 1461 | ): Map; 1462 | function GetOrCreateMetadataMap( 1463 | O: any, 1464 | P: string | symbol | undefined, 1465 | Create: false 1466 | ): Map | undefined; 1467 | function GetOrCreateMetadataMap( 1468 | O: any, 1469 | P: string | symbol | undefined, 1470 | Create: boolean 1471 | ): Map | undefined { 1472 | let targetMetadata = Metadata.get(O); 1473 | if (IsUndefined(targetMetadata)) { 1474 | if (!Create) return undefined; 1475 | targetMetadata = new _Map>(); 1476 | Metadata.set(O, targetMetadata); 1477 | } 1478 | let metadataMap = targetMetadata.get(P); 1479 | if (IsUndefined(metadataMap)) { 1480 | if (!Create) return undefined; 1481 | metadataMap = new _Map(); 1482 | targetMetadata.set(P, metadataMap); 1483 | } 1484 | return metadataMap; 1485 | } 1486 | 1487 | // 3.1.1.1 OrdinaryHasMetadata(MetadataKey, O, P) 1488 | // https://rbuckton.github.io/reflect-metadata/#ordinaryhasmetadata 1489 | function OrdinaryHasMetadata( 1490 | MetadataKey: any, 1491 | O: any, 1492 | P: string | symbol | undefined 1493 | ): boolean { 1494 | const hasOwn = OrdinaryHasOwnMetadata(MetadataKey, O, P); 1495 | if (hasOwn) return true; 1496 | const parent = OrdinaryGetPrototypeOf(O); 1497 | if (!IsNull(parent)) return OrdinaryHasMetadata(MetadataKey, parent, P); 1498 | return false; 1499 | } 1500 | 1501 | // 3.1.2.1 OrdinaryHasOwnMetadata(MetadataKey, O, P) 1502 | // https://rbuckton.github.io/reflect-metadata/#ordinaryhasownmetadata 1503 | function OrdinaryHasOwnMetadata( 1504 | MetadataKey: any, 1505 | O: any, 1506 | P: string | symbol | undefined 1507 | ): boolean { 1508 | const metadataMap = GetOrCreateMetadataMap(O, P, /*Create*/ false); 1509 | if (IsUndefined(metadataMap)) return false; 1510 | return ToBoolean(metadataMap.has(MetadataKey)); 1511 | } 1512 | 1513 | // 3.1.3.1 OrdinaryGetMetadata(MetadataKey, O, P) 1514 | // https://rbuckton.github.io/reflect-metadata/#ordinarygetmetadata 1515 | function OrdinaryGetMetadata( 1516 | MetadataKey: any, 1517 | O: any, 1518 | P: string | symbol | undefined 1519 | ): any { 1520 | const hasOwn = OrdinaryHasOwnMetadata(MetadataKey, O, P); 1521 | if (hasOwn) return OrdinaryGetOwnMetadata(MetadataKey, O, P); 1522 | const parent = OrdinaryGetPrototypeOf(O); 1523 | if (!IsNull(parent)) return OrdinaryGetMetadata(MetadataKey, parent, P); 1524 | return undefined; 1525 | } 1526 | 1527 | // 3.1.4.1 OrdinaryGetOwnMetadata(MetadataKey, O, P) 1528 | // https://rbuckton.github.io/reflect-metadata/#ordinarygetownmetadata 1529 | function OrdinaryGetOwnMetadata( 1530 | MetadataKey: any, 1531 | O: any, 1532 | P: string | symbol | undefined 1533 | ): any { 1534 | const metadataMap = GetOrCreateMetadataMap(O, P, /*Create*/ false); 1535 | if (IsUndefined(metadataMap)) return undefined; 1536 | return metadataMap.get(MetadataKey); 1537 | } 1538 | 1539 | // 3.1.5.1 OrdinaryDefineOwnMetadata(MetadataKey, MetadataValue, O, P) 1540 | // https://rbuckton.github.io/reflect-metadata/#ordinarydefineownmetadata 1541 | function OrdinaryDefineOwnMetadata( 1542 | MetadataKey: any, 1543 | MetadataValue: any, 1544 | O: any, 1545 | P: string | symbol | undefined 1546 | ): void { 1547 | const metadataMap = GetOrCreateMetadataMap(O, P, /*Create*/ true); 1548 | metadataMap.set(MetadataKey, MetadataValue); 1549 | } 1550 | 1551 | // 3.1.6.1 OrdinaryMetadataKeys(O, P) 1552 | // https://rbuckton.github.io/reflect-metadata/#ordinarymetadatakeys 1553 | function OrdinaryMetadataKeys( 1554 | O: any, 1555 | P: string | symbol | undefined 1556 | ): any[] { 1557 | const ownKeys = OrdinaryOwnMetadataKeys(O, P); 1558 | const parent = OrdinaryGetPrototypeOf(O); 1559 | if (parent === null) return ownKeys; 1560 | const parentKeys = OrdinaryMetadataKeys(parent, P); 1561 | if (parentKeys.length <= 0) return ownKeys; 1562 | if (ownKeys.length <= 0) return parentKeys; 1563 | const set = new _Set(); 1564 | const keys: any[] = []; 1565 | for (const key of ownKeys) { 1566 | const hasKey = set.has(key); 1567 | if (!hasKey) { 1568 | set.add(key); 1569 | keys.push(key); 1570 | } 1571 | } 1572 | for (const key of parentKeys) { 1573 | const hasKey = set.has(key); 1574 | if (!hasKey) { 1575 | set.add(key); 1576 | keys.push(key); 1577 | } 1578 | } 1579 | return keys; 1580 | } 1581 | 1582 | // 3.1.7.1 OrdinaryOwnMetadataKeys(O, P) 1583 | // https://rbuckton.github.io/reflect-metadata/#ordinaryownmetadatakeys 1584 | function OrdinaryOwnMetadataKeys( 1585 | O: any, 1586 | P: string | symbol | undefined 1587 | ): any[] { 1588 | const keys: any[] = []; 1589 | const metadataMap = GetOrCreateMetadataMap(O, P, /*Create*/ false); 1590 | if (IsUndefined(metadataMap)) return keys; 1591 | const keysObj = metadataMap.keys(); 1592 | const iterator = GetIterator(keysObj); 1593 | let k = 0; 1594 | while (true) { 1595 | const next = IteratorStep(iterator); 1596 | if (!next) { 1597 | keys.length = k; 1598 | return keys; 1599 | } 1600 | const nextValue = IteratorValue(next); 1601 | try { 1602 | keys[k] = nextValue; 1603 | } catch (e) { 1604 | try { 1605 | IteratorClose(iterator); 1606 | } finally { 1607 | throw e; 1608 | } 1609 | } 1610 | k++; 1611 | } 1612 | } 1613 | 1614 | // 6 ECMAScript Data Typ0es and Values 1615 | // https://tc39.github.io/ecma262/#sec-ecmascript-data-types-and-values 1616 | function Type(x: any): Tag { 1617 | if (x === null) return Tag.Null; 1618 | switch (typeof x) { 1619 | case "undefined": 1620 | return Tag.Undefined; 1621 | case "boolean": 1622 | return Tag.Boolean; 1623 | case "string": 1624 | return Tag.String; 1625 | case "symbol": 1626 | return Tag.Symbol; 1627 | case "number": 1628 | return Tag.Number; 1629 | case "object": 1630 | return x === null ? Tag.Null : Tag.Object; 1631 | default: 1632 | return Tag.Object; 1633 | } 1634 | } 1635 | 1636 | // 6.1 ECMAScript Language Types 1637 | // https://tc39.github.io/ecma262/#sec-ecmascript-language-types 1638 | const enum Tag { 1639 | Undefined, 1640 | Null, 1641 | Boolean, 1642 | String, 1643 | Symbol, 1644 | Number, 1645 | Object 1646 | } 1647 | 1648 | // 6.1.1 The Undefined Type 1649 | // https://tc39.github.io/ecma262/#sec-ecmascript-language-types-undefined-type 1650 | function IsUndefined(x: any): x is undefined { 1651 | return x === undefined; 1652 | } 1653 | 1654 | // 6.1.2 The Null Type 1655 | // https://tc39.github.io/ecma262/#sec-ecmascript-language-types-null-type 1656 | function IsNull(x: any): x is null { 1657 | return x === null; 1658 | } 1659 | 1660 | // 6.1.5 The Symbol Type 1661 | // https://tc39.github.io/ecma262/#sec-ecmascript-language-types-symbol-type 1662 | function IsSymbol(x: any): x is symbol { 1663 | return typeof x === "symbol"; 1664 | } 1665 | 1666 | // 6.1.7 The Object Type 1667 | // https://tc39.github.io/ecma262/#sec-object-type 1668 | function IsObject< 1669 | T 1670 | >(x: T | undefined | null | boolean | string | symbol | number): x is T { 1671 | return typeof x === "object" ? x !== null : typeof x === "function"; 1672 | } 1673 | 1674 | // 7.1 Type Conversion 1675 | // https://tc39.github.io/ecma262/#sec-type-conversion 1676 | 1677 | // 7.1.1 ToPrimitive(input [, PreferredType]) 1678 | // https://tc39.github.io/ecma262/#sec-toprimitive 1679 | function ToPrimitive( 1680 | input: any, 1681 | PreferredType?: Tag 1682 | ): undefined | null | boolean | string | symbol | number { 1683 | switch (Type(input)) { 1684 | case Tag.Undefined: 1685 | return input; 1686 | case Tag.Null: 1687 | return input; 1688 | case Tag.Boolean: 1689 | return input; 1690 | case Tag.String: 1691 | return input; 1692 | case Tag.Symbol: 1693 | return input; 1694 | case Tag.Number: 1695 | return input; 1696 | } 1697 | const hint: "string" | "number" | "default" = 1698 | PreferredType === Tag.String 1699 | ? "string" 1700 | : PreferredType === Tag.Number 1701 | ? "number" 1702 | : "default"; 1703 | const exoticToPrim = GetMethod(input, toPrimitiveSymbol); 1704 | if (exoticToPrim !== undefined) { 1705 | const result = exoticToPrim.call(input, hint); 1706 | if (IsObject(result)) throw new TypeError(); 1707 | return result; 1708 | } 1709 | return OrdinaryToPrimitive(input, hint === "default" ? "number" : hint); 1710 | } 1711 | 1712 | // 7.1.1.1 OrdinaryToPrimitive(O, hint) 1713 | // https://tc39.github.io/ecma262/#sec-ordinarytoprimitive 1714 | function OrdinaryToPrimitive( 1715 | O: any, 1716 | hint: "string" | "number" 1717 | ): undefined | null | boolean | string | symbol | number { 1718 | if (hint === "string") { 1719 | const toString = O.toString; 1720 | if (IsCallable(toString)) { 1721 | const result = toString.call(O); 1722 | if (!IsObject(result)) return result; 1723 | } 1724 | const valueOf = O.valueOf; 1725 | if (IsCallable(valueOf)) { 1726 | const result = valueOf.call(O); 1727 | if (!IsObject(result)) return result; 1728 | } 1729 | } else { 1730 | const valueOf = O.valueOf; 1731 | if (IsCallable(valueOf)) { 1732 | const result = valueOf.call(O); 1733 | if (!IsObject(result)) return result; 1734 | } 1735 | const toString = O.toString; 1736 | if (IsCallable(toString)) { 1737 | const result = toString.call(O); 1738 | if (!IsObject(result)) return result; 1739 | } 1740 | } 1741 | throw new TypeError(); 1742 | } 1743 | 1744 | // 7.1.2 ToBoolean(argument) 1745 | // https://tc39.github.io/ecma262/2016/#sec-toboolean 1746 | function ToBoolean(argument: any): boolean { 1747 | return !!argument; 1748 | } 1749 | 1750 | // 7.1.12 ToString(argument) 1751 | // https://tc39.github.io/ecma262/#sec-tostring 1752 | function ToString(argument: any): string { 1753 | return "" + argument; 1754 | } 1755 | 1756 | // 7.1.14 ToPropertyKey(argument) 1757 | // https://tc39.github.io/ecma262/#sec-topropertykey 1758 | function ToPropertyKey(argument: any): string | symbol { 1759 | const key = ToPrimitive(argument, Tag.String); 1760 | if (IsSymbol(key)) return key; 1761 | return ToString(key); 1762 | } 1763 | 1764 | // 7.2 Testing and Comparison Operations 1765 | // https://tc39.github.io/ecma262/#sec-testing-and-comparison-operations 1766 | 1767 | // 7.2.2 IsArray(argument) 1768 | // https://tc39.github.io/ecma262/#sec-isarray 1769 | function IsArray(argument: any): argument is any[] { 1770 | return Array.isArray 1771 | ? Array.isArray(argument) 1772 | : argument instanceof Object 1773 | ? argument instanceof Array 1774 | : Object.prototype.toString.call(argument) === "[object Array]"; 1775 | } 1776 | 1777 | // 7.2.3 IsCallable(argument) 1778 | // https://tc39.github.io/ecma262/#sec-iscallable 1779 | function IsCallable(argument: any): argument is Function { 1780 | // NOTE: This is an approximation as we cannot check for [[Call]] internal method. 1781 | return typeof argument === "function"; 1782 | } 1783 | 1784 | // 7.2.4 IsConstructor(argument) 1785 | // https://tc39.github.io/ecma262/#sec-isconstructor 1786 | function IsConstructor(argument: any): argument is Function { 1787 | // NOTE: This is an approximation as we cannot check for [[Construct]] internal method. 1788 | return typeof argument === "function"; 1789 | } 1790 | 1791 | // 7.2.7 IsPropertyKey(argument) 1792 | // https://tc39.github.io/ecma262/#sec-ispropertykey 1793 | function IsPropertyKey(argument: any): argument is string | symbol { 1794 | switch (Type(argument)) { 1795 | case Tag.String: 1796 | return true; 1797 | case Tag.Symbol: 1798 | return true; 1799 | default: 1800 | return false; 1801 | } 1802 | } 1803 | 1804 | // 7.3 Operations on Objects 1805 | // https://tc39.github.io/ecma262/#sec-operations-on-objects 1806 | 1807 | // 7.3.9 GetMethod(V, P) 1808 | // https://tc39.github.io/ecma262/#sec-getmethod 1809 | function GetMethod(V: any, P: any): Function | undefined { 1810 | const func = V[P]; 1811 | if (func === undefined || func === null) return undefined; 1812 | if (!IsCallable(func)) throw new TypeError(); 1813 | return func; 1814 | } 1815 | 1816 | // 7.4 Operations on Iterator Objects 1817 | // https://tc39.github.io/ecma262/#sec-operations-on-iterator-objects 1818 | 1819 | function GetIterator(obj: Iterable): Iterator { 1820 | const method = GetMethod(obj, iteratorSymbol); 1821 | if (!IsCallable(method)) throw new TypeError(); // from Call 1822 | const iterator = method.call(obj); 1823 | if (!IsObject(iterator)) throw new TypeError(); 1824 | return iterator; 1825 | } 1826 | 1827 | // 7.4.4 IteratorValue(iterResult) 1828 | // https://tc39.github.io/ecma262/2016/#sec-iteratorvalue 1829 | function IteratorValue(iterResult: IteratorResult): T { 1830 | return iterResult.value; 1831 | } 1832 | 1833 | // 7.4.5 IteratorStep(iterator) 1834 | // https://tc39.github.io/ecma262/#sec-iteratorstep 1835 | function IteratorStep(iterator: Iterator): IteratorResult | false { 1836 | const result = iterator.next(); 1837 | return result.done ? false : result; 1838 | } 1839 | 1840 | // 7.4.6 IteratorClose(iterator, completion) 1841 | // https://tc39.github.io/ecma262/#sec-iteratorclose 1842 | function IteratorClose(iterator: Iterator) { 1843 | const f = iterator["return"]; 1844 | if (f) f.call(iterator); 1845 | } 1846 | 1847 | // 9.1 Ordinary Object Internal Methods and Internal Slots 1848 | // https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots 1849 | 1850 | // 9.1.1.1 OrdinaryGetPrototypeOf(O) 1851 | // https://tc39.github.io/ecma262/#sec-ordinarygetprototypeof 1852 | function OrdinaryGetPrototypeOf(O: any): any { 1853 | const proto = Object.getPrototypeOf(O); 1854 | if (typeof O !== "function" || O === functionPrototype) return proto; 1855 | 1856 | // TypeScript doesn't set __proto__ in ES5, as it's non-standard. 1857 | // Try to determine the superclass constructor. Compatible implementations 1858 | // must either set __proto__ on a subclass constructor to the superclass constructor, 1859 | // or ensure each class has a valid `constructor` property on its prototype that 1860 | // points back to the constructor. 1861 | 1862 | // If this is not the same as Function.[[Prototype]], then this is definately inherited. 1863 | // This is the case when in ES6 or when using __proto__ in a compatible browser. 1864 | if (proto !== functionPrototype) return proto; 1865 | 1866 | // If the super prototype is Object.prototype, null, or undefined, then we cannot determine the heritage. 1867 | const prototype = O.prototype; 1868 | const prototypeProto = prototype && Object.getPrototypeOf(prototype); 1869 | if (prototypeProto == null || prototypeProto === Object.prototype) 1870 | return proto; 1871 | 1872 | // If the constructor was not a function, then we cannot determine the heritage. 1873 | const constructor = prototypeProto.constructor; 1874 | if (typeof constructor !== "function") return proto; 1875 | 1876 | // If we have some kind of self-reference, then we cannot determine the heritage. 1877 | if (constructor === O) return proto; 1878 | 1879 | // we have a pretty good guess at the heritage. 1880 | return constructor; 1881 | } 1882 | 1883 | // naive Map shim 1884 | function CreateMapPolyfill(): MapConstructor { 1885 | const cacheSentinel = {}; 1886 | const arraySentinel: any[] = []; 1887 | 1888 | class MapIterator< 1889 | K, 1890 | V, 1891 | R extends K | V | [K, V] 1892 | > implements IterableIterator { 1893 | private _keys: K[]; 1894 | private _values: V[]; 1895 | private _index = 0; 1896 | private _selector: (key: K, value: V) => R; 1897 | constructor(keys: K[], values: V[], selector: (key: K, value: V) => R) { 1898 | this._keys = keys; 1899 | this._values = values; 1900 | this._selector = selector; 1901 | } 1902 | "@@iterator"() { 1903 | return this; 1904 | } 1905 | [iteratorSymbol]() { 1906 | return this; 1907 | } 1908 | next(): IteratorResult { 1909 | const index = this._index; 1910 | if (index >= 0 && index < this._keys.length) { 1911 | const result = this._selector( 1912 | this._keys[index], 1913 | this._values[index] 1914 | ); 1915 | if (index + 1 >= this._keys.length) { 1916 | this._index = -1; 1917 | this._keys = arraySentinel; 1918 | this._values = arraySentinel; 1919 | } else { 1920 | this._index++; 1921 | } 1922 | return { value: result, done: false }; 1923 | } 1924 | return { value: undefined, done: true }; 1925 | } 1926 | throw(error: any): IteratorResult { 1927 | if (this._index >= 0) { 1928 | this._index = -1; 1929 | this._keys = arraySentinel; 1930 | this._values = arraySentinel; 1931 | } 1932 | throw error; 1933 | } 1934 | return(value?: R): IteratorResult { 1935 | if (this._index >= 0) { 1936 | this._index = -1; 1937 | this._keys = arraySentinel; 1938 | this._values = arraySentinel; 1939 | } 1940 | return { value: value, done: true }; 1941 | } 1942 | } 1943 | 1944 | return class Map { 1945 | private _keys: K[] = []; 1946 | private _values: (V | undefined)[] = []; 1947 | private _cacheKey = cacheSentinel; 1948 | private _cacheIndex = -2; 1949 | get size() { 1950 | return this._keys.length; 1951 | } 1952 | has(key: K): boolean { 1953 | return this._find(key, /*insert*/ false) >= 0; 1954 | } 1955 | get(key: K): V | undefined { 1956 | const index = this._find(key, /*insert*/ false); 1957 | return index >= 0 ? this._values[index] : undefined; 1958 | } 1959 | set(key: K, value: V): this { 1960 | const index = this._find(key, /*insert*/ true); 1961 | this._values[index] = value; 1962 | return this; 1963 | } 1964 | delete(key: K): boolean { 1965 | const index = this._find(key, /*insert*/ false); 1966 | if (index >= 0) { 1967 | const size = this._keys.length; 1968 | for (let i = index + 1; i < size; i++) { 1969 | this._keys[i - 1] = this._keys[i]; 1970 | this._values[i - 1] = this._values[i]; 1971 | } 1972 | this._keys.length--; 1973 | this._values.length--; 1974 | if (key === this._cacheKey) { 1975 | this._cacheKey = cacheSentinel; 1976 | this._cacheIndex = -2; 1977 | } 1978 | return true; 1979 | } 1980 | return false; 1981 | } 1982 | clear(): void { 1983 | this._keys.length = 0; 1984 | this._values.length = 0; 1985 | this._cacheKey = cacheSentinel; 1986 | this._cacheIndex = -2; 1987 | } 1988 | keys() { 1989 | return new MapIterator(this._keys, this._values, getKey); 1990 | } 1991 | values() { 1992 | return new MapIterator(this._keys, this._values, getValue); 1993 | } 1994 | entries() { 1995 | return new MapIterator(this._keys, this._values, getEntry); 1996 | } 1997 | "@@iterator"() { 1998 | return this.entries(); 1999 | } 2000 | [iteratorSymbol]() { 2001 | return this.entries(); 2002 | } 2003 | private _find(key: K, insert?: boolean): number { 2004 | if (this._cacheKey !== key) { 2005 | this._cacheIndex = this._keys.indexOf((this._cacheKey = key)); 2006 | } 2007 | if (this._cacheIndex < 0 && insert) { 2008 | this._cacheIndex = this._keys.length; 2009 | this._keys.push(key); 2010 | this._values.push(undefined); 2011 | } 2012 | return this._cacheIndex; 2013 | } 2014 | }; 2015 | 2016 | function getKey(key: K, _: V) { 2017 | return key; 2018 | } 2019 | 2020 | function getValue(_: K, value: V) { 2021 | return value; 2022 | } 2023 | 2024 | function getEntry(key: K, value: V) { 2025 | return [key, value] as [K, V]; 2026 | } 2027 | } 2028 | 2029 | // naive Set shim 2030 | function CreateSetPolyfill(): SetConstructor { 2031 | return class Set { 2032 | private _map = new _Map(); 2033 | get size() { 2034 | return this._map.size; 2035 | } 2036 | has(value: T): boolean { 2037 | return this._map.has(value); 2038 | } 2039 | add(value: T): Set { 2040 | return this._map.set(value, value), this; 2041 | } 2042 | delete(value: T): boolean { 2043 | return this._map.delete(value); 2044 | } 2045 | clear(): void { 2046 | this._map.clear(); 2047 | } 2048 | keys() { 2049 | return this._map.keys(); 2050 | } 2051 | values() { 2052 | return this._map.values(); 2053 | } 2054 | entries() { 2055 | return this._map.entries(); 2056 | } 2057 | "@@iterator"() { 2058 | return this.keys(); 2059 | } 2060 | [iteratorSymbol]() { 2061 | return this.keys(); 2062 | } 2063 | }; 2064 | } 2065 | 2066 | // naive WeakMap shim 2067 | function CreateWeakMapPolyfill(): WeakMapConstructor { 2068 | const UUID_SIZE = 16; 2069 | const keys = HashMap.create(); 2070 | const rootKey = CreateUniqueKey(); 2071 | return class WeakMap { 2072 | private _key = CreateUniqueKey(); 2073 | has(target: K): boolean { 2074 | const table = GetOrCreateWeakMapTable(target, /*create*/ false); 2075 | return table !== undefined ? HashMap.has(table, this._key) : false; 2076 | } 2077 | get(target: K): V { 2078 | const table = GetOrCreateWeakMapTable(target, /*create*/ false); 2079 | return table !== undefined 2080 | ? HashMap.get(table, this._key) 2081 | : undefined; 2082 | } 2083 | set(target: K, value: V): WeakMap { 2084 | const table = GetOrCreateWeakMapTable(target, /*create*/ true); 2085 | table[this._key] = value; 2086 | return this; 2087 | } 2088 | delete(target: K): boolean { 2089 | const table = GetOrCreateWeakMapTable(target, /*create*/ false); 2090 | return table !== undefined ? delete table[this._key] : false; 2091 | } 2092 | clear(): void { 2093 | // NOTE: not a real clear, just makes the previous data unreachable 2094 | this._key = CreateUniqueKey(); 2095 | } 2096 | }; 2097 | 2098 | function CreateUniqueKey(): string { 2099 | let key: string; 2100 | do key = "@@WeakMap@@" + CreateUUID(); 2101 | while (HashMap.has(keys, key)); 2102 | keys[key] = true; 2103 | return key; 2104 | } 2105 | 2106 | function GetOrCreateWeakMapTable< 2107 | K 2108 | >(target: K, create: true): HashMap; 2109 | function GetOrCreateWeakMapTable< 2110 | K 2111 | >(target: K, create: false): HashMap | undefined; 2112 | function GetOrCreateWeakMapTable< 2113 | K 2114 | >(target: K, create: boolean): HashMap | undefined { 2115 | if (!hasOwn.call(target, rootKey)) { 2116 | if (!create) return undefined; 2117 | Object.defineProperty(target, rootKey, { 2118 | value: HashMap.create() 2119 | }); 2120 | } 2121 | return (target)[rootKey]; 2122 | } 2123 | 2124 | function FillRandomBytes(buffer: BufferLike, size: number): BufferLike { 2125 | for (let i = 0; i < size; ++i) buffer[i] = (Math.random() * 0xff) | 0; 2126 | return buffer; 2127 | } 2128 | 2129 | function GenRandomBytes(size: number): BufferLike { 2130 | if (typeof Uint8Array === "function") { 2131 | if (typeof crypto !== "undefined") 2132 | return crypto.getRandomValues(new Uint8Array(size)) as Uint8Array; 2133 | if (typeof msCrypto !== "undefined") 2134 | return msCrypto.getRandomValues(new Uint8Array(size)) as Uint8Array; 2135 | return FillRandomBytes(new Uint8Array(size), size); 2136 | } 2137 | return FillRandomBytes(new Array(size), size); 2138 | } 2139 | 2140 | function CreateUUID() { 2141 | const data = GenRandomBytes(UUID_SIZE); 2142 | // mark as random - RFC 4122 § 4.4 2143 | data[6] = (data[6] & 0x4f) | 0x40; 2144 | data[8] = (data[8] & 0xbf) | 0x80; 2145 | let result = ""; 2146 | for (let offset = 0; offset < UUID_SIZE; ++offset) { 2147 | const byte = data[offset]; 2148 | if (offset === 4 || offset === 6 || offset === 8) result += "-"; 2149 | if (byte < 16) result += "0"; 2150 | result += byte.toString(16).toLowerCase(); 2151 | } 2152 | return result; 2153 | } 2154 | } 2155 | 2156 | // uses a heuristic used by v8 and chakra to force an object into dictionary mode. 2157 | function MakeDictionary(obj: T): T { 2158 | (obj).__ = undefined; 2159 | delete (obj).__; 2160 | return obj; 2161 | } 2162 | }); 2163 | } 2164 | -------------------------------------------------------------------------------- /server/common/base_controller.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 基础Controller类。支持路径映射、自动参数注入,自动参数类型转换 3 | * 4 | * ```ts 5 | * @Controller("/test") 6 | * class Test extends BaseController { 7 | * @Post("/test") 8 | * @Get("/test") 9 | * test( 10 | * @Param("name") name: string, 11 | * @Param("time") date: Date, 12 | * @Param("show") show: boolean, 13 | * @Param("num") num: number 14 | * ) { 15 | * return { name, date, show, num }; 16 | * } 17 | * } 18 | * ``` 19 | */ 20 | 21 | import { Router, RouterContext, Status } from "oak"; 22 | import "./Reflect.ts"; 23 | import { State } from "./state.ts"; 24 | import { getAllRequestParams } from "./util.ts"; 25 | import { SessionSchema } from "./session.ts"; 26 | 27 | export const router = new Router(); 28 | 29 | function registerRoute( 30 | method: string, 31 | path: string, 32 | controller: BaseController, 33 | actionName: string, 34 | interceptor?: (ctx: RouterContext, params: any) => Promise 35 | ) { 36 | (router as any)[method](path, async (ctx: RouterContext) => { 37 | controller.ctx = ctx as any; 38 | 39 | // 获取方法参数信息 40 | const params: any[] = 41 | Reflect.getMetadata("http:params", controller, actionName) || []; 42 | 43 | const extraParams = 44 | Reflect.getMetadata(`http:extra`, controller, actionName) || {}; 45 | 46 | // 获取请求参数 47 | const requestParams = { 48 | ...extraParams, 49 | ...(await getAllRequestParams(ctx)) 50 | }; 51 | 52 | // 转换参数类型,填充参数值 53 | const data = params.map(({ name, type }) => { 54 | const val: string = requestParams[name]; 55 | const Type = type; 56 | if (type === String) { 57 | return val; 58 | } else if (type === Boolean) { 59 | if (!val) { 60 | requestParams[name] = false; 61 | } else { 62 | requestParams[name] = 63 | val.toLowerCase() !== "false" && val.toLowerCase() !== "0"; 64 | } 65 | return requestParams[name]; 66 | } else if (!val) { 67 | return undefined; 68 | } else if (type === Number) { 69 | requestParams[name] = new Number(val).valueOf(); 70 | return requestParams[name]; 71 | } else { 72 | requestParams[name] = new Type(val); 73 | return requestParams[name]; 74 | } 75 | }); 76 | 77 | const canNext = interceptor ? await interceptor(ctx, requestParams) : true; 78 | // 调用action 79 | if (canNext) { 80 | const action = (controller as any)[actionName]; 81 | return await action.call(controller, ...(data || [])); 82 | } 83 | }); 84 | } 85 | 86 | export class BaseController { 87 | public ctx!: RouterContext; 88 | get session(): SessionSchema { 89 | return this.ctx.state.session; 90 | } 91 | get cookies() { 92 | return this.ctx.state.cookies; 93 | } 94 | redirect(url: string) { 95 | this.ctx.response.headers.append("Location", url); 96 | this.ctx.response.status = Status.Found; 97 | } 98 | } 99 | 100 | interface ControllerConstructor { 101 | new (): BaseController; 102 | } 103 | 104 | // Controller 装饰器 105 | export function Controller( 106 | parentPath: string = "", 107 | interceptor?: (ctx: RouterContext, params: any) => Promise 108 | ) { 109 | return function(target: T) { 110 | const _target = target.prototype; 111 | console.log("\nRegister Controller:", target.name); 112 | const register = (method: string) => { 113 | const meta = Reflect.getMetadata(`http:method:${method}`, _target) || {}; 114 | for (const actionPath of Reflect.ownKeys(meta) as string[]) { 115 | const path = parentPath + actionPath; 116 | console.log( 117 | method.toUpperCase(), 118 | path, 119 | "=>", 120 | `${target.name}.${meta[actionPath]}` 121 | ); 122 | registerRoute(method, path, _target, meta[actionPath], interceptor); 123 | } 124 | }; 125 | register("get"); 126 | register("post"); 127 | }; 128 | } 129 | 130 | // Controller http action 装饰器 131 | export function httpMethod(method: string, url: string, params?: any) { 132 | return function( 133 | target: any, 134 | methodName: string, 135 | descriptor: PropertyDescriptor 136 | ) { 137 | const meta = Reflect.getMetadata(`http:method:${method}`, target) || {}; 138 | meta[url] = methodName; 139 | Reflect.defineMetadata(`http:method:${method}`, meta, target); 140 | Reflect.defineMetadata(`http:extra`, params, target, methodName); 141 | }; 142 | } 143 | 144 | // Controller get action 装饰器 145 | export function Get(url: string, params?: any) { 146 | return httpMethod("get", url, params); 147 | } 148 | 149 | // Controller post action 装饰器 150 | export function Post(url: string, params?: any) { 151 | return httpMethod("post", url, params); 152 | } 153 | 154 | // Action 参数装饰器 155 | export function Param(name: string) { 156 | return (target: any, methodName: string, propertyIndex: number) => { 157 | const meta = Reflect.getMetadata("http:params", target, methodName) || []; 158 | const types = Reflect.getMetadata("design:paramtypes", target, methodName); 159 | meta[propertyIndex] = { name, type: types[propertyIndex] }; 160 | Reflect.defineMetadata("http:params", meta, target, methodName); 161 | }; 162 | } 163 | -------------------------------------------------------------------------------- /server/common/cookis.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "oak"; 2 | 3 | export async function cookie(ctx: Context, next: () => void) { 4 | const cookie = ctx.request.headers.get("cookie"); 5 | const cookies = new Map(); 6 | if (cookie) { 7 | cookie.split(";").forEach(pair => { 8 | const kv = pair.split("="); 9 | cookies.set(kv[0].trim(), kv[1].trim()); 10 | }); 11 | } 12 | ctx.state.cookies = cookies; 13 | await next(); 14 | } 15 | -------------------------------------------------------------------------------- /server/common/json_result.ts: -------------------------------------------------------------------------------- 1 | import * as logger from "logger"; 2 | import { Context, Status } from "oak"; 3 | 4 | export default async function jsonResultConvertor( 5 | ctx: Context, 6 | next: Function 7 | ) { 8 | let result: any; 9 | let success = true; 10 | let msg: string = ""; 11 | try { 12 | result = await next(); 13 | } catch (error) { 14 | logger.error(error); 15 | success = false; 16 | msg = error.message; 17 | } 18 | if (result === undefined && !msg) { 19 | return; 20 | } 21 | if (typeof result === "object" || msg) { 22 | ctx.response.body = { 23 | data: result, 24 | msg, 25 | success 26 | }; 27 | } else { 28 | ctx.response.body = result; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /server/common/mail.ts: -------------------------------------------------------------------------------- 1 | import { SmtpClient } from "../deps.ts"; 2 | 3 | const client = new SmtpClient({}); 4 | 5 | export async function sendMail() {} 6 | -------------------------------------------------------------------------------- /server/common/mongo.ts: -------------------------------------------------------------------------------- 1 | import { init, MongoClient } from "mongo"; 2 | import { CONFIG_MONGODB_URI } from "../config.ts"; 3 | 4 | await init(); 5 | 6 | const mongo = new MongoClient(); 7 | const db = mongo.database("denocn"); 8 | 9 | export { db }; 10 | export async function connectMongodb() { 11 | mongo.connectWithUri(CONFIG_MONGODB_URI); 12 | } 13 | -------------------------------------------------------------------------------- /server/common/query-util.ts: -------------------------------------------------------------------------------- 1 | export function lookupUser(field: string, as: string) { 2 | return { 3 | $lookup: { 4 | from: "users", 5 | let: { [field]: `$${field}` }, 6 | as, 7 | pipeline: [ 8 | { 9 | $match: { 10 | $expr: { 11 | $eq: [`$_id`, `$$${field}`] 12 | } 13 | } 14 | }, 15 | { 16 | $project: { 17 | name: 1, 18 | nick_name: 1, 19 | avatar: 1 20 | } 21 | } 22 | ] 23 | } 24 | }; 25 | } 26 | 27 | export function lookupReply(field: string, as: string) { 28 | return { 29 | $lookup: { 30 | from: "replies", 31 | let: { [field]: `$${field}` }, 32 | as, 33 | pipeline: [ 34 | { 35 | $match: { 36 | $expr: { 37 | $eq: [`$_id`, `$$${field}`] 38 | } 39 | } 40 | }, 41 | lookupUser("author_id", "author"), 42 | { $unwind: "$author" } 43 | ] 44 | } 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /server/common/redis.ts: -------------------------------------------------------------------------------- 1 | import { connect, Redis } from "redis"; 2 | import { redis as config } from "../config.ts"; 3 | 4 | let redis: Redis; 5 | 6 | export function getRedis() { 7 | return redis ? redis : connectRedis(); 8 | } 9 | 10 | export async function connectRedis() { 11 | const { host: hostname, port } = config; 12 | redis = await connect({ hostname, port }); 13 | if (config.password) { 14 | await redis.auth(config.password); 15 | } 16 | return redis; 17 | } 18 | -------------------------------------------------------------------------------- /server/common/render.ts: -------------------------------------------------------------------------------- 1 | import appRender from "../../public/server.js"; 2 | import { startup, website } from "../config.ts"; 3 | 4 | const decoder = new TextDecoder(); 5 | const htmlTemplate = decoder.decode(await Deno.readFile("./public/index.html")); 6 | 7 | export async function render( 8 | params: { url: string; search?: string }, 9 | data: any = {} 10 | ): Promise { 11 | const { html, state, meta } = await appRender({ 12 | api_base: `http://127.0.0.1:${startup.port}`, 13 | params, 14 | data 15 | }); 16 | return htmlTemplate 17 | .replace("${page_title}", meta?.title || website.title) 18 | .replace("${content}", html) 19 | .replace( 20 | ``, 21 | ` 22 | 23 | ` 25 | ) 26 | .replace( 27 | ``, 28 | `` 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /server/common/session.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "asserts"; 2 | import { ObjectId } from "mongo"; 3 | import { Context } from "oak"; 4 | import { MongoModel, WithId } from "../models/base.ts"; 5 | import { UserSchema } from "../models/user.ts"; 6 | import { State } from "./state.ts"; 7 | 8 | const SESSION_KEY = "oaksessionid"; 9 | const EXPIRE = 60 * 60 * 24; // one day 10 | 11 | export class SessionSchema { 12 | user?: UserSchema; 13 | updated_at?: Date; 14 | } 15 | 16 | const SessionModel = new MongoModel(SessionSchema, "sessions"); 17 | 18 | export async function redisSession( 19 | ctx: Context, 20 | next: () => Promise 21 | ) { 22 | let sessionId: ObjectId; 23 | let session: (SessionSchema & WithId) | null; 24 | try { 25 | sessionId = ObjectId(ctx.state.cookies.get(SESSION_KEY) || ""); 26 | session = await SessionModel.findById(sessionId); 27 | assert(session); 28 | } catch (err) { 29 | session = await SessionModel.create({ user: undefined }); 30 | sessionId = session._id!; 31 | const cookie = `${SESSION_KEY}=${sessionId.$oid}; Path=/; HttpOnly`; 32 | ctx.response.headers.append(`Set-Cookie`, cookie); 33 | } 34 | ctx.state.session = session; 35 | await next(); 36 | await SessionModel.update({ ...session, updated_at: new Date() }); 37 | } 38 | -------------------------------------------------------------------------------- /server/common/state.ts: -------------------------------------------------------------------------------- 1 | export interface State { 2 | cookies: Map; 3 | session: any; 4 | } 5 | -------------------------------------------------------------------------------- /server/common/util.ts: -------------------------------------------------------------------------------- 1 | import { RouterContext } from "oak"; 2 | import { dayjs } from "../deps.ts"; 3 | 4 | export function dateFormat(format: string, date: Date) { 5 | return dayjs(date).format(format); 6 | } 7 | 8 | export async function getAllRequestParams(ctx: RouterContext) { 9 | let params: any = {}; 10 | for (const pair of ctx.request.searchParams.entries()) { 11 | params[pair[0]] = pair[1]; 12 | } 13 | const body = await ctx.request.body(); 14 | if (body.type === "form") { 15 | const searchParams = body.value as URLSearchParams; 16 | for (const key of searchParams.keys()) { 17 | params[key] = searchParams.get(key); 18 | } 19 | } else if (body.type === "json") { 20 | params = { ...params, ...body.value }; 21 | } 22 | return { ...ctx.params, ...params }; 23 | } 24 | 25 | export function isSpider(ctx: RouterContext) { 26 | let ua = ctx.request.headers.get("user-agent") || ""; 27 | ua = ua.toLowerCase(); 28 | if (ua.includes("spider")) { 29 | return true; 30 | } 31 | if (ua.includes("googlebot")) { 32 | return true; 33 | } 34 | if (ua.includes("webspider")) { 35 | return true; 36 | } 37 | return false; 38 | } 39 | -------------------------------------------------------------------------------- /server/config.ts: -------------------------------------------------------------------------------- 1 | import "https://deno.land/x/dotenv@v0.2.3/load.ts"; 2 | 3 | const { 4 | GITHUB_SECRET, 5 | GITHUB_CLIENTID, 6 | GITHUB_REDIRECT, 7 | MONGODB_URI 8 | } = Deno.env(); 9 | 10 | export const CONFIG_MONGODB_URI = MONGODB_URI || "mongodb://127.0.0.1:27017"; 11 | 12 | // Web启动配置 13 | export const startup = { 14 | host: "0.0.0.0", 15 | port: 3000 16 | }; 17 | 18 | export const website = { 19 | title: "DENOCN: Deno中文社区 | Deno中国", 20 | description: 21 | "DENOCN 社区为国内最专业的 Deno 开源技术社区,致力于 Deno 技术学习与研究。", 22 | domain: "https://denocn.org" 23 | }; 24 | 25 | export const github = { 26 | clientId: GITHUB_CLIENTID || "32503fcf79dea41e69af", 27 | clientSecret: GITHUB_SECRET || "7f861c6aa9385e910fdf5b680792a442ee3f3ca2", 28 | redirectUri: GITHUB_REDIRECT || "http://127.0.0.1:1234/api/user/github" 29 | }; 30 | 31 | export const smtp = { 32 | server: "" 33 | }; 34 | -------------------------------------------------------------------------------- /server/controller.ts: -------------------------------------------------------------------------------- 1 | import { Application } from "oak"; 2 | import { router } from "./common/base_controller.ts"; 3 | import { State } from "./common/state.ts"; 4 | 5 | export async function loadControllers() { 6 | const dirs = await Deno.readdir("./server/controllers"); 7 | for (const file of dirs) { 8 | await import(`./controllers/${file.name}`); 9 | } 10 | } 11 | 12 | export default async function initControllers(app: Application) { 13 | await loadControllers(); 14 | const routes = router.routes(); 15 | app.use(routes); 16 | console.log("\nInit Controllers"); 17 | } 18 | -------------------------------------------------------------------------------- /server/controllers/file.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BaseController, 3 | Controller, 4 | Param, 5 | Post 6 | } from "../common/base_controller.ts"; 7 | import { uuid } from "../deps.ts"; 8 | 9 | @Controller("/file") 10 | export default class FileController extends BaseController { 11 | @Post("/upload") 12 | async upload() { 13 | const body = await this.ctx.request.body(); 14 | body.type; 15 | } 16 | 17 | @Post("/base64upload") 18 | async base64upload(@Param("data") data: string) { 19 | let format = ""; 20 | if (data.indexOf("data:image/jpeg;base64,") > -1) { 21 | data = data.replace("data:image/jpeg;base64,", ""); 22 | format = ".jpg"; 23 | } else if (data.indexOf("data:image/png;base64,") > -1) { 24 | data = data.replace("data:image/png;base64,", ""); 25 | format = ".png"; 26 | } else { 27 | throw new Error("不支持的格式"); 28 | } 29 | 30 | const str = atob(data); 31 | 32 | const bytes: number[] = []; 33 | for (const char of str) { 34 | bytes.push(char.charCodeAt(0)); 35 | } 36 | 37 | const path = "upload/" + uuid.generate() + format; 38 | Deno.mkdirSync("./public/upload", true); 39 | Deno.writeFileSync("./public/" + path, new Uint8Array(bytes)); 40 | 41 | return { path: "https://data.denocn.org/" + path }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /server/controllers/reply.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from "https://deno.land/x/mongo@v0.4.0/ts/types.ts"; 2 | import { 3 | BaseController, 4 | Controller, 5 | Get, 6 | Param, 7 | Post 8 | } from "../common/base_controller.ts"; 9 | import { lookupUser } from "../common/query-util.ts"; 10 | import { Reply, ReplySchema } from "../models/reply.ts"; 11 | import { Topic } from "../models/topic.ts"; 12 | @Controller("/api/reply") 13 | class ReplyController extends BaseController { 14 | @Get("/list/:topicId") 15 | async list( 16 | @Param("topicId") topicId: string, 17 | @Param("size") size: number = 10, 18 | @Param("page") page: number 19 | ) { 20 | const replies: ReplySchema[] = await Reply.aggregate([ 21 | { 22 | $match: { 23 | topic_id: ObjectId(topicId) 24 | } 25 | }, 26 | lookupUser("author_id", "author"), 27 | { $unwind: "$author" } 28 | ]); 29 | 30 | return { 31 | list: replies, 32 | total: replies.length, 33 | page, 34 | size 35 | }; 36 | } 37 | 38 | @Post("/add") 39 | async add( 40 | @Param("topic_id") topicId: string, 41 | @Param("content") content: string, 42 | @Param("reply_to") replyTo: string 43 | ) { 44 | const topic = await Topic.findById(topicId); 45 | if (!this.session.user) throw new Error("用户未登录"); 46 | if (!topic) throw new Error("主题不存在"); 47 | 48 | const reply = await Reply.create({ 49 | author_id: this.session.user._id, 50 | topic_id: topic._id, 51 | content, 52 | reply_to: replyTo ? ObjectId(replyTo) : undefined 53 | }); 54 | 55 | Topic.update({ 56 | _id: ObjectId(topicId), 57 | last_reply_id: reply._id, 58 | last_reply_time: new Date(), 59 | reply_count: topic.reply_count + 1 60 | }); 61 | 62 | return reply; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /server/controllers/topic.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from "mongo"; 2 | import { 3 | BaseController, 4 | Controller, 5 | Get, 6 | Param, 7 | Post 8 | } from "../common/base_controller.ts"; 9 | import { lookupReply, lookupUser } from "../common/query-util.ts"; 10 | import { Reply } from "../models/reply.ts"; 11 | import { Topic, TopicType } from "../models/topic.ts"; 12 | import { User } from "../models/user.ts"; 13 | 14 | @Controller("/api") 15 | class TopicController extends BaseController { 16 | @Get("/topic/detail/:id") 17 | async detail(@Param("id") id: string) { 18 | const topic = await Topic.findById(id); 19 | if (!topic) return null; 20 | 21 | topic.view_count++; 22 | await Topic.update(topic); 23 | 24 | const user = await User.findById(topic.author_id); 25 | const { last_reply_id } = topic; 26 | const lastReply = last_reply_id 27 | ? await Reply.findById(last_reply_id) 28 | : null; 29 | 30 | const replyUser = 31 | lastReply && lastReply.author_id 32 | ? await User.findById(lastReply.author_id) 33 | : null; 34 | 35 | return { 36 | ...topic, 37 | author: user ? { ...user, github_token: null, password: null } : null, 38 | last_reply: lastReply ? { ...lastReply, author: replyUser } : null 39 | }; 40 | } 41 | 42 | @Post("/topic/add") 43 | async add( 44 | @Param("content") content: string, 45 | @Param("type") type: TopicType, 46 | @Param("title") title: string 47 | ) { 48 | if (!this.session.user) throw new Error("用户未登录"); 49 | if (!content || content.length < 20) { 50 | throw new Error("内容长度最少20个字符"); 51 | } 52 | if (!title || title.length < 5) { 53 | throw new Error("标题长度至少5个字符"); 54 | } 55 | const topic = await Topic.create({ 56 | title, 57 | content, 58 | author_id: this.session.user._id, 59 | type: type 60 | }); 61 | 62 | return topic; 63 | } 64 | 65 | @Post("/topic/edit") 66 | async edit( 67 | @Param("id") id: string, 68 | @Param("content") content: string, 69 | @Param("type") type: TopicType, 70 | @Param("title") title: string 71 | ) { 72 | if (!this.session.user) throw new Error("用户未登录"); 73 | if (!content || content.length < 10) { 74 | throw new Error("内容长度最少10个字符"); 75 | } 76 | if (!title || title.length < 5) { 77 | throw new Error("标题长度至少5个字符"); 78 | } 79 | await Topic.update({ 80 | _id: ObjectId(id), 81 | title, 82 | content, 83 | author_id: this.session.user._id, 84 | type: type 85 | }); 86 | 87 | return { id }; 88 | } 89 | 90 | @Get("/topic/delete/:id") 91 | async delete(@Param("id") id: string) { 92 | const user = this.session.user; 93 | if (!user) throw new Error("未登录"); 94 | const topic = await Topic.findById(id); 95 | if (topic?.author_id.$oid !== user._id.$oid) { 96 | throw new Error("你没有删除权限"); 97 | } 98 | await Topic.update({ _id: ObjectId(id), deleted: true }); 99 | return { data: "success" }; 100 | } 101 | 102 | @Get("/topic/:type") 103 | async list( 104 | @Param("type") type: "all" | "new" | "good" | "cold" | "job", 105 | @Param("page") page: number = 1, 106 | @Param("size") size: number = 10 107 | ) { 108 | const filter: any = {}; 109 | let sort: any = { $sort: { created_at: -1, "last_reply.created_at": -1 } }; 110 | 111 | switch (type) { 112 | case "job": 113 | filter.type = "招聘"; 114 | break; 115 | case "good": 116 | filter.is_good = true; 117 | break; 118 | case "cold": 119 | sort = { 120 | $sort: { 121 | view_count: 1, 122 | reply_count: 1, 123 | created_at: -1, 124 | "last_reply.created_at": -1 125 | } 126 | }; 127 | break; 128 | case "new": 129 | sort = { $sort: { created_at: -1, "last_reply.created_at": -1 } }; 130 | break; 131 | } 132 | 133 | const total = await Topic.count(filter); 134 | const topics = await Topic.aggregate([ 135 | { $match: filter }, 136 | lookupUser("author_id", "author"), 137 | lookupReply("last_reply_id", "last_reply"), 138 | { $unwind: { path: "$author" } }, 139 | { $unwind: { path: "$last_reply", preserveNullAndEmptyArrays: true } }, 140 | sort, 141 | { $skip: (page - 1) * size }, 142 | { $limit: size } 143 | ]); 144 | 145 | return { 146 | page, 147 | size, 148 | total, 149 | list: topics 150 | }; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /server/controllers/user.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BaseController, 3 | Controller, 4 | Get, 5 | Param 6 | } from "../common/base_controller.ts"; 7 | import { github } from "../config.ts"; 8 | import { User } from "../models/user.ts"; 9 | 10 | @Controller("/api/user") 11 | export default class UserController extends BaseController { 12 | @Get("/login") 13 | async login(@Param("redirect") url: string = "/") { 14 | if (!this.ctx.state.session.user) { 15 | const state = Math.round(Date.now() * Math.random()); 16 | url = 17 | `https://github.com/login/oauth/authorize` + 18 | `?scope=user&allow_signup=true` + 19 | `&client_id=${github.clientId}` + 20 | `&state=${state}` + 21 | `&redirect_uri=${github.redirectUri}`; 22 | } 23 | this.redirect(url); 24 | } 25 | 26 | @Get("/logout") 27 | async logout() { 28 | this.ctx.state.session.user = null; 29 | this.redirect("/"); 30 | } 31 | 32 | @Get("/github") 33 | async github(@Param("code") code: string, @Param("state") state: string) { 34 | const body = new URLSearchParams(); 35 | body.append("client_id", github.clientId); 36 | body.append("client_secret", github.clientSecret); 37 | body.append("redirect_uri", github.redirectUri); 38 | body.append("code", code); 39 | body.append("state", state); 40 | let result = await fetch(`https://github.com/login/oauth/access_token`, { 41 | method: "POST", 42 | headers: { "Content-Type": "application/x-www-form-urlencoded" }, 43 | body 44 | }); 45 | const params = new URLSearchParams(await result.text()); 46 | const accessToken = params.get("access_token"); 47 | 48 | result = await fetch( 49 | `https://api.github.com/user?access_token=${accessToken}`, 50 | { headers: { "User-Agent": "denocn agent" } } 51 | ); 52 | 53 | const info = await result.json(); 54 | let user = await User.findOne({ github_id: info.id }); 55 | 56 | const userInfo: any = { 57 | github_id: info.id, 58 | github_name: info.login, 59 | name: info.login, 60 | github_token: accessToken, 61 | nick_name: info.name, 62 | location: info.location, 63 | avatar: `https://avatars1.githubusercontent.com/u/${info.id}?v=4`, 64 | email: info.email, 65 | company: info.company, 66 | home_page: info.blog, 67 | signature: info.bio 68 | }; 69 | 70 | if (user) { 71 | userInfo._id = user._id; 72 | await User.update(userInfo); 73 | } else { 74 | user = await User.create(userInfo); 75 | } 76 | 77 | this.session.user = user; 78 | this.redirect(`/user/${user._id.$oid}`); 79 | } 80 | 81 | @Get("/info/:id") 82 | async info(@Param("id") id: string) { 83 | const user = await User.findById(id); 84 | return { ...user, password: null, github_token: null }; 85 | } 86 | 87 | @Get("/me") 88 | async me() { 89 | const user = this.ctx.state.session.user; 90 | return user || null; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /server/deps.ts: -------------------------------------------------------------------------------- 1 | export { v4 as uuid } from "std/uuid/mod.ts"; 2 | export { default as Marked } from "https://raw.githubusercontent.com/olaven/marked/strict-types/main.ts"; 3 | export { Template } from "https://raw.githubusercontent.com/zekth/deno_tiny_templates/master/mod.ts"; 4 | import { dew as dewDayjs } from "https://dev.jspm.io/npm:dayjs@1.8.21/dayjs.min.dew.js"; 5 | 6 | export const dayjs: any = dewDayjs(); 7 | -------------------------------------------------------------------------------- /server/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module React {} 2 | declare module "*.less"; 3 | declare module "*.css"; 4 | declare module "*.svg"; 5 | declare module "highlight.js"; 6 | -------------------------------------------------------------------------------- /server/models/base.ts: -------------------------------------------------------------------------------- 1 | import { Collection, ObjectId } from "mongo"; 2 | import { db } from "../common/mongo.ts"; 3 | 4 | export interface WithId { 5 | _id?: ObjectId; 6 | } 7 | 8 | export class MongoModel { 9 | private collection: Collection; 10 | 11 | constructor(private readonly modelType: { new (): Schema }, name: string) { 12 | this.collection = db.collection(name); 13 | } 14 | 15 | private mergeDefaults(doc: Partial) { 16 | const _doc: any = { ...new this.modelType(), ...doc }; 17 | return _doc; 18 | } 19 | 20 | public async findById(_id: ObjectId | string): Promise { 21 | if (typeof _id === "string") _id = ObjectId(_id); 22 | return this.collection.findOne({ _id }); 23 | } 24 | 25 | public async findOne(filter?: Partial): Promise { 26 | return this.collection.findOne(filter); 27 | } 28 | 29 | public async find(filter?: Object): Promise { 30 | return this.collection.find(filter); 31 | } 32 | 33 | public async aggregate(pipleline: Object[]): Promise { 34 | return this.collection.aggregate(pipleline); 35 | } 36 | 37 | public async count(filter?: Object): Promise { 38 | return this.collection.count(filter); 39 | } 40 | 41 | public async create(doc: Partial): Promise { 42 | const _doc: any = this.mergeDefaults(doc); 43 | const insertId = await this.collection.insertOne(_doc); 44 | _doc._id = insertId; 45 | return _doc; 46 | } 47 | 48 | public async update(doc: Partial & WithId) { 49 | const { _id, ...updateDoc } = this.mergeDefaults(doc); 50 | this.collection.updateOne({ _id }, { $set: updateDoc }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /server/models/reply.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from "mongo"; 2 | import { MongoModel } from "./base.ts"; 3 | 4 | export class ReplySchema { 5 | topic_id?: ObjectId; 6 | reply_to?: ObjectId; 7 | author_id!: ObjectId; 8 | content!: string; 9 | deleted: boolean = false; 10 | 11 | created_at?: Date = new Date(); 12 | updated_at?: Date = new Date(); 13 | } 14 | 15 | export const Reply = new MongoModel(ReplySchema, "replies"); 16 | -------------------------------------------------------------------------------- /server/models/topic.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from "mongo"; 2 | import { MongoModel } from "./base.ts"; 3 | 4 | export type TopicType = "分享" | "问答" | "招聘"; 5 | export class TopicSchema { 6 | _id!: ObjectId; 7 | type: TopicType = "分享"; 8 | title!: string; 9 | author_id!: ObjectId; 10 | content!: string; 11 | is_top: boolean = false; // 置顶 12 | is_good: boolean = false; // 精华 13 | is_lock: boolean = false; // 锁定 14 | reply_count: number = 0; 15 | view_count: number = 0; 16 | collect_count: number = 0; 17 | last_reply_id?: ObjectId; 18 | last_reply_time?: Date; 19 | tags?: string[] = []; 20 | deleted: boolean = false; 21 | 22 | created_at?: Date = new Date(); 23 | updated_at?: Date = new Date(); 24 | } 25 | 26 | export const Topic = new MongoModel(TopicSchema, "topics"); 27 | -------------------------------------------------------------------------------- /server/models/user.ts: -------------------------------------------------------------------------------- 1 | import { ObjectId } from "mongo"; 2 | import { MongoModel } from "./base.ts"; 3 | 4 | export class UserSchema { 5 | _id!: ObjectId; 6 | name!: string; 7 | nick_name!: string; 8 | password?: string; 9 | email?: string; 10 | home_page?: string; 11 | avatar?: string; 12 | location?: string; 13 | signature?: string; 14 | company?: string; 15 | 16 | github_id?: string; 17 | github_name?: string; 18 | github_token?: string; 19 | 20 | level?: string; 21 | score: number = 0; 22 | 23 | created_at?: Date = new Date(); 24 | } 25 | 26 | export const User = new MongoModel(UserSchema, "users"); 27 | -------------------------------------------------------------------------------- /server/server.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "asserts"; 2 | import * as logger from "logger"; 3 | import { Application, HttpError, send, Status } from "oak"; 4 | import * as path from "path"; 5 | import { cookie } from "./common/cookis.ts"; 6 | import jsonResultConvertor from "./common/json_result.ts"; 7 | import "./common/mongo.ts"; 8 | import { connectMongodb } from "./common/mongo.ts"; 9 | import { render } from "./common/render.ts"; 10 | import { redisSession } from "./common/session.ts"; 11 | import { State } from "./common/state.ts"; 12 | import * as config from "./config.ts"; 13 | import initControllers from "./controller.ts"; 14 | const app = new Application(); 15 | 16 | await logger.setup({}); 17 | await connectMongodb(); 18 | 19 | // Error handler middleware 20 | app.use(async (context, next) => { 21 | try { 22 | await next(); 23 | } catch (e) { 24 | console.log("dddd"); 25 | if (e instanceof HttpError) { 26 | context.response.status = e.status as any; 27 | if (e.expose) { 28 | context.response.body = await render( 29 | { url: "/error" }, 30 | { 31 | title: `${e.status}`, 32 | error: e.message 33 | } 34 | ); 35 | } else { 36 | context.response.body = await render( 37 | { url: "/error" }, 38 | { 39 | title: `${e.status} - ${Status[e.status]}` 40 | } 41 | ); 42 | } 43 | } else if (e instanceof Error) { 44 | context.response.status = 500; 45 | context.response.body = await render( 46 | { url: "/error" }, 47 | { 48 | title: "500 - Internal Server Error", 49 | error: `Unhandled Error: ${e.message}` 50 | } 51 | ); 52 | logger.error(`Unhandled Error: ${e.message}`); 53 | logger.error(JSON.stringify(e.stack)); 54 | } 55 | } 56 | }); 57 | 58 | // Logger 59 | app.use(async (ctx, next) => { 60 | const start = Date.now(); 61 | await next(); 62 | const ms = Date.now() - start; 63 | ctx.response.headers.set("X-Response-Time", `${ms}ms`); 64 | logger.info(`${ctx.request.method} ${ctx.request.url} - ${ms}ms`); 65 | }); 66 | 67 | app.use(cookie); 68 | app.use(redisSession); 69 | app.use(jsonResultConvertor); 70 | 71 | await initControllers(app); 72 | 73 | app.use(async ctx => { 74 | const requestPath = ctx.request.path; 75 | try { 76 | const resolvePath = await send(ctx, requestPath, { 77 | root: path.resolve(Deno.cwd(), `./public`) 78 | }); 79 | assert(resolvePath); 80 | } catch (err) { 81 | ctx.response.body = await render({ 82 | url: requestPath, 83 | search: ctx.request.search 84 | }); 85 | } 86 | }); 87 | 88 | const addr = `${config.startup.host}:${config.startup.port}`; 89 | logger.info(`Server statup on ${addr}\n`); 90 | await app.listen(addr); 91 | -------------------------------------------------------------------------------- /startup.sh: -------------------------------------------------------------------------------- 1 | rm -rf public 2 | cd web 3 | npm install 4 | npm run build 5 | cp -rf ./dist/ ../public 6 | cd ../ 7 | 8 | deno -c tsconfig.json --importmap importmap.json -A server/server.ts 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "module": "esnext", 5 | "target": "esnext", 6 | "strict": false, 7 | "noImplicitAny": false, 8 | "experimentalDecorators": true, 9 | "emitDecoratorMetadata": true 10 | } 11 | } 12 | --------------------------------------------------------------------------------