dn-react-router-toolkit 0.7.15 → 0.8.1

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.
Files changed (67) hide show
  1. package/dist/api/create_api_handler.d.mts +5 -4
  2. package/dist/api/create_api_handler.d.ts +5 -4
  3. package/dist/api/create_api_handler.js +0 -1
  4. package/dist/api/create_api_handler.mjs +0 -1
  5. package/dist/api/index.d.mts +1 -0
  6. package/dist/api/index.d.ts +1 -0
  7. package/dist/api/index.js +13 -6
  8. package/dist/api/index.mjs +14 -7
  9. package/dist/api/item_api_handler.d.mts +8 -5
  10. package/dist/api/item_api_handler.d.ts +8 -5
  11. package/dist/api/item_api_handler.js +13 -5
  12. package/dist/api/item_api_handler.mjs +14 -6
  13. package/dist/crud/crud_loader.d.mts +6 -5
  14. package/dist/crud/crud_loader.d.ts +6 -5
  15. package/dist/crud/crud_loader.js +79 -38
  16. package/dist/crud/crud_loader.mjs +81 -39
  17. package/dist/crud/crud_page.d.mts +3 -2
  18. package/dist/crud/crud_page.d.ts +3 -2
  19. package/dist/crud/crud_page.js +279 -201
  20. package/dist/crud/crud_page.mjs +277 -205
  21. package/dist/crud/generate_handlers.d.mts +3 -2
  22. package/dist/crud/generate_handlers.d.ts +3 -2
  23. package/dist/crud/generate_pages.d.mts +2 -1
  24. package/dist/crud/generate_pages.d.ts +2 -1
  25. package/dist/crud/index.d.mts +5 -3
  26. package/dist/crud/index.d.ts +5 -3
  27. package/dist/crud/index.js +338 -219
  28. package/dist/crud/index.mjs +346 -232
  29. package/dist/post/index.js +71 -64
  30. package/dist/post/index.mjs +76 -74
  31. package/dist/post/post_form_page.js +71 -64
  32. package/dist/post/post_form_page.mjs +76 -74
  33. package/dist/table/index.d.mts +7 -3
  34. package/dist/table/index.d.ts +7 -3
  35. package/dist/table/index.js +233 -111
  36. package/dist/table/index.mjs +230 -116
  37. package/dist/table/item_loader.d.mts +5 -4
  38. package/dist/table/item_loader.d.ts +5 -4
  39. package/dist/table/load_table.d.mts +36 -0
  40. package/dist/table/load_table.d.ts +36 -0
  41. package/dist/table/load_table.js +87 -0
  42. package/dist/table/load_table.mjs +66 -0
  43. package/dist/table/loader.d.mts +10 -15
  44. package/dist/table/loader.d.ts +10 -15
  45. package/dist/table/loader.js +67 -31
  46. package/dist/table/loader.mjs +67 -32
  47. package/dist/table/page.d.mts +6 -16
  48. package/dist/table/page.d.ts +6 -16
  49. package/dist/table/page.js +247 -169
  50. package/dist/table/page.mjs +248 -176
  51. package/dist/table/repository.d.mts +14 -10
  52. package/dist/table/repository.d.ts +14 -10
  53. package/dist/table/repository.js +5 -1
  54. package/dist/table/repository.mjs +5 -1
  55. package/dist/table/table.d.mts +4 -1
  56. package/dist/table/table.d.ts +4 -1
  57. package/dist/table/table.js +55 -6
  58. package/dist/table/table.mjs +55 -6
  59. package/dist/table/table_form.d.mts +13 -0
  60. package/dist/table/table_form.d.ts +13 -0
  61. package/dist/table/table_form.js +345 -0
  62. package/dist/table/table_form.mjs +320 -0
  63. package/dist/table/use_table.d.mts +4 -0
  64. package/dist/table/use_table.d.ts +4 -0
  65. package/dist/table/use_table.js +34 -0
  66. package/dist/table/use_table.mjs +9 -0
  67. package/package.json +2 -2
@@ -1,12 +1,13 @@
1
- import { Table, InferSelectModel, SQLWrapper } from 'drizzle-orm';
1
+ import { InferSelectModel, SQLWrapper } from 'drizzle-orm';
2
2
  import { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router';
3
3
  import { TableRepository } from '../table/repository.mjs';
4
+ import { PgTableWithColumns } from 'drizzle-orm/pg-core';
4
5
  import { WithAuthHandler } from '../auth/with_auth.mjs';
5
- import 'drizzle-orm/pg-core';
6
+ import 'drizzle-orm/node-postgres';
6
7
  import 'dn-react-toolkit/auth';
7
8
  import 'dn-react-toolkit/auth/server';
8
9
 
9
- type APIHandlerOptions<T extends Table, TSelect> = {
10
+ type APIHandlerOptions<T extends PgTableWithColumns<any>, TSelect> = {
10
11
  withAuthAction: WithAuthHandler<ActionFunctionArgs>;
11
12
  repository: TableRepository<T, TSelect>;
12
13
  validators?: {
@@ -21,7 +22,7 @@ type APIHandlerOptions<T extends Table, TSelect> = {
21
22
  injectUserId?: boolean;
22
23
  roles?: string[];
23
24
  };
24
- declare function apiHandler<T extends Table, TSelect>({ withAuthAction, repository, validators, existingConditions, injectUserId, roles, }: APIHandlerOptions<T, TSelect>): {
25
+ declare function apiHandler<T extends PgTableWithColumns<any>, TSelect>({ withAuthAction, repository, validators, existingConditions, injectUserId, roles, }: APIHandlerOptions<T, TSelect>): {
25
26
  loader: ({ request }: LoaderFunctionArgs) => Promise<{}>;
26
27
  action: (arg: ActionFunctionArgs<any>) => Promise<unknown> | unknown;
27
28
  };
@@ -1,12 +1,13 @@
1
- import { Table, InferSelectModel, SQLWrapper } from 'drizzle-orm';
1
+ import { InferSelectModel, SQLWrapper } from 'drizzle-orm';
2
2
  import { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router';
3
3
  import { TableRepository } from '../table/repository.js';
4
+ import { PgTableWithColumns } from 'drizzle-orm/pg-core';
4
5
  import { WithAuthHandler } from '../auth/with_auth.js';
5
- import 'drizzle-orm/pg-core';
6
+ import 'drizzle-orm/node-postgres';
6
7
  import 'dn-react-toolkit/auth';
7
8
  import 'dn-react-toolkit/auth/server';
8
9
 
9
- type APIHandlerOptions<T extends Table, TSelect> = {
10
+ type APIHandlerOptions<T extends PgTableWithColumns<any>, TSelect> = {
10
11
  withAuthAction: WithAuthHandler<ActionFunctionArgs>;
11
12
  repository: TableRepository<T, TSelect>;
12
13
  validators?: {
@@ -21,7 +22,7 @@ type APIHandlerOptions<T extends Table, TSelect> = {
21
22
  injectUserId?: boolean;
22
23
  roles?: string[];
23
24
  };
24
- declare function apiHandler<T extends Table, TSelect>({ withAuthAction, repository, validators, existingConditions, injectUserId, roles, }: APIHandlerOptions<T, TSelect>): {
25
+ declare function apiHandler<T extends PgTableWithColumns<any>, TSelect>({ withAuthAction, repository, validators, existingConditions, injectUserId, roles, }: APIHandlerOptions<T, TSelect>): {
25
26
  loader: ({ request }: LoaderFunctionArgs) => Promise<{}>;
26
27
  action: (arg: ActionFunctionArgs<any>) => Promise<unknown> | unknown;
27
28
  };
@@ -25,7 +25,6 @@ __export(create_api_handler_exports, {
25
25
  module.exports = __toCommonJS(create_api_handler_exports);
26
26
  var import_http = require("dn-react-toolkit/http");
27
27
  var import_drizzle_orm = require("drizzle-orm");
28
- var import_react_router = require("react-router");
29
28
  var import_uuid = require("uuid");
30
29
 
31
30
  // src/crud/serialize.ts
@@ -10,7 +10,6 @@ import {
10
10
  import {
11
11
  and
12
12
  } from "drizzle-orm";
13
- import "react-router";
14
13
  import { v4 } from "uuid";
15
14
 
16
15
  // src/crud/serialize.ts
@@ -6,6 +6,7 @@ import 'dn-react-toolkit/auth/server';
6
6
  import 'dn-react-toolkit/file/server';
7
7
  import 'drizzle-orm';
8
8
  import '../table/repository.mjs';
9
+ import 'drizzle-orm/node-postgres';
9
10
  import 'drizzle-orm/pg-core';
10
11
  import '../auth/with_auth.mjs';
11
12
  import 'dn-react-toolkit/auth';
@@ -6,6 +6,7 @@ import 'dn-react-toolkit/auth/server';
6
6
  import 'dn-react-toolkit/file/server';
7
7
  import 'drizzle-orm';
8
8
  import '../table/repository.js';
9
+ import 'drizzle-orm/node-postgres';
9
10
  import 'drizzle-orm/pg-core';
10
11
  import '../auth/with_auth.js';
11
12
  import 'dn-react-toolkit/auth';
package/dist/api/index.js CHANGED
@@ -250,7 +250,6 @@ var createAPIHandler = ({
250
250
  // src/api/create_api_handler.ts
251
251
  var import_http2 = require("dn-react-toolkit/http");
252
252
  var import_drizzle_orm = require("drizzle-orm");
253
- var import_react_router2 = require("react-router");
254
253
  var import_uuid = require("uuid");
255
254
 
256
255
  // src/crud/serialize.ts
@@ -378,21 +377,29 @@ function apiHandler({
378
377
 
379
378
  // src/api/item_api_handler.ts
380
379
  var import_http3 = require("dn-react-toolkit/http");
381
- var import_react_router3 = require("react-router");
382
380
  function itemApiHandler({
383
381
  withAuthAction,
384
- repository
382
+ repository,
383
+ isOwnedBy,
384
+ roles
385
385
  }) {
386
386
  const loader = async ({ request }) => {
387
387
  return {};
388
388
  };
389
389
  const action = withAuthAction((auth) => async ({ params, request }) => {
390
- if (!auth || auth.role !== "admin") {
391
- return (0, import_http3.UNAUTHORIZED)();
390
+ if (roles && roles.length > 0 && (!auth || !roles.includes(auth.role))) {
391
+ throw (0, import_http3.UNAUTHORIZED)();
392
+ }
393
+ const itemId = params.itemId;
394
+ const existing = await repository.find(itemId);
395
+ if (!existing) {
396
+ throw (0, import_http3.NOT_FOUND)();
397
+ }
398
+ if (isOwnedBy && !isOwnedBy(existing, auth)) {
399
+ throw (0, import_http3.FORBIDDEN)();
392
400
  }
393
401
  switch (request.method) {
394
402
  case "DELETE": {
395
- const itemId = params.itemId;
396
403
  await repository.delete(itemId);
397
404
  return {};
398
405
  }
@@ -245,7 +245,6 @@ import {
245
245
  import {
246
246
  and
247
247
  } from "drizzle-orm";
248
- import "react-router";
249
248
  import { v4 } from "uuid";
250
249
 
251
250
  // src/crud/serialize.ts
@@ -372,22 +371,30 @@ function apiHandler({
372
371
  }
373
372
 
374
373
  // src/api/item_api_handler.ts
375
- import { UNAUTHORIZED as UNAUTHORIZED2 } from "dn-react-toolkit/http";
376
- import "react-router";
374
+ import { FORBIDDEN, NOT_FOUND as NOT_FOUND2, UNAUTHORIZED as UNAUTHORIZED2 } from "dn-react-toolkit/http";
377
375
  function itemApiHandler({
378
376
  withAuthAction,
379
- repository
377
+ repository,
378
+ isOwnedBy,
379
+ roles
380
380
  }) {
381
381
  const loader = async ({ request }) => {
382
382
  return {};
383
383
  };
384
384
  const action = withAuthAction((auth) => async ({ params, request }) => {
385
- if (!auth || auth.role !== "admin") {
386
- return UNAUTHORIZED2();
385
+ if (roles && roles.length > 0 && (!auth || !roles.includes(auth.role))) {
386
+ throw UNAUTHORIZED2();
387
+ }
388
+ const itemId = params.itemId;
389
+ const existing = await repository.find(itemId);
390
+ if (!existing) {
391
+ throw NOT_FOUND2();
392
+ }
393
+ if (isOwnedBy && !isOwnedBy(existing, auth)) {
394
+ throw FORBIDDEN();
387
395
  }
388
396
  switch (request.method) {
389
397
  case "DELETE": {
390
- const itemId = params.itemId;
391
398
  await repository.delete(itemId);
392
399
  return {};
393
400
  }
@@ -1,16 +1,19 @@
1
- import { Table } from 'drizzle-orm';
2
1
  import { LoaderFunctionArgs } from 'react-router';
3
2
  import { TableRepository } from '../table/repository.mjs';
3
+ import { PgTableWithColumns } from 'drizzle-orm/pg-core';
4
4
  import { WithAuthHandler } from '../auth/with_auth.mjs';
5
- import 'drizzle-orm/pg-core';
6
- import 'dn-react-toolkit/auth';
5
+ import { AccessTokenPayload } from 'dn-react-toolkit/auth';
6
+ import 'drizzle-orm';
7
+ import 'drizzle-orm/node-postgres';
7
8
  import 'dn-react-toolkit/auth/server';
8
9
 
9
- type ItemAPIHandlerOptions<T extends Table, TSelect> = {
10
+ type ItemAPIHandlerOptions<T extends PgTableWithColumns<any>, TSelect> = {
10
11
  withAuthAction: WithAuthHandler<LoaderFunctionArgs>;
11
12
  repository: TableRepository<T, TSelect>;
13
+ isOwnedBy?: (item: TSelect, auth: AccessTokenPayload | undefined) => boolean;
14
+ roles?: string[];
12
15
  };
13
- declare function itemApiHandler<T extends Table, TSelect>({ withAuthAction, repository, }: ItemAPIHandlerOptions<T, TSelect>): {
16
+ declare function itemApiHandler<T extends PgTableWithColumns<any>, TSelect>({ withAuthAction, repository, isOwnedBy, roles, }: ItemAPIHandlerOptions<T, TSelect>): {
14
17
  loader: ({ request }: LoaderFunctionArgs) => Promise<{}>;
15
18
  action: (arg: LoaderFunctionArgs<any>) => Promise<unknown> | unknown;
16
19
  };
@@ -1,16 +1,19 @@
1
- import { Table } from 'drizzle-orm';
2
1
  import { LoaderFunctionArgs } from 'react-router';
3
2
  import { TableRepository } from '../table/repository.js';
3
+ import { PgTableWithColumns } from 'drizzle-orm/pg-core';
4
4
  import { WithAuthHandler } from '../auth/with_auth.js';
5
- import 'drizzle-orm/pg-core';
6
- import 'dn-react-toolkit/auth';
5
+ import { AccessTokenPayload } from 'dn-react-toolkit/auth';
6
+ import 'drizzle-orm';
7
+ import 'drizzle-orm/node-postgres';
7
8
  import 'dn-react-toolkit/auth/server';
8
9
 
9
- type ItemAPIHandlerOptions<T extends Table, TSelect> = {
10
+ type ItemAPIHandlerOptions<T extends PgTableWithColumns<any>, TSelect> = {
10
11
  withAuthAction: WithAuthHandler<LoaderFunctionArgs>;
11
12
  repository: TableRepository<T, TSelect>;
13
+ isOwnedBy?: (item: TSelect, auth: AccessTokenPayload | undefined) => boolean;
14
+ roles?: string[];
12
15
  };
13
- declare function itemApiHandler<T extends Table, TSelect>({ withAuthAction, repository, }: ItemAPIHandlerOptions<T, TSelect>): {
16
+ declare function itemApiHandler<T extends PgTableWithColumns<any>, TSelect>({ withAuthAction, repository, isOwnedBy, roles, }: ItemAPIHandlerOptions<T, TSelect>): {
14
17
  loader: ({ request }: LoaderFunctionArgs) => Promise<{}>;
15
18
  action: (arg: LoaderFunctionArgs<any>) => Promise<unknown> | unknown;
16
19
  };
@@ -24,21 +24,29 @@ __export(item_api_handler_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(item_api_handler_exports);
26
26
  var import_http = require("dn-react-toolkit/http");
27
- var import_react_router = require("react-router");
28
27
  function itemApiHandler({
29
28
  withAuthAction,
30
- repository
29
+ repository,
30
+ isOwnedBy,
31
+ roles
31
32
  }) {
32
33
  const loader = async ({ request }) => {
33
34
  return {};
34
35
  };
35
36
  const action = withAuthAction((auth) => async ({ params, request }) => {
36
- if (!auth || auth.role !== "admin") {
37
- return (0, import_http.UNAUTHORIZED)();
37
+ if (roles && roles.length > 0 && (!auth || !roles.includes(auth.role))) {
38
+ throw (0, import_http.UNAUTHORIZED)();
39
+ }
40
+ const itemId = params.itemId;
41
+ const existing = await repository.find(itemId);
42
+ if (!existing) {
43
+ throw (0, import_http.NOT_FOUND)();
44
+ }
45
+ if (isOwnedBy && !isOwnedBy(existing, auth)) {
46
+ throw (0, import_http.FORBIDDEN)();
38
47
  }
39
48
  switch (request.method) {
40
49
  case "DELETE": {
41
- const itemId = params.itemId;
42
50
  await repository.delete(itemId);
43
51
  return {};
44
52
  }
@@ -1,20 +1,28 @@
1
1
  // src/api/item_api_handler.ts
2
- import { UNAUTHORIZED } from "dn-react-toolkit/http";
3
- import "react-router";
2
+ import { FORBIDDEN, NOT_FOUND, UNAUTHORIZED } from "dn-react-toolkit/http";
4
3
  function itemApiHandler({
5
4
  withAuthAction,
6
- repository
5
+ repository,
6
+ isOwnedBy,
7
+ roles
7
8
  }) {
8
9
  const loader = async ({ request }) => {
9
10
  return {};
10
11
  };
11
12
  const action = withAuthAction((auth) => async ({ params, request }) => {
12
- if (!auth || auth.role !== "admin") {
13
- return UNAUTHORIZED();
13
+ if (roles && roles.length > 0 && (!auth || !roles.includes(auth.role))) {
14
+ throw UNAUTHORIZED();
15
+ }
16
+ const itemId = params.itemId;
17
+ const existing = await repository.find(itemId);
18
+ if (!existing) {
19
+ throw NOT_FOUND();
20
+ }
21
+ if (isOwnedBy && !isOwnedBy(existing, auth)) {
22
+ throw FORBIDDEN();
14
23
  }
15
24
  switch (request.method) {
16
25
  case "DELETE": {
17
- const itemId = params.itemId;
18
26
  await repository.delete(itemId);
19
27
  return {};
20
28
  }
@@ -1,15 +1,16 @@
1
1
  import { LoaderFunctionArgs, LoaderFunction } from 'react-router';
2
- import { TableLoaderOptions } from '../table/loader.mjs';
3
2
  import { TableItemLoaderOptions } from '../table/item_loader.mjs';
4
- import { Table } from 'drizzle-orm';
5
3
  import { TableRepository } from '../table/repository.mjs';
4
+ import { TableLoaderOptions } from '../table/load_table.mjs';
5
+ import { PgTableWithColumns } from 'drizzle-orm/pg-core';
6
6
  import { APIHandlerOptions } from '../api/create_api_handler.mjs';
7
- import 'drizzle-orm/pg-core';
7
+ import 'drizzle-orm';
8
+ import 'drizzle-orm/node-postgres';
8
9
  import '../auth/with_auth.mjs';
9
10
  import 'dn-react-toolkit/auth';
10
11
  import 'dn-react-toolkit/auth/server';
11
12
 
12
- type CrudHandlerOptions<T extends Table, TSelect> = {
13
+ type CrudHandlerOptions<T extends PgTableWithColumns<any>, TSelect> = {
13
14
  repository: TableRepository<T, TSelect>;
14
15
  apiHandlerOptions: Omit<APIHandlerOptions<T, TSelect>, "repository">;
15
16
  loaderOptions: Omit<TableLoaderOptions<T, TSelect>, "repository"> & {
@@ -20,6 +21,6 @@ type CrudHandlerOptions<T extends Table, TSelect> = {
20
21
  };
21
22
  };
22
23
  type CrudHandler = (prefix: string) => (args: LoaderFunctionArgs) => Promise<any>;
23
- declare function crudHandler<T extends Table, TSelect>({ repository, apiHandlerOptions, loaderOptions, itemLoaderOptions, }: CrudHandlerOptions<T, TSelect>): (prefix: string) => (args: LoaderFunctionArgs) => Promise<unknown>;
24
+ declare function crudHandler<T extends PgTableWithColumns<any>, TSelect>({ repository, apiHandlerOptions, loaderOptions, itemLoaderOptions, }: CrudHandlerOptions<T, TSelect>): (prefix: string) => (args: LoaderFunctionArgs) => Promise<unknown>;
24
25
 
25
26
  export { type CrudHandler, type CrudHandlerOptions, crudHandler };
@@ -1,15 +1,16 @@
1
1
  import { LoaderFunctionArgs, LoaderFunction } from 'react-router';
2
- import { TableLoaderOptions } from '../table/loader.js';
3
2
  import { TableItemLoaderOptions } from '../table/item_loader.js';
4
- import { Table } from 'drizzle-orm';
5
3
  import { TableRepository } from '../table/repository.js';
4
+ import { TableLoaderOptions } from '../table/load_table.js';
5
+ import { PgTableWithColumns } from 'drizzle-orm/pg-core';
6
6
  import { APIHandlerOptions } from '../api/create_api_handler.js';
7
- import 'drizzle-orm/pg-core';
7
+ import 'drizzle-orm';
8
+ import 'drizzle-orm/node-postgres';
8
9
  import '../auth/with_auth.js';
9
10
  import 'dn-react-toolkit/auth';
10
11
  import 'dn-react-toolkit/auth/server';
11
12
 
12
- type CrudHandlerOptions<T extends Table, TSelect> = {
13
+ type CrudHandlerOptions<T extends PgTableWithColumns<any>, TSelect> = {
13
14
  repository: TableRepository<T, TSelect>;
14
15
  apiHandlerOptions: Omit<APIHandlerOptions<T, TSelect>, "repository">;
15
16
  loaderOptions: Omit<TableLoaderOptions<T, TSelect>, "repository"> & {
@@ -20,6 +21,6 @@ type CrudHandlerOptions<T extends Table, TSelect> = {
20
21
  };
21
22
  };
22
23
  type CrudHandler = (prefix: string) => (args: LoaderFunctionArgs) => Promise<any>;
23
- declare function crudHandler<T extends Table, TSelect>({ repository, apiHandlerOptions, loaderOptions, itemLoaderOptions, }: CrudHandlerOptions<T, TSelect>): (prefix: string) => (args: LoaderFunctionArgs) => Promise<unknown>;
24
+ declare function crudHandler<T extends PgTableWithColumns<any>, TSelect>({ repository, apiHandlerOptions, loaderOptions, itemLoaderOptions, }: CrudHandlerOptions<T, TSelect>): (prefix: string) => (args: LoaderFunctionArgs) => Promise<unknown>;
24
25
 
25
26
  export { type CrudHandler, type CrudHandlerOptions, crudHandler };
@@ -24,45 +24,79 @@ __export(crud_loader_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(crud_loader_exports);
26
26
 
27
- // src/table/loader.tsx
27
+ // src/table/load_table.tsx
28
28
  var import_drizzle_orm = require("drizzle-orm");
29
+ async function loadTable({
30
+ request,
31
+ repository,
32
+ options
33
+ }) {
34
+ const searchParams = new URL(request.url).searchParams;
35
+ const { where, searchKey, defaultOrderBy, defaultDirection } = options;
36
+ const query = searchParams.get("query") ?? void 0;
37
+ const limit = Number(searchParams.get("limit") ?? "20");
38
+ const offset = Number(searchParams.get("offset") ?? "0");
39
+ const orderBy = searchParams.get("orderBy") ?? defaultOrderBy;
40
+ const direction = searchParams.get("direction") ?? defaultDirection;
41
+ const filterWhere = Object.entries(options.filters ?? {}).map(([key, value]) => {
42
+ const param = searchParams.get(key);
43
+ if (param) {
44
+ return (0, import_drizzle_orm.eq)(
45
+ repository.schema[key],
46
+ decodeURIComponent(param)
47
+ );
48
+ }
49
+ return void 0;
50
+ }).filter(Boolean);
51
+ const whereClauses = (0, import_drizzle_orm.and)(
52
+ searchKey && query ? (0, import_drizzle_orm.ilike)(
53
+ repository.schema[searchKey],
54
+ `%${query}%`
55
+ ) : void 0,
56
+ ...filterWhere,
57
+ ...where ?? []
58
+ );
59
+ const total = await repository.countTotal({ where: whereClauses });
60
+ const items = await repository.findAll({
61
+ orderBy,
62
+ direction,
63
+ limit,
64
+ offset,
65
+ where: whereClauses
66
+ });
67
+ const filters = Object.fromEntries(
68
+ await Promise.all(
69
+ Object.keys(options.filters ?? {}).map(async (key) => {
70
+ const values = await repository.select(key);
71
+ return [key, values.filter(Boolean)];
72
+ })
73
+ )
74
+ );
75
+ return {
76
+ items,
77
+ total,
78
+ limit,
79
+ offset,
80
+ orderBy,
81
+ direction,
82
+ searchKey,
83
+ filters
84
+ };
85
+ }
86
+
87
+ // src/table/loader.tsx
29
88
  function tableLoader({
30
89
  repository,
31
- tableOptions
90
+ options
32
91
  }) {
33
92
  return async ({ request }) => {
34
- const searchParams = new URL(request.url).searchParams;
35
- const { where, searchKey, defaultOrderBy, defaultDirection } = tableOptions;
36
- const query = searchParams.get("query") ?? void 0;
37
- const limit = Number(searchParams.get("limit") ?? "10");
38
- const offset = Number(searchParams.get("offset") ?? "0");
39
- const orderBy = searchParams.get("orderBy") ?? defaultOrderBy;
40
- const direction = searchParams.get("direction") ?? defaultDirection;
41
- const whereClauses = (0, import_drizzle_orm.and)(
42
- searchKey && query ? (0, import_drizzle_orm.ilike)(
43
- repository.schema[searchKey],
44
- `%${query}%`
45
- ) : void 0,
46
- ...where ?? []
47
- );
48
- const total = await repository.countTotal({ where: whereClauses });
49
- const items = await repository.findAll({
50
- orderBy,
51
- direction,
52
- limit,
53
- offset,
54
- where: whereClauses
93
+ const table = await loadTable({
94
+ request,
95
+ repository,
96
+ options
55
97
  });
56
98
  return {
57
- table: {
58
- items,
59
- total,
60
- limit,
61
- offset,
62
- orderBy,
63
- direction,
64
- searchKey
65
- }
99
+ table
66
100
  };
67
101
  };
68
102
  }
@@ -86,7 +120,6 @@ var tableItemloader = ({
86
120
  // src/api/create_api_handler.ts
87
121
  var import_http = require("dn-react-toolkit/http");
88
122
  var import_drizzle_orm2 = require("drizzle-orm");
89
- var import_react_router = require("react-router");
90
123
  var import_uuid = require("uuid");
91
124
 
92
125
  // src/crud/serialize.ts
@@ -214,21 +247,29 @@ function apiHandler({
214
247
 
215
248
  // src/api/item_api_handler.ts
216
249
  var import_http2 = require("dn-react-toolkit/http");
217
- var import_react_router2 = require("react-router");
218
250
  function itemApiHandler({
219
251
  withAuthAction,
220
- repository
252
+ repository,
253
+ isOwnedBy,
254
+ roles
221
255
  }) {
222
256
  const loader = async ({ request }) => {
223
257
  return {};
224
258
  };
225
259
  const action = withAuthAction((auth) => async ({ params, request }) => {
226
- if (!auth || auth.role !== "admin") {
227
- return (0, import_http2.UNAUTHORIZED)();
260
+ if (roles && roles.length > 0 && (!auth || !roles.includes(auth.role))) {
261
+ throw (0, import_http2.UNAUTHORIZED)();
262
+ }
263
+ const itemId = params.itemId;
264
+ const existing = await repository.find(itemId);
265
+ if (!existing) {
266
+ throw (0, import_http2.NOT_FOUND)();
267
+ }
268
+ if (isOwnedBy && !isOwnedBy(existing, auth)) {
269
+ throw (0, import_http2.FORBIDDEN)();
228
270
  }
229
271
  switch (request.method) {
230
272
  case "DELETE": {
231
- const itemId = params.itemId;
232
273
  await repository.delete(itemId);
233
274
  return {};
234
275
  }
@@ -1,45 +1,80 @@
1
- // src/table/loader.tsx
1
+ // src/table/load_table.tsx
2
2
  import {
3
3
  and,
4
+ eq,
4
5
  ilike
5
6
  } from "drizzle-orm";
7
+ async function loadTable({
8
+ request,
9
+ repository,
10
+ options
11
+ }) {
12
+ const searchParams = new URL(request.url).searchParams;
13
+ const { where, searchKey, defaultOrderBy, defaultDirection } = options;
14
+ const query = searchParams.get("query") ?? void 0;
15
+ const limit = Number(searchParams.get("limit") ?? "20");
16
+ const offset = Number(searchParams.get("offset") ?? "0");
17
+ const orderBy = searchParams.get("orderBy") ?? defaultOrderBy;
18
+ const direction = searchParams.get("direction") ?? defaultDirection;
19
+ const filterWhere = Object.entries(options.filters ?? {}).map(([key, value]) => {
20
+ const param = searchParams.get(key);
21
+ if (param) {
22
+ return eq(
23
+ repository.schema[key],
24
+ decodeURIComponent(param)
25
+ );
26
+ }
27
+ return void 0;
28
+ }).filter(Boolean);
29
+ const whereClauses = and(
30
+ searchKey && query ? ilike(
31
+ repository.schema[searchKey],
32
+ `%${query}%`
33
+ ) : void 0,
34
+ ...filterWhere,
35
+ ...where ?? []
36
+ );
37
+ const total = await repository.countTotal({ where: whereClauses });
38
+ const items = await repository.findAll({
39
+ orderBy,
40
+ direction,
41
+ limit,
42
+ offset,
43
+ where: whereClauses
44
+ });
45
+ const filters = Object.fromEntries(
46
+ await Promise.all(
47
+ Object.keys(options.filters ?? {}).map(async (key) => {
48
+ const values = await repository.select(key);
49
+ return [key, values.filter(Boolean)];
50
+ })
51
+ )
52
+ );
53
+ return {
54
+ items,
55
+ total,
56
+ limit,
57
+ offset,
58
+ orderBy,
59
+ direction,
60
+ searchKey,
61
+ filters
62
+ };
63
+ }
64
+
65
+ // src/table/loader.tsx
6
66
  function tableLoader({
7
67
  repository,
8
- tableOptions
68
+ options
9
69
  }) {
10
70
  return async ({ request }) => {
11
- const searchParams = new URL(request.url).searchParams;
12
- const { where, searchKey, defaultOrderBy, defaultDirection } = tableOptions;
13
- const query = searchParams.get("query") ?? void 0;
14
- const limit = Number(searchParams.get("limit") ?? "10");
15
- const offset = Number(searchParams.get("offset") ?? "0");
16
- const orderBy = searchParams.get("orderBy") ?? defaultOrderBy;
17
- const direction = searchParams.get("direction") ?? defaultDirection;
18
- const whereClauses = and(
19
- searchKey && query ? ilike(
20
- repository.schema[searchKey],
21
- `%${query}%`
22
- ) : void 0,
23
- ...where ?? []
24
- );
25
- const total = await repository.countTotal({ where: whereClauses });
26
- const items = await repository.findAll({
27
- orderBy,
28
- direction,
29
- limit,
30
- offset,
31
- where: whereClauses
71
+ const table = await loadTable({
72
+ request,
73
+ repository,
74
+ options
32
75
  });
33
76
  return {
34
- table: {
35
- items,
36
- total,
37
- limit,
38
- offset,
39
- orderBy,
40
- direction,
41
- searchKey
42
- }
77
+ table
43
78
  };
44
79
  };
45
80
  }
@@ -72,7 +107,6 @@ import {
72
107
  import {
73
108
  and as and2
74
109
  } from "drizzle-orm";
75
- import "react-router";
76
110
  import { v4 } from "uuid";
77
111
 
78
112
  // src/crud/serialize.ts
@@ -199,22 +233,30 @@ function apiHandler({
199
233
  }
200
234
 
201
235
  // src/api/item_api_handler.ts
202
- import { UNAUTHORIZED as UNAUTHORIZED2 } from "dn-react-toolkit/http";
203
- import "react-router";
236
+ import { FORBIDDEN, NOT_FOUND, UNAUTHORIZED as UNAUTHORIZED2 } from "dn-react-toolkit/http";
204
237
  function itemApiHandler({
205
238
  withAuthAction,
206
- repository
239
+ repository,
240
+ isOwnedBy,
241
+ roles
207
242
  }) {
208
243
  const loader = async ({ request }) => {
209
244
  return {};
210
245
  };
211
246
  const action = withAuthAction((auth) => async ({ params, request }) => {
212
- if (!auth || auth.role !== "admin") {
213
- return UNAUTHORIZED2();
247
+ if (roles && roles.length > 0 && (!auth || !roles.includes(auth.role))) {
248
+ throw UNAUTHORIZED2();
249
+ }
250
+ const itemId = params.itemId;
251
+ const existing = await repository.find(itemId);
252
+ if (!existing) {
253
+ throw NOT_FOUND();
254
+ }
255
+ if (isOwnedBy && !isOwnedBy(existing, auth)) {
256
+ throw FORBIDDEN();
214
257
  }
215
258
  switch (request.method) {
216
259
  case "DELETE": {
217
- const itemId = params.itemId;
218
260
  await repository.delete(itemId);
219
261
  return {};
220
262
  }