fix: update
This commit is contained in:
parent
eede990ec8
commit
e31b19f931
@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package",
|
||||
"name": "@kevisual/router",
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.21-beta",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"main": "./dist/router.js",
|
||||
|
@ -1,8 +1,12 @@
|
||||
import { pathToRegexp, Key } from 'path-to-regexp';
|
||||
import type { IncomingMessage, ServerResponse } from 'node:http';
|
||||
import type { IncomingMessage, ServerResponse, Server } from 'node:http';
|
||||
import { parseBody, parseSearch, parseSearchValue } from './server/parse-body.ts';
|
||||
import { ListenOptions } from 'node:net';
|
||||
|
||||
type Req = IncomingMessage & { params?: Record<string, string> };
|
||||
type SimpleObject = {
|
||||
[key: string]: any;
|
||||
};
|
||||
interface Route {
|
||||
method: string;
|
||||
regexp: RegExp;
|
||||
@ -28,7 +32,6 @@ export class SimpleRouter {
|
||||
use(method: string, route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>) {
|
||||
const handlers = Array.isArray(fns) ? fns.flat() : [];
|
||||
const pattern = pathToRegexp(route);
|
||||
|
||||
this.routes.push({ method: method.toLowerCase(), regexp: pattern.regexp, keys: pattern.keys, handlers });
|
||||
return this;
|
||||
}
|
||||
@ -38,11 +41,35 @@ export class SimpleRouter {
|
||||
post(route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>) {
|
||||
return this.use('post', route, ...fns);
|
||||
}
|
||||
sse(route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>) {
|
||||
return this.use('sse', route, ...fns);
|
||||
}
|
||||
all(route: string, ...fns: Array<(req: Req, res: ServerResponse) => Promise<void> | void>) {
|
||||
this.use('post', route, ...fns);
|
||||
this.use('get', route, ...fns);
|
||||
this.use('sse', route, ...fns);
|
||||
return this;
|
||||
}
|
||||
getJson(v: string | number | boolean | SimpleObject) {
|
||||
if (typeof v === 'object') {
|
||||
return v;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(v as string);
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
isSse(req: Req) {
|
||||
const { headers } = req;
|
||||
if (headers['accept'] && headers['accept'].includes('text/event-stream')) {
|
||||
return true;
|
||||
}
|
||||
if (headers['content-type'] && headers['content-type'].includes('text/event-stream')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 解析 req 和 res 请求
|
||||
* @param req
|
||||
@ -51,10 +78,12 @@ export class SimpleRouter {
|
||||
*/
|
||||
parse(req: Req, res: ServerResponse) {
|
||||
const { pathname } = new URL(req.url, 'http://localhost');
|
||||
const method = req.method.toLowerCase();
|
||||
let method = req.method.toLowerCase();
|
||||
if (this.exclude.includes(pathname)) {
|
||||
return 'is_exclude';
|
||||
}
|
||||
const isSse = this.isSse(req);
|
||||
if (isSse) method = 'sse';
|
||||
const route = this.routes.find((route) => {
|
||||
const matchResult = route.regexp.exec(pathname);
|
||||
if (matchResult && route.method === method) {
|
||||
@ -74,4 +103,166 @@ export class SimpleRouter {
|
||||
|
||||
return 'not_found';
|
||||
}
|
||||
/**
|
||||
* 创建一个新的 HttpChain 实例
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
chain(req?: Req, res?: ServerResponse) {
|
||||
const chain = new HttpChain({ req, res, simpleRouter: this });
|
||||
return chain;
|
||||
}
|
||||
static Chain(opts?: HttpChainOpts) {
|
||||
return new HttpChain(opts);
|
||||
}
|
||||
}
|
||||
|
||||
type HttpChainOpts = {
|
||||
req?: Req;
|
||||
res?: ServerResponse;
|
||||
simpleRouter?: SimpleRouter;
|
||||
};
|
||||
export class HttpChain {
|
||||
req: Req;
|
||||
res: ServerResponse;
|
||||
simpleRouter: SimpleRouter;
|
||||
server: Server;
|
||||
hasSetHeader: boolean = false;
|
||||
isSseSet: boolean = false;
|
||||
constructor(opts?: HttpChainOpts) {
|
||||
this.req = opts?.req;
|
||||
this.res = opts?.res;
|
||||
this.simpleRouter = opts?.simpleRouter;
|
||||
}
|
||||
setReq(req: Req) {
|
||||
this.req = req;
|
||||
return this;
|
||||
}
|
||||
setRes(res: ServerResponse) {
|
||||
this.res = res;
|
||||
return this;
|
||||
}
|
||||
setRouter(router: SimpleRouter) {
|
||||
this.simpleRouter = router;
|
||||
return this;
|
||||
}
|
||||
setServer(server: Server) {
|
||||
this.server = server;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 兼容 express 的一点功能
|
||||
* @param status
|
||||
* @returns
|
||||
*/
|
||||
status(status: number) {
|
||||
if (!this.res) return this;
|
||||
if (this.hasSetHeader) {
|
||||
return this;
|
||||
}
|
||||
this.hasSetHeader = true;
|
||||
this.res.writeHead(status);
|
||||
return this;
|
||||
}
|
||||
writeHead(status: number) {
|
||||
if (!this.res) return this;
|
||||
if (this.hasSetHeader) {
|
||||
return this;
|
||||
}
|
||||
this.hasSetHeader = true;
|
||||
this.res.writeHead(status);
|
||||
return this;
|
||||
}
|
||||
json(data: SimpleObject) {
|
||||
if (!this.res) return this;
|
||||
this.res.end(JSON.stringify(data));
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 兼容 express 的一点功能
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
end(data: SimpleObject | string) {
|
||||
if (!this.res) return this;
|
||||
if (typeof data === 'object') {
|
||||
this.res.end(JSON.stringify(data));
|
||||
} else if (typeof data === 'string') {
|
||||
this.res.end(data);
|
||||
} else {
|
||||
this.res.end('nothing');
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
listen(opts: ListenOptions, callback?: () => void) {
|
||||
this.server.listen(opts, callback);
|
||||
return this;
|
||||
}
|
||||
parse() {
|
||||
if (!this.server || !this.simpleRouter) {
|
||||
throw new Error('Server and SimpleRouter must be set before calling parse');
|
||||
}
|
||||
const that = this;
|
||||
const listener = (req: Req, res: ServerResponse) => {
|
||||
try {
|
||||
that.simpleRouter.parse(req, res);
|
||||
} catch (error) {
|
||||
console.error('Error parsing request:', error);
|
||||
if (!res.headersSent) {
|
||||
res.writeHead(500);
|
||||
res.end(JSON.stringify({ code: 500, message: 'Internal Server Error' }));
|
||||
}
|
||||
}
|
||||
};
|
||||
this.server.on('request', listener);
|
||||
return () => {
|
||||
that.server.removeListener('request', listener);
|
||||
};
|
||||
}
|
||||
getString(value: string | SimpleObject) {
|
||||
if (typeof value === 'string') {
|
||||
return value;
|
||||
}
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
sse(value: string | SimpleObject) {
|
||||
const res = this.res;
|
||||
const req = this.req;
|
||||
if (!res || !req) return;
|
||||
const data = this.getString(value);
|
||||
if (this.isSseSet) {
|
||||
res.write(`data: ${data}\n\n`);
|
||||
return this;
|
||||
}
|
||||
const headersMap = new Map<string, string>([
|
||||
['Content-Type', 'text/event-stream'],
|
||||
['Cache-Control', 'no-cache'],
|
||||
['Connection', 'keep-alive'],
|
||||
]);
|
||||
this.isSseSet = true;
|
||||
let intervalId: NodeJS.Timeout;
|
||||
if (!this.hasSetHeader) {
|
||||
this.hasSetHeader = true;
|
||||
res.setHeaders(headersMap);
|
||||
// 每隔 2 秒发送一个空行,保持连接
|
||||
setInterval(() => {
|
||||
res.write('\n'); // 发送一个空行,保持连接
|
||||
}, 3000);
|
||||
// 客户端断开连接时清理
|
||||
req.on('close', () => {
|
||||
clearInterval(intervalId);
|
||||
res.end();
|
||||
});
|
||||
}
|
||||
this.res.write(`data: ${data}\n\n`);
|
||||
return this;
|
||||
}
|
||||
close() {
|
||||
if (this.req?.destroy) {
|
||||
this.req.destroy();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ type BaseRule = {
|
||||
|
||||
type RuleString = {
|
||||
type: 'string';
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
regex?: string;
|
||||
} & BaseRule;
|
||||
|
||||
@ -26,8 +26,6 @@ type RuleBoolean = {
|
||||
type RuleArray = {
|
||||
type: 'array';
|
||||
items: Rule;
|
||||
minItems?: number;
|
||||
maxItems?: number;
|
||||
} & BaseRule;
|
||||
|
||||
type RuleObject = {
|
||||
@ -45,8 +43,8 @@ export const schemaFormRule = (rule: Rule): z.ZodType<any, any, any> => {
|
||||
switch (rule.type) {
|
||||
case 'string':
|
||||
let stringSchema = z.string();
|
||||
if (rule.minLength) stringSchema = stringSchema.min(rule.minLength, `String must be at least ${rule.minLength} characters long.`);
|
||||
if (rule.maxLength) stringSchema = stringSchema.max(rule.maxLength, `String must not exceed ${rule.maxLength} characters.`);
|
||||
if (rule.min) stringSchema = stringSchema.min(rule.min, `String must be at least ${rule.min} characters long.`);
|
||||
if (rule.max) stringSchema = stringSchema.max(rule.max, `String must not exceed ${rule.max} characters.`);
|
||||
if (rule.regex) stringSchema = stringSchema.regex(new RegExp(rule.regex), 'Invalid format');
|
||||
return stringSchema;
|
||||
case 'number':
|
||||
|
Loading…
x
Reference in New Issue
Block a user