@axiom-lattice/gateway 3.0.2 → 3.0.3
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/index.js +31 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +31 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -6
- package/.eslintrc.json +0 -22
- package/.turbo/turbo-build.log +0 -46
- package/AGENTS.md +0 -112
- package/CHANGELOG.md +0 -1190
- package/LOGGER_CONFIG.md +0 -135
- package/RESUME_STREAM_CONTENT_BASED.md +0 -326
- package/RESUME_STREAM_README.md +0 -388
- package/jest.config.js +0 -72
- package/public/sdk/README.md +0 -695
- package/public/sdk/dashboard-engine-skill.md +0 -1122
- package/public/sdk/dashboard-single-file-spec.md +0 -357
- package/public/sdk/data-query-sdk-skill.md +0 -307
- package/public/sdk/data-query-sdk.d.ts +0 -252
- package/public/sdk/data-query-sdk.js +0 -970
- package/public/sdk/occupancy-dashboard.html +0 -363
- package/public/sdk/test-dashboard.html +0 -690
- package/scripts/seed-lark-installation.ts +0 -54
- package/src/__tests__/__mocks__/e2b.ts +0 -1
- package/src/__tests__/a2a.test.ts +0 -428
- package/src/__tests__/channel-installations.test.ts +0 -199
- package/src/__tests__/data-query.test.ts +0 -77
- package/src/__tests__/sandbox-provider-registration.test.ts +0 -74
- package/src/__tests__/setup.ts +0 -5
- package/src/__tests__/workspace.test.ts +0 -400
- package/src/channels/__tests__/routes.test.ts +0 -50
- package/src/channels/lark/LarkChannelAdapter.ts +0 -217
- package/src/channels/lark/README.md +0 -187
- package/src/channels/lark/__tests__/controller.test.ts +0 -136
- package/src/channels/lark/__tests__/mapping-service.test.ts +0 -118
- package/src/channels/lark/__tests__/parser.test.ts +0 -72
- package/src/channels/lark/__tests__/sender.test.ts +0 -37
- package/src/channels/lark/__tests__/verification.test.ts +0 -157
- package/src/channels/lark/controller.ts +0 -48
- package/src/channels/lark/mapping-service.ts +0 -138
- package/src/channels/lark/parser.ts +0 -68
- package/src/channels/lark/routes.ts +0 -22
- package/src/channels/lark/sender.ts +0 -58
- package/src/channels/lark/types.ts +0 -20
- package/src/channels/lark/verification.ts +0 -32
- package/src/channels/registry.ts +0 -20
- package/src/channels/routes.ts +0 -30
- package/src/channels/wechat/WechatChannelAdapter.ts +0 -294
- package/src/channels/wechat/__tests__/context-store.test.ts +0 -148
- package/src/channels/wechat/context-store.ts +0 -54
- package/src/channels/wechat/controller.ts +0 -59
- package/src/channels/wechat/routes.ts +0 -7
- package/src/channels/wechat/types.ts +0 -62
- package/src/channels/wechat/wechat-client.ts +0 -162
- package/src/config/local-a2a-templates.ts +0 -59
- package/src/config.ts +0 -124
- package/src/controllers/__tests__/assistant-local-a2a.test.ts +0 -147
- package/src/controllers/__tests__/local-a2a-templates.test.ts +0 -112
- package/src/controllers/__tests__/mime.test.ts +0 -74
- package/src/controllers/__tests__/resources.test.ts +0 -122
- package/src/controllers/__tests__/run.test.ts +0 -115
- package/src/controllers/__tests__/tasks.test.ts +0 -215
- package/src/controllers/__tests__/workflow-tracking.test.ts +0 -113
- package/src/controllers/agent_task.ts +0 -72
- package/src/controllers/assistant.ts +0 -490
- package/src/controllers/auth.ts +0 -524
- package/src/controllers/channel-bindings.ts +0 -135
- package/src/controllers/channel-installations.ts +0 -409
- package/src/controllers/collections.ts +0 -176
- package/src/controllers/config.ts +0 -126
- package/src/controllers/connections.ts +0 -181
- package/src/controllers/data-query.ts +0 -236
- package/src/controllers/database-configs.ts +0 -440
- package/src/controllers/embeddings.ts +0 -22
- package/src/controllers/entries.ts +0 -167
- package/src/controllers/eval.ts +0 -513
- package/src/controllers/export-import.ts +0 -166
- package/src/controllers/health.ts +0 -36
- package/src/controllers/local-a2a-templates.ts +0 -18
- package/src/controllers/local-a2a.ts +0 -149
- package/src/controllers/mcp-configs.ts +0 -673
- package/src/controllers/memory.ts +0 -257
- package/src/controllers/menu-items.ts +0 -114
- package/src/controllers/metrics-configs.ts +0 -1061
- package/src/controllers/middleware-types.ts +0 -15
- package/src/controllers/models.ts +0 -199
- package/src/controllers/personal-assistant.ts +0 -191
- package/src/controllers/resources.ts +0 -477
- package/src/controllers/run.ts +0 -338
- package/src/controllers/sandbox.ts +0 -255
- package/src/controllers/schedules.ts +0 -336
- package/src/controllers/skills.ts +0 -256
- package/src/controllers/tasks.ts +0 -213
- package/src/controllers/tenants.ts +0 -121
- package/src/controllers/thread_status.ts +0 -66
- package/src/controllers/threads.ts +0 -260
- package/src/controllers/tools.ts +0 -139
- package/src/controllers/users.ts +0 -135
- package/src/controllers/workflow-tracking.ts +0 -693
- package/src/controllers/workspace.ts +0 -879
- package/src/export_registrations/a2a-api-key.registration.ts +0 -89
- package/src/export_registrations/agent.registration.ts +0 -80
- package/src/export_registrations/binding.registration.ts +0 -106
- package/src/export_registrations/channel-installation.registration.ts +0 -88
- package/src/export_registrations/collection.registration.ts +0 -85
- package/src/export_registrations/connection.registration.ts +0 -97
- package/src/export_registrations/database-config.registration.ts +0 -94
- package/src/export_registrations/eval.registration.ts +0 -334
- package/src/export_registrations/index.ts +0 -31
- package/src/export_registrations/mcp-config.registration.ts +0 -97
- package/src/export_registrations/menu.registration.ts +0 -91
- package/src/export_registrations/metrics-config.registration.ts +0 -94
- package/src/export_registrations/skill.registration.ts +0 -88
- package/src/export_registrations/task.registration.ts +0 -99
- package/src/index.ts +0 -497
- package/src/logger/Logger.ts +0 -186
- package/src/router/MessageContext.ts +0 -14
- package/src/router/MessageRouter.ts +0 -490
- package/src/router/middlewares/auditLogger.ts +0 -34
- package/src/router/middlewares/deduplication.ts +0 -38
- package/src/router/middlewares/index.ts +0 -3
- package/src/router/middlewares/rateLimit.ts +0 -46
- package/src/routes/__tests__/a2a-test-connection.test.ts +0 -177
- package/src/routes/a2a-bridge.ts +0 -266
- package/src/routes/a2a-test-connection.ts +0 -126
- package/src/routes/a2a.ts +0 -851
- package/src/routes/channel-bindings.ts +0 -11
- package/src/routes/channel-installations.ts +0 -33
- package/src/routes/index.ts +0 -640
- package/src/routes/menu-items.ts +0 -10
- package/src/routes/resource-routes.ts +0 -35
- package/src/routes/stt.ts +0 -102
- package/src/schemas/data-query.ts +0 -69
- package/src/schemas/index.ts +0 -852
- package/src/services/ExportImportService.ts +0 -296
- package/src/services/__tests__/ExportImportService.test.ts +0 -130
- package/src/services/__tests__/eval-runner.test.ts +0 -131
- package/src/services/__tests__/local-a2a.test.ts +0 -124
- package/src/services/agent_task_consumer.ts +0 -348
- package/src/services/collection_service.ts +0 -258
- package/src/services/eval-runner.ts +0 -283
- package/src/services/local-a2a.ts +0 -354
- package/src/services/queue_service.ts +0 -104
- package/src/services/sandbox_service.ts +0 -223
- package/src/services/skill_service.ts +0 -97
- package/src/services/supabase.ts +0 -60
- package/src/swagger.ts +0 -77
- package/src/types/index.ts +0 -118
- package/src/types/koa.d.ts +0 -7
- package/src/utils/__tests__/local-a2a-runtime.test.ts +0 -77
- package/src/utils/__tests__/path-containment.test.ts +0 -35
- package/src/utils/a2a-url.ts +0 -20
- package/src/utils/local-a2a-runtime.ts +0 -107
- package/src/utils/mime.ts +0 -44
- package/src/utils/path-containment.ts +0 -38
- package/tsconfig.json +0 -26
package/src/controllers/auth.ts
DELETED
|
@@ -1,524 +0,0 @@
|
|
|
1
|
-
import { FastifyInstance, FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
-
import { getStoreLattice } from "@axiom-lattice/core";
|
|
3
|
-
import { v4 as uuidv4 } from "uuid";
|
|
4
|
-
import type { CreateUserRequest, CreateTenantRequest, UserTenantRole } from "@axiom-lattice/protocols";
|
|
5
|
-
|
|
6
|
-
interface RegisterRequest {
|
|
7
|
-
email: string;
|
|
8
|
-
password: string;
|
|
9
|
-
name: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface LoginRequest {
|
|
13
|
-
email: string;
|
|
14
|
-
password: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface SelectTenantRequest {
|
|
18
|
-
tenantId: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface ApproveUserRequest {
|
|
22
|
-
userId: string;
|
|
23
|
-
approved: boolean;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface AssignTenantRequest {
|
|
27
|
-
userId: string;
|
|
28
|
-
tenantId: string;
|
|
29
|
-
role?: UserTenantRole;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
interface ChangePasswordRequest {
|
|
33
|
-
currentPassword: string;
|
|
34
|
-
newPassword: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface AuthConfig {
|
|
38
|
-
autoApproveUsers: boolean;
|
|
39
|
-
allowTenantRegistration: boolean;
|
|
40
|
-
jwtSecret?: string;
|
|
41
|
-
tokenExpiration: number;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const defaultAuthConfig: AuthConfig = {
|
|
45
|
-
autoApproveUsers: true,
|
|
46
|
-
allowTenantRegistration: true,
|
|
47
|
-
tokenExpiration: 86400,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
export class AuthController {
|
|
51
|
-
private userStore;
|
|
52
|
-
private tenantStore;
|
|
53
|
-
private userTenantLinkStore;
|
|
54
|
-
private config: AuthConfig;
|
|
55
|
-
|
|
56
|
-
constructor(config: Partial<AuthConfig> = {}) {
|
|
57
|
-
this.userStore = getStoreLattice("default", "user").store;
|
|
58
|
-
this.tenantStore = getStoreLattice("default", "tenant").store;
|
|
59
|
-
this.userTenantLinkStore = getStoreLattice("default", "userTenantLink").store;
|
|
60
|
-
this.config = { ...defaultAuthConfig, ...config };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async register(
|
|
64
|
-
request: FastifyRequest<{ Body: RegisterRequest }>,
|
|
65
|
-
reply: FastifyReply
|
|
66
|
-
) {
|
|
67
|
-
const { email, password, name } = request.body;
|
|
68
|
-
|
|
69
|
-
try {
|
|
70
|
-
const existingUser = await this.userStore.getUserByEmail(email);
|
|
71
|
-
if (existingUser) {
|
|
72
|
-
return reply.status(409).send({
|
|
73
|
-
success: false,
|
|
74
|
-
error: "User with this email already exists",
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const userId = uuidv4();
|
|
79
|
-
const userStatus = this.config.autoApproveUsers ? "active" : "pending";
|
|
80
|
-
|
|
81
|
-
const userData: CreateUserRequest = {
|
|
82
|
-
email,
|
|
83
|
-
name,
|
|
84
|
-
status: userStatus,
|
|
85
|
-
metadata: {
|
|
86
|
-
passwordHash: await this.hashPassword(password),
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const user = await this.userStore.createUser(userId, userData);
|
|
91
|
-
|
|
92
|
-
return reply.status(201).send({
|
|
93
|
-
success: true,
|
|
94
|
-
message: this.config.autoApproveUsers
|
|
95
|
-
? "Registration successful"
|
|
96
|
-
: "Registration successful. Please wait for admin approval.",
|
|
97
|
-
data: {
|
|
98
|
-
user: {
|
|
99
|
-
id: user.id,
|
|
100
|
-
email: user.email,
|
|
101
|
-
name: user.name,
|
|
102
|
-
status: user.status,
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
} catch (error) {
|
|
107
|
-
console.error("Registration error:", error);
|
|
108
|
-
return reply.status(500).send({
|
|
109
|
-
success: false,
|
|
110
|
-
error: "Registration failed",
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async login(
|
|
116
|
-
request: FastifyRequest<{ Body: LoginRequest }>,
|
|
117
|
-
reply: FastifyReply
|
|
118
|
-
) {
|
|
119
|
-
const { email, password } = request.body;
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
const user = await this.userStore.getUserByEmail(email);
|
|
123
|
-
if (!user) {
|
|
124
|
-
return reply.status(401).send({
|
|
125
|
-
success: false,
|
|
126
|
-
error: "Invalid credentials",
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (user.status !== "active") {
|
|
131
|
-
return reply.status(403).send({
|
|
132
|
-
success: false,
|
|
133
|
-
error: `Account is ${user.status}. Please wait for admin approval.`,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const isValidPassword = await this.verifyPassword(
|
|
138
|
-
password,
|
|
139
|
-
user.metadata?.passwordHash || ""
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
if (!isValidPassword) {
|
|
143
|
-
return reply.status(401).send({
|
|
144
|
-
success: false,
|
|
145
|
-
error: "Invalid credentials",
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const tenantLinks = await this.userTenantLinkStore.getTenantsByUser(user.id);
|
|
150
|
-
|
|
151
|
-
const token = await this.generateToken(user.id);
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
success: true,
|
|
155
|
-
data: {
|
|
156
|
-
user: {
|
|
157
|
-
id: user.id,
|
|
158
|
-
email: user.email,
|
|
159
|
-
name: user.name,
|
|
160
|
-
status: user.status,
|
|
161
|
-
},
|
|
162
|
-
tenants: tenantLinks.map(link => ({
|
|
163
|
-
id: link.tenantId,
|
|
164
|
-
role: link.role,
|
|
165
|
-
})),
|
|
166
|
-
token,
|
|
167
|
-
requiresTenantSelection: tenantLinks.length > 1,
|
|
168
|
-
hasTenants: tenantLinks.length > 0,
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
} catch (error) {
|
|
172
|
-
console.error("Login error:", error);
|
|
173
|
-
return reply.status(500).send({
|
|
174
|
-
success: false,
|
|
175
|
-
error: "Login failed",
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
async getUserTenants(
|
|
181
|
-
request: FastifyRequest,
|
|
182
|
-
reply: FastifyReply
|
|
183
|
-
) {
|
|
184
|
-
const userId = (request as any).user?.id;
|
|
185
|
-
|
|
186
|
-
if (!userId) {
|
|
187
|
-
return reply.status(401).send({
|
|
188
|
-
success: false,
|
|
189
|
-
error: "Unauthorized",
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
try {
|
|
194
|
-
const links = await this.userTenantLinkStore.getTenantsByUser(userId);
|
|
195
|
-
|
|
196
|
-
const tenantsWithDetails = await Promise.all(
|
|
197
|
-
links.map(async (link) => {
|
|
198
|
-
const tenant = await this.tenantStore.getTenantById(link.tenantId);
|
|
199
|
-
return {
|
|
200
|
-
...link,
|
|
201
|
-
tenant: tenant || null,
|
|
202
|
-
};
|
|
203
|
-
})
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
return {
|
|
207
|
-
success: true,
|
|
208
|
-
data: tenantsWithDetails,
|
|
209
|
-
};
|
|
210
|
-
} catch (error) {
|
|
211
|
-
console.error("Get user tenants error:", error);
|
|
212
|
-
return reply.status(500).send({
|
|
213
|
-
success: false,
|
|
214
|
-
error: "Failed to get user tenants",
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
async selectTenant(
|
|
220
|
-
request: FastifyRequest<{ Body: SelectTenantRequest }>,
|
|
221
|
-
reply: FastifyReply
|
|
222
|
-
) {
|
|
223
|
-
const userId = (request as any).user?.id;
|
|
224
|
-
const { tenantId } = request.body;
|
|
225
|
-
|
|
226
|
-
if (!userId) {
|
|
227
|
-
return reply.status(401).send({
|
|
228
|
-
success: false,
|
|
229
|
-
error: "Unauthorized",
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
try {
|
|
234
|
-
const hasLink = await this.userTenantLinkStore.hasLink(userId, tenantId);
|
|
235
|
-
|
|
236
|
-
if (!hasLink) {
|
|
237
|
-
return reply.status(403).send({
|
|
238
|
-
success: false,
|
|
239
|
-
error: "You do not have access to this tenant",
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const tenant = await this.tenantStore.getTenantById(tenantId);
|
|
244
|
-
|
|
245
|
-
if (!tenant) {
|
|
246
|
-
return reply.status(404).send({
|
|
247
|
-
success: false,
|
|
248
|
-
error: "Tenant not found",
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const token = await this.generateToken(userId, tenantId);
|
|
253
|
-
|
|
254
|
-
// Check for personal assistant in this tenant
|
|
255
|
-
const link = await this.userTenantLinkStore.getLink(userId, tenantId);
|
|
256
|
-
const linkMeta = (link?.metadata || {}) as Record<string, unknown>;
|
|
257
|
-
|
|
258
|
-
let personalAssistant = null;
|
|
259
|
-
if (linkMeta.personalAssistantId) {
|
|
260
|
-
const assistantStore = getStoreLattice("default", "assistant").store;
|
|
261
|
-
const exists = await assistantStore.hasAssistant(tenantId, linkMeta.personalAssistantId as string);
|
|
262
|
-
if (exists) {
|
|
263
|
-
personalAssistant = {
|
|
264
|
-
assistantId: linkMeta.personalAssistantId as string,
|
|
265
|
-
projectId: linkMeta.personalProjectId as string || "default",
|
|
266
|
-
workspaceId: linkMeta.personalWorkspaceId as string || "default",
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return {
|
|
272
|
-
success: true,
|
|
273
|
-
data: {
|
|
274
|
-
tenant,
|
|
275
|
-
token,
|
|
276
|
-
personalAssistant,
|
|
277
|
-
},
|
|
278
|
-
};
|
|
279
|
-
} catch (error) {
|
|
280
|
-
console.error("Select tenant error:", error);
|
|
281
|
-
return reply.status(500).send({
|
|
282
|
-
success: false,
|
|
283
|
-
error: "Failed to select tenant",
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
async approveUser(
|
|
289
|
-
request: FastifyRequest<{ Body: ApproveUserRequest }>,
|
|
290
|
-
reply: FastifyReply
|
|
291
|
-
) {
|
|
292
|
-
const { userId, approved } = request.body;
|
|
293
|
-
|
|
294
|
-
try {
|
|
295
|
-
const user = await this.userStore.getUserById(userId);
|
|
296
|
-
if (!user) {
|
|
297
|
-
return reply.status(404).send({
|
|
298
|
-
success: false,
|
|
299
|
-
error: "User not found",
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (user.status !== "pending") {
|
|
304
|
-
return reply.status(400).send({
|
|
305
|
-
success: false,
|
|
306
|
-
error: `User is already ${user.status}`,
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const updatedUser = await this.userStore.updateUser(userId, {
|
|
311
|
-
status: approved ? "active" : "suspended",
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
return {
|
|
315
|
-
success: true,
|
|
316
|
-
data: updatedUser,
|
|
317
|
-
};
|
|
318
|
-
} catch (error) {
|
|
319
|
-
console.error("Approval error:", error);
|
|
320
|
-
return reply.status(500).send({
|
|
321
|
-
success: false,
|
|
322
|
-
error: "Approval failed",
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
async listPendingUsers(
|
|
328
|
-
request: FastifyRequest,
|
|
329
|
-
reply: FastifyReply
|
|
330
|
-
) {
|
|
331
|
-
try {
|
|
332
|
-
const users = await this.userStore.getAllUsers();
|
|
333
|
-
const pendingUsers = users.filter((u) => u.status === "pending");
|
|
334
|
-
|
|
335
|
-
return {
|
|
336
|
-
success: true,
|
|
337
|
-
data: pendingUsers,
|
|
338
|
-
};
|
|
339
|
-
} catch (error) {
|
|
340
|
-
console.error("List pending users error:", error);
|
|
341
|
-
return reply.status(500).send({
|
|
342
|
-
success: false,
|
|
343
|
-
error: "Failed to list pending users",
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
async assignTenant(
|
|
349
|
-
request: FastifyRequest<{ Body: AssignTenantRequest }>,
|
|
350
|
-
reply: FastifyReply
|
|
351
|
-
) {
|
|
352
|
-
const { userId, tenantId, role = "member" } = request.body;
|
|
353
|
-
|
|
354
|
-
try {
|
|
355
|
-
const user = await this.userStore.getUserById(userId);
|
|
356
|
-
if (!user) {
|
|
357
|
-
return reply.status(404).send({
|
|
358
|
-
success: false,
|
|
359
|
-
error: "User not found",
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const tenant = await this.tenantStore.getTenantById(tenantId);
|
|
364
|
-
if (!tenant) {
|
|
365
|
-
return reply.status(404).send({
|
|
366
|
-
success: false,
|
|
367
|
-
error: "Tenant not found",
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
const link = await this.userTenantLinkStore.createLink({
|
|
372
|
-
userId,
|
|
373
|
-
tenantId,
|
|
374
|
-
role,
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
return {
|
|
378
|
-
success: true,
|
|
379
|
-
data: link,
|
|
380
|
-
};
|
|
381
|
-
} catch (error) {
|
|
382
|
-
console.error("Assign tenant error:", error);
|
|
383
|
-
return reply.status(500).send({
|
|
384
|
-
success: false,
|
|
385
|
-
error: "Failed to assign tenant",
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
private async hashPassword(password: string): Promise<string> {
|
|
391
|
-
const encoder = new TextEncoder();
|
|
392
|
-
const data = encoder.encode(password + "salt");
|
|
393
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
394
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
395
|
-
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
private async verifyPassword(password: string, hash: string): Promise<boolean> {
|
|
399
|
-
const hashedPassword = await this.hashPassword(password);
|
|
400
|
-
return hashedPassword === hash;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
private async generateToken(userId: string, tenantId?: string): Promise<string> {
|
|
404
|
-
const payload: any = {
|
|
405
|
-
userId,
|
|
406
|
-
exp: Date.now() + this.config.tokenExpiration * 1000,
|
|
407
|
-
};
|
|
408
|
-
if (tenantId) {
|
|
409
|
-
payload.tenantId = tenantId;
|
|
410
|
-
}
|
|
411
|
-
return btoa(JSON.stringify(payload));
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
async changePassword(
|
|
415
|
-
request: FastifyRequest<{ Body: ChangePasswordRequest }>,
|
|
416
|
-
reply: FastifyReply
|
|
417
|
-
) {
|
|
418
|
-
const userId = (request as any).user?.id;
|
|
419
|
-
const { currentPassword, newPassword } = request.body;
|
|
420
|
-
|
|
421
|
-
if (!userId) {
|
|
422
|
-
return reply.status(401).send({
|
|
423
|
-
success: false,
|
|
424
|
-
error: "Unauthorized",
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
try {
|
|
429
|
-
const user = await this.userStore.getUserById(userId);
|
|
430
|
-
if (!user) {
|
|
431
|
-
return reply.status(404).send({
|
|
432
|
-
success: false,
|
|
433
|
-
error: "User not found",
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// Verify current password
|
|
438
|
-
const isValidPassword = await this.verifyPassword(
|
|
439
|
-
currentPassword,
|
|
440
|
-
user.metadata?.passwordHash || ""
|
|
441
|
-
);
|
|
442
|
-
|
|
443
|
-
if (!isValidPassword) {
|
|
444
|
-
return reply.status(401).send({
|
|
445
|
-
success: false,
|
|
446
|
-
error: "Current password is incorrect",
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// Validate new password length
|
|
451
|
-
if (newPassword.length < 6) {
|
|
452
|
-
return reply.status(400).send({
|
|
453
|
-
success: false,
|
|
454
|
-
error: "New password must be at least 6 characters",
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// Update password
|
|
459
|
-
await this.userStore.updateUser(userId, {
|
|
460
|
-
metadata: {
|
|
461
|
-
...user.metadata,
|
|
462
|
-
passwordHash: await this.hashPassword(newPassword),
|
|
463
|
-
},
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
return reply.send({
|
|
467
|
-
success: true,
|
|
468
|
-
message: "Password changed successfully",
|
|
469
|
-
});
|
|
470
|
-
} catch (error) {
|
|
471
|
-
console.error("Change password error:", error);
|
|
472
|
-
return reply.status(500).send({
|
|
473
|
-
success: false,
|
|
474
|
-
error: "Failed to change password",
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
export function extractUserFromAuthHeader(
|
|
481
|
-
authHeader: string | undefined
|
|
482
|
-
): { id: string; tenantId?: string } | undefined {
|
|
483
|
-
if (!authHeader?.startsWith("Bearer ")) return undefined;
|
|
484
|
-
const token = authHeader.substring(7);
|
|
485
|
-
try {
|
|
486
|
-
const payload = JSON.parse(atob(token));
|
|
487
|
-
if (payload.exp && payload.exp < Date.now()) return undefined;
|
|
488
|
-
return {
|
|
489
|
-
id: payload.userId,
|
|
490
|
-
tenantId: payload.tenantId,
|
|
491
|
-
};
|
|
492
|
-
} catch {
|
|
493
|
-
return undefined;
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
export function registerAuthRoutes(
|
|
498
|
-
app: FastifyInstance,
|
|
499
|
-
config?: Partial<AuthConfig>
|
|
500
|
-
) {
|
|
501
|
-
const controller = new AuthController(config);
|
|
502
|
-
|
|
503
|
-
const authHook = async (request: FastifyRequest, reply: FastifyReply) => {
|
|
504
|
-
const user = extractUserFromAuthHeader(request.headers.authorization);
|
|
505
|
-
if (!user) {
|
|
506
|
-
return reply.status(401).send({
|
|
507
|
-
success: false,
|
|
508
|
-
error: "Unauthorized",
|
|
509
|
-
});
|
|
510
|
-
}
|
|
511
|
-
(request as any).user = user;
|
|
512
|
-
};
|
|
513
|
-
|
|
514
|
-
app.post("/api/auth/register", controller.register.bind(controller));
|
|
515
|
-
app.post("/api/auth/login", controller.login.bind(controller));
|
|
516
|
-
app.get("/api/auth/tenants", { preHandler: authHook }, (req, res) => controller.getUserTenants(req, res));
|
|
517
|
-
app.post("/api/auth/select-tenant", { preHandler: authHook }, (req, res) => controller.selectTenant(req as any, res));
|
|
518
|
-
app.post("/api/auth/approve", { preHandler: authHook }, (req, res) => controller.approveUser(req as any, res));
|
|
519
|
-
app.get("/api/auth/pending", { preHandler: authHook }, (req, res) => controller.listPendingUsers(req, res));
|
|
520
|
-
app.post("/api/auth/assign-tenant", { preHandler: authHook }, (req, res) => controller.assignTenant(req as any, res));
|
|
521
|
-
app.post("/api/auth/change-password", { preHandler: authHook },
|
|
522
|
-
(req, res) => controller.changePassword(req as any, res)
|
|
523
|
-
);
|
|
524
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import type { FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
-
import type { CreateBindingInput } from "@axiom-lattice/protocols";
|
|
3
|
-
import { getBindingRegistry } from "@axiom-lattice/core";
|
|
4
|
-
|
|
5
|
-
function getTenantId(request: FastifyRequest): string {
|
|
6
|
-
const userTenantId = (request as any).user?.tenantId;
|
|
7
|
-
if (userTenantId) return userTenantId;
|
|
8
|
-
return (request.headers["x-tenant-id"] as string) || "default";
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export async function getBindingList(
|
|
12
|
-
request: FastifyRequest<{
|
|
13
|
-
Querystring: {
|
|
14
|
-
channel?: string;
|
|
15
|
-
agentId?: string;
|
|
16
|
-
channelInstallationId?: string;
|
|
17
|
-
limit?: number;
|
|
18
|
-
offset?: number;
|
|
19
|
-
};
|
|
20
|
-
}>,
|
|
21
|
-
_reply: FastifyReply,
|
|
22
|
-
) {
|
|
23
|
-
const tenantId = getTenantId(request);
|
|
24
|
-
const { channel, agentId, channelInstallationId, limit, offset } = request.query;
|
|
25
|
-
try {
|
|
26
|
-
const registry = getBindingRegistry();
|
|
27
|
-
const bindings = await registry.list({ channel, agentId, tenantId, channelInstallationId, limit, offset });
|
|
28
|
-
return { success: true, message: "Bindings retrieved", data: { records: bindings, total: bindings.length } };
|
|
29
|
-
} catch (error) {
|
|
30
|
-
console.error("Failed to get bindings:", error);
|
|
31
|
-
return { success: false, message: "Failed to retrieve bindings", data: { records: [], total: 0 } };
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export async function getBinding(
|
|
36
|
-
request: FastifyRequest<{ Params: { id: string } }>,
|
|
37
|
-
reply: FastifyReply,
|
|
38
|
-
) {
|
|
39
|
-
const tenantId = getTenantId(request);
|
|
40
|
-
try {
|
|
41
|
-
const registry = getBindingRegistry();
|
|
42
|
-
const bindings = await registry.list({ tenantId });
|
|
43
|
-
const binding = bindings.find((b) => b.id === request.params.id);
|
|
44
|
-
if (!binding || binding.tenantId !== tenantId) {
|
|
45
|
-
reply.status(404);
|
|
46
|
-
return { success: false, message: "Binding not found" };
|
|
47
|
-
}
|
|
48
|
-
return { success: true, message: "Binding retrieved", data: binding };
|
|
49
|
-
} catch (error) {
|
|
50
|
-
console.error("Failed to get binding:", error);
|
|
51
|
-
return { success: false, message: "Failed to retrieve binding" };
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export async function createBinding(
|
|
56
|
-
request: FastifyRequest<{ Body: CreateBindingInput }>,
|
|
57
|
-
reply: FastifyReply,
|
|
58
|
-
) {
|
|
59
|
-
const tenantId = getTenantId(request);
|
|
60
|
-
try {
|
|
61
|
-
const registry = getBindingRegistry();
|
|
62
|
-
const binding = await registry.create({ ...request.body, tenantId });
|
|
63
|
-
reply.status(201);
|
|
64
|
-
return { success: true, message: "Binding created", data: binding };
|
|
65
|
-
} catch (error) {
|
|
66
|
-
console.error("Failed to create binding:", error);
|
|
67
|
-
reply.status(500);
|
|
68
|
-
return { success: false, message: "Failed to create binding" };
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export async function updateBinding(
|
|
73
|
-
request: FastifyRequest<{ Params: { id: string }; Body: Partial<CreateBindingInput & { enabled: boolean }> }>,
|
|
74
|
-
reply: FastifyReply,
|
|
75
|
-
) {
|
|
76
|
-
try {
|
|
77
|
-
const tenantId = getTenantId(request);
|
|
78
|
-
const registry = getBindingRegistry();
|
|
79
|
-
const bindings = await registry.list({ tenantId });
|
|
80
|
-
const existing = bindings.find((b) => b.id === request.params.id);
|
|
81
|
-
if (!existing || existing.tenantId !== tenantId) {
|
|
82
|
-
reply.status(404);
|
|
83
|
-
return { success: false, message: "Binding not found" };
|
|
84
|
-
}
|
|
85
|
-
const binding = await registry.update(request.params.id, request.body);
|
|
86
|
-
return { success: true, message: "Binding updated", data: binding };
|
|
87
|
-
} catch (error) {
|
|
88
|
-
console.error("Failed to update binding:", error);
|
|
89
|
-
reply.status(500);
|
|
90
|
-
return { success: false, message: "Failed to update binding" };
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export async function deleteBinding(
|
|
95
|
-
request: FastifyRequest<{ Params: { id: string } }>,
|
|
96
|
-
reply: FastifyReply,
|
|
97
|
-
) {
|
|
98
|
-
try {
|
|
99
|
-
const tenantId = getTenantId(request);
|
|
100
|
-
const registry = getBindingRegistry();
|
|
101
|
-
const bindings = await registry.list({ tenantId });
|
|
102
|
-
const existing = bindings.find((b) => b.id === request.params.id);
|
|
103
|
-
if (!existing || existing.tenantId !== tenantId) {
|
|
104
|
-
reply.status(404);
|
|
105
|
-
return { success: false, message: "Binding not found" };
|
|
106
|
-
}
|
|
107
|
-
await registry.delete(request.params.id);
|
|
108
|
-
return { success: true, message: "Binding deleted" };
|
|
109
|
-
} catch (error) {
|
|
110
|
-
console.error("Failed to delete binding:", error);
|
|
111
|
-
reply.status(500);
|
|
112
|
-
return { success: false, message: "Failed to delete binding" };
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export async function resolveBinding(
|
|
117
|
-
request: FastifyRequest<{
|
|
118
|
-
Querystring: { channel: string; senderId: string; channelInstallationId: string };
|
|
119
|
-
}>,
|
|
120
|
-
_reply: FastifyReply,
|
|
121
|
-
) {
|
|
122
|
-
const tenantId = getTenantId(request);
|
|
123
|
-
const { channel, senderId, channelInstallationId } = request.query;
|
|
124
|
-
try {
|
|
125
|
-
const registry = getBindingRegistry();
|
|
126
|
-
const binding = await registry.resolve({ channel, senderId, channelInstallationId, tenantId });
|
|
127
|
-
if (!binding) {
|
|
128
|
-
return { success: false, message: "No binding found", data: null };
|
|
129
|
-
}
|
|
130
|
-
return { success: true, message: "Binding found", data: binding };
|
|
131
|
-
} catch (error) {
|
|
132
|
-
console.error("Failed to resolve binding:", error);
|
|
133
|
-
return { success: false, message: "Failed to resolve binding", data: null };
|
|
134
|
-
}
|
|
135
|
-
}
|