This commit is contained in:
DCCONSTRUCTIONS
2026-04-18 18:39:25 +03:00
commit 3ba092b60c
4944 changed files with 497564 additions and 0 deletions
@@ -0,0 +1,108 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import type { RequestHandler, Router, Request } from "express";
import type { WebSocket } from "ws";
import "reflect-metadata";
export type HttpMethod = "get" | "post" | "put" | "delete" | "patch" | "options" | "head" | "ws";
type ControllerInstance = {
[key: string]: any;
};
export type ControllerConstructor = {
new (...args: any[]): ControllerInstance;
prototype: ControllerInstance;
};
export function registerController(
router: Router,
Controller: ControllerConstructor,
dependencies: unknown[] = []
): void {
// Create the controller instance with dependencies
const instance = new Controller(...dependencies);
// Determine if it's a WebSocket controller or REST controller by checking
// if it has any methods with the "ws" method metadata
const isWebsocket = Object.getOwnPropertyNames(Controller.prototype).some((methodName) => {
if (methodName === "constructor") return false;
return Reflect.getMetadata("method", instance, methodName) === "ws";
});
if (isWebsocket) {
// Register as WebSocket controller
// Pass the existing instance with dependencies to avoid creating a new instance without them
registerWebSocketController(router, Controller, instance);
} else {
// Register as REST controller with the existing instance
registerRestController(router, Controller, instance);
}
}
function registerRestController(
router: Router,
Controller: ControllerConstructor,
existingInstance?: ControllerInstance
): void {
const instance = existingInstance || new Controller();
const baseRoute = Reflect.getMetadata("baseRoute", Controller) as string;
Object.getOwnPropertyNames(Controller.prototype).forEach((methodName) => {
if (methodName === "constructor") return; // Skip the constructor
const method = Reflect.getMetadata("method", instance, methodName) as HttpMethod;
const route = Reflect.getMetadata("route", instance, methodName) as string;
const middlewares = (Reflect.getMetadata("middlewares", instance, methodName) as RequestHandler[]) || [];
if (method && route) {
const handler = instance[methodName] as unknown;
if (typeof handler === "function") {
if (method !== "ws") {
(router[method] as (path: string, ...handlers: RequestHandler[]) => void)(
`${baseRoute}${route}`,
...middlewares,
handler.bind(instance)
);
}
}
}
});
}
function registerWebSocketController(
router: Router,
Controller: ControllerConstructor,
existingInstance?: ControllerInstance
): void {
const instance = existingInstance || new Controller();
const baseRoute = Reflect.getMetadata("baseRoute", Controller) as string;
Object.getOwnPropertyNames(Controller.prototype).forEach((methodName) => {
if (methodName === "constructor") return; // Skip the constructor
const method = Reflect.getMetadata("method", instance, methodName) as string;
const route = Reflect.getMetadata("route", instance, methodName) as string;
if (method === "ws" && route) {
const handler = instance[methodName] as unknown;
if (typeof handler === "function" && "ws" in router && typeof router.ws === "function") {
router.ws(`${baseRoute}${route}`, (ws: WebSocket, req: Request) => {
try {
handler.call(instance, ws, req);
} catch (error) {
console.error(`WebSocket error in ${Controller.name}.${methodName}`, error);
ws.close(1011, error instanceof Error ? error.message : "Internal server error");
}
});
}
}
});
}
@@ -0,0 +1,9 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
export * from "./controller";
export * from "./rest";
export * from "./websocket";
+57
View File
@@ -0,0 +1,57 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import "reflect-metadata";
import type { RequestHandler } from "express";
// Define valid HTTP methods
type RestMethod = "get" | "post" | "put" | "patch" | "delete";
/**
* Controller decorator
* @param baseRoute
* @returns
*/
export function Controller(baseRoute: string = ""): ClassDecorator {
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
return function (target: Function) {
Reflect.defineMetadata("baseRoute", baseRoute, target);
};
}
/**
* Factory function to create HTTP method decorators
* @param method HTTP method to handle
* @returns Method decorator
*/
function createHttpMethodDecorator(method: RestMethod): (route: string) => MethodDecorator {
return function (route: string): MethodDecorator {
return function (target: object, propertyKey: string | symbol) {
Reflect.defineMetadata("method", method, target, propertyKey);
Reflect.defineMetadata("route", route, target, propertyKey);
};
};
}
// Export HTTP method decorators using the factory
export const Get = createHttpMethodDecorator("get");
export const Post = createHttpMethodDecorator("post");
export const Put = createHttpMethodDecorator("put");
export const Patch = createHttpMethodDecorator("patch");
export const Delete = createHttpMethodDecorator("delete");
/**
* Middleware decorator
* @param middleware
* @returns
*/
export function Middleware(middleware: RequestHandler): MethodDecorator {
return function (target: object, propertyKey: string | symbol) {
const middlewares = Reflect.getMetadata("middlewares", target, propertyKey) || [];
middlewares.push(middleware);
Reflect.defineMetadata("middlewares", middlewares, target, propertyKey);
};
}
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import "reflect-metadata";
/**
* WebSocket method decorator
* @param route
* @returns
*/
export function WebSocket(route: string): MethodDecorator {
return function (target: object, propertyKey: string | symbol) {
Reflect.defineMetadata("method", "ws", target, propertyKey);
Reflect.defineMetadata("route", route, target, propertyKey);
};
}