@channel.io/app-sdk-server 0.1.0
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/dist/__tests__/decorators.test.d.ts +2 -0
- package/dist/__tests__/decorators.test.d.ts.map +1 -0
- package/dist/__tests__/decorators.test.js +259 -0
- package/dist/__tests__/decorators.test.js.map +1 -0
- package/dist/__tests__/testing-utils.test.d.ts +2 -0
- package/dist/__tests__/testing-utils.test.d.ts.map +1 -0
- package/dist/__tests__/testing-utils.test.js +118 -0
- package/dist/__tests__/testing-utils.test.js.map +1 -0
- package/dist/appstore/client.d.ts +70 -0
- package/dist/appstore/client.d.ts.map +1 -0
- package/dist/appstore/client.js +141 -0
- package/dist/appstore/client.js.map +1 -0
- package/dist/appstore/index.d.ts +3 -0
- package/dist/appstore/index.d.ts.map +1 -0
- package/dist/appstore/index.js +3 -0
- package/dist/appstore/index.js.map +1 -0
- package/dist/appstore/types.d.ts +87 -0
- package/dist/appstore/types.d.ts.map +1 -0
- package/dist/appstore/types.js +5 -0
- package/dist/appstore/types.js.map +1 -0
- package/dist/decorators/extension.decorator.d.ts +33 -0
- package/dist/decorators/extension.decorator.d.ts.map +1 -0
- package/dist/decorators/extension.decorator.js +36 -0
- package/dist/decorators/extension.decorator.js.map +1 -0
- package/dist/decorators/function.decorator.d.ts +34 -0
- package/dist/decorators/function.decorator.d.ts.map +1 -0
- package/dist/decorators/function.decorator.js +45 -0
- package/dist/decorators/function.decorator.js.map +1 -0
- package/dist/decorators/index.d.ts +5 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/dist/decorators/index.js +9 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/param.decorator.d.ts +49 -0
- package/dist/decorators/param.decorator.d.ts.map +1 -0
- package/dist/decorators/param.decorator.js +62 -0
- package/dist/decorators/param.decorator.js.map +1 -0
- package/dist/decorators/schema.decorator.d.ts +48 -0
- package/dist/decorators/schema.decorator.d.ts.map +1 -0
- package/dist/decorators/schema.decorator.js +59 -0
- package/dist/decorators/schema.decorator.js.map +1 -0
- package/dist/discovery/extension-discovery.service.d.ts +45 -0
- package/dist/discovery/extension-discovery.service.d.ts.map +1 -0
- package/dist/discovery/extension-discovery.service.js +192 -0
- package/dist/discovery/extension-discovery.service.js.map +1 -0
- package/dist/discovery/index.d.ts +3 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +2 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/discovery/metadata.interface.d.ts +42 -0
- package/dist/discovery/metadata.interface.d.ts.map +1 -0
- package/dist/discovery/metadata.interface.js +2 -0
- package/dist/discovery/metadata.interface.js.map +1 -0
- package/dist/guards/index.d.ts +3 -0
- package/dist/guards/index.d.ts.map +1 -0
- package/dist/guards/index.js +3 -0
- package/dist/guards/index.js.map +1 -0
- package/dist/guards/signature.guard.d.ts +65 -0
- package/dist/guards/signature.guard.d.ts.map +1 -0
- package/dist/guards/signature.guard.js +156 -0
- package/dist/guards/signature.guard.js.map +1 -0
- package/dist/guards/signature.middleware.d.ts +76 -0
- package/dist/guards/signature.middleware.d.ts.map +1 -0
- package/dist/guards/signature.middleware.js +110 -0
- package/dist/guards/signature.middleware.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/native/client.d.ts +105 -0
- package/dist/native/client.d.ts.map +1 -0
- package/dist/native/client.js +210 -0
- package/dist/native/client.js.map +1 -0
- package/dist/native/index.d.ts +3 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +3 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native/types.d.ts +198 -0
- package/dist/native/types.d.ts.map +1 -0
- package/dist/native/types.js +8 -0
- package/dist/native/types.js.map +1 -0
- package/dist/nestjs/channel-app.controller.d.ts +22 -0
- package/dist/nestjs/channel-app.controller.d.ts.map +1 -0
- package/dist/nestjs/channel-app.controller.js +87 -0
- package/dist/nestjs/channel-app.controller.js.map +1 -0
- package/dist/nestjs/channel-app.module.d.ts +47 -0
- package/dist/nestjs/channel-app.module.d.ts.map +1 -0
- package/dist/nestjs/channel-app.module.js +93 -0
- package/dist/nestjs/channel-app.module.js.map +1 -0
- package/dist/nestjs/channel-app.service.d.ts +44 -0
- package/dist/nestjs/channel-app.service.d.ts.map +1 -0
- package/dist/nestjs/channel-app.service.js +190 -0
- package/dist/nestjs/channel-app.service.js.map +1 -0
- package/dist/nestjs/index.d.ts +5 -0
- package/dist/nestjs/index.d.ts.map +1 -0
- package/dist/nestjs/index.js +5 -0
- package/dist/nestjs/index.js.map +1 -0
- package/dist/nestjs/types.d.ts +70 -0
- package/dist/nestjs/types.d.ts.map +1 -0
- package/dist/nestjs/types.js +5 -0
- package/dist/nestjs/types.js.map +1 -0
- package/dist/simple/channel-app.d.ts +80 -0
- package/dist/simple/channel-app.d.ts.map +1 -0
- package/dist/simple/channel-app.js +253 -0
- package/dist/simple/channel-app.js.map +1 -0
- package/dist/simple/index.d.ts +2 -0
- package/dist/simple/index.d.ts.map +1 -0
- package/dist/simple/index.js +2 -0
- package/dist/simple/index.js.map +1 -0
- package/dist/testing/index.d.ts +9 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +9 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/mock-context.d.ts +32 -0
- package/dist/testing/mock-context.d.ts.map +1 -0
- package/dist/testing/mock-context.js +33 -0
- package/dist/testing/mock-context.js.map +1 -0
- package/dist/testing/test-extension.d.ts +60 -0
- package/dist/testing/test-extension.d.ts.map +1 -0
- package/dist/testing/test-extension.js +113 -0
- package/dist/testing/test-extension.js.map +1 -0
- package/dist/token/cache.d.ts +31 -0
- package/dist/token/cache.d.ts.map +1 -0
- package/dist/token/cache.js +69 -0
- package/dist/token/cache.js.map +1 -0
- package/dist/token/index.d.ts +4 -0
- package/dist/token/index.d.ts.map +1 -0
- package/dist/token/index.js +4 -0
- package/dist/token/index.js.map +1 -0
- package/dist/token/manager.d.ts +111 -0
- package/dist/token/manager.d.ts.map +1 -0
- package/dist/token/manager.js +246 -0
- package/dist/token/manager.js.map +1 -0
- package/dist/token/types.d.ts +85 -0
- package/dist/token/types.d.ts.map +1 -0
- package/dist/token/types.js +5 -0
- package/dist/token/types.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var ExtensionDiscoveryService_1;
|
|
11
|
+
import { Injectable, Logger } from "@nestjs/common";
|
|
12
|
+
import { DiscoveryService, Reflector } from "@nestjs/core";
|
|
13
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
14
|
+
import { EXTENSION_METADATA, FUNCTION_METADATA, FUNCTIONS_METADATA, INPUT_SCHEMA_METADATA, OUTPUT_SCHEMA_METADATA, DESCRIPTION_METADATA, PARAM_METADATA, FunctionParamType, } from "../decorators/index.js";
|
|
15
|
+
let ExtensionDiscoveryService = ExtensionDiscoveryService_1 = class ExtensionDiscoveryService {
|
|
16
|
+
discoveryService;
|
|
17
|
+
reflector;
|
|
18
|
+
logger = new Logger(ExtensionDiscoveryService_1.name);
|
|
19
|
+
extensions = new Map();
|
|
20
|
+
functionRegistry = new Map();
|
|
21
|
+
constructor(discoveryService, reflector) {
|
|
22
|
+
this.discoveryService = discoveryService;
|
|
23
|
+
this.reflector = reflector;
|
|
24
|
+
}
|
|
25
|
+
onModuleInit() {
|
|
26
|
+
this.discoverExtensions();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Discover all @Extension decorated classes
|
|
30
|
+
*/
|
|
31
|
+
discoverExtensions() {
|
|
32
|
+
const providers = this.discoveryService.getProviders();
|
|
33
|
+
for (const wrapper of providers) {
|
|
34
|
+
const { instance, metatype } = wrapper;
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
36
|
+
if (!instance || !metatype)
|
|
37
|
+
continue;
|
|
38
|
+
const extensionMeta = this.reflector.get(EXTENSION_METADATA, metatype);
|
|
39
|
+
if (!extensionMeta)
|
|
40
|
+
continue;
|
|
41
|
+
this.logger.log(`Discovered extension: ${extensionMeta.name}`);
|
|
42
|
+
// Discover functions on this extension
|
|
43
|
+
const functions = this.discoverFunctions(instance, metatype, extensionMeta.name);
|
|
44
|
+
const metadata = {
|
|
45
|
+
name: extensionMeta.name,
|
|
46
|
+
systemVersion: extensionMeta.systemVersion ?? "v1",
|
|
47
|
+
exclusive: extensionMeta.exclusive ?? false,
|
|
48
|
+
description: extensionMeta.description,
|
|
49
|
+
instance,
|
|
50
|
+
functions,
|
|
51
|
+
};
|
|
52
|
+
this.extensions.set(extensionMeta.name, metadata);
|
|
53
|
+
// Register functions globally for routing
|
|
54
|
+
for (const func of functions) {
|
|
55
|
+
this.functionRegistry.set(func.fullName, func);
|
|
56
|
+
this.logger.debug(`Registered function: ${func.fullName}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Discover @Function decorated methods on an extension class
|
|
62
|
+
*/
|
|
63
|
+
discoverFunctions(instance, metatype, extensionName) {
|
|
64
|
+
const functionKeys = Reflect.getMetadata(FUNCTIONS_METADATA, metatype) ?? [];
|
|
65
|
+
const results = [];
|
|
66
|
+
for (const methodName of functionKeys) {
|
|
67
|
+
const funcMeta = Reflect.getMetadata(FUNCTION_METADATA, metatype, methodName);
|
|
68
|
+
if (!funcMeta)
|
|
69
|
+
continue;
|
|
70
|
+
// Get schema metadata
|
|
71
|
+
const inputSchema = Reflect.getMetadata(INPUT_SCHEMA_METADATA, metatype, methodName);
|
|
72
|
+
const outputSchema = Reflect.getMetadata(OUTPUT_SCHEMA_METADATA, metatype, methodName);
|
|
73
|
+
const description = Reflect.getMetadata(DESCRIPTION_METADATA, metatype, methodName) ?? funcMeta.description;
|
|
74
|
+
// Get parameter metadata for injection
|
|
75
|
+
const paramMeta = Reflect.getMetadata(PARAM_METADATA, metatype, methodName) ?? [];
|
|
76
|
+
// Build full method name for routing
|
|
77
|
+
const fullName = `extension.${extensionName}.${funcMeta.name}`;
|
|
78
|
+
// Convert schemas to JSON Schema
|
|
79
|
+
const inputJsonSchema = inputSchema
|
|
80
|
+
? zodToJsonSchema(inputSchema, { $refStrategy: "none" })
|
|
81
|
+
: undefined;
|
|
82
|
+
const outputJsonSchema = outputSchema
|
|
83
|
+
? zodToJsonSchema(outputSchema, { $refStrategy: "none" })
|
|
84
|
+
: undefined;
|
|
85
|
+
// Create bound handler with parameter injection
|
|
86
|
+
const handler = this.createHandler(instance, methodName, paramMeta, inputSchema, outputSchema);
|
|
87
|
+
const funcMetadata = {
|
|
88
|
+
name: funcMeta.name,
|
|
89
|
+
fullName,
|
|
90
|
+
methodName,
|
|
91
|
+
handler,
|
|
92
|
+
};
|
|
93
|
+
if (description) {
|
|
94
|
+
funcMetadata.description = description;
|
|
95
|
+
}
|
|
96
|
+
if (inputSchema) {
|
|
97
|
+
funcMetadata.inputSchema = inputSchema;
|
|
98
|
+
}
|
|
99
|
+
if (outputSchema) {
|
|
100
|
+
funcMetadata.outputSchema = outputSchema;
|
|
101
|
+
}
|
|
102
|
+
if (inputJsonSchema) {
|
|
103
|
+
funcMetadata.inputJsonSchema = inputJsonSchema;
|
|
104
|
+
}
|
|
105
|
+
if (outputJsonSchema) {
|
|
106
|
+
funcMetadata.outputJsonSchema = outputJsonSchema;
|
|
107
|
+
}
|
|
108
|
+
results.push(funcMetadata);
|
|
109
|
+
}
|
|
110
|
+
return results;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Create a handler function with parameter injection and validation
|
|
114
|
+
*/
|
|
115
|
+
createHandler(instance, methodName, paramMeta, inputSchema, outputSchema) {
|
|
116
|
+
const method = instance[methodName];
|
|
117
|
+
return async (ctx, params) => {
|
|
118
|
+
// Validate input if schema is defined
|
|
119
|
+
let validatedParams = params;
|
|
120
|
+
if (inputSchema) {
|
|
121
|
+
validatedParams = inputSchema.parse(params);
|
|
122
|
+
}
|
|
123
|
+
// Build arguments based on parameter decorators
|
|
124
|
+
const args = [];
|
|
125
|
+
// Sort by index to maintain order
|
|
126
|
+
const sortedParams = [...paramMeta].sort((a, b) => a.index - b.index);
|
|
127
|
+
for (const param of sortedParams) {
|
|
128
|
+
switch (param.type) {
|
|
129
|
+
case FunctionParamType.CTX:
|
|
130
|
+
args[param.index] = ctx;
|
|
131
|
+
break;
|
|
132
|
+
case FunctionParamType.INPUT:
|
|
133
|
+
args[param.index] = validatedParams;
|
|
134
|
+
break;
|
|
135
|
+
case FunctionParamType.BODY:
|
|
136
|
+
args[param.index] = { context: ctx, params: validatedParams };
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// If no parameter decorators, use default order: (ctx, params)
|
|
141
|
+
if (sortedParams.length === 0) {
|
|
142
|
+
args.push(ctx, validatedParams);
|
|
143
|
+
}
|
|
144
|
+
// Call the method
|
|
145
|
+
const result = await method.apply(instance, args);
|
|
146
|
+
// Validate output if schema is defined
|
|
147
|
+
if (outputSchema) {
|
|
148
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
149
|
+
return outputSchema.parse(result);
|
|
150
|
+
}
|
|
151
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
152
|
+
return result;
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get all discovered extensions
|
|
157
|
+
*/
|
|
158
|
+
getExtensions() {
|
|
159
|
+
return Array.from(this.extensions.values());
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get a specific extension by name
|
|
163
|
+
*/
|
|
164
|
+
getExtension(name) {
|
|
165
|
+
return this.extensions.get(name);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get a function by its full name (e.g., "extension.calendar.getAvailability")
|
|
169
|
+
*/
|
|
170
|
+
getFunction(fullName) {
|
|
171
|
+
return this.functionRegistry.get(fullName);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get all registered functions
|
|
175
|
+
*/
|
|
176
|
+
getAllFunctions() {
|
|
177
|
+
return Array.from(this.functionRegistry.values());
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Check if any extensions are registered
|
|
181
|
+
*/
|
|
182
|
+
hasExtensions() {
|
|
183
|
+
return this.extensions.size > 0;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
ExtensionDiscoveryService = ExtensionDiscoveryService_1 = __decorate([
|
|
187
|
+
Injectable(),
|
|
188
|
+
__metadata("design:paramtypes", [DiscoveryService,
|
|
189
|
+
Reflector])
|
|
190
|
+
], ExtensionDiscoveryService);
|
|
191
|
+
export { ExtensionDiscoveryService };
|
|
192
|
+
//# sourceMappingURL=extension-discovery.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension-discovery.service.js","sourceRoot":"","sources":["../../src/discovery/extension-discovery.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Metadata for a discovered extension
|
|
4
|
+
*/
|
|
5
|
+
export interface ExtensionMetadata {
|
|
6
|
+
/** Extension name */
|
|
7
|
+
name: string;
|
|
8
|
+
/** System version (e.g., "v1") */
|
|
9
|
+
systemVersion: string;
|
|
10
|
+
/** Whether this is an exclusive extension */
|
|
11
|
+
exclusive: boolean;
|
|
12
|
+
/** Extension description */
|
|
13
|
+
description?: string;
|
|
14
|
+
/** Class instance */
|
|
15
|
+
instance: unknown;
|
|
16
|
+
/** Discovered functions */
|
|
17
|
+
functions: FunctionMetadata[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Metadata for a discovered function
|
|
21
|
+
*/
|
|
22
|
+
export interface FunctionMetadata {
|
|
23
|
+
/** Function name (e.g., "getAvailability" or "booking.create") */
|
|
24
|
+
name: string;
|
|
25
|
+
/** Full method name for routing (e.g., "extension.calendar.getAvailability") */
|
|
26
|
+
fullName: string;
|
|
27
|
+
/** Method name on the class */
|
|
28
|
+
methodName: string | symbol;
|
|
29
|
+
/** Function description */
|
|
30
|
+
description?: string;
|
|
31
|
+
/** Input schema (Zod) */
|
|
32
|
+
inputSchema?: z.ZodSchema;
|
|
33
|
+
/** Output schema (Zod) */
|
|
34
|
+
outputSchema?: z.ZodSchema;
|
|
35
|
+
/** JSON Schema for input */
|
|
36
|
+
inputJsonSchema?: Record<string, unknown>;
|
|
37
|
+
/** JSON Schema for output */
|
|
38
|
+
outputJsonSchema?: Record<string, unknown>;
|
|
39
|
+
/** Bound handler function */
|
|
40
|
+
handler: (ctx: unknown, params: unknown) => Promise<unknown>;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=metadata.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.interface.d.ts","sourceRoot":"","sources":["../../src/discovery/metadata.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,SAAS,EAAE,OAAO,CAAC;IACnB,4BAA4B;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,2BAA2B;IAC3B,SAAS,EAAE,gBAAgB,EAAE,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,2BAA2B;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yBAAyB;IACzB,WAAW,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IAC1B,0BAA0B;IAC1B,YAAY,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IAC3B,4BAA4B;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,6BAA6B;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,6BAA6B;IAC7B,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.interface.js","sourceRoot":"","sources":["../../src/discovery/metadata.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guards/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/guards/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { CanActivate, ExecutionContext, type Type } from "@nestjs/common";
|
|
2
|
+
import { type ChannelAppModuleOptions } from "../nestjs/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Header name for the signature
|
|
5
|
+
*/
|
|
6
|
+
export declare const SIGNATURE_HEADER = "x-signature";
|
|
7
|
+
/**
|
|
8
|
+
* Verify HMAC-SHA256 signature from ch-app-store
|
|
9
|
+
*
|
|
10
|
+
* Algorithm:
|
|
11
|
+
* 1. Decode signing key from hex format
|
|
12
|
+
* 2. Create HMAC-SHA256 hash of the raw request body
|
|
13
|
+
* 3. Encode result as Base64
|
|
14
|
+
* 4. Compare with X-Signature header using timing-safe comparison
|
|
15
|
+
*
|
|
16
|
+
* @param signature The X-Signature header value
|
|
17
|
+
* @param signingKey The hex-encoded signing key (from app secret or dedicated signing key)
|
|
18
|
+
* @param body Raw request body bytes
|
|
19
|
+
* @returns true if signature is valid
|
|
20
|
+
*/
|
|
21
|
+
export declare function verifySignature(signature: string, signingKey: string, body: Buffer): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* NestJS Guard for verifying X-Signature header from ch-app-store
|
|
24
|
+
*
|
|
25
|
+
* This guard verifies that incoming requests are authentically from ch-app-store
|
|
26
|
+
* by validating the HMAC-SHA256 signature in the X-Signature header.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* // Apply to a specific controller
|
|
31
|
+
* @UseGuards(SignatureGuard)
|
|
32
|
+
* @Controller('functions')
|
|
33
|
+
* export class FunctionsController { ... }
|
|
34
|
+
*
|
|
35
|
+
* // Or apply globally in module
|
|
36
|
+
* @Module({
|
|
37
|
+
* providers: [
|
|
38
|
+
* { provide: APP_GUARD, useClass: SignatureGuard },
|
|
39
|
+
* ],
|
|
40
|
+
* })
|
|
41
|
+
* export class AppModule {}
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* IMPORTANT: For this guard to work, you must configure raw body parsing:
|
|
45
|
+
* ```typescript
|
|
46
|
+
* // main.ts
|
|
47
|
+
* app.useBodyParser('raw', { type: 'application/json' });
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* Or use the `SignatureMiddleware` which handles body buffering automatically.
|
|
51
|
+
*/
|
|
52
|
+
export declare class SignatureGuard implements CanActivate {
|
|
53
|
+
private readonly options;
|
|
54
|
+
private readonly logger;
|
|
55
|
+
constructor(options: ChannelAppModuleOptions);
|
|
56
|
+
canActivate(context: ExecutionContext): boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Factory function to create SignatureGuard with custom options
|
|
60
|
+
* Useful when you need to customize the guard behavior
|
|
61
|
+
*/
|
|
62
|
+
export declare function createSignatureGuard(options?: {
|
|
63
|
+
skipForRoutes?: string[];
|
|
64
|
+
}): Type<CanActivate>;
|
|
65
|
+
//# sourceMappingURL=signature.guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.guard.d.ts","sourceRoot":"","sources":["../../src/guards/signature.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,gBAAgB,EAIhB,KAAK,IAAI,EACV,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAuB,KAAK,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAEvF;;GAEG;AACH,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAE9C;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAyB5F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBACa,cAAe,YAAW,WAAW;IAK9C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJ1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;gBAIvC,OAAO,EAAE,uBAAuB;IAGnD,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO;CA0ChD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAAC,WAAW,CAAC,CAiB9F"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
var SignatureGuard_1;
|
|
14
|
+
import { Injectable, UnauthorizedException, Logger, Inject, } from "@nestjs/common";
|
|
15
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
16
|
+
import { CHANNEL_APP_OPTIONS } from "../nestjs/types.js";
|
|
17
|
+
/**
|
|
18
|
+
* Header name for the signature
|
|
19
|
+
*/
|
|
20
|
+
export const SIGNATURE_HEADER = "x-signature";
|
|
21
|
+
/**
|
|
22
|
+
* Verify HMAC-SHA256 signature from ch-app-store
|
|
23
|
+
*
|
|
24
|
+
* Algorithm:
|
|
25
|
+
* 1. Decode signing key from hex format
|
|
26
|
+
* 2. Create HMAC-SHA256 hash of the raw request body
|
|
27
|
+
* 3. Encode result as Base64
|
|
28
|
+
* 4. Compare with X-Signature header using timing-safe comparison
|
|
29
|
+
*
|
|
30
|
+
* @param signature The X-Signature header value
|
|
31
|
+
* @param signingKey The hex-encoded signing key (from app secret or dedicated signing key)
|
|
32
|
+
* @param body Raw request body bytes
|
|
33
|
+
* @returns true if signature is valid
|
|
34
|
+
*/
|
|
35
|
+
export function verifySignature(signature, signingKey, body) {
|
|
36
|
+
try {
|
|
37
|
+
// Decode the signing key from hex format
|
|
38
|
+
const keyBuffer = Buffer.from(signingKey, "hex");
|
|
39
|
+
// Create HMAC-SHA256 hash
|
|
40
|
+
const hmac = createHmac("sha256", keyBuffer);
|
|
41
|
+
hmac.update(body);
|
|
42
|
+
// Get Base64 encoded signature
|
|
43
|
+
const computedSignature = hmac.digest("base64");
|
|
44
|
+
// Use timing-safe comparison to prevent timing attacks
|
|
45
|
+
const signatureBuffer = Buffer.from(signature, "utf-8");
|
|
46
|
+
const computedBuffer = Buffer.from(computedSignature, "utf-8");
|
|
47
|
+
// Buffers must be same length for timingSafeEqual
|
|
48
|
+
if (signatureBuffer.length !== computedBuffer.length) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return timingSafeEqual(signatureBuffer, computedBuffer);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* NestJS Guard for verifying X-Signature header from ch-app-store
|
|
59
|
+
*
|
|
60
|
+
* This guard verifies that incoming requests are authentically from ch-app-store
|
|
61
|
+
* by validating the HMAC-SHA256 signature in the X-Signature header.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* // Apply to a specific controller
|
|
66
|
+
* @UseGuards(SignatureGuard)
|
|
67
|
+
* @Controller('functions')
|
|
68
|
+
* export class FunctionsController { ... }
|
|
69
|
+
*
|
|
70
|
+
* // Or apply globally in module
|
|
71
|
+
* @Module({
|
|
72
|
+
* providers: [
|
|
73
|
+
* { provide: APP_GUARD, useClass: SignatureGuard },
|
|
74
|
+
* ],
|
|
75
|
+
* })
|
|
76
|
+
* export class AppModule {}
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* IMPORTANT: For this guard to work, you must configure raw body parsing:
|
|
80
|
+
* ```typescript
|
|
81
|
+
* // main.ts
|
|
82
|
+
* app.useBodyParser('raw', { type: 'application/json' });
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* Or use the `SignatureMiddleware` which handles body buffering automatically.
|
|
86
|
+
*/
|
|
87
|
+
let SignatureGuard = SignatureGuard_1 = class SignatureGuard {
|
|
88
|
+
options;
|
|
89
|
+
logger = new Logger(SignatureGuard_1.name);
|
|
90
|
+
constructor(options) {
|
|
91
|
+
this.options = options;
|
|
92
|
+
}
|
|
93
|
+
canActivate(context) {
|
|
94
|
+
// Skip if signature verification is disabled
|
|
95
|
+
if (this.options.skipSignatureVerification) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
const request = context.switchToHttp().getRequest();
|
|
99
|
+
const signature = request.headers[SIGNATURE_HEADER];
|
|
100
|
+
if (!signature) {
|
|
101
|
+
this.logger.warn("Missing X-Signature header");
|
|
102
|
+
throw new UnauthorizedException("Missing X-Signature header");
|
|
103
|
+
}
|
|
104
|
+
// Get the signing key (use signingKey if provided, otherwise derive from appSecret)
|
|
105
|
+
const signingKey = this.options.signingKey ?? this.options.appSecret;
|
|
106
|
+
if (!signingKey) {
|
|
107
|
+
this.logger.error("No signing key configured");
|
|
108
|
+
throw new UnauthorizedException("Signature verification not configured");
|
|
109
|
+
}
|
|
110
|
+
// Get raw body - requires raw body middleware
|
|
111
|
+
const rawBody = request.rawBody;
|
|
112
|
+
if (!rawBody) {
|
|
113
|
+
this.logger.error("Raw body not available. Ensure raw body middleware is configured.");
|
|
114
|
+
throw new UnauthorizedException("Cannot verify signature - raw body not available");
|
|
115
|
+
}
|
|
116
|
+
const isValid = verifySignature(signature, signingKey, rawBody);
|
|
117
|
+
if (!isValid) {
|
|
118
|
+
this.logger.warn("Invalid signature");
|
|
119
|
+
throw new UnauthorizedException("Invalid signature");
|
|
120
|
+
}
|
|
121
|
+
if (this.options.debug) {
|
|
122
|
+
this.logger.debug("Signature verified successfully");
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
SignatureGuard = SignatureGuard_1 = __decorate([
|
|
128
|
+
Injectable(),
|
|
129
|
+
__param(0, Inject(CHANNEL_APP_OPTIONS)),
|
|
130
|
+
__metadata("design:paramtypes", [Object])
|
|
131
|
+
], SignatureGuard);
|
|
132
|
+
export { SignatureGuard };
|
|
133
|
+
/**
|
|
134
|
+
* Factory function to create SignatureGuard with custom options
|
|
135
|
+
* Useful when you need to customize the guard behavior
|
|
136
|
+
*/
|
|
137
|
+
export function createSignatureGuard(options) {
|
|
138
|
+
let ConfigurableSignatureGuard = class ConfigurableSignatureGuard extends SignatureGuard {
|
|
139
|
+
canActivate(context) {
|
|
140
|
+
// Check if route should be skipped
|
|
141
|
+
if (options?.skipForRoutes) {
|
|
142
|
+
const request = context.switchToHttp().getRequest();
|
|
143
|
+
const path = request.path || request.url;
|
|
144
|
+
if (options.skipForRoutes.some((route) => path.startsWith(route))) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return super.canActivate(context);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
ConfigurableSignatureGuard = __decorate([
|
|
152
|
+
Injectable()
|
|
153
|
+
], ConfigurableSignatureGuard);
|
|
154
|
+
return ConfigurableSignatureGuard;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=signature.guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.guard.js","sourceRoot":"","sources":["../../src/guards/signature.guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type NestMiddleware } from "@nestjs/common";
|
|
2
|
+
import type { Request, Response, NextFunction } from "express";
|
|
3
|
+
/**
|
|
4
|
+
* Extend Express Request to include rawBody
|
|
5
|
+
*/
|
|
6
|
+
declare global {
|
|
7
|
+
namespace Express {
|
|
8
|
+
interface Request {
|
|
9
|
+
rawBody?: Buffer;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Middleware to capture raw request body for signature verification
|
|
15
|
+
*
|
|
16
|
+
* This middleware reads the raw body and stores it in `request.rawBody`
|
|
17
|
+
* while still allowing the body to be parsed by subsequent middlewares.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* // Apply in NestJS module
|
|
22
|
+
* export class AppModule implements NestModule {
|
|
23
|
+
* configure(consumer: MiddlewareConsumer) {
|
|
24
|
+
* consumer
|
|
25
|
+
* .apply(SignatureMiddleware)
|
|
26
|
+
* .forRoutes('functions');
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Or configure globally in main.ts:
|
|
32
|
+
* ```typescript
|
|
33
|
+
* // main.ts - Alternative approach with express
|
|
34
|
+
* import express from 'express';
|
|
35
|
+
*
|
|
36
|
+
* app.use(express.json({
|
|
37
|
+
* verify: (req, res, buf) => {
|
|
38
|
+
* req.rawBody = buf;
|
|
39
|
+
* },
|
|
40
|
+
* }));
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare class SignatureMiddleware implements NestMiddleware {
|
|
44
|
+
use(req: Request, _res: Response, next: NextFunction): void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Express middleware function for raw body capture
|
|
48
|
+
* Use this if you prefer a function-based middleware
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* // main.ts
|
|
53
|
+
* app.use('/functions', rawBodyMiddleware);
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function rawBodyMiddleware(req: Request, _res: Response, next: NextFunction): void;
|
|
57
|
+
/**
|
|
58
|
+
* Create express.json() options with raw body capture
|
|
59
|
+
* This is the recommended approach for signature verification
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* // main.ts
|
|
64
|
+
* import express from 'express';
|
|
65
|
+
*
|
|
66
|
+
* const app = await NestFactory.create(AppModule, {
|
|
67
|
+
* bodyParser: false, // Disable default body parser
|
|
68
|
+
* });
|
|
69
|
+
*
|
|
70
|
+
* app.use(express.json(jsonWithRawBody()));
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export declare function jsonWithRawBody(): {
|
|
74
|
+
verify: (req: Request, res: Response, buf: Buffer) => void;
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=signature.middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.middleware.d.ts","sourceRoot":"","sources":["../../src/guards/signature.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE/D;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,OAAO,CAAC,EAAE,MAAM,CAAC;SAClB;KACF;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBACa,mBAAoB,YAAW,cAAc;IACxD,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI;CAqB5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAmBxF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,IAAI;IACjC,MAAM,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5D,CAMA"}
|