@axiom-lattice/gateway 2.1.52 → 2.1.54
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/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +22 -0
- package/dist/index.js +717 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +713 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
- package/src/__tests__/channel-installations.test.ts +199 -0
- package/src/channels/__tests__/routes.test.ts +71 -0
- package/src/channels/lark/README.md +187 -0
- package/src/channels/lark/__tests__/aggregator.test.ts +23 -0
- package/src/channels/lark/__tests__/controller.test.ts +270 -0
- package/src/channels/lark/__tests__/mapping-service.test.ts +118 -0
- package/src/channels/lark/__tests__/parser.test.ts +72 -0
- package/src/channels/lark/__tests__/sender.test.ts +37 -0
- package/src/channels/lark/__tests__/verification.test.ts +157 -0
- package/src/channels/lark/aggregator.ts +16 -0
- package/src/channels/lark/config.ts +44 -0
- package/src/channels/lark/controller.ts +189 -0
- package/src/channels/lark/mapping-service.ts +138 -0
- package/src/channels/lark/parser.ts +68 -0
- package/src/channels/lark/routes.ts +121 -0
- package/src/channels/lark/runner.ts +37 -0
- package/src/channels/lark/sender.ts +58 -0
- package/src/channels/lark/types.ts +33 -0
- package/src/channels/lark/verification.ts +67 -0
- package/src/channels/routes.ts +25 -0
- package/src/controllers/channel-installations.ts +354 -0
- package/src/controllers/threads.ts +8 -6
- package/src/routes/channel-installations.ts +33 -0
- package/src/routes/index.ts +6 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
import type { FastifyRequest } from "fastify";
|
|
3
|
+
import type { LarkIngressConfig, LarkUrlVerificationPayload } from "./types";
|
|
4
|
+
|
|
5
|
+
export function parseLarkRequestBody(
|
|
6
|
+
body: unknown,
|
|
7
|
+
encryptKey?: string,
|
|
8
|
+
): LarkUrlVerificationPayload {
|
|
9
|
+
const parsed = (body || {}) as LarkUrlVerificationPayload;
|
|
10
|
+
|
|
11
|
+
if (encryptKey && typeof parsed.encrypt === "string") {
|
|
12
|
+
return decryptLarkPayload(encryptKey, parsed.encrypt);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return parsed;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function decryptLarkPayload(
|
|
19
|
+
encryptKey: string,
|
|
20
|
+
encryptedPayload: string,
|
|
21
|
+
): LarkUrlVerificationPayload {
|
|
22
|
+
const key = crypto.createHash("sha256").update(encryptKey).digest();
|
|
23
|
+
const buffer = Buffer.from(encryptedPayload, "base64");
|
|
24
|
+
const iv = buffer.subarray(0, 16);
|
|
25
|
+
const ciphertext = buffer.subarray(16);
|
|
26
|
+
const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
|
|
27
|
+
const plaintext = Buffer.concat([
|
|
28
|
+
decipher.update(ciphertext),
|
|
29
|
+
decipher.final(),
|
|
30
|
+
]).toString("utf8");
|
|
31
|
+
|
|
32
|
+
return JSON.parse(plaintext) as LarkUrlVerificationPayload;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function createLarkRequestVerifier(config: LarkIngressConfig) {
|
|
36
|
+
return function verifyRequest(request: FastifyRequest): boolean {
|
|
37
|
+
const body = parseLarkRequestBody(request.body, config.encryptKey);
|
|
38
|
+
|
|
39
|
+
return verifyLarkParsedBody(body, config);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function verifyLarkParsedBody(
|
|
44
|
+
body: LarkUrlVerificationPayload,
|
|
45
|
+
config: LarkIngressConfig,
|
|
46
|
+
): boolean {
|
|
47
|
+
|
|
48
|
+
if (!config.verificationToken) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return extractVerificationToken(body) === config.verificationToken;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function extractVerificationToken(
|
|
56
|
+
body: LarkUrlVerificationPayload,
|
|
57
|
+
): string | undefined {
|
|
58
|
+
if (typeof body.token === "string") {
|
|
59
|
+
return body.token;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof body.header?.token === "string") {
|
|
63
|
+
return body.header.token;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { LarkEventHandlerDependencies } from "./lark/controller";
|
|
3
|
+
import { registerLarkChannelRoutes } from "./lark/routes";
|
|
4
|
+
|
|
5
|
+
interface ChannelRouteDependencies {
|
|
6
|
+
lark?: LarkEventHandlerDependencies;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type ChannelRouteRegistrar = (
|
|
10
|
+
app: FastifyInstance,
|
|
11
|
+
dependencies: ChannelRouteDependencies,
|
|
12
|
+
) => void;
|
|
13
|
+
|
|
14
|
+
const channelRouteRegistrars: ChannelRouteRegistrar[] = [
|
|
15
|
+
(app, dependencies) => registerLarkChannelRoutes(app, dependencies.lark),
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export function registerChannelRoutes(
|
|
19
|
+
app: FastifyInstance,
|
|
20
|
+
dependencies: ChannelRouteDependencies = {},
|
|
21
|
+
): void {
|
|
22
|
+
for (const registerRoutes of channelRouteRegistrars) {
|
|
23
|
+
registerRoutes(app, dependencies);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
+
import { randomUUID } from "crypto";
|
|
3
|
+
import {
|
|
4
|
+
ChannelInstallationStore,
|
|
5
|
+
ChannelInstallation,
|
|
6
|
+
CreateChannelInstallationRequest,
|
|
7
|
+
UpdateChannelInstallationRequest,
|
|
8
|
+
ChannelInstallationType,
|
|
9
|
+
} from "@axiom-lattice/protocols";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Channel Installation Controller
|
|
13
|
+
* Handles channel installation CRUD operations for Lark/Feishu and future channels
|
|
14
|
+
* All operations are scoped to a tenant ID
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get tenant ID from authenticated user context
|
|
19
|
+
* Falls back to request headers for backward compatibility
|
|
20
|
+
*/
|
|
21
|
+
function getTenantId(request: FastifyRequest): string {
|
|
22
|
+
// First try to get from authenticated user context
|
|
23
|
+
const userTenantId = (request as any).user?.tenantId;
|
|
24
|
+
if (userTenantId) {
|
|
25
|
+
return userTenantId;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Fallback to request headers for backward compatibility
|
|
29
|
+
return (request.headers["x-tenant-id"] as string) || "default";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Channel installation list response
|
|
34
|
+
*/
|
|
35
|
+
interface ChannelInstallationListResponse {
|
|
36
|
+
success: boolean;
|
|
37
|
+
message: string;
|
|
38
|
+
data: {
|
|
39
|
+
records: ChannelInstallation[];
|
|
40
|
+
total: number;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Channel installation response
|
|
46
|
+
*/
|
|
47
|
+
interface ChannelInstallationResponse {
|
|
48
|
+
success: boolean;
|
|
49
|
+
message: string;
|
|
50
|
+
data?: ChannelInstallation;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get channel installation store
|
|
55
|
+
*/
|
|
56
|
+
function getInstallationStore(): ChannelInstallationStore {
|
|
57
|
+
// Import dynamically to avoid circular dependencies
|
|
58
|
+
const { PostgreSQLChannelInstallationStore } = require("@axiom-lattice/pg-stores");
|
|
59
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
60
|
+
|
|
61
|
+
if (!databaseUrl) {
|
|
62
|
+
throw new Error("DATABASE_URL is required for channel installation store");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return new PostgreSQLChannelInstallationStore({
|
|
66
|
+
poolConfig: databaseUrl,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get all channel installations for a tenant
|
|
72
|
+
*/
|
|
73
|
+
export async function getChannelInstallationList(
|
|
74
|
+
request: FastifyRequest<{
|
|
75
|
+
Querystring: { channel?: ChannelInstallationType };
|
|
76
|
+
}>,
|
|
77
|
+
reply: FastifyReply,
|
|
78
|
+
): Promise<ChannelInstallationListResponse> {
|
|
79
|
+
const tenantId = getTenantId(request);
|
|
80
|
+
const { channel } = request.query;
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const store = getInstallationStore();
|
|
84
|
+
const installations = await store.getInstallationsByTenant(tenantId, channel);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
success: true,
|
|
88
|
+
message: "Channel installations retrieved successfully",
|
|
89
|
+
data: {
|
|
90
|
+
records: installations,
|
|
91
|
+
total: installations.length,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error("Failed to get channel installations:", error);
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
message: "Failed to retrieve channel installations",
|
|
99
|
+
data: {
|
|
100
|
+
records: [],
|
|
101
|
+
total: 0,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get a single channel installation by ID
|
|
109
|
+
*/
|
|
110
|
+
export async function getChannelInstallation(
|
|
111
|
+
request: FastifyRequest<{
|
|
112
|
+
Params: { installationId: string };
|
|
113
|
+
}>,
|
|
114
|
+
reply: FastifyReply,
|
|
115
|
+
): Promise<ChannelInstallationResponse> {
|
|
116
|
+
const tenantId = getTenantId(request);
|
|
117
|
+
const { installationId } = request.params;
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const store = getInstallationStore();
|
|
121
|
+
const installation = await store.getInstallationById(installationId);
|
|
122
|
+
|
|
123
|
+
if (!installation) {
|
|
124
|
+
reply.code(404);
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
message: "Channel installation not found",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Verify tenant access
|
|
132
|
+
if (installation.tenantId !== tenantId) {
|
|
133
|
+
reply.code(403);
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
message: "Access denied",
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
success: true,
|
|
142
|
+
message: "Channel installation retrieved successfully",
|
|
143
|
+
data: installation,
|
|
144
|
+
};
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error("Failed to get channel installation:", error);
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
message: "Failed to retrieve channel installation",
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Create new channel installation
|
|
156
|
+
*/
|
|
157
|
+
export async function createChannelInstallation(
|
|
158
|
+
request: FastifyRequest<{
|
|
159
|
+
Body: CreateChannelInstallationRequest & { id?: string };
|
|
160
|
+
}>,
|
|
161
|
+
reply: FastifyReply,
|
|
162
|
+
): Promise<ChannelInstallationResponse> {
|
|
163
|
+
const tenantId = getTenantId(request);
|
|
164
|
+
const body = request.body;
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
// Validate required fields
|
|
168
|
+
if (!body.channel) {
|
|
169
|
+
reply.code(400);
|
|
170
|
+
return {
|
|
171
|
+
success: false,
|
|
172
|
+
message: "Channel type is required",
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!body.config) {
|
|
177
|
+
reply.code(400);
|
|
178
|
+
return {
|
|
179
|
+
success: false,
|
|
180
|
+
message: "Configuration is required",
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Validate Lark-specific config
|
|
185
|
+
if (body.channel === "lark") {
|
|
186
|
+
const larkConfig = body.config;
|
|
187
|
+
if (!larkConfig.appId || !larkConfig.appSecret) {
|
|
188
|
+
reply.code(400);
|
|
189
|
+
return {
|
|
190
|
+
success: false,
|
|
191
|
+
message: "appId and appSecret are required for Lark installations",
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (!larkConfig.assistantId) {
|
|
195
|
+
reply.code(400);
|
|
196
|
+
return {
|
|
197
|
+
success: false,
|
|
198
|
+
message: "assistantId is required for Lark installations",
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const store = getInstallationStore();
|
|
204
|
+
const installationId = body.id || randomUUID();
|
|
205
|
+
|
|
206
|
+
const installation = await store.createInstallation(
|
|
207
|
+
tenantId,
|
|
208
|
+
installationId,
|
|
209
|
+
body,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
reply.code(201);
|
|
213
|
+
return {
|
|
214
|
+
success: true,
|
|
215
|
+
message: "Channel installation created successfully",
|
|
216
|
+
data: installation,
|
|
217
|
+
};
|
|
218
|
+
} catch (error: any) {
|
|
219
|
+
console.error("Failed to create channel installation:", error);
|
|
220
|
+
|
|
221
|
+
// Check for duplicate key error
|
|
222
|
+
if (error.message?.includes("duplicate") || error.code === "23505") {
|
|
223
|
+
reply.code(409);
|
|
224
|
+
return {
|
|
225
|
+
success: false,
|
|
226
|
+
message: "Channel installation with this ID already exists",
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
success: false,
|
|
232
|
+
message: "Failed to create channel installation",
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Update channel installation
|
|
239
|
+
*/
|
|
240
|
+
export async function updateChannelInstallation(
|
|
241
|
+
request: FastifyRequest<{
|
|
242
|
+
Params: { installationId: string };
|
|
243
|
+
Body: UpdateChannelInstallationRequest;
|
|
244
|
+
}>,
|
|
245
|
+
reply: FastifyReply,
|
|
246
|
+
): Promise<ChannelInstallationResponse> {
|
|
247
|
+
const tenantId = getTenantId(request);
|
|
248
|
+
const { installationId } = request.params;
|
|
249
|
+
const body = request.body;
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
const store = getInstallationStore();
|
|
253
|
+
|
|
254
|
+
// Verify installation exists and belongs to tenant
|
|
255
|
+
const existing = await store.getInstallationById(installationId);
|
|
256
|
+
if (!existing) {
|
|
257
|
+
reply.code(404);
|
|
258
|
+
return {
|
|
259
|
+
success: false,
|
|
260
|
+
message: "Channel installation not found",
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (existing.tenantId !== tenantId) {
|
|
265
|
+
reply.code(403);
|
|
266
|
+
return {
|
|
267
|
+
success: false,
|
|
268
|
+
message: "Access denied",
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const installation = await store.updateInstallation(
|
|
273
|
+
tenantId,
|
|
274
|
+
installationId,
|
|
275
|
+
body,
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
if (!installation) {
|
|
279
|
+
reply.code(404);
|
|
280
|
+
return {
|
|
281
|
+
success: false,
|
|
282
|
+
message: "Channel installation not found",
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
success: true,
|
|
288
|
+
message: "Channel installation updated successfully",
|
|
289
|
+
data: installation,
|
|
290
|
+
};
|
|
291
|
+
} catch (error) {
|
|
292
|
+
console.error("Failed to update channel installation:", error);
|
|
293
|
+
return {
|
|
294
|
+
success: false,
|
|
295
|
+
message: "Failed to update channel installation",
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Delete channel installation
|
|
302
|
+
*/
|
|
303
|
+
export async function deleteChannelInstallation(
|
|
304
|
+
request: FastifyRequest<{
|
|
305
|
+
Params: { installationId: string };
|
|
306
|
+
}>,
|
|
307
|
+
reply: FastifyReply,
|
|
308
|
+
): Promise<{ success: boolean; message: string }> {
|
|
309
|
+
const tenantId = getTenantId(request);
|
|
310
|
+
const { installationId } = request.params;
|
|
311
|
+
|
|
312
|
+
try {
|
|
313
|
+
const store = getInstallationStore();
|
|
314
|
+
|
|
315
|
+
// Verify installation exists and belongs to tenant
|
|
316
|
+
const existing = await store.getInstallationById(installationId);
|
|
317
|
+
if (!existing) {
|
|
318
|
+
reply.code(404);
|
|
319
|
+
return {
|
|
320
|
+
success: false,
|
|
321
|
+
message: "Channel installation not found",
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (existing.tenantId !== tenantId) {
|
|
326
|
+
reply.code(403);
|
|
327
|
+
return {
|
|
328
|
+
success: false,
|
|
329
|
+
message: "Access denied",
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const deleted = await store.deleteInstallation(tenantId, installationId);
|
|
334
|
+
|
|
335
|
+
if (!deleted) {
|
|
336
|
+
reply.code(404);
|
|
337
|
+
return {
|
|
338
|
+
success: false,
|
|
339
|
+
message: "Channel installation not found",
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
success: true,
|
|
345
|
+
message: "Channel installation deleted successfully",
|
|
346
|
+
};
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.error("Failed to delete channel installation:", error);
|
|
349
|
+
return {
|
|
350
|
+
success: false,
|
|
351
|
+
message: "Failed to delete channel installation",
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
@@ -58,20 +58,22 @@ interface ThreadUpdateBody {
|
|
|
58
58
|
export async function getThreadList(
|
|
59
59
|
request: FastifyRequest<{
|
|
60
60
|
Params: { assistantId: string };
|
|
61
|
-
Querystring: { workspaceId?: string; projectId?: string };
|
|
62
61
|
}>,
|
|
63
62
|
reply: FastifyReply
|
|
64
63
|
): Promise<ThreadListResponse> {
|
|
65
64
|
const tenantId = getTenantId(request);
|
|
66
65
|
const { assistantId } = request.params;
|
|
67
66
|
|
|
68
|
-
// Build metadata filter from
|
|
67
|
+
// Build metadata filter from headers (workspace/project isolation)
|
|
69
68
|
const metadataFilter: Record<string, string> = {};
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
const workspaceId = request.headers["x-workspace-id"] as string | undefined;
|
|
70
|
+
const projectId = request.headers["x-project-id"] as string | undefined;
|
|
71
|
+
|
|
72
|
+
if (workspaceId) {
|
|
73
|
+
metadataFilter.workspaceId = workspaceId;
|
|
72
74
|
}
|
|
73
|
-
if (
|
|
74
|
-
metadataFilter.projectId =
|
|
75
|
+
if (projectId) {
|
|
76
|
+
metadataFilter.projectId = projectId;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
const storeLattice = getStoreLattice("default", "thread");
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { FastifyInstance } from "fastify";
|
|
2
|
+
import * as channelInstallationController from "../controllers/channel-installations";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Register channel installation management routes
|
|
6
|
+
*/
|
|
7
|
+
export function registerChannelInstallationRoutes(app: FastifyInstance): void {
|
|
8
|
+
// List all installations for current tenant
|
|
9
|
+
app.get<{
|
|
10
|
+
Querystring: { channel?: "lark" };
|
|
11
|
+
}>("/api/channel-installations", channelInstallationController.getChannelInstallationList);
|
|
12
|
+
|
|
13
|
+
// Get single installation by ID
|
|
14
|
+
app.get<{
|
|
15
|
+
Params: { installationId: string };
|
|
16
|
+
}>("/api/channel-installations/:installationId", channelInstallationController.getChannelInstallation);
|
|
17
|
+
|
|
18
|
+
// Create new installation
|
|
19
|
+
app.post<{
|
|
20
|
+
Body: any;
|
|
21
|
+
}>("/api/channel-installations", channelInstallationController.createChannelInstallation);
|
|
22
|
+
|
|
23
|
+
// Update installation
|
|
24
|
+
app.put<{
|
|
25
|
+
Params: { installationId: string };
|
|
26
|
+
Body: any;
|
|
27
|
+
}>("/api/channel-installations/:installationId", channelInstallationController.updateChannelInstallation);
|
|
28
|
+
|
|
29
|
+
// Delete installation
|
|
30
|
+
app.delete<{
|
|
31
|
+
Params: { installationId: string };
|
|
32
|
+
}>("/api/channel-installations/:installationId", channelInstallationController.deleteChannelInstallation);
|
|
33
|
+
}
|
package/src/routes/index.ts
CHANGED
|
@@ -39,6 +39,8 @@ import { registerMcpServerConfigRoutes } from "../controllers/mcp-configs";
|
|
|
39
39
|
import { registerUserRoutes } from "../controllers/users";
|
|
40
40
|
import { registerTenantRoutes } from "../controllers/tenants";
|
|
41
41
|
import { registerAuthRoutes } from "../controllers/auth";
|
|
42
|
+
import { registerChannelRoutes } from "../channels/routes";
|
|
43
|
+
import { registerChannelInstallationRoutes } from "./channel-installations";
|
|
42
44
|
// import {
|
|
43
45
|
// getThreadStatusHandler,
|
|
44
46
|
// getAgentThreadsHandler,
|
|
@@ -343,6 +345,10 @@ export const registerLatticeRoutes = (app: FastifyInstance): void => {
|
|
|
343
345
|
allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false",
|
|
344
346
|
});
|
|
345
347
|
|
|
348
|
+
registerChannelRoutes(app);
|
|
349
|
+
|
|
350
|
+
registerChannelInstallationRoutes(app);
|
|
351
|
+
|
|
346
352
|
// // Thread 状态查询路由
|
|
347
353
|
// app.get("/api/threads/:thread_id/status", getThreadStatusHandler);
|
|
348
354
|
// app.get("/api/assistants/:assistant_id/threads/status", getAgentThreadsHandler);
|