Files
foka-ci/apps/server/decorators/route.ts
hurole d22fdc9618 feat: 实现环境变量预设功能 & 移除稀疏检出
## 后端改动
- 添加 Project.envPresets 字段(JSON 格式)
- 移除 Deployment.env 字段,统一使用 envVars
- 更新部署 DTO,支持 envVars (Record<string, string>)
- pipeline-runner 支持解析并注入 envVars 到环境
- 移除稀疏检出模板和相关环境变量
- 优化代码格式(Biome lint & format)

## 前端改动
- 新增 EnvPresetsEditor 组件(支持单选/多选/输入框类型)
- 项目创建/编辑界面集成环境预设编辑器
- 部署界面基于预设动态生成环境变量表单
- 移除稀疏检出表单项
- 项目详情页添加环境变量预设配置 tab
- 优化部署界面布局(基本参数 & 环境变量分区)

## 文档
- 添加完整文档目录结构(docs/)
- 创建设计文档 design-0005(部署流程重构)
- 添加 API 文档、架构设计文档等

## 数据库
- 执行 prisma db push 同步 schema 变更
2026-01-03 22:59:20 +08:00

148 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 路由元数据键
*/
export const ROUTE_METADATA_KEY = Symbol('route');
/**
* HTTP 方法类型
*/
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
/**
* 路由元数据接口
*/
export interface RouteMetadata {
method: HttpMethod;
path: string;
propertyKey: string;
}
/**
* 元数据存储(降级方案)
*/
const metadataStore = new WeakMap<any, Map<string | symbol, any>>();
/**
* 设置元数据(降级方案)
*/
function setMetadata<T = any>(
key: string | symbol,
value: T,
target: any,
): void {
if (!metadataStore.has(target)) {
metadataStore.set(target, new Map());
}
metadataStore.get(target)?.set(key, value);
}
/**
* 获取元数据(降级方案)
*/
function getMetadata<T = any>(
key: string | symbol,
target: any,
): T | undefined {
return metadataStore.get(target)?.get(key);
}
/**
* 创建HTTP方法装饰器的工厂函数TC39标准
*/
function createMethodDecorator(method: HttpMethod) {
return (path: string = '') =>
<This, Args extends any[], Return>(
target: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext<
This,
(this: This, ...args: Args) => Return
>,
) => {
// 在类初始化时执行
context.addInitializer(function () {
// 使用 this.constructor 时需要类型断言
const ctor = (this as any).constructor;
// 获取现有的路由元数据
const existingRoutes: RouteMetadata[] =
getMetadata(ROUTE_METADATA_KEY, ctor) || [];
// 添加新的路由元数据
const newRoute: RouteMetadata = {
method,
path,
propertyKey: String(context.name),
};
existingRoutes.push(newRoute);
// 保存路由元数据到类的构造函数上
setMetadata(ROUTE_METADATA_KEY, existingRoutes, ctor);
});
return target;
};
}
/**
* GET 请求装饰器TC39标准
* @param path 路由路径
*/
export const Get = createMethodDecorator('GET');
/**
* POST 请求装饰器TC39标准
* @param path 路由路径
*/
export const Post = createMethodDecorator('POST');
/**
* PUT 请求装饰器TC39标准
* @param path 路由路径
*/
export const Put = createMethodDecorator('PUT');
/**
* DELETE 请求装饰器TC39标准
* @param path 路由路径
*/
export const Delete = createMethodDecorator('DELETE');
/**
* PATCH 请求装饰器TC39标准
* @param path 路由路径
*/
export const Patch = createMethodDecorator('PATCH');
/**
* 控制器装饰器TC39标准
* @param prefix 路由前缀
*/
export function Controller(prefix: string = '') {
return <T extends abstract new (...args: any) => any>(
target: T,
context: ClassDecoratorContext<T>,
) => {
// 在类初始化时保存控制器前缀
context.addInitializer(function () {
setMetadata('prefix', prefix, this);
});
return target;
};
}
/**
* 获取控制器的路由元数据
*/
export function getRouteMetadata(ctor: any): RouteMetadata[] {
return getMetadata(ROUTE_METADATA_KEY, ctor) || [];
}
/**
* 获取控制器的路由前缀
*/
export function getControllerPrefix(ctor: any): string {
return getMetadata('prefix', ctor) || '';
}