@zintrust/core 0.1.33 → 0.1.35

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/core",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "description": "Production-grade TypeScript backend framework for JavaScript",
5
5
  "homepage": "https://zintrust.com",
6
6
  "repository": {
@@ -1 +1 @@
1
- {"version":3,"file":"Application.d.ts","sourceRoot":"","sources":["../../../src/boot/Application.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAOrE,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,qBAAqB,CAAC;AAK3D,MAAM,WAAW,YAAY;IAC3B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,QAAQ,IAAI,OAAO,CAAC;IACpB,aAAa,IAAI,OAAO,CAAC;IACzB,YAAY,IAAI,OAAO,CAAC;IACxB,SAAS,IAAI,OAAO,CAAC;IACrB,cAAc,IAAI,MAAM,CAAC;IACzB,SAAS,IAAI,OAAO,CAAC;IACrB,YAAY,IAAI,iBAAiB,CAAC;IAClC,kBAAkB,IAAI,gBAAgB,CAAC;IACvC,WAAW,IAAI,MAAM,CAAC;CACvB;AAqZD;;GAEG;AACH,eAAO,MAAM,WAAW;IACtB;;OAEG;sBACe,MAAM,GAAG,YAAY;EA0CvC,CAAC;AAEH,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"Application.d.ts","sourceRoot":"","sources":["../../../src/boot/Application.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAOrE,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,qBAAqB,CAAC;AAK3D,MAAM,WAAW,YAAY;IAC3B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,QAAQ,IAAI,OAAO,CAAC;IACpB,aAAa,IAAI,OAAO,CAAC;IACzB,YAAY,IAAI,OAAO,CAAC;IACxB,SAAS,IAAI,OAAO,CAAC;IACrB,cAAc,IAAI,MAAM,CAAC;IACzB,SAAS,IAAI,OAAO,CAAC;IACrB,YAAY,IAAI,iBAAiB,CAAC;IAClC,kBAAkB,IAAI,gBAAgB,CAAC;IACvC,WAAW,IAAI,MAAM,CAAC;CACvB;AA8ZD;;GAEG;AACH,eAAO,MAAM,WAAW;IACtB;;OAEG;sBACe,MAAM,GAAG,YAAY;EA0CvC,CAAC;AAEH,eAAe,WAAW,CAAC"}
@@ -164,8 +164,13 @@ const registerRoutes = async (resolvedBasePath, router) => {
164
164
  mod.registerRoutes(router);
165
165
  }
166
166
  else {
167
- const { registerRoutes: registerFrameworkRoutes } = await import('../../routes/api.js');
168
- registerFrameworkRoutes(router);
167
+ const frameworkRoutes = await tryImportOptional('@routes/api');
168
+ if (typeof frameworkRoutes?.registerRoutes === 'function') {
169
+ frameworkRoutes.registerRoutes(router);
170
+ }
171
+ else {
172
+ Logger.warn('No app routes found and framework routes are unavailable. Ensure routes/api.ts exists in the project.');
173
+ }
169
174
  }
170
175
  // Always register core framework routes (health, metrics, doc) after app routes
171
176
  // This ensures app can override but core routes always exist
@@ -103,9 +103,15 @@ const gracefulShutdown = async (signal) => {
103
103
  // Shutdown worker management system FIRST (before database closes)
104
104
  if (appConfig.detectRuntime() === 'nodejs' || appConfig.detectRuntime() === 'lambda') {
105
105
  try {
106
- const { WorkerShutdown } = await import('@zintrust/workers');
106
+ const { createRequire } = await import('../node-singletons/module.js');
107
+ const require = createRequire(import.meta.url);
108
+ const workers = require('../../packages/workers/src/index.js');
107
109
  const workerBudgetMs = Math.min(15000, remainingMs());
108
- await withTimeout(WorkerShutdown.shutdown({ signal, timeout: workerBudgetMs, forceExit: false }), workerBudgetMs, 'Worker shutdown timed out');
110
+ await withTimeout(workers.WorkerShutdown.shutdown({
111
+ signal,
112
+ timeout: workerBudgetMs,
113
+ forceExit: false,
114
+ }), workerBudgetMs, 'Worker shutdown timed out');
109
115
  }
110
116
  catch (error) {
111
117
  Logger.warn('Worker shutdown failed (continuing with app shutdown)', error);
@@ -136,9 +142,11 @@ async function useWorkerStarter() {
136
142
  // Initialize worker management system
137
143
  let workerInit = null;
138
144
  try {
139
- const { WorkerInit } = await import('@zintrust/workers');
140
- workerInit = WorkerInit;
141
- await WorkerInit.initialize({
145
+ const { createRequire } = await import('../node-singletons/module.js');
146
+ const require = createRequire(import.meta.url);
147
+ const workers = require('../../packages/workers/src/index.js');
148
+ workerInit = workers.WorkerInit;
149
+ await workers.WorkerInit.initialize({
142
150
  enableResourceMonitoring: true,
143
151
  enableHealthMonitoring: true,
144
152
  enableAutoScaling: false, // Disabled by default, enable via config
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @zintrust/core v0.1.33
2
+ * @zintrust/core v0.1.35
3
3
  *
4
4
  * ZinTrust Framework - Production-Grade TypeScript Backend
5
5
  * Built for performance, type safety, and exceptional developer experience
6
6
  *
7
7
  * Build Information:
8
- * Built: 2026-01-29T09:58:51.202Z
8
+ * Built: 2026-01-29T10:36:27.388Z
9
9
  * Node: >=20.0.0
10
10
  * License: MIT
11
11
  *
@@ -21,7 +21,7 @@
21
21
  * Available at runtime for debugging and health checks
22
22
  */
23
23
  export const ZINTRUST_VERSION = '0.1.23';
24
- export const ZINTRUST_BUILD_DATE = '2026-01-29T09:58:51.164Z'; // Replaced during build
24
+ export const ZINTRUST_BUILD_DATE = '2026-01-29T10:36:27.345Z'; // Replaced during build
25
25
  import { Application } from './boot/Application.js';
26
26
  import { AwsSigV4 } from './common/index.js';
27
27
  import { SignedRequest } from './security/SignedRequest.js';
@@ -562,7 +562,7 @@ function attachReadExecutionMethods(builder, state, db) {
562
562
  }
563
563
  const isNonEmptyString = (value) => typeof value === 'string' && value.length > 0;
564
564
  const isKeyValue = (value) => typeof value === 'string' || typeof value === 'number';
565
- const getModelIds = (models, key) => models.map((model) => model.getAttribute(key)).filter(isKeyValue);
565
+ const getModelIds = (models, key) => models.map((model) => model.getAttribute(key)).filter((element) => isKeyValue(element));
566
566
  const applyConstraint = (query, constraint) => {
567
567
  if (typeof constraint === 'function') {
568
568
  return constraint(query) ?? query;
@@ -0,0 +1,468 @@
1
+ /**
2
+ * User QueryBuilder Controller
3
+ * QueryBuilder-backed controller for the users resource.
4
+ */
5
+
6
+ import type { IUserController, JsonRecord, ValidationErrorLike } from '@app/Types/controller';
7
+ import type { IRequest, IResponse, SanitizerError } from '@zintrust/core';
8
+ import {
9
+ Logger,
10
+ QueryBuilder,
11
+ Sanitizer,
12
+ Schema,
13
+ Validator,
14
+ getValidatedBody,
15
+ nowIso,
16
+ randomBytes,
17
+ useDatabase,
18
+ } from '@zintrust/core';
19
+
20
+ const isValidationError = (error: unknown): error is ValidationErrorLike => {
21
+ if (typeof error !== 'object' || error === null) return false;
22
+ const maybe = error as ValidationErrorLike;
23
+ return maybe.name === 'ValidationError' && typeof maybe.toObject === 'function';
24
+ };
25
+
26
+ const isSanitizerError = (error: unknown): error is SanitizerError => {
27
+ if (typeof error !== 'object' || error === null) return false;
28
+ const maybe = error as SanitizerError;
29
+ return maybe.name === 'SanitizerError';
30
+ };
31
+
32
+ const toJsonRecord = (value: unknown): JsonRecord => {
33
+ if (typeof value !== 'object' || value === null) return {};
34
+ if (Array.isArray(value)) return {};
35
+ return value as JsonRecord;
36
+ };
37
+
38
+ const resolveBody = (req: IRequest): JsonRecord => {
39
+ return toJsonRecord(getValidatedBody(req) ?? req.body ?? {});
40
+ };
41
+
42
+ const getParamCompat = (req: IRequest, name: string): unknown => {
43
+ try {
44
+ const anyReq = req as unknown as { getParam?: (key: string) => unknown };
45
+ if (typeof anyReq.getParam === 'function') return anyReq.getParam(name);
46
+ } catch {
47
+ // ignore
48
+ }
49
+
50
+ const anyReq = req as unknown as { params?: Record<string, unknown> };
51
+ const params = anyReq.params;
52
+ if (typeof params === 'object' && params !== null) return params[name];
53
+ return undefined;
54
+ };
55
+
56
+ const requireSelf = (
57
+ req: IRequest,
58
+ res: IResponse,
59
+ userId: string | undefined
60
+ ): userId is string => {
61
+ if (typeof userId !== 'string' || userId.length === 0) {
62
+ res.status(400).json({ error: 'Missing user id' });
63
+ return false;
64
+ }
65
+
66
+ const subject = typeof req.user?.sub === 'string' ? req.user.sub : undefined;
67
+ if (subject === undefined || subject.length === 0) {
68
+ res.status(401).json({ error: 'Unauthorized' });
69
+ return false;
70
+ }
71
+ if (subject !== userId) {
72
+ res.status(403).json({ error: 'Forbidden' });
73
+ return false;
74
+ }
75
+ return true;
76
+ };
77
+
78
+ const randomInt = (min: number, max: number): number => {
79
+ const lo = Math.ceil(min);
80
+ const hi = Math.floor(max);
81
+ return Math.floor(lo + Math.random() * (hi - lo + 1)); // NOSONAR is just a test utility
82
+ };
83
+
84
+ const randomName = (): string => {
85
+ const first = ['Alex', 'Jordan', 'Taylor', 'Sam', 'Casey', 'Riley', 'Morgan'];
86
+ const last = ['Lee', 'Kim', 'Patel', 'Garcia', 'Brown', 'Nguyen', 'Smith'];
87
+ return `${first[randomInt(0, first.length - 1)]} ${last[randomInt(0, last.length - 1)]}`;
88
+ };
89
+
90
+ const randomEmail = (): string => {
91
+ const n = randomInt(10000, 99999);
92
+ return `user${n}@example.com`;
93
+ };
94
+
95
+ const randomPassword = (): string => {
96
+ // Not cryptographically perfect UX-wise, but avoids hard-coded credentials.
97
+ // `base64url` keeps it URL-safe and reasonably short.
98
+ return randomBytes(12).toString('base64url');
99
+ };
100
+
101
+ const pickAllowed = (body: JsonRecord, allowed: Set<string>): JsonRecord => {
102
+ const out: JsonRecord = {};
103
+ for (const [k, v] of Object.entries(body)) {
104
+ if (allowed.has(k)) out[k] = v;
105
+ }
106
+ return out;
107
+ };
108
+
109
+ const hasUnknownKeys = (body: JsonRecord, allowed: Set<string>): string | null => {
110
+ for (const k of Object.keys(body)) {
111
+ if (!allowed.has(k)) return k;
112
+ }
113
+ return null;
114
+ };
115
+
116
+ const sanitizeUserUpdateBody = (updateBody: JsonRecord): JsonRecord => {
117
+ const sanitizedUpdateBody: JsonRecord = {};
118
+ if ('name' in updateBody) {
119
+ sanitizedUpdateBody['name'] = Sanitizer.nameText(updateBody['name']).trim();
120
+ }
121
+ if ('email' in updateBody) {
122
+ sanitizedUpdateBody['email'] = Sanitizer.email(updateBody['email']).trim().toLowerCase();
123
+ }
124
+ if ('password' in updateBody) {
125
+ sanitizedUpdateBody['password'] = Sanitizer.safePasswordChars(updateBody['password']);
126
+ }
127
+ return sanitizedUpdateBody;
128
+ };
129
+
130
+ const buildUserUpdateSchema = (): ReturnType<typeof Schema.create> => {
131
+ return Schema.create()
132
+ .custom(
133
+ 'name',
134
+ (v: unknown) => v === undefined || typeof v === 'string',
135
+ 'name must be a string'
136
+ )
137
+ .minLength('name', 1)
138
+ .custom(
139
+ 'email',
140
+ (v: unknown) => v === undefined || typeof v === 'string',
141
+ 'email must be a string'
142
+ )
143
+ .custom(
144
+ 'password',
145
+ (v: unknown) => v === undefined || typeof v === 'string',
146
+ 'password must be a string'
147
+ )
148
+ .minLength('password', 8);
149
+ };
150
+
151
+ const buildUserStoreSchema = (): ReturnType<typeof Schema.create> => {
152
+ return Schema.create()
153
+ .custom('name', (v: unknown) => typeof v === 'string', 'name must be a string')
154
+ .minLength('name', 1)
155
+ .custom('email', (v: unknown) => typeof v === 'string', 'email must be a string')
156
+ .email('email')
157
+ .custom('password', (v: unknown) => typeof v === 'string', 'password must be a string')
158
+ .minLength('password', 8);
159
+ };
160
+
161
+ /**
162
+ * User Controller Methods
163
+ */
164
+ const userControllerMethods: IUserController = {
165
+ /**
166
+ * List all users
167
+ * GET /users
168
+ */
169
+ async index(req: IRequest, res: IResponse): Promise<void> {
170
+ try {
171
+ const subject = typeof req.user?.sub === 'string' ? req.user.sub : undefined;
172
+ if (subject === undefined || subject.length === 0) {
173
+ res.status(401).json({ error: 'Unauthorized' });
174
+ return;
175
+ }
176
+
177
+ const db = useDatabase();
178
+ const users = await QueryBuilder.create('users', db)
179
+ .select('id', 'name', 'email', 'created_at', 'updated_at')
180
+ .where('id', '=', subject)
181
+ .limit(1)
182
+ .get();
183
+
184
+ res.json({ data: users });
185
+ } catch (error) {
186
+ Logger.error('Error fetching users:', error);
187
+ res.status(500).json({ error: 'Failed to fetch users' });
188
+ }
189
+ },
190
+
191
+ /**
192
+ * Show a specific user
193
+ * GET /users/:id
194
+ */
195
+ async show(req: IRequest, res: IResponse): Promise<void> {
196
+ try {
197
+ const db = useDatabase();
198
+
199
+ const rawId = getParamCompat(req, 'id');
200
+ const id = Sanitizer.digitsOnly(rawId); // Zero trust protection for db id
201
+ if (typeof id !== 'string' || id.length === 0) {
202
+ // ✅ Good
203
+ res.status(400).json({ error: 'Missing user id' });
204
+ return;
205
+ }
206
+ if (!requireSelf(req, res, id)) return;
207
+
208
+ const user = await QueryBuilder.create('users', db)
209
+ .select('id', 'name', 'email', 'created_at', 'updated_at')
210
+ .where('id', '=', id)
211
+ .limit(1)
212
+ .first();
213
+
214
+ if (user === null) {
215
+ res.status(404).json({ error: 'User not found' });
216
+ return;
217
+ }
218
+ res.json({ data: user });
219
+ } catch (error) {
220
+ if (isSanitizerError(error)) {
221
+ res.status(400).json({ error: error.message });
222
+ return;
223
+ }
224
+ Logger.error('Error fetching user:', error);
225
+ res.status(500).json({ error: 'Failed to fetch user' });
226
+ }
227
+ },
228
+
229
+ /**
230
+ * Show create form
231
+ * GET /users/create
232
+ */
233
+ async create(_req: IRequest, res: IResponse): Promise<void> {
234
+ res.json({ form: 'Create User Form' });
235
+ },
236
+
237
+ /**
238
+ * Store a new user
239
+ * POST /users
240
+ */
241
+ async store(req: IRequest, res: IResponse): Promise<void> {
242
+ try {
243
+ // Use validated body if available (already sanitized by middleware), otherwise fallback to raw
244
+ const body = resolveBody(req);
245
+
246
+ const required = ['name', 'email', 'password'] as const;
247
+ const missing: Record<string, string[]> = {};
248
+ for (const key of required) {
249
+ const val = body[key];
250
+ if (typeof val !== 'string' || val.trim() === '') {
251
+ missing[key] = ['Required'];
252
+ }
253
+ }
254
+ if (Object.keys(missing).length > 0) {
255
+ res.status(422).json({ errors: missing });
256
+ return;
257
+ }
258
+
259
+ // Trust middleware for sanitization if validation passed.
260
+ // If we are here, validation ostensibly passed or we are in a context where we must self-validate.
261
+ // To satisfy defense-in-depth without double-sanitization bottleneck:
262
+ // We assume body is safe-ish if it came from resolved validated body.
263
+ // But to be explicit and type-safe, we cast or read fields directly.
264
+
265
+ const db = useDatabase();
266
+ const ts = nowIso();
267
+
268
+ // Apply bulletproof sanitization for defense-in-depth
269
+ const name = Sanitizer.nameText(body['name']);
270
+ const email = Sanitizer.email(body['email']);
271
+ const password = Sanitizer.safePasswordChars(body['password']);
272
+
273
+ Validator.validate({ name, email, password }, buildUserStoreSchema());
274
+
275
+ await QueryBuilder.create('users', db).insert({
276
+ name,
277
+ email,
278
+ password, // Hashing should be handled by model/service or here if raw
279
+ created_at: ts,
280
+ updated_at: ts,
281
+ });
282
+
283
+ res.status(201).json({ message: 'User created' });
284
+ } catch (error) {
285
+ if (isSanitizerError(error)) {
286
+ res.status(400).json({ error: error.message });
287
+ return;
288
+ }
289
+ if (isValidationError(error)) {
290
+ res.status(422).json({ errors: error.toObject?.() ?? {} });
291
+ return;
292
+ }
293
+ Logger.error('Error creating user:', error);
294
+ res.status(500).json({ error: 'Failed to create user' });
295
+ }
296
+ },
297
+
298
+ /**
299
+ * Fill users table with random users
300
+ * POST /users/fill
301
+ */
302
+ async fill(req: IRequest, res: IResponse): Promise<void> {
303
+ try {
304
+ const body = resolveBody(req);
305
+ const countVal = body['count'];
306
+
307
+ // Ensure count is a number (middleware validation handles this, but we double check or default)
308
+ let count = typeof countVal === 'number' ? countVal : 10;
309
+ if (count < 1) count = 1;
310
+ if (count > 100) count = 100;
311
+
312
+ const db = useDatabase();
313
+ const ts = nowIso();
314
+
315
+ // Optimize: Bulk insert instead of N+1 inserts to reduce IO bottleneck and memory overhead
316
+ const users = Array.from({ length: count }, () => ({
317
+ name: randomName(),
318
+ email: randomEmail(),
319
+ password: randomPassword(),
320
+ created_at: ts,
321
+ updated_at: ts,
322
+ }));
323
+
324
+ await QueryBuilder.create('users', db).insert(users);
325
+
326
+ res.status(201).json({ message: 'Users filled', count });
327
+ } catch (error) {
328
+ Logger.error('Error filling users:', error);
329
+ res.status(500).json({ error: 'Failed to fill users' });
330
+ }
331
+ },
332
+
333
+ /**
334
+ * Show edit form
335
+ * GET /users/:id/edit
336
+ */
337
+ async edit(_req: IRequest, res: IResponse): Promise<void> {
338
+ try {
339
+ res.json({ form: 'Edit User Form' });
340
+ } catch (error) {
341
+ Logger.error('Error loading edit form:', error);
342
+ res.status(500).json({ error: 'Failed to load edit form' });
343
+ }
344
+ },
345
+
346
+ /**
347
+ * Update a user
348
+ * PUT /users/:id
349
+ */
350
+ async update(req: IRequest, res: IResponse): Promise<void> {
351
+ // NOSONAR bulletproof sanitization requires explicit validation steps
352
+ try {
353
+ const db = useDatabase();
354
+ const rawId = getParamCompat(req, 'id');
355
+ const id = Sanitizer.digitsOnly(rawId);
356
+ if (typeof id !== 'string' || id.length === 0) {
357
+ res.status(400).json({ error: 'Missing user id' });
358
+ return;
359
+ }
360
+ if (!requireSelf(req, res, id)) return;
361
+
362
+ const allowed = new Set(['name', 'email', 'password']);
363
+ const body = resolveBody(req);
364
+ const unknown = hasUnknownKeys(body, allowed);
365
+ if (unknown !== null) {
366
+ res.status(422).json({ errors: { [unknown]: ['Unknown field'] } });
367
+ return;
368
+ }
369
+
370
+ const updateBody = pickAllowed(body, allowed);
371
+ if (Object.keys(updateBody).length === 0) {
372
+ res.status(422).json({ errors: { body: ['No fields to update'] } });
373
+ return;
374
+ }
375
+
376
+ const sanitizedUpdateBody = sanitizeUserUpdateBody(updateBody);
377
+ Validator.validate(sanitizedUpdateBody, buildUserUpdateSchema());
378
+
379
+ const existing = await QueryBuilder.create('users', db)
380
+ .select('id')
381
+ .where('id', '=', id)
382
+ .limit(1)
383
+ .first();
384
+
385
+ if (existing === null) {
386
+ res.status(404).json({ error: 'User not found' });
387
+ return;
388
+ }
389
+
390
+ const ts = nowIso();
391
+ await QueryBuilder.create('users', db)
392
+ .where('id', '=', id)
393
+ .update({ ...sanitizedUpdateBody, updated_at: ts });
394
+
395
+ const user = await QueryBuilder.create('users', db)
396
+ .select('id', 'name', 'email', 'created_at', 'updated_at')
397
+ .where('id', '=', id)
398
+ .limit(1)
399
+ .first();
400
+
401
+ res.json({ message: 'User updated', user });
402
+ } catch (error) {
403
+ if (isSanitizerError(error)) {
404
+ res.status(400).json({ error: error.message });
405
+ return;
406
+ }
407
+ if (isValidationError(error)) {
408
+ res.status(422).json({ errors: error.toObject?.() ?? {} });
409
+ return;
410
+ }
411
+ Logger.error('Error updating user:', error);
412
+ res.status(500).json({ error: 'Failed to update user' });
413
+ }
414
+ },
415
+
416
+ /**
417
+ * Delete a user
418
+ * DELETE /users/:id
419
+ */
420
+ async destroy(req: IRequest, res: IResponse): Promise<void> {
421
+ try {
422
+ const db = useDatabase();
423
+ const rawId = getParamCompat(req, 'id');
424
+ const id = Sanitizer.digitsOnly(rawId);
425
+ if (typeof id !== 'string' || id.length === 0) {
426
+ res.status(400).json({ error: 'Missing user id' });
427
+ return;
428
+ }
429
+
430
+ if (!requireSelf(req, res, id)) return;
431
+
432
+ const existing = await QueryBuilder.create('users', db)
433
+ .select('id')
434
+ .where('id', '=', id)
435
+ .limit(1)
436
+ .first();
437
+
438
+ if (existing === null) {
439
+ res.status(404).json({ error: 'User not found' });
440
+ return;
441
+ }
442
+
443
+ await QueryBuilder.create('users', db).where('id', '=', id).delete();
444
+ res.json({ message: 'User deleted' });
445
+ } catch (error) {
446
+ if (isSanitizerError(error)) {
447
+ res.status(400).json({ error: error.message });
448
+ return;
449
+ }
450
+ Logger.error('Error deleting user:', error);
451
+ res.status(500).json({ error: 'Failed to delete user' });
452
+ }
453
+ },
454
+ };
455
+
456
+ /**
457
+ * User QueryBuilder Controller Factory
458
+ */
459
+ export const UserQueryBuilderController = {
460
+ /**
461
+ * Create a new user controller instance
462
+ */
463
+ create(): IUserController {
464
+ return userControllerMethods;
465
+ },
466
+ };
467
+
468
+ export default UserQueryBuilderController;