@avleon/core 0.0.29 → 0.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -3
- package/src/application.ts +0 -104
- package/src/authentication.ts +0 -16
- package/src/cache.ts +0 -91
- package/src/collection.test.ts +0 -71
- package/src/collection.ts +0 -344
- package/src/config.test.ts +0 -35
- package/src/config.ts +0 -85
- package/src/constants.ts +0 -1
- package/src/container.ts +0 -54
- package/src/controller.ts +0 -125
- package/src/decorators.ts +0 -27
- package/src/environment-variables.ts +0 -53
- package/src/event-dispatcher.ts +0 -100
- package/src/event-subscriber.ts +0 -79
- package/src/exceptions/http-exceptions.ts +0 -86
- package/src/exceptions/index.ts +0 -1
- package/src/exceptions/system-exception.ts +0 -35
- package/src/file-storage.ts +0 -206
- package/src/helpers.ts +0 -324
- package/src/icore.ts +0 -1106
- package/src/index.ts +0 -32
- package/src/interfaces/avleon-application.ts +0 -32
- package/src/logger.ts +0 -72
- package/src/map-types.ts +0 -159
- package/src/middleware.ts +0 -121
- package/src/multipart.ts +0 -116
- package/src/openapi.ts +0 -372
- package/src/params.ts +0 -111
- package/src/queue.ts +0 -126
- package/src/response.ts +0 -74
- package/src/results.ts +0 -30
- package/src/route-methods.ts +0 -186
- package/src/swagger-schema.ts +0 -213
- package/src/testing.ts +0 -220
- package/src/types/app-builder.interface.ts +0 -18
- package/src/types/application.interface.ts +0 -7
- package/src/utils/hash.ts +0 -8
- package/src/utils/index.ts +0 -2
- package/src/utils/optional-require.ts +0 -50
- package/src/validation.ts +0 -160
- package/src/validator-extend.ts +0 -25
- package/src/websocket.ts +0 -47
package/src/config.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2024
|
|
3
|
-
* @author Tareq Hossain
|
|
4
|
-
* @email xtrinsic96@gmail.com
|
|
5
|
-
* @url https://github.com/xtareq
|
|
6
|
-
*/
|
|
7
|
-
import { Container, Service, Constructable, Token } from "typedi";
|
|
8
|
-
import { Environment } from "./environment-variables";
|
|
9
|
-
import { inject } from "./helpers";
|
|
10
|
-
|
|
11
|
-
export interface IConfig<T = any> {
|
|
12
|
-
config(env: Environment): T;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function AppConfig<T extends IConfig>(target: Constructable<T>) {
|
|
16
|
-
Container.set({ id: target, type: target });
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export class AvleonConfig {
|
|
20
|
-
get<T extends IConfig<R>, R>(configClass: Constructable<T>): R {
|
|
21
|
-
const instance = Container.get(configClass);
|
|
22
|
-
if (!instance) {
|
|
23
|
-
throw new Error(`Configuration for ${configClass.name} not found.`);
|
|
24
|
-
}
|
|
25
|
-
return instance.config(new Environment());
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// export function GetConfig<
|
|
30
|
-
// T extends IConfig<R>,
|
|
31
|
-
// R = ReturnType<InstanceType<Constructable<T>>["config"]>,
|
|
32
|
-
// >(ConfigClass: Constructable<T>): R {
|
|
33
|
-
// const instance = Container.get(ConfigClass);
|
|
34
|
-
// if (!instance) {
|
|
35
|
-
// throw new Error(
|
|
36
|
-
// `Class "${ConfigClass.name}" is not registered as a config.`,
|
|
37
|
-
// );
|
|
38
|
-
// }
|
|
39
|
-
// return instance.config(new Environment());
|
|
40
|
-
// }
|
|
41
|
-
|
|
42
|
-
export function GetConfig<
|
|
43
|
-
T extends IConfig<R>,
|
|
44
|
-
R = ReturnType<InstanceType<Constructable<T>>["config"]>,
|
|
45
|
-
>(ConfigClass: Constructable<T>): R;
|
|
46
|
-
|
|
47
|
-
export function GetConfig<T = any>(config: string | symbol): T;
|
|
48
|
-
|
|
49
|
-
// Implementation
|
|
50
|
-
export function GetConfig<R>(token: any): R {
|
|
51
|
-
// 1. Class‐based: token.prototype.config is a function
|
|
52
|
-
if (
|
|
53
|
-
typeof token === "function" &&
|
|
54
|
-
token.prototype != null &&
|
|
55
|
-
typeof token.prototype.config === "function"
|
|
56
|
-
) {
|
|
57
|
-
const instance = Container.get(token as Constructable<any>);
|
|
58
|
-
if (!instance) {
|
|
59
|
-
throw new Error(`Class "${token.name}" is not registered as a config.`);
|
|
60
|
-
}
|
|
61
|
-
return instance.config(inject(Environment));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 2. Functional: token is the callback itself
|
|
65
|
-
const stored = Container.get(token);
|
|
66
|
-
if (!stored) {
|
|
67
|
-
throw new Error("Config object is not registered.");
|
|
68
|
-
}
|
|
69
|
-
return stored as R;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function CreateConfig<T>(
|
|
73
|
-
token: string | symbol,
|
|
74
|
-
callback: (env: Environment) => T,
|
|
75
|
-
) {
|
|
76
|
-
let env!: Environment;
|
|
77
|
-
try {
|
|
78
|
-
env = Container.get(Environment);
|
|
79
|
-
} catch (error) {
|
|
80
|
-
env = new Environment();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
let config: T = callback(env);
|
|
84
|
-
Container.set<T>(token as Token<T>, config);
|
|
85
|
-
}
|
package/src/constants.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const TEST_DATASOURCE_OPTIONS_KEY = Symbol("itestdatasource");
|
package/src/container.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2024
|
|
3
|
-
* @author Tareq Hossain
|
|
4
|
-
* @email xtrinsic96@gmail.com
|
|
5
|
-
* @url https://github.com/xtareq
|
|
6
|
-
*/
|
|
7
|
-
import TypediContainer, { ContainerInstance, Token } from "typedi";
|
|
8
|
-
import { DataSource } from "typeorm";
|
|
9
|
-
|
|
10
|
-
export const FEATURE_KEY = Symbol.for("features");
|
|
11
|
-
export const ROUTE_META_KEY = Symbol("iroute:options");
|
|
12
|
-
export const CONTROLLER_META_KEY = Symbol("icontroller:options");
|
|
13
|
-
export const PARAM_META_KEY = Symbol("iparam:options");
|
|
14
|
-
export const QUERY_META_KEY = Symbol("iparam:options");
|
|
15
|
-
export const REQUEST_BODY_META_KEY = Symbol("iparam:options");
|
|
16
|
-
export const REQUEST_BODY_FILE_KEY = Symbol("iparam:options");
|
|
17
|
-
export const REQUEST_BODY_FILES_KEY = Symbol("iparam:options");
|
|
18
|
-
export const REQUEST_USER_META_KEY = Symbol("iparam:options");
|
|
19
|
-
export const REQUEST_HEADER_META_KEY = Symbol("iheader:options");
|
|
20
|
-
export const DATASOURCE_META_KEY = Symbol("idatasource:options");
|
|
21
|
-
export const AUTHORIZATION_META_KEY = Symbol("idatasource:authorization");
|
|
22
|
-
|
|
23
|
-
const controllerRegistry = new Set<Function>();
|
|
24
|
-
const serviceRegistry = new Set<Function>();
|
|
25
|
-
const optionsRegistry = new Map<string, any>();
|
|
26
|
-
|
|
27
|
-
const Container = TypediContainer;
|
|
28
|
-
|
|
29
|
-
export function registerController(controller: Function) {
|
|
30
|
-
controllerRegistry.add(controller);
|
|
31
|
-
}
|
|
32
|
-
export function registerService(service: Function) {
|
|
33
|
-
Container.set(service, service);
|
|
34
|
-
serviceRegistry.add(service);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function getRegisteredServices(): Function[] {
|
|
38
|
-
return Array.from(serviceRegistry);
|
|
39
|
-
}
|
|
40
|
-
export function getRegisteredControllers(): Function[] {
|
|
41
|
-
return Array.from(controllerRegistry);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const API_CONTROLLER_METADATA_KEY = Symbol("apiController");
|
|
45
|
-
|
|
46
|
-
export function isApiController(target: Function): boolean {
|
|
47
|
-
return Reflect.getMetadata(API_CONTROLLER_METADATA_KEY, target) === true;
|
|
48
|
-
}
|
|
49
|
-
Container.set<string>("appName", "Iqra");
|
|
50
|
-
|
|
51
|
-
export function registerDataSource(dataSource: any) {
|
|
52
|
-
Container.set<DataSource>("idatasource", dataSource);
|
|
53
|
-
}
|
|
54
|
-
export default Container;
|
package/src/controller.ts
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2024
|
|
3
|
-
* @author Tareq Hossain
|
|
4
|
-
* @email xtrinsic96@gmail.com
|
|
5
|
-
* @url https://github.com/xtareq
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import Container, { Service } from "typedi";
|
|
9
|
-
import container, {
|
|
10
|
-
API_CONTROLLER_METADATA_KEY,
|
|
11
|
-
CONTROLLER_META_KEY,
|
|
12
|
-
registerController,
|
|
13
|
-
} from "./container";
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Options for configuring a controller.
|
|
17
|
-
* @remarks
|
|
18
|
-
* Controller default options
|
|
19
|
-
* @type {Object} ControllerOptions
|
|
20
|
-
* @property {string} [name] - The name of the controller.
|
|
21
|
-
* @property {string} [path] - The base path for the controller's routes.
|
|
22
|
-
* @property {string} [version] - The version of the controller. If not provided, it will default to the version from `package.json`.
|
|
23
|
-
* @property {string} [since] - The date or version since the controller was introduced.
|
|
24
|
-
* @property {any} [meta] - Additional metadata associated with the controller.
|
|
25
|
-
*/
|
|
26
|
-
export type ControllerOptions = {
|
|
27
|
-
/**
|
|
28
|
-
*@property {string} name
|
|
29
|
-
*@description Name of the controller. If specified it'll used as swagger tags
|
|
30
|
-
*@default Contorller class name
|
|
31
|
-
* */
|
|
32
|
-
name?: string;
|
|
33
|
-
path?: string;
|
|
34
|
-
version?: string; // Will look at package.json if not set
|
|
35
|
-
since?: string;
|
|
36
|
-
meta?: any;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export function createControllerDecorator(
|
|
40
|
-
type: "api" | "web" = "web",
|
|
41
|
-
): (
|
|
42
|
-
pathOrOptions?: string | ControllerOptions,
|
|
43
|
-
maybeOptions?: ControllerOptions,
|
|
44
|
-
) => ClassDecorator {
|
|
45
|
-
return function (
|
|
46
|
-
pathOrOptions?: string | ControllerOptions,
|
|
47
|
-
maybeOptions?: ControllerOptions,
|
|
48
|
-
): ClassDecorator {
|
|
49
|
-
return function (target: Function) {
|
|
50
|
-
let path = "/";
|
|
51
|
-
let options: ControllerOptions = {};
|
|
52
|
-
|
|
53
|
-
if (typeof pathOrOptions === "string") {
|
|
54
|
-
path = pathOrOptions;
|
|
55
|
-
options = maybeOptions || {};
|
|
56
|
-
} else if (typeof pathOrOptions === "object") {
|
|
57
|
-
options = pathOrOptions;
|
|
58
|
-
path = options.path || "/";
|
|
59
|
-
}
|
|
60
|
-
Reflect.defineMetadata(API_CONTROLLER_METADATA_KEY, true, target);
|
|
61
|
-
// Ensure Service is applied as a ClassDecorator
|
|
62
|
-
if (typeof Service === "function") {
|
|
63
|
-
registerController(target); // Add to custom registry
|
|
64
|
-
Service()(target); // Apply DI decorator
|
|
65
|
-
Reflect.defineMetadata(
|
|
66
|
-
CONTROLLER_META_KEY,
|
|
67
|
-
{ type, path, options },
|
|
68
|
-
target,
|
|
69
|
-
);
|
|
70
|
-
} else {
|
|
71
|
-
throw new Error("Service decorator is not a function");
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Predefined Controller Decorators
|
|
78
|
-
//export const Controller = createControllerDecorator("web");
|
|
79
|
-
/**
|
|
80
|
-
*@description Api controller's are used for rest . It will populate
|
|
81
|
-
* json on return and all it http methods {get} {post} etc must return
|
|
82
|
-
*Results.*
|
|
83
|
-
* @param path {string} this will used as route prefix
|
|
84
|
-
*
|
|
85
|
-
**/
|
|
86
|
-
|
|
87
|
-
export function ApiController(target: Function): void;
|
|
88
|
-
export function ApiController(path: string): ClassDecorator;
|
|
89
|
-
/**
|
|
90
|
-
*@description Api controller's are used for rest . It will populate
|
|
91
|
-
* json on return and all it http methods {get} {post} etc must return
|
|
92
|
-
* Results.*
|
|
93
|
-
* @param {ControllerOptions} options this will used as route prefix
|
|
94
|
-
*
|
|
95
|
-
**/
|
|
96
|
-
export function ApiController(options: ControllerOptions): ClassDecorator;
|
|
97
|
-
export function ApiController(
|
|
98
|
-
path: string,
|
|
99
|
-
options?: ControllerOptions,
|
|
100
|
-
): ClassDecorator;
|
|
101
|
-
export function ApiController(
|
|
102
|
-
pathOrOptions: Function | string | ControllerOptions = "/",
|
|
103
|
-
mayBeOptions?: ControllerOptions,
|
|
104
|
-
): any {
|
|
105
|
-
if (typeof pathOrOptions == "function") {
|
|
106
|
-
Reflect.defineMetadata(API_CONTROLLER_METADATA_KEY, true, pathOrOptions);
|
|
107
|
-
// Ensure Service is applied as a ClassDecorator
|
|
108
|
-
if (typeof Service === "function") {
|
|
109
|
-
registerController(pathOrOptions); // Add to custom registry
|
|
110
|
-
Service()(pathOrOptions); // Apply DI decorator
|
|
111
|
-
Reflect.defineMetadata(
|
|
112
|
-
CONTROLLER_META_KEY,
|
|
113
|
-
{ type: "api", path: "/", options: {} },
|
|
114
|
-
pathOrOptions,
|
|
115
|
-
);
|
|
116
|
-
} else {
|
|
117
|
-
throw new Error("Service decorator is not a function");
|
|
118
|
-
}
|
|
119
|
-
} else {
|
|
120
|
-
if (mayBeOptions) {
|
|
121
|
-
return createControllerDecorator("api")(pathOrOptions, mayBeOptions);
|
|
122
|
-
}
|
|
123
|
-
return createControllerDecorator("api")(pathOrOptions);
|
|
124
|
-
}
|
|
125
|
-
}
|
package/src/decorators.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2024
|
|
3
|
-
* @author Tareq Hossain
|
|
4
|
-
* @email xtrinsic96@gmail.com
|
|
5
|
-
* @url https://github.com/xtareq
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { Service as _service } from "typedi";
|
|
9
|
-
import container, { registerService } from "./container";
|
|
10
|
-
export function AppService(target: any): void;
|
|
11
|
-
export function AppService(): any;
|
|
12
|
-
export function AppService(target?: any) {
|
|
13
|
-
if (target) {
|
|
14
|
-
_service()(target);
|
|
15
|
-
} else {
|
|
16
|
-
return function (tg: any) {
|
|
17
|
-
_service()(tg);
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export * from "./controller";
|
|
23
|
-
export * from "./route-methods";
|
|
24
|
-
export * from "./openapi";
|
|
25
|
-
export const Utility = _service;
|
|
26
|
-
export const Helper = _service;
|
|
27
|
-
export * from "./params";
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2024
|
|
3
|
-
* @author Tareq Hossain
|
|
4
|
-
* @email xtrinsic96@gmail.com
|
|
5
|
-
* @url https://github.com/xtareq
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import dotenv from "dotenv";
|
|
9
|
-
import path from "path";
|
|
10
|
-
import fs, { existsSync } from "fs";
|
|
11
|
-
import { Service } from "typedi";
|
|
12
|
-
import {
|
|
13
|
-
EnvironmentVariableNotFound,
|
|
14
|
-
SystemUseError,
|
|
15
|
-
} from "./exceptions/system-exception";
|
|
16
|
-
|
|
17
|
-
dotenv.config({ path: path.join(process.cwd(), ".env") });
|
|
18
|
-
|
|
19
|
-
@Service()
|
|
20
|
-
export class Environment {
|
|
21
|
-
private parseEnvFile(filePath: string): any {
|
|
22
|
-
try {
|
|
23
|
-
const isExis = existsSync(filePath);
|
|
24
|
-
if (!isExis) {
|
|
25
|
-
return { ...process.env };
|
|
26
|
-
}
|
|
27
|
-
const fileContent = fs.readFileSync(filePath, "utf8");
|
|
28
|
-
const parsedEnv = dotenv.parse(fileContent);
|
|
29
|
-
return { ...parsedEnv, ...process.env };
|
|
30
|
-
} catch (error) {
|
|
31
|
-
console.error(`Error parsing .env file: ${error}`);
|
|
32
|
-
return {};
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
get<T = any>(key: string): T {
|
|
37
|
-
const parsedEnv = this.parseEnvFile(path.join(process.cwd(), ".env"));
|
|
38
|
-
return parsedEnv[key] as T;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
getOrThrow<T = any>(key: string): T {
|
|
42
|
-
const parsedEnv = this.parseEnvFile(path.join(process.cwd(), ".env"));
|
|
43
|
-
if (!Object(parsedEnv).hasOwnProperty(key)) {
|
|
44
|
-
throw new EnvironmentVariableNotFound(key);
|
|
45
|
-
}
|
|
46
|
-
return parsedEnv[key] as T;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
getAll<T = any>(): T {
|
|
50
|
-
const parsedEnv = this.parseEnvFile(path.join(process.cwd(), ".env"));
|
|
51
|
-
return parsedEnv as T;
|
|
52
|
-
}
|
|
53
|
-
}
|
package/src/event-dispatcher.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { Service, Container } from "typedi";
|
|
2
|
-
import { Server as SocketIOServer, Socket, ServerOptions } from "socket.io";
|
|
3
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
@Service()
|
|
7
|
-
export class SocketContextService {
|
|
8
|
-
private readonly storage = new AsyncLocalStorage<{ socket: Socket }>();
|
|
9
|
-
|
|
10
|
-
run(socket: Socket, fn: () => void | Promise<void>) {
|
|
11
|
-
this.storage.run({ socket }, fn);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
getSocket(): Socket | undefined {
|
|
15
|
-
return this.storage.getStore()?.socket;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type DispatchOptions = {
|
|
20
|
-
room?: string;
|
|
21
|
-
broadcast?: boolean;
|
|
22
|
-
transports?: ("socket" | "kafka" | "rabbitmq")[];
|
|
23
|
-
retry?: number;
|
|
24
|
-
retryDelay?: number;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export function sleep(ms: number) {
|
|
28
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
@Service()
|
|
32
|
-
export class EventDispatcher {
|
|
33
|
-
constructor(private readonly _context: SocketContextService) {}
|
|
34
|
-
|
|
35
|
-
async dispatch<T = any>(
|
|
36
|
-
event: string,
|
|
37
|
-
data: T,
|
|
38
|
-
options: DispatchOptions = {}
|
|
39
|
-
) {
|
|
40
|
-
const retryCount = options.retry ?? 0;
|
|
41
|
-
const delay = options.retryDelay ?? 300;
|
|
42
|
-
|
|
43
|
-
for (let attempt = 0; attempt <= retryCount; attempt++) {
|
|
44
|
-
try {
|
|
45
|
-
await this.dispatchToTransports(event, data, options);
|
|
46
|
-
break;
|
|
47
|
-
} catch (err) {
|
|
48
|
-
if (attempt === retryCount) throw err;
|
|
49
|
-
await sleep(delay * (attempt + 1));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
private async dispatchToTransports(event: string, data: any, options: DispatchOptions) {
|
|
55
|
-
const transports = options.transports ?? ["socket"];
|
|
56
|
-
|
|
57
|
-
for (const transport of transports) {
|
|
58
|
-
if (transport === "socket") {
|
|
59
|
-
const io = Container.get(SocketIOServer);
|
|
60
|
-
|
|
61
|
-
//console.log('SOckert', Container.get(SocketContextService));
|
|
62
|
-
|
|
63
|
-
const context = Container.get(SocketContextService);
|
|
64
|
-
const socket = context.getSocket();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (options.broadcast && socket) {
|
|
69
|
-
if (options.room) {
|
|
70
|
-
socket.broadcast.to(options.room).emit(event, data);
|
|
71
|
-
} else {
|
|
72
|
-
socket.broadcast.emit(event, data);
|
|
73
|
-
}
|
|
74
|
-
} else {
|
|
75
|
-
if (options.room) {
|
|
76
|
-
io.to(options.room).emit(event, data);
|
|
77
|
-
} else {
|
|
78
|
-
io.emit(event, data);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
export function Dispatch(event: string, options?: Omit<DispatchOptions, "transports"> & { transports?: DispatchOptions["transports"] }) {
|
|
88
|
-
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
89
|
-
const original = descriptor.value;
|
|
90
|
-
|
|
91
|
-
descriptor.value = async function (...args: any[]) {
|
|
92
|
-
const result = await original.apply(this, args);
|
|
93
|
-
|
|
94
|
-
const dispatcher = Container.get(EventDispatcher);
|
|
95
|
-
await dispatcher.dispatch(event, result, options);
|
|
96
|
-
|
|
97
|
-
return result;
|
|
98
|
-
};
|
|
99
|
-
};
|
|
100
|
-
}
|
package/src/event-subscriber.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { Container, Service } from "typedi";
|
|
2
|
-
import { Socket, Server } from "socket.io";
|
|
3
|
-
import { SocketContextService } from "./event-dispatcher";
|
|
4
|
-
import "reflect-metadata";
|
|
5
|
-
|
|
6
|
-
const PRIVATE_META_KEY = "avleon:private";
|
|
7
|
-
|
|
8
|
-
export function Private(channelResolver?: (socket: any) => string) {
|
|
9
|
-
return function (target: any, propertyKey: string) {
|
|
10
|
-
Reflect.defineMetadata(PRIVATE_META_KEY, true, target, propertyKey);
|
|
11
|
-
Reflect.defineMetadata(`private:channel:${propertyKey}`, channelResolver, target);
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function isPrivate(target: any, propertyKey: string): boolean {
|
|
16
|
-
return Reflect.getMetadata(PRIVATE_META_KEY, target, propertyKey) || false;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function getPrivateChannelResolver(target: any, propertyKey: string): ((socket: any) => string) | undefined {
|
|
20
|
-
return Reflect.getMetadata(`private:channel:${propertyKey}`, target);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const socketSubscriberClasses = new Set<Function>();
|
|
24
|
-
|
|
25
|
-
export function registerSocketSubscriber(target: Function) {
|
|
26
|
-
socketSubscriberClasses.add(target);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function getSocketSubscribers(): Function[] {
|
|
30
|
-
return Array.from(socketSubscriberClasses);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export function Subscribe(event: string): MethodDecorator {
|
|
35
|
-
return (target, propertyKey) => {
|
|
36
|
-
Reflect.defineMetadata("socket:event", event, target, propertyKey);
|
|
37
|
-
registerSocketSubscriber(target.constructor);
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@Service()
|
|
44
|
-
export class EventSubscriberRegistry {
|
|
45
|
-
constructor(private readonly socketContext: SocketContextService) { }
|
|
46
|
-
|
|
47
|
-
register(socket: Socket) {
|
|
48
|
-
const subscriberClasses = getSocketSubscribers();
|
|
49
|
-
|
|
50
|
-
for (const SubscriberClass of subscriberClasses) {
|
|
51
|
-
const instance: any = Container.get(SubscriberClass);
|
|
52
|
-
const prototype = Object.getPrototypeOf(instance);
|
|
53
|
-
|
|
54
|
-
const methodNames = Object.getOwnPropertyNames(prototype)
|
|
55
|
-
.filter(name => typeof prototype[name] === "function");
|
|
56
|
-
|
|
57
|
-
for (const methodName of methodNames) {
|
|
58
|
-
const event = Reflect.getMetadata("socket:event", prototype, methodName);
|
|
59
|
-
const isPrivateListener = isPrivate(instance, methodName);
|
|
60
|
-
const channelResolver = getPrivateChannelResolver(instance, methodName);
|
|
61
|
-
|
|
62
|
-
if (event) {
|
|
63
|
-
const channel = isPrivateListener && channelResolver ? channelResolver(socket) : event;
|
|
64
|
-
console.log("Channel", channel);
|
|
65
|
-
socket.on(channel, (payload: any) => {
|
|
66
|
-
this.socketContext.run(socket, async () => {
|
|
67
|
-
if (isPrivateListener) {
|
|
68
|
-
const user = socket.data.user;
|
|
69
|
-
if (!user) return; // unauthorized
|
|
70
|
-
// optionally add more validation here
|
|
71
|
-
}
|
|
72
|
-
await instance[methodName](payload, socket.data);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2024
|
|
3
|
-
* @author Tareq Hossain
|
|
4
|
-
* @email xtrinsic96@gmail.com
|
|
5
|
-
* @url https://github.com/xtareq
|
|
6
|
-
*/
|
|
7
|
-
export abstract class BaseHttpException extends Error {
|
|
8
|
-
code: number = 500;
|
|
9
|
-
name: string = "HttpException";
|
|
10
|
-
constructor(message: any) {
|
|
11
|
-
super(JSON.stringify(message));
|
|
12
|
-
}
|
|
13
|
-
isCustomException() {
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class BadRequestException extends BaseHttpException {
|
|
19
|
-
name: string = "BadRequest";
|
|
20
|
-
code: number = 400;
|
|
21
|
-
|
|
22
|
-
constructor(message: any) {
|
|
23
|
-
super(message);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export class ValidationErrorException extends BadRequestException {
|
|
28
|
-
name: string = "ValidationError";
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export class InternalErrorException extends BaseHttpException {
|
|
32
|
-
name: string = "InternalError";
|
|
33
|
-
code: number = 500;
|
|
34
|
-
|
|
35
|
-
constructor(message: any = "Something going wrong") {
|
|
36
|
-
super(message);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export class NotFoundException extends BaseHttpException {
|
|
41
|
-
name: string = "NotFound";
|
|
42
|
-
code: number = 404;
|
|
43
|
-
constructor(message: any) {
|
|
44
|
-
super(message);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export class UnauthorizedException extends BaseHttpException {
|
|
49
|
-
name: string = "Unauthorized";
|
|
50
|
-
code: number = 401;
|
|
51
|
-
constructor(message: any) {
|
|
52
|
-
super(message);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export class ForbiddenException extends BaseHttpException {
|
|
57
|
-
name: string = "Forbidden";
|
|
58
|
-
code: number = 403;
|
|
59
|
-
constructor(message: any) {
|
|
60
|
-
super(message);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export type HttpExceptionTypes =
|
|
65
|
-
| NotFoundException
|
|
66
|
-
| BadRequestException
|
|
67
|
-
| UnauthorizedException
|
|
68
|
-
| InternalErrorException
|
|
69
|
-
| ForbiddenException;
|
|
70
|
-
|
|
71
|
-
// export type HttpExceptions = {
|
|
72
|
-
// NotFound: (message: any) => NotFoundException,
|
|
73
|
-
// ValidationError: (message: any) =>ValidationErrorException,
|
|
74
|
-
// BadRequest: (message: any) => BadRequestException,
|
|
75
|
-
// Unauthorized: (message: any) => UnauthorizedException,
|
|
76
|
-
// Forbidden: (message: any) => ForbiddenException,
|
|
77
|
-
// InternalError: (message: any) => InternalErrorException
|
|
78
|
-
// }
|
|
79
|
-
export const HttpExceptions = {
|
|
80
|
-
notFound: (message: any = "") => new NotFoundException(message),
|
|
81
|
-
validationError: (message: any = "") => new ValidationErrorException(message),
|
|
82
|
-
badRequest: (message: any = "") => new BadRequestException(message),
|
|
83
|
-
unauthorized: (message: any = "") => new UnauthorizedException(message),
|
|
84
|
-
forbidden: (message: any = "") => new ForbiddenException(message),
|
|
85
|
-
internalError: (message: any = "") => new InternalErrorException(message),
|
|
86
|
-
};
|
package/src/exceptions/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./http-exceptions";
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2024
|
|
3
|
-
* @author Tareq Hossain
|
|
4
|
-
* @email xtrinsic96@gmail.com
|
|
5
|
-
* @url https://github.com/xtareq
|
|
6
|
-
*/
|
|
7
|
-
export interface IRouteDuplicateErr {
|
|
8
|
-
path: string;
|
|
9
|
-
mpath: string;
|
|
10
|
-
method: string;
|
|
11
|
-
controller: string;
|
|
12
|
-
inverseController?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class SystemUseError extends Error {
|
|
16
|
-
constructor(message: string) {
|
|
17
|
-
super(message);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
export class DuplicateRouteException extends Error {
|
|
21
|
-
constructor(params: IRouteDuplicateErr) {
|
|
22
|
-
let sameController = params.controller == params.inverseController;
|
|
23
|
-
let message = `Duplicate route found for method ${params.method.toUpperCase()}:${params.path == "" ? "'/'" : params.path} `;
|
|
24
|
-
message += sameController
|
|
25
|
-
? `in ${params.controller}`
|
|
26
|
-
: `both in ${params.controller} and ${params.inverseController}`;
|
|
27
|
-
super(message);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export class EnvironmentVariableNotFound extends Error {
|
|
32
|
-
constructor(key: string) {
|
|
33
|
-
super(`${key} not found in environment variables.`);
|
|
34
|
-
}
|
|
35
|
-
}
|