Skip to content

Commit

Permalink
feat: move feiyun-handler to this project, rename to @feiyun/handler
Browse files Browse the repository at this point in the history
  • Loading branch information
hxg2050 committed Mar 13, 2024
1 parent 703be8b commit 31e9d89
Show file tree
Hide file tree
Showing 13 changed files with 983 additions and 31 deletions.
1 change: 1 addition & 0 deletions class-transformer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from 'class-transformer';
1 change: 1 addition & 0 deletions class-validator/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from 'class-validator';
1 change: 1 addition & 0 deletions packages/handler/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/types
1 change: 1 addition & 0 deletions packages/handler/class-transformer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from 'class-transformer'
6 changes: 6 additions & 0 deletions packages/handler/class-transformer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "../types/class-transformer/index.d.ts"
}
1 change: 1 addition & 0 deletions packages/handler/class-validator/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from 'class-validator'
6 changes: 6 additions & 0 deletions packages/handler/class-validator/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "../types/class-validator/index.d.ts"
}
69 changes: 69 additions & 0 deletions packages/handler/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"name": "feiyun-handler",
"version": "0.0.21",
"license": "MIT",
"type": "module",
"main": "./src/index.ts",
"publishConfig": {
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./types/src/index.d.ts",
"exports": {
".": {
"types": "./types/src/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./class-validator": {
"types": "./types/class-validator/index.d.ts",
"import": "./class-validator/dist/index.mjs",
"require": "./class-validator/dist/index.cjs"
},
"./class-transformer": {
"types": "./types/class-transformer/index.d.ts",
"import": "./class-transformer/dist/index.mjs",
"require": "./class-transformer/dist/index.cjs"
}
}
},
"files": [
"dist",
"class-validator/dist",
"class-validator/package.json",
"class-transformer/dist",
"class-transformer/package.json",
"types"
],
"exports": {
".": {
"import": "./src/index.ts"
},
"./class-validator": {
"import": "./class-validator/index.ts"
},
"./class-transformer": {
"import": "./class-transformer/index.ts"
}
},
"scripts": {
"build": "rimraf **/dist && tsc && rollup -c",
"prepublishOnly": "pnpm build"
},
"devDependencies": {
"@feiyun/server": "workspace:^",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.6",
"@types/node": "^20.11.19",
"feiyun": "^0.2.3",
"reflect-metadata": "^0.2.1",
"rollup": "^4.12.0",
"rollup-plugin-ts": "^3.4.5",
"tslib": "^2.6.2",
"typescript": "^5.3.3"
},
"dependencies": {
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"glob": "^10.3.10"
}
}
53 changes: 53 additions & 0 deletions packages/handler/rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import typescript from '@rollup/plugin-typescript';
import resolve from '@rollup/plugin-node-resolve';

import { defineConfig } from 'rollup';
export default defineConfig([{
input: 'src/index.ts',
output: [{
file: 'dist/index.cjs',
format: 'cjs'
}, {
file: 'dist/index.mjs',
format: 'esm'
}],
plugins: [typescript({
compilerOptions: {
outDir: "./dist",
declaration: false,
declarationDir: undefined
}
})]
}, {
input: 'class-transformer/index.ts',
output: [{
file: 'class-transformer/dist/index.cjs',
format: 'cjs'
}, {
file: 'class-transformer/dist/index.mjs',
format: 'esm'
}],
plugins: [typescript({
compilerOptions: {
outDir: "./class-validator/dist",
declaration: false,
declarationDir: undefined
}
})]
}, {
input: 'class-validator/index.ts',
output: [{
file: 'class-validator/dist/index.cjs',
format: 'cjs'
}, {
file: 'class-validator/dist/index.mjs',
format: 'esm'
}],
plugins: [typescript({
compilerOptions: {
outDir: "./class-validator/dist",
declaration: false,
declarationDir: undefined
}
})]
}]);
208 changes: 208 additions & 0 deletions packages/handler/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import "reflect-metadata";
import { glob } from "glob";
import path from "node:path";
import { plainToInstance } from "class-transformer";
import { validate } from "class-validator";

import type { Context, FeiyunMiddleware } from 'feiyun'
const PATH_METADATA = 'path';
const METHOD_METADATA = 'method';
const PARAM_METADATA = 'param';
const PARAM_VALIDATE_METADATA = 'param_validate'

/**
* 类装饰
* @param path 路径
* @returns
*/
export const Handler = (path?: string): ClassDecorator => {
return target => {
path = typeof path === 'undefined' ? target.name : path;
Reflect.defineMetadata(PATH_METADATA, path, target);
}
}

/**
* 方法装饰
* @param path 路径
* @returns
*/
export const Method = (path?: string): MethodDecorator => {
return (target, key, descriptor: any) => {
if (typeof key === 'string') {
path = typeof path === 'undefined' ? key : path;
Reflect.defineMetadata(METHOD_METADATA, path, descriptor.value);
}
}
}

/**
* 参数装饰
* @param name 参数名
* @param checker 检查器/处理器
* @returns
*/
export const Param = (name: string, checker?: (val: any) => void): ParameterDecorator => {
return (target, key, index) => {
if (!key) {
return;
}

const param = Reflect.get(target, key);
let params = Reflect.getMetadata(PARAM_METADATA, param);
if (!params) {
params = [];
}
params.push([index, name, checker]);
Reflect.defineMetadata(PARAM_METADATA, params, param);
}
}

/**
* 声明参数需要进行校验,并用目标类型重新实例
* @param target
* @param key
* @param index
*/
export const ParamType: ParameterDecorator = (target, key, index) => {
if (!key) {
return;
}
const types = Reflect.getMetadata("design:paramtypes", target, key);
// console.log(Object.getOwnPropertyDescriptors(Reflect.ownKeys(types[index].prototype)));
Reflect.defineMetadata(PARAM_VALIDATE_METADATA, types[index], Reflect.get(target, key));
}


const METHOD_DOC_METADATA = 'method_doc';

type Options = {
/**
* 标题
*/
title: string
/**
* 描述
*/
description: string
}
/**
* 文档
*/
export const ApiDoc = (options: Options): MethodDecorator => {
return (target, key, descriptor: any) => {
Reflect.defineMetadata(METHOD_DOC_METADATA, options, descriptor.value);
}
}


export const useMapRoute = (handlers: any[]) => {

const all = new Map();

for (let i = 0; i < handlers.length; i++) {
console.log(handlers[i]);
const handler = new handlers[i]();
const prototype = Object.getPrototypeOf(handler) as ClassDecorator;
// 取出路径
const handlerPathMeta = Reflect.getMetadata(PATH_METADATA, prototype.constructor);
const methods = Object.getOwnPropertyNames(prototype).filter(item => {
// 构造函数,暂时不处理
if (item === 'constructor') {
return false;
}
return typeof Reflect.get(prototype, item) === 'function';
});

methods.forEach(methodName => {
const method = Reflect.get(prototype, methodName);
const methodPath = Reflect.getMetadata(METHOD_METADATA, method);
if (!methodPath) {
return;
}
const path = handlerPathMeta + '/' + methodPath;

all.set(path, {
method: method.bind(handler),
validate: Reflect.getMetadata(PARAM_VALIDATE_METADATA, method),
doc: Reflect.getMetadata(METHOD_DOC_METADATA, method),
});
})
}

return all;
}

/**
* 异常抛出基类
*/
export class ResponseError extends Error {
constructor(msg: string, public code = 500) {
super(msg);
}
}

const runHandler = async (handler: any, ctx: Context) => {
if (handler.validate) {
const errors = await validate(plainToInstance(handler.validate, ctx.request.data));
if (errors.length > 0) {
console.error(errors);
ctx.response.data = {
code: 500,
msg: Object.values(errors![0].constraints!)[0]
}
}
return;
}

try {
const res = await handler.method(ctx.request.data, ctx.socket);
if (res) {
ctx.response.data = {
data: res
};
}
} catch (e) {
if (e instanceof ResponseError) {
ctx.response.data = {
code: e.code,
msg: e.message
}
} else {
console.error(e);
}
}
}

/**
* 自动导入应用文件
* @param baseDir 应用文件夹
* @param rule 导入路径规则
* @returns
*/
export const include = async (baseDir: string, rule = '**/*.handler.ts'): Promise<FeiyunMiddleware> => {
// const dirs = await readdir(path);
// const files = await findUp(path + '/' + rule);
// console.log(files);
console.log(path.resolve(baseDir, rule));
const handlerPaths = await glob(path.resolve(baseDir, rule));

const handlers = [];

for (let i = 0; i < handlerPaths.length; i++) {
const handler = await import(handlerPaths[i]);
if (handler.default) {
handlers.push(handler.default);
}
}

const mapRoute = useMapRoute(handlers);

return async (ctx, next) => {
const handler = mapRoute.get(ctx.request.url);
if (handler) {
await runHandler(handler, ctx);
}
next();
};
}
15 changes: 15 additions & 0 deletions packages/handler/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitReturns": false,
"declaration": true,
"declarationDir": "./types",
},
"include": [
"src",
"class-validator",
"class-transformer",
]
}
Loading

0 comments on commit 31e9d89

Please sign in to comment.