@wordrhyme/auto-crud-server 0.2.0 → 0.4.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/README.md +94 -35
- package/dist/index.cjs +188 -62
- package/dist/index.d.cts +293 -127
- package/dist/index.d.ts +283 -116
- package/dist/index.js +188 -62
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -136,7 +136,7 @@ export { handler as GET, handler as POST };
|
|
|
136
136
|
|
|
137
137
|
## 📖 自动生成的路由
|
|
138
138
|
|
|
139
|
-
`createCrudRouter` 会自动生成以下
|
|
139
|
+
`createCrudRouter` 会自动生成以下 7 个路由:
|
|
140
140
|
|
|
141
141
|
### 1. `list` - 列表查询
|
|
142
142
|
|
|
@@ -276,6 +276,29 @@ void
|
|
|
276
276
|
await trpc.tasks.deleteMany({ ids: ["1", "2", "3"] });
|
|
277
277
|
```
|
|
278
278
|
|
|
279
|
+
### 7. `updateMany` - 批量更新
|
|
280
|
+
|
|
281
|
+
**输入**:
|
|
282
|
+
```typescript
|
|
283
|
+
{
|
|
284
|
+
ids: string[];
|
|
285
|
+
data: Partial<Omit<Task, "id" | "createdAt" | "updatedAt">>;
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**输出**:
|
|
290
|
+
```typescript
|
|
291
|
+
{ updated: number }
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**示例**:
|
|
295
|
+
```typescript
|
|
296
|
+
await trpc.tasks.updateMany({
|
|
297
|
+
ids: ["1", "2", "3"],
|
|
298
|
+
data: { status: "done" },
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
279
302
|
---
|
|
280
303
|
|
|
281
304
|
## 🎯 高级过滤
|
|
@@ -392,13 +415,21 @@ interface CrudRouterConfig<TTable, TSelect, TInsert, TUpdate> {
|
|
|
392
415
|
#### 返回值
|
|
393
416
|
|
|
394
417
|
```typescript
|
|
418
|
+
// 返回增强的 tRPC router,带有 .procedures 属性
|
|
395
419
|
{
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
420
|
+
// 标准 tRPC router(可直接嵌套到 appRouter)
|
|
421
|
+
...routerMethods,
|
|
422
|
+
|
|
423
|
+
// 可 spread 的 procedures 对象(用于扩展自定义路由)
|
|
424
|
+
procedures: {
|
|
425
|
+
list: Procedure<ListInput, ListOutput>,
|
|
426
|
+
getById: Procedure<string, TSelect>,
|
|
427
|
+
create: Procedure<TInsert, TSelect>,
|
|
428
|
+
update: Procedure<{ id: string, data: TUpdate }, TSelect>,
|
|
429
|
+
delete: Procedure<string, TSelect>,
|
|
430
|
+
deleteMany: Procedure<string[], { deleted: number }>,
|
|
431
|
+
updateMany: Procedure<{ ids: string[], data: TUpdate }, { updated: number }>,
|
|
432
|
+
}
|
|
402
433
|
}
|
|
403
434
|
```
|
|
404
435
|
|
|
@@ -487,24 +518,29 @@ const result = await db
|
|
|
487
518
|
|
|
488
519
|
## 🎨 自定义扩展
|
|
489
520
|
|
|
490
|
-
###
|
|
521
|
+
### 添加自定义路由(推荐方式)
|
|
522
|
+
|
|
523
|
+
使用 `.procedures` 属性 spread 出 CRUD 路由,然后添加自定义路由:
|
|
491
524
|
|
|
492
525
|
```typescript
|
|
493
|
-
import { createCrudRouter } from "@wordrhyme/auto-crud-server";
|
|
494
|
-
import {
|
|
526
|
+
import { createCrudRouter, router } from "@wordrhyme/auto-crud-server";
|
|
527
|
+
import { protectedProcedure } from "../trpc";
|
|
528
|
+
import { z } from "zod";
|
|
495
529
|
|
|
496
|
-
|
|
530
|
+
// 创建基础 CRUD
|
|
531
|
+
const tasksCrud = createCrudRouter({
|
|
497
532
|
table: tasks,
|
|
498
533
|
selectSchema: selectTaskSchema,
|
|
499
534
|
insertSchema: insertTaskSchema,
|
|
500
535
|
updateSchema: updateTaskSchema,
|
|
501
536
|
});
|
|
502
537
|
|
|
503
|
-
|
|
504
|
-
|
|
538
|
+
// 使用 .procedures spread 并添加自定义路由
|
|
539
|
+
export const tasksRouter = router({
|
|
540
|
+
...tasksCrud.procedures,
|
|
505
541
|
|
|
506
|
-
//
|
|
507
|
-
archive:
|
|
542
|
+
// 自定义路由:归档
|
|
543
|
+
archive: protectedProcedure
|
|
508
544
|
.input(z.object({ id: z.string() }))
|
|
509
545
|
.mutation(async ({ input, ctx }) => {
|
|
510
546
|
return ctx.db
|
|
@@ -513,7 +549,8 @@ export const tasksRouter = {
|
|
|
513
549
|
.where(eq(tasks.id, input.id));
|
|
514
550
|
}),
|
|
515
551
|
|
|
516
|
-
|
|
552
|
+
// 自定义路由:取消归档
|
|
553
|
+
unarchive: protectedProcedure
|
|
517
554
|
.input(z.object({ id: z.string() }))
|
|
518
555
|
.mutation(async ({ input, ctx }) => {
|
|
519
556
|
return ctx.db
|
|
@@ -521,30 +558,52 @@ export const tasksRouter = {
|
|
|
521
558
|
.set({ archived: false })
|
|
522
559
|
.where(eq(tasks.id, input.id));
|
|
523
560
|
}),
|
|
524
|
-
};
|
|
561
|
+
});
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### 直接使用(不需要扩展时)
|
|
565
|
+
|
|
566
|
+
如果不需要添加自定义路由,可以直接使用返回的 router:
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
const tasksCrud = createCrudRouter({
|
|
570
|
+
table: tasks,
|
|
571
|
+
selectSchema: selectTaskSchema,
|
|
572
|
+
insertSchema: insertTaskSchema,
|
|
573
|
+
updateSchema: updateTaskSchema,
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
// 直接嵌套到 appRouter
|
|
577
|
+
export const appRouter = router({
|
|
578
|
+
tasks: tasksCrud,
|
|
579
|
+
});
|
|
525
580
|
```
|
|
526
581
|
|
|
527
582
|
### 自定义过滤逻辑
|
|
528
583
|
|
|
529
584
|
```typescript
|
|
530
|
-
//
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
585
|
+
// 覆盖 list 路由,添加自定义过滤逻辑
|
|
586
|
+
const tasksCrud = createCrudRouter({ ... });
|
|
587
|
+
|
|
588
|
+
export const tasksRouter = router({
|
|
589
|
+
...tasksCrud.procedures,
|
|
590
|
+
|
|
591
|
+
// 覆盖 list,添加自定义逻辑
|
|
592
|
+
list: protectedProcedure
|
|
593
|
+
.input(listInputSchema)
|
|
594
|
+
.query(async ({ input, ctx }) => {
|
|
595
|
+
// 自定义过滤逻辑
|
|
596
|
+
const customFilters = input.filters?.map(filter => {
|
|
597
|
+
if (filter.id === "customField") {
|
|
598
|
+
return { ...filter, operator: "custom" };
|
|
599
|
+
}
|
|
600
|
+
return filter;
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// 调用原始 list(需要手动实现或使用 db 查询)
|
|
604
|
+
return ctx.db.select().from(tasks).where(...);
|
|
605
|
+
}),
|
|
606
|
+
});
|
|
548
607
|
```
|
|
549
608
|
|
|
550
609
|
---
|
package/dist/index.cjs
CHANGED
|
@@ -401,9 +401,58 @@ const dataTableConfig = {
|
|
|
401
401
|
|
|
402
402
|
//#endregion
|
|
403
403
|
//#region src/routers/_factory.ts
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
404
|
+
function resolveSoftDelete(option) {
|
|
405
|
+
if (!option) return null;
|
|
406
|
+
if (option === true) return {
|
|
407
|
+
column: "deletedAt",
|
|
408
|
+
getValue: () => /* @__PURE__ */ new Date()
|
|
409
|
+
};
|
|
410
|
+
if (typeof option === "string") return {
|
|
411
|
+
column: option,
|
|
412
|
+
getValue: () => /* @__PURE__ */ new Date()
|
|
413
|
+
};
|
|
414
|
+
const config = option;
|
|
415
|
+
return {
|
|
416
|
+
column: config.column,
|
|
417
|
+
getValue: config.value ?? (() => /* @__PURE__ */ new Date())
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function resolveConfig(config) {
|
|
421
|
+
const mode = config.mode || "declarative";
|
|
422
|
+
if (mode === "factory") {
|
|
423
|
+
const factoryConfig = config;
|
|
424
|
+
return {
|
|
425
|
+
procedureFactory: factoryConfig.procedureFactory,
|
|
426
|
+
guard: void 0,
|
|
427
|
+
getScope: () => void 0,
|
|
428
|
+
getInject: (ctx, op) => factoryConfig.inject?.(ctx, op) ?? {},
|
|
429
|
+
checkAuthorize: async () => true
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
if (mode === "procedures") {
|
|
433
|
+
const proceduresConfig = config;
|
|
434
|
+
const defaultProc = proceduresConfig.defaultProcedure ?? publicProcedure;
|
|
435
|
+
return {
|
|
436
|
+
procedureFactory: (op) => proceduresConfig.procedures[op] ?? defaultProc,
|
|
437
|
+
guard: void 0,
|
|
438
|
+
getScope: () => void 0,
|
|
439
|
+
getInject: (ctx, op) => proceduresConfig.inject?.(ctx, op) ?? {},
|
|
440
|
+
checkAuthorize: async () => true
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
const declarativeConfig = config;
|
|
444
|
+
const baseProcedure = declarativeConfig.procedure ?? publicProcedure;
|
|
445
|
+
return {
|
|
446
|
+
procedureFactory: () => baseProcedure,
|
|
447
|
+
guard: declarativeConfig.guard,
|
|
448
|
+
getScope: (ctx, tbl, op) => declarativeConfig.scope?.(ctx, tbl, op),
|
|
449
|
+
getInject: (ctx, op) => declarativeConfig.inject?.(ctx, op) ?? {},
|
|
450
|
+
checkAuthorize: async (ctx, resource, op) => {
|
|
451
|
+
if (declarativeConfig.authorize) return declarativeConfig.authorize(ctx, resource, op);
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
}
|
|
407
456
|
const filterItemSchema = zod.z.object({
|
|
408
457
|
id: zod.z.string(),
|
|
409
458
|
value: zod.z.union([zod.z.string(), zod.z.array(zod.z.string())]),
|
|
@@ -411,9 +460,6 @@ const filterItemSchema = zod.z.object({
|
|
|
411
460
|
operator: zod.z.enum(dataTableConfig.operators),
|
|
412
461
|
filterId: zod.z.string()
|
|
413
462
|
});
|
|
414
|
-
/**
|
|
415
|
-
* 列表查询输入 Schema
|
|
416
|
-
*/
|
|
417
463
|
const listInputSchema = zod.z.object({
|
|
418
464
|
page: zod.z.number().min(1).default(1),
|
|
419
465
|
perPage: zod.z.number().min(1).max(100).default(10),
|
|
@@ -424,98 +470,178 @@ const listInputSchema = zod.z.object({
|
|
|
424
470
|
filters: zod.z.array(filterItemSchema).optional(),
|
|
425
471
|
joinOperator: zod.z.enum(["and", "or"]).default("and")
|
|
426
472
|
});
|
|
427
|
-
|
|
428
|
-
* 验证列是否在白名单中
|
|
429
|
-
*/
|
|
430
|
-
function validateColumn(columnId, allowedColumns, columnType) {
|
|
473
|
+
function validateColumn(columnId, allowedColumns) {
|
|
431
474
|
if (!allowedColumns || allowedColumns.length === 0) return true;
|
|
432
475
|
return allowedColumns.includes(columnId);
|
|
433
476
|
}
|
|
434
|
-
/**
|
|
435
|
-
* 创建通用 CRUD Router
|
|
436
|
-
*/
|
|
437
477
|
function createCrudRouter(config) {
|
|
438
|
-
const { table, insertSchema, updateSchema, idField = "id", filterableColumns, sortableColumns, maxBatchSize = 100,
|
|
478
|
+
const { table, insertSchema, updateSchema, idField = "id", filterableColumns, sortableColumns, maxBatchSize = 100, softDelete: softDeleteOption, hooks = {} } = config;
|
|
479
|
+
const resolved = resolveConfig(config);
|
|
480
|
+
const softDelete = resolveSoftDelete(softDeleteOption);
|
|
481
|
+
const h = hooks;
|
|
439
482
|
const getIdColumn = () => table[idField];
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
483
|
+
const getSoftDeleteColumn = () => softDelete ? table[softDelete.column] : null;
|
|
484
|
+
const buildWhere = (ctx, operation, additionalCondition) => {
|
|
485
|
+
const conditions = [];
|
|
486
|
+
const scopeCondition = resolved.getScope(ctx, table, operation);
|
|
487
|
+
if (scopeCondition) conditions.push(scopeCondition);
|
|
488
|
+
if (softDelete && (operation === "list" || operation === "get")) {
|
|
489
|
+
const col = getSoftDeleteColumn();
|
|
490
|
+
if (col) conditions.push((0, drizzle_orm.isNull)(col));
|
|
491
|
+
}
|
|
492
|
+
if (additionalCondition) conditions.push(additionalCondition);
|
|
493
|
+
if (conditions.length === 0) return void 0;
|
|
494
|
+
if (conditions.length === 1) return conditions[0];
|
|
495
|
+
return (0, drizzle_orm.and)(...conditions);
|
|
496
|
+
};
|
|
497
|
+
const withGuard = async (ctx, operation) => {
|
|
498
|
+
if (resolved.guard) {
|
|
499
|
+
if (!await resolved.guard(ctx, operation)) throw new Error(`Forbidden: ${operation} not allowed`);
|
|
446
500
|
}
|
|
447
501
|
};
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
502
|
+
const withAuthorize = async (ctx, resource, operation) => {
|
|
503
|
+
if (!resource) return;
|
|
504
|
+
if (!await resolved.checkAuthorize(ctx, resource, operation)) throw new Error(`Forbidden: Cannot ${operation} this resource`);
|
|
505
|
+
};
|
|
506
|
+
const procedures = {
|
|
507
|
+
list: resolved.procedureFactory("list").input(listInputSchema).query(async ({ ctx, input }) => {
|
|
508
|
+
await withGuard(ctx, "list");
|
|
509
|
+
let processedInput = input;
|
|
510
|
+
if (h.beforeList) {
|
|
511
|
+
const result = await h.beforeList(ctx, processedInput);
|
|
512
|
+
if (result) processedInput = result;
|
|
513
|
+
}
|
|
514
|
+
const offset = (processedInput.page - 1) * processedInput.perPage;
|
|
515
|
+
const validatedFilters = processedInput.filters?.filter((filter) => validateColumn(filter.id, filterableColumns));
|
|
516
|
+
const where = buildWhere(ctx, "list", validatedFilters?.length ? filterColumns({
|
|
454
517
|
table,
|
|
455
518
|
filters: validatedFilters,
|
|
456
|
-
joinOperator:
|
|
457
|
-
}) : void 0;
|
|
519
|
+
joinOperator: processedInput.joinOperator
|
|
520
|
+
}) : void 0);
|
|
458
521
|
let query = ctx.db.select().from(table).$dynamic();
|
|
459
522
|
if (where) query = query.where(where);
|
|
460
|
-
if (
|
|
461
|
-
const sortField =
|
|
462
|
-
if (sortField && validateColumn(sortField.id, sortableColumns
|
|
523
|
+
if (processedInput.sort?.length) {
|
|
524
|
+
const sortField = processedInput.sort[0];
|
|
525
|
+
if (sortField && validateColumn(sortField.id, sortableColumns)) {
|
|
463
526
|
const column = table[sortField.id];
|
|
464
527
|
if (column) query = query.orderBy(sortField.desc ? (0, drizzle_orm.desc)(column) : (0, drizzle_orm.asc)(column));
|
|
465
528
|
}
|
|
466
529
|
}
|
|
467
|
-
const data = await query.limit(
|
|
530
|
+
const data = await query.limit(processedInput.perPage).offset(offset);
|
|
468
531
|
let countQuery = ctx.db.select({ count: drizzle_orm.sql`count(*)::int` }).from(table).$dynamic();
|
|
469
532
|
if (where) countQuery = countQuery.where(where);
|
|
470
533
|
const count = (await countQuery)[0]?.count ?? 0;
|
|
471
|
-
|
|
534
|
+
let listResult = {
|
|
472
535
|
data,
|
|
473
536
|
total: count,
|
|
474
|
-
page:
|
|
475
|
-
perPage:
|
|
476
|
-
pageCount: Math.ceil(count /
|
|
537
|
+
page: processedInput.page,
|
|
538
|
+
perPage: processedInput.perPage,
|
|
539
|
+
pageCount: Math.ceil(count / processedInput.perPage)
|
|
477
540
|
};
|
|
541
|
+
if (h.afterList) {
|
|
542
|
+
const result = await h.afterList(ctx, listResult);
|
|
543
|
+
if (result) listResult = result;
|
|
544
|
+
}
|
|
545
|
+
return listResult;
|
|
478
546
|
}),
|
|
479
|
-
getById:
|
|
480
|
-
await
|
|
481
|
-
|
|
482
|
-
const
|
|
483
|
-
|
|
547
|
+
getById: resolved.procedureFactory("get").input(zod.z.string()).query(async ({ ctx, input }) => {
|
|
548
|
+
await withGuard(ctx, "get");
|
|
549
|
+
if (h.beforeGet) await h.beforeGet(ctx, input);
|
|
550
|
+
const where = buildWhere(ctx, "get", (0, drizzle_orm.eq)(getIdColumn(), input));
|
|
551
|
+
const [item] = await ctx.db.select().from(table).where(where);
|
|
552
|
+
await withAuthorize(ctx, item, "get");
|
|
553
|
+
let result = item ?? null;
|
|
554
|
+
if (h.afterGet) {
|
|
555
|
+
const hookResult = await h.afterGet(ctx, result);
|
|
556
|
+
if (hookResult !== void 0) result = hookResult;
|
|
557
|
+
}
|
|
558
|
+
return result;
|
|
484
559
|
}),
|
|
485
|
-
create:
|
|
486
|
-
await
|
|
487
|
-
|
|
560
|
+
create: resolved.procedureFactory("create").input(insertSchema).mutation(async ({ ctx, input }) => {
|
|
561
|
+
await withGuard(ctx, "create");
|
|
562
|
+
let processedData = input;
|
|
563
|
+
if (h.beforeCreate) {
|
|
564
|
+
const result = await h.beforeCreate(ctx, processedData);
|
|
565
|
+
if (result) processedData = result;
|
|
566
|
+
}
|
|
567
|
+
const injectData = resolved.getInject(ctx, "create");
|
|
568
|
+
const data = {
|
|
569
|
+
...processedData,
|
|
570
|
+
...injectData
|
|
571
|
+
};
|
|
572
|
+
const [created] = await ctx.db.insert(table).values(data).returning();
|
|
573
|
+
if (h.afterCreate) await h.afterCreate(ctx, created);
|
|
488
574
|
return created;
|
|
489
575
|
}),
|
|
490
|
-
update:
|
|
576
|
+
update: resolved.procedureFactory("update").input(zod.z.object({
|
|
491
577
|
id: zod.z.string(),
|
|
492
578
|
data: updateSchema
|
|
493
579
|
})).mutation(async ({ ctx, input }) => {
|
|
494
|
-
await
|
|
495
|
-
const
|
|
496
|
-
const [
|
|
580
|
+
await withGuard(ctx, "update");
|
|
581
|
+
const where = buildWhere(ctx, "update", (0, drizzle_orm.eq)(getIdColumn(), input.id));
|
|
582
|
+
const [existing] = await ctx.db.select().from(table).where(where);
|
|
583
|
+
await withAuthorize(ctx, existing, "update");
|
|
584
|
+
if (!existing) throw new Error("Resource not found or access denied");
|
|
585
|
+
let processedData = input.data;
|
|
586
|
+
if (h.beforeUpdate) {
|
|
587
|
+
const result = await h.beforeUpdate(ctx, input.id, processedData, existing);
|
|
588
|
+
if (result) processedData = result;
|
|
589
|
+
}
|
|
590
|
+
const injectData = resolved.getInject(ctx, "update");
|
|
591
|
+
const data = {
|
|
592
|
+
...processedData,
|
|
593
|
+
...injectData
|
|
594
|
+
};
|
|
595
|
+
const [updated] = await ctx.db.update(table).set(data).where(where).returning();
|
|
596
|
+
if (h.afterUpdate) await h.afterUpdate(ctx, updated);
|
|
497
597
|
return updated;
|
|
498
598
|
}),
|
|
499
|
-
delete:
|
|
500
|
-
await
|
|
501
|
-
const
|
|
502
|
-
const [
|
|
599
|
+
delete: resolved.procedureFactory("delete").input(zod.z.string()).mutation(async ({ ctx, input }) => {
|
|
600
|
+
await withGuard(ctx, "delete");
|
|
601
|
+
const where = buildWhere(ctx, "delete", (0, drizzle_orm.eq)(getIdColumn(), input));
|
|
602
|
+
const [existing] = await ctx.db.select().from(table).where(where);
|
|
603
|
+
await withAuthorize(ctx, existing, "delete");
|
|
604
|
+
if (!existing) throw new Error("Resource not found or access denied");
|
|
605
|
+
if (h.beforeDelete) await h.beforeDelete(ctx, input, existing);
|
|
606
|
+
let deleted;
|
|
607
|
+
if (softDelete) [deleted] = await ctx.db.update(table).set({ [softDelete.column]: softDelete.getValue() }).where(where).returning();
|
|
608
|
+
else [deleted] = await ctx.db.delete(table).where(where).returning();
|
|
609
|
+
if (h.afterDelete) await h.afterDelete(ctx, deleted);
|
|
503
610
|
return deleted;
|
|
504
611
|
}),
|
|
505
|
-
deleteMany:
|
|
506
|
-
await
|
|
507
|
-
|
|
508
|
-
|
|
612
|
+
deleteMany: resolved.procedureFactory("deleteMany").input(zod.z.array(zod.z.string()).max(maxBatchSize)).mutation(async ({ ctx, input }) => {
|
|
613
|
+
await withGuard(ctx, "deleteMany");
|
|
614
|
+
if (h.beforeDeleteMany) await h.beforeDeleteMany(ctx, input);
|
|
615
|
+
const where = buildWhere(ctx, "deleteMany", (0, drizzle_orm.inArray)(getIdColumn(), input));
|
|
616
|
+
let result;
|
|
617
|
+
if (softDelete) result = { deleted: (await ctx.db.update(table).set({ [softDelete.column]: softDelete.getValue() }).where(where).returning()).length };
|
|
618
|
+
else result = { deleted: (await ctx.db.delete(table).where(where).returning()).length };
|
|
619
|
+
if (h.afterDeleteMany) await h.afterDeleteMany(ctx, result);
|
|
620
|
+
return result;
|
|
509
621
|
}),
|
|
510
|
-
updateMany:
|
|
511
|
-
ids: zod.z.array(zod.z.string()).max(maxBatchSize
|
|
622
|
+
updateMany: resolved.procedureFactory("updateMany").input(zod.z.object({
|
|
623
|
+
ids: zod.z.array(zod.z.string()).max(maxBatchSize),
|
|
512
624
|
data: updateSchema
|
|
513
625
|
})).mutation(async ({ ctx, input }) => {
|
|
514
|
-
await
|
|
515
|
-
|
|
516
|
-
|
|
626
|
+
await withGuard(ctx, "updateMany");
|
|
627
|
+
let processedData = input.data;
|
|
628
|
+
if (h.beforeUpdateMany) {
|
|
629
|
+
const result$1 = await h.beforeUpdateMany(ctx, input.ids, processedData);
|
|
630
|
+
if (result$1) processedData = result$1;
|
|
631
|
+
}
|
|
632
|
+
const where = buildWhere(ctx, "updateMany", (0, drizzle_orm.inArray)(getIdColumn(), input.ids));
|
|
633
|
+
const injectData = resolved.getInject(ctx, "update");
|
|
634
|
+
const data = {
|
|
635
|
+
...processedData,
|
|
636
|
+
...injectData
|
|
637
|
+
};
|
|
638
|
+
const result = { updated: (await ctx.db.update(table).set(data).where(where).returning()).length };
|
|
639
|
+
if (h.afterUpdateMany) await h.afterUpdateMany(ctx, result);
|
|
640
|
+
return result;
|
|
517
641
|
})
|
|
518
|
-
}
|
|
642
|
+
};
|
|
643
|
+
const crudRouter = router(procedures);
|
|
644
|
+
return Object.assign(crudRouter, { procedures });
|
|
519
645
|
}
|
|
520
646
|
|
|
521
647
|
//#endregion
|