appflare 0.2.25 → 0.2.26
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/Documentation.md +758 -758
- package/cli/commands/index.ts +238 -238
- package/cli/generate.ts +178 -178
- package/cli/index.ts +120 -120
- package/cli/load-config.ts +184 -184
- package/cli/schema-compiler.ts +1183 -1183
- package/cli/templates/auth/README.md +156 -156
- package/cli/templates/auth/config.ts +61 -61
- package/cli/templates/auth/route-config.ts +1 -1
- package/cli/templates/auth/route-handler.ts +1 -1
- package/cli/templates/auth/route-request-utils.ts +5 -5
- package/cli/templates/auth/route.config.ts +18 -18
- package/cli/templates/auth/route.handler.ts +18 -18
- package/cli/templates/auth/route.request-utils.ts +55 -55
- package/cli/templates/auth/route.ts +14 -14
- package/cli/templates/core/README.md +266 -266
- package/cli/templates/core/app-creation.ts +19 -19
- package/cli/templates/core/client/appflare.ts +112 -112
- package/cli/templates/core/client/handlers/index.ts +748 -748
- package/cli/templates/core/client/handlers.ts +1 -1
- package/cli/templates/core/client/index.ts +7 -7
- package/cli/templates/core/client/storage.ts +180 -180
- package/cli/templates/core/client/types.ts +184 -184
- package/cli/templates/core/client-modules/appflare.ts +1 -1
- package/cli/templates/core/client-modules/handlers.ts +1 -1
- package/cli/templates/core/client-modules/index.ts +1 -1
- package/cli/templates/core/client-modules/storage.ts +1 -1
- package/cli/templates/core/client-modules/types.ts +1 -1
- package/cli/templates/core/client.artifacts.ts +39 -39
- package/cli/templates/core/client.ts +4 -4
- package/cli/templates/core/drizzle.ts +15 -15
- package/cli/templates/core/export.ts +14 -14
- package/cli/templates/core/handlers.route.ts +24 -24
- package/cli/templates/core/handlers.ts +1 -1
- package/cli/templates/core/imports.ts +9 -9
- package/cli/templates/core/server.ts +38 -38
- package/cli/templates/core/types.ts +6 -6
- package/cli/templates/core/wrangler.ts +109 -109
- package/cli/templates/dashboard/builders/functions/index.ts +17 -17
- package/cli/templates/dashboard/builders/functions/render-page/header.ts +20 -20
- package/cli/templates/dashboard/builders/functions/render-page/index.ts +33 -33
- package/cli/templates/dashboard/builders/functions/render-page/request-panel.ts +171 -171
- package/cli/templates/dashboard/builders/functions/render-page/result-panel.ts +85 -85
- package/cli/templates/dashboard/builders/functions/render-page/scripts.ts +554 -554
- package/cli/templates/dashboard/builders/navigation.ts +122 -122
- package/cli/templates/dashboard/builders/storage/index.ts +13 -13
- package/cli/templates/dashboard/builders/storage/routes/create-directory-route.ts +29 -29
- package/cli/templates/dashboard/builders/storage/routes/delete-route.ts +18 -18
- package/cli/templates/dashboard/builders/storage/routes/download-route.ts +23 -23
- package/cli/templates/dashboard/builders/storage/routes/index.ts +22 -22
- package/cli/templates/dashboard/builders/storage/routes/list-route.ts +25 -25
- package/cli/templates/dashboard/builders/storage/routes/preview-route.ts +21 -21
- package/cli/templates/dashboard/builders/storage/routes/upload-route.ts +21 -21
- package/cli/templates/dashboard/builders/storage/runtime/helpers.ts +72 -72
- package/cli/templates/dashboard/builders/storage/runtime/storage-page.ts +130 -130
- package/cli/templates/dashboard/builders/table-routes/common/drawer-panel.ts +27 -27
- package/cli/templates/dashboard/builders/table-routes/common/pagination.ts +30 -30
- package/cli/templates/dashboard/builders/table-routes/common/search-bar.ts +23 -23
- package/cli/templates/dashboard/builders/table-routes/fragments.ts +217 -217
- package/cli/templates/dashboard/builders/table-routes/helpers.ts +45 -45
- package/cli/templates/dashboard/builders/table-routes/index.ts +8 -8
- package/cli/templates/dashboard/builders/table-routes/table/actions-cell.ts +71 -71
- package/cli/templates/dashboard/builders/table-routes/table/get-route.ts +291 -291
- package/cli/templates/dashboard/builders/table-routes/table/index.ts +80 -80
- package/cli/templates/dashboard/builders/table-routes/table/post-routes.ts +163 -163
- package/cli/templates/dashboard/builders/table-routes/table-route.ts +7 -7
- package/cli/templates/dashboard/builders/table-routes/users/get-route.ts +69 -69
- package/cli/templates/dashboard/builders/table-routes/users/html/modals.ts +57 -57
- package/cli/templates/dashboard/builders/table-routes/users/html/page.ts +27 -27
- package/cli/templates/dashboard/builders/table-routes/users/html/table.ts +128 -128
- package/cli/templates/dashboard/builders/table-routes/users/index.ts +32 -32
- package/cli/templates/dashboard/builders/table-routes/users/post-routes.ts +150 -150
- package/cli/templates/dashboard/builders/table-routes/users/redirect.ts +14 -14
- package/cli/templates/dashboard/builders/table-routes/users-route.ts +10 -10
- package/cli/templates/dashboard/components/dashboard-home.ts +23 -23
- package/cli/templates/dashboard/components/layout.ts +388 -388
- package/cli/templates/dashboard/components/login-page.ts +65 -65
- package/cli/templates/dashboard/index.ts +61 -61
- package/cli/templates/dashboard/types.ts +9 -9
- package/cli/templates/handlers/README.md +353 -353
- package/cli/templates/handlers/auth.ts +37 -37
- package/cli/templates/handlers/execution.ts +42 -42
- package/cli/templates/handlers/generators/context/context-creation.ts +101 -101
- package/cli/templates/handlers/generators/context/error-helpers.ts +11 -11
- package/cli/templates/handlers/generators/context/scheduler.ts +24 -24
- package/cli/templates/handlers/generators/context/storage-api.ts +134 -112
- package/cli/templates/handlers/generators/context/storage-helpers.ts +59 -59
- package/cli/templates/handlers/generators/context/types.ts +18 -18
- package/cli/templates/handlers/generators/context.ts +43 -43
- package/cli/templates/handlers/generators/execution.ts +15 -15
- package/cli/templates/handlers/generators/handlers.ts +13 -13
- package/cli/templates/handlers/generators/registration/modules/cron.ts +26 -26
- package/cli/templates/handlers/generators/registration/modules/realtime/auth.ts +75 -75
- package/cli/templates/handlers/generators/registration/modules/realtime/durable-object.ts +144 -144
- package/cli/templates/handlers/generators/registration/modules/realtime/index.ts +14 -14
- package/cli/templates/handlers/generators/registration/modules/realtime/publisher.ts +102 -102
- package/cli/templates/handlers/generators/registration/modules/realtime/routes.ts +164 -164
- package/cli/templates/handlers/generators/registration/modules/realtime/types.ts +30 -30
- package/cli/templates/handlers/generators/registration/modules/realtime/utils.ts +516 -516
- package/cli/templates/handlers/generators/registration/modules/scheduler.ts +56 -56
- package/cli/templates/handlers/generators/registration/modules/storage.ts +196 -194
- package/cli/templates/handlers/generators/registration/sections.ts +210 -210
- package/cli/templates/handlers/generators/types/context.ts +68 -66
- package/cli/templates/handlers/generators/types/core.ts +106 -106
- package/cli/templates/handlers/generators/types/operations.ts +135 -135
- package/cli/templates/handlers/generators/types/query-definitions/filter-and-where-types.ts +259 -259
- package/cli/templates/handlers/generators/types/query-definitions/query-api-types.ts +135 -135
- package/cli/templates/handlers/generators/types/query-definitions/query-helper-functions.ts +1031 -1031
- package/cli/templates/handlers/generators/types/query-definitions/schema-and-table-types.ts +246 -246
- package/cli/templates/handlers/generators/types/query-definitions.ts +13 -13
- package/cli/templates/handlers/generators/types/query-runtime/handled-error.ts +13 -13
- package/cli/templates/handlers/generators/types/query-runtime/runtime-aggregate-and-footer.ts +174 -174
- package/cli/templates/handlers/generators/types/query-runtime/runtime-read.ts +121 -121
- package/cli/templates/handlers/generators/types/query-runtime/runtime-setup.ts +45 -45
- package/cli/templates/handlers/generators/types/query-runtime/runtime-write.ts +676 -676
- package/cli/templates/handlers/generators/types/query-runtime.ts +15 -15
- package/cli/templates/handlers/index.ts +43 -43
- package/cli/templates/handlers/operations.ts +116 -116
- package/cli/templates/handlers/registration.ts +91 -91
- package/cli/templates/handlers/types.ts +15 -15
- package/cli/templates/handlers/utils.ts +48 -48
- package/cli/types.ts +110 -110
- package/cli/utils/handler-discovery.ts +466 -466
- package/cli/utils/json-utils.ts +24 -24
- package/cli/utils/path-utils.ts +19 -19
- package/cli/utils/schema-discovery.ts +399 -399
- package/dist/cli/index.js +43 -17
- package/dist/cli/index.mjs +43 -17
- package/index.ts +18 -18
- package/package.json +58 -58
- package/react/index.ts +5 -5
- package/react/use-infinite-query.ts +252 -252
- package/react/use-mutation.ts +89 -89
- package/react/use-query.ts +207 -207
- package/schema.ts +415 -415
- package/test-better-auth-hash.ts +2 -2
- package/tsconfig.json +6 -6
- package/tsup.config.ts +82 -82
- package/dist/cli/index.d.mts +0 -2
- package/dist/cli/index.d.ts +0 -2
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
import { DiscoveredTable } from "../../../types";
|
|
2
|
-
import {
|
|
3
|
-
buildColumnHeaders,
|
|
4
|
-
buildFieldInput,
|
|
5
|
-
buildPayloadAssignments,
|
|
6
|
-
buildRowCells,
|
|
7
|
-
buildSearchConditions,
|
|
8
|
-
} from "../fragments";
|
|
9
|
-
import {
|
|
10
|
-
resolvePrimaryKey,
|
|
11
|
-
shouldIncludeCreateField,
|
|
12
|
-
shouldIncludeEditField,
|
|
13
|
-
} from "../helpers";
|
|
14
|
-
import { buildActionsCell } from "./actions-cell";
|
|
15
|
-
import { buildTableGetRoute } from "./get-route";
|
|
16
|
-
import { buildTablePostRoutes } from "./post-routes";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Builds the complete set of Hono route handlers for a discovered table.
|
|
20
|
-
* Delegates to focused builder functions for GET and POST handlers.
|
|
21
|
-
*/
|
|
22
|
-
export function buildTableRoute(table: DiscoveredTable): string {
|
|
23
|
-
const primaryKey = resolvePrimaryKey(table);
|
|
24
|
-
const hasPrimaryKey = Boolean(primaryKey);
|
|
25
|
-
const columns = table.columns.map((column) => column.name);
|
|
26
|
-
const createColumns = columns.filter((columnName) =>
|
|
27
|
-
shouldIncludeCreateField(table, columnName),
|
|
28
|
-
);
|
|
29
|
-
const editColumns = columns.filter((columnName) =>
|
|
30
|
-
shouldIncludeEditField(table, columnName),
|
|
31
|
-
);
|
|
32
|
-
const searchConditions = buildSearchConditions(table);
|
|
33
|
-
const headers = buildColumnHeaders(table, columns);
|
|
34
|
-
const rowCells = buildRowCells(columns, primaryKey);
|
|
35
|
-
const createInputs = createColumns
|
|
36
|
-
.map((columnName) => buildFieldInput(table, columnName, "create"))
|
|
37
|
-
.join("");
|
|
38
|
-
const editInputs = editColumns
|
|
39
|
-
.map((columnName) => buildFieldInput(table, columnName, "edit"))
|
|
40
|
-
.join("");
|
|
41
|
-
const createAssignments = buildPayloadAssignments(table, createColumns);
|
|
42
|
-
const editAssignments = buildPayloadAssignments(table, editColumns);
|
|
43
|
-
const defaultSort = hasPrimaryKey ? primaryKey : columns[0] || "id";
|
|
44
|
-
const primaryKeyType = table.columns.find(
|
|
45
|
-
(column) => column.name === primaryKey,
|
|
46
|
-
)?.type;
|
|
47
|
-
|
|
48
|
-
const actionsCell = buildActionsCell(
|
|
49
|
-
table,
|
|
50
|
-
hasPrimaryKey,
|
|
51
|
-
primaryKey,
|
|
52
|
-
editInputs,
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
return (
|
|
56
|
-
buildTableGetRoute(
|
|
57
|
-
table,
|
|
58
|
-
defaultSort,
|
|
59
|
-
primaryKey,
|
|
60
|
-
hasPrimaryKey,
|
|
61
|
-
columns,
|
|
62
|
-
searchConditions,
|
|
63
|
-
headers,
|
|
64
|
-
rowCells,
|
|
65
|
-
actionsCell,
|
|
66
|
-
createInputs,
|
|
67
|
-
) +
|
|
68
|
-
"\n" +
|
|
69
|
-
buildTablePostRoutes(
|
|
70
|
-
table.exportName,
|
|
71
|
-
defaultSort,
|
|
72
|
-
primaryKey,
|
|
73
|
-
primaryKeyType,
|
|
74
|
-
hasPrimaryKey,
|
|
75
|
-
searchConditions,
|
|
76
|
-
createAssignments,
|
|
77
|
-
editAssignments,
|
|
78
|
-
)
|
|
79
|
-
);
|
|
80
|
-
}
|
|
1
|
+
import { DiscoveredTable } from "../../../types";
|
|
2
|
+
import {
|
|
3
|
+
buildColumnHeaders,
|
|
4
|
+
buildFieldInput,
|
|
5
|
+
buildPayloadAssignments,
|
|
6
|
+
buildRowCells,
|
|
7
|
+
buildSearchConditions,
|
|
8
|
+
} from "../fragments";
|
|
9
|
+
import {
|
|
10
|
+
resolvePrimaryKey,
|
|
11
|
+
shouldIncludeCreateField,
|
|
12
|
+
shouldIncludeEditField,
|
|
13
|
+
} from "../helpers";
|
|
14
|
+
import { buildActionsCell } from "./actions-cell";
|
|
15
|
+
import { buildTableGetRoute } from "./get-route";
|
|
16
|
+
import { buildTablePostRoutes } from "./post-routes";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Builds the complete set of Hono route handlers for a discovered table.
|
|
20
|
+
* Delegates to focused builder functions for GET and POST handlers.
|
|
21
|
+
*/
|
|
22
|
+
export function buildTableRoute(table: DiscoveredTable): string {
|
|
23
|
+
const primaryKey = resolvePrimaryKey(table);
|
|
24
|
+
const hasPrimaryKey = Boolean(primaryKey);
|
|
25
|
+
const columns = table.columns.map((column) => column.name);
|
|
26
|
+
const createColumns = columns.filter((columnName) =>
|
|
27
|
+
shouldIncludeCreateField(table, columnName),
|
|
28
|
+
);
|
|
29
|
+
const editColumns = columns.filter((columnName) =>
|
|
30
|
+
shouldIncludeEditField(table, columnName),
|
|
31
|
+
);
|
|
32
|
+
const searchConditions = buildSearchConditions(table);
|
|
33
|
+
const headers = buildColumnHeaders(table, columns);
|
|
34
|
+
const rowCells = buildRowCells(columns, primaryKey);
|
|
35
|
+
const createInputs = createColumns
|
|
36
|
+
.map((columnName) => buildFieldInput(table, columnName, "create"))
|
|
37
|
+
.join("");
|
|
38
|
+
const editInputs = editColumns
|
|
39
|
+
.map((columnName) => buildFieldInput(table, columnName, "edit"))
|
|
40
|
+
.join("");
|
|
41
|
+
const createAssignments = buildPayloadAssignments(table, createColumns);
|
|
42
|
+
const editAssignments = buildPayloadAssignments(table, editColumns);
|
|
43
|
+
const defaultSort = hasPrimaryKey ? primaryKey : columns[0] || "id";
|
|
44
|
+
const primaryKeyType = table.columns.find(
|
|
45
|
+
(column) => column.name === primaryKey,
|
|
46
|
+
)?.type;
|
|
47
|
+
|
|
48
|
+
const actionsCell = buildActionsCell(
|
|
49
|
+
table,
|
|
50
|
+
hasPrimaryKey,
|
|
51
|
+
primaryKey,
|
|
52
|
+
editInputs,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
buildTableGetRoute(
|
|
57
|
+
table,
|
|
58
|
+
defaultSort,
|
|
59
|
+
primaryKey,
|
|
60
|
+
hasPrimaryKey,
|
|
61
|
+
columns,
|
|
62
|
+
searchConditions,
|
|
63
|
+
headers,
|
|
64
|
+
rowCells,
|
|
65
|
+
actionsCell,
|
|
66
|
+
createInputs,
|
|
67
|
+
) +
|
|
68
|
+
"\n" +
|
|
69
|
+
buildTablePostRoutes(
|
|
70
|
+
table.exportName,
|
|
71
|
+
defaultSort,
|
|
72
|
+
primaryKey,
|
|
73
|
+
primaryKeyType,
|
|
74
|
+
hasPrimaryKey,
|
|
75
|
+
searchConditions,
|
|
76
|
+
createAssignments,
|
|
77
|
+
editAssignments,
|
|
78
|
+
)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -1,163 +1,163 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Builds the POST route handlers code string for create/edit/delete operations
|
|
3
|
-
* on a generic table (/admin/table/:tableName/create|edit|delete).
|
|
4
|
-
*/
|
|
5
|
-
export function buildTablePostRoutes(
|
|
6
|
-
exportName: string,
|
|
7
|
-
defaultSort: string,
|
|
8
|
-
primaryKey: string,
|
|
9
|
-
primaryKeyType: string | undefined,
|
|
10
|
-
hasPrimaryKey: boolean,
|
|
11
|
-
searchConditions: string,
|
|
12
|
-
createAssignments: string,
|
|
13
|
-
editAssignments: string,
|
|
14
|
-
): string {
|
|
15
|
-
const numericIdGuard =
|
|
16
|
-
primaryKeyType === "number"
|
|
17
|
-
? `
|
|
18
|
-
\t\tconst parsedId = Number(rawId);
|
|
19
|
-
\t\tif (Number.isNaN(parsedId)) return c.text('${primaryKey} must be a valid number', 400);
|
|
20
|
-
\t\tidValue = parsedId;
|
|
21
|
-
\t\t`
|
|
22
|
-
: "";
|
|
23
|
-
|
|
24
|
-
const parseBulkIds =
|
|
25
|
-
primaryKeyType === "number"
|
|
26
|
-
? "idValues.map((value) => Number(value)).filter((value) => !Number.isNaN(value))"
|
|
27
|
-
: "idValues";
|
|
28
|
-
|
|
29
|
-
const editRoute = hasPrimaryKey
|
|
30
|
-
? `
|
|
31
|
-
\tadminApp.post('/table/${exportName}/edit', async (c) => {
|
|
32
|
-
\t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
|
|
33
|
-
\t\tconst tableSchema = (schema as any).${exportName};
|
|
34
|
-
\t\tif (!tableSchema) return c.text('Table missing', 404);
|
|
35
|
-
|
|
36
|
-
\t\tconst body = await c.req.parseBody();
|
|
37
|
-
\t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
|
|
38
|
-
\t\tconst rawId = getValue(body['${primaryKey}']);
|
|
39
|
-
\t\tif (rawId === '') return c.text('${primaryKey} is required', 400);
|
|
40
|
-
|
|
41
|
-
\t\tconst payload: Record<string, unknown> = {};
|
|
42
|
-
|
|
43
|
-
\t\t${editAssignments}
|
|
44
|
-
|
|
45
|
-
\t\tlet idValue: unknown = rawId;
|
|
46
|
-
\t\t${numericIdGuard}
|
|
47
|
-
|
|
48
|
-
\t\tif (Object.keys(payload).length > 0) {
|
|
49
|
-
\t\t\tawait db
|
|
50
|
-
\t\t\t\t.update(tableSchema)
|
|
51
|
-
\t\t\t\t.set(payload as any)
|
|
52
|
-
\t\t\t\t.where(eq(tableSchema.${primaryKey}, idValue as any))
|
|
53
|
-
\t\t\t\t.execute();
|
|
54
|
-
\t\t}
|
|
55
|
-
|
|
56
|
-
\t\tconst query = new URLSearchParams({
|
|
57
|
-
\t\t\tpage: getValue(body.page) || '1',
|
|
58
|
-
\t\t\tsort: getValue(body.sort) || '${defaultSort}',
|
|
59
|
-
\t\t\torder: getValue(body.order) || 'desc',
|
|
60
|
-
\t\t\tsearch: getValue(body.search) || '',
|
|
61
|
-
\t\t});
|
|
62
|
-
\t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
|
|
63
|
-
\t});
|
|
64
|
-
|
|
65
|
-
\tadminApp.post('/table/${exportName}/delete', async (c) => {
|
|
66
|
-
\t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
|
|
67
|
-
\t\tconst tableSchema = (schema as any).${exportName};
|
|
68
|
-
\t\tif (!tableSchema) return c.text('Table missing', 404);
|
|
69
|
-
|
|
70
|
-
\t\tconst body = await c.req.parseBody();
|
|
71
|
-
\t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
|
|
72
|
-
\t\tconst rawId = getValue(body['${primaryKey}']);
|
|
73
|
-
\t\tif (rawId === '') return c.text('${primaryKey} is required', 400);
|
|
74
|
-
|
|
75
|
-
\t\tlet idValue: unknown = rawId;
|
|
76
|
-
\t\t${numericIdGuard}
|
|
77
|
-
|
|
78
|
-
\t\tawait db
|
|
79
|
-
\t\t\t.delete(tableSchema)
|
|
80
|
-
\t\t\t.where(eq(tableSchema.${primaryKey}, idValue as any))
|
|
81
|
-
\t\t\t.execute();
|
|
82
|
-
|
|
83
|
-
\t\tconst query = new URLSearchParams({
|
|
84
|
-
\t\t\tpage: getValue(body.page) || '1',
|
|
85
|
-
\t\t\tsort: getValue(body.sort) || '${defaultSort}',
|
|
86
|
-
\t\t\torder: getValue(body.order) || 'desc',
|
|
87
|
-
\t\t\tsearch: getValue(body.search) || '',
|
|
88
|
-
\t\t});
|
|
89
|
-
\t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
|
|
90
|
-
\t});
|
|
91
|
-
|
|
92
|
-
\tadminApp.post('/table/${exportName}/delete-bulk', async (c) => {
|
|
93
|
-
\t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
|
|
94
|
-
\t\tconst tableSchema = (schema as any).${exportName};
|
|
95
|
-
\t\tif (!tableSchema) return c.text('Table missing', 404);
|
|
96
|
-
|
|
97
|
-
\t\tconst body = await c.req.parseBody();
|
|
98
|
-
\t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
|
|
99
|
-
\t\tconst mode = getValue(body.bulkMode);
|
|
100
|
-
\t\tconst selectedIdsRaw = getValue(body.selectedIds);
|
|
101
|
-
\t\tconst search = getValue(body.search);
|
|
102
|
-
|
|
103
|
-
\t\tif (mode === 'all-matching') {
|
|
104
|
-
\t\t\tlet deleteQuery = db.delete(tableSchema);
|
|
105
|
-
\t\t\tif (search) {
|
|
106
|
-
const searchConditions: any[] = [];
|
|
107
|
-
\t\t\t\t${searchConditions}
|
|
108
|
-
\t\t\t\tif (searchConditions.length > 0) {
|
|
109
|
-
\t\t\t\t\tdeleteQuery = deleteQuery.where(or(...searchConditions)) as any;
|
|
110
|
-
\t\t\t\t}
|
|
111
|
-
\t\t\t}
|
|
112
|
-
\t\t\tawait deleteQuery.execute();
|
|
113
|
-
\t\t} else {
|
|
114
|
-
\t\t\tconst idValues = selectedIdsRaw
|
|
115
|
-
\t\t\t\t.split(',')
|
|
116
|
-
\t\t\t\t.map((value) => value.trim())
|
|
117
|
-
\t\t\t\t.filter((value) => value.length > 0);
|
|
118
|
-
\t\t\tif (idValues.length === 0) return c.text('No rows selected', 400);
|
|
119
|
-
|
|
120
|
-
\t\t\tconst parsedIds: unknown[] = ${parseBulkIds};
|
|
121
|
-
\t\t\tif (parsedIds.length === 0) return c.text('No valid selected rows', 400);
|
|
122
|
-
|
|
123
|
-
\t\t\tawait db
|
|
124
|
-
\t\t\t\t.delete(tableSchema)
|
|
125
|
-
\t\t\t\t.where(inArray(tableSchema.${primaryKey}, parsedIds as any))
|
|
126
|
-
\t\t\t\t.execute();
|
|
127
|
-
\t\t}
|
|
128
|
-
|
|
129
|
-
\t\tconst query = new URLSearchParams({
|
|
130
|
-
\t\t\tpage: getValue(body.page) || '1',
|
|
131
|
-
\t\t\tsort: getValue(body.sort) || '${defaultSort}',
|
|
132
|
-
\t\t\torder: getValue(body.order) || 'desc',
|
|
133
|
-
\t\t\tsearch: getValue(body.search) || '',
|
|
134
|
-
\t\t});
|
|
135
|
-
\t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
|
|
136
|
-
\t});`
|
|
137
|
-
: "";
|
|
138
|
-
|
|
139
|
-
return `
|
|
140
|
-
\tadminApp.post('/table/${exportName}/create', async (c) => {
|
|
141
|
-
\t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
|
|
142
|
-
\t\tconst tableSchema = (schema as any).${exportName};
|
|
143
|
-
\t\tif (!tableSchema) return c.text('Table missing', 404);
|
|
144
|
-
|
|
145
|
-
\t\tconst body = await c.req.parseBody();
|
|
146
|
-
\t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
|
|
147
|
-
\t\tconst payload: Record<string, unknown> = {};
|
|
148
|
-
|
|
149
|
-
\t\t${createAssignments}
|
|
150
|
-
|
|
151
|
-
\t\tawait db.insert(tableSchema).values(payload as any).execute();
|
|
152
|
-
|
|
153
|
-
\t\tconst query = new URLSearchParams({
|
|
154
|
-
\t\t\tpage: getValue(body.page) || '1',
|
|
155
|
-
\t\t\tsort: getValue(body.sort) || '${defaultSort}',
|
|
156
|
-
\t\t\torder: getValue(body.order) || 'desc',
|
|
157
|
-
\t\t\tsearch: getValue(body.search) || '',
|
|
158
|
-
\t\t});
|
|
159
|
-
\t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
|
|
160
|
-
\t});
|
|
161
|
-
${editRoute}
|
|
162
|
-
\t`;
|
|
163
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Builds the POST route handlers code string for create/edit/delete operations
|
|
3
|
+
* on a generic table (/admin/table/:tableName/create|edit|delete).
|
|
4
|
+
*/
|
|
5
|
+
export function buildTablePostRoutes(
|
|
6
|
+
exportName: string,
|
|
7
|
+
defaultSort: string,
|
|
8
|
+
primaryKey: string,
|
|
9
|
+
primaryKeyType: string | undefined,
|
|
10
|
+
hasPrimaryKey: boolean,
|
|
11
|
+
searchConditions: string,
|
|
12
|
+
createAssignments: string,
|
|
13
|
+
editAssignments: string,
|
|
14
|
+
): string {
|
|
15
|
+
const numericIdGuard =
|
|
16
|
+
primaryKeyType === "number"
|
|
17
|
+
? `
|
|
18
|
+
\t\tconst parsedId = Number(rawId);
|
|
19
|
+
\t\tif (Number.isNaN(parsedId)) return c.text('${primaryKey} must be a valid number', 400);
|
|
20
|
+
\t\tidValue = parsedId;
|
|
21
|
+
\t\t`
|
|
22
|
+
: "";
|
|
23
|
+
|
|
24
|
+
const parseBulkIds =
|
|
25
|
+
primaryKeyType === "number"
|
|
26
|
+
? "idValues.map((value) => Number(value)).filter((value) => !Number.isNaN(value))"
|
|
27
|
+
: "idValues";
|
|
28
|
+
|
|
29
|
+
const editRoute = hasPrimaryKey
|
|
30
|
+
? `
|
|
31
|
+
\tadminApp.post('/table/${exportName}/edit', async (c) => {
|
|
32
|
+
\t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
|
|
33
|
+
\t\tconst tableSchema = (schema as any).${exportName};
|
|
34
|
+
\t\tif (!tableSchema) return c.text('Table missing', 404);
|
|
35
|
+
|
|
36
|
+
\t\tconst body = await c.req.parseBody();
|
|
37
|
+
\t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
|
|
38
|
+
\t\tconst rawId = getValue(body['${primaryKey}']);
|
|
39
|
+
\t\tif (rawId === '') return c.text('${primaryKey} is required', 400);
|
|
40
|
+
|
|
41
|
+
\t\tconst payload: Record<string, unknown> = {};
|
|
42
|
+
|
|
43
|
+
\t\t${editAssignments}
|
|
44
|
+
|
|
45
|
+
\t\tlet idValue: unknown = rawId;
|
|
46
|
+
\t\t${numericIdGuard}
|
|
47
|
+
|
|
48
|
+
\t\tif (Object.keys(payload).length > 0) {
|
|
49
|
+
\t\t\tawait db
|
|
50
|
+
\t\t\t\t.update(tableSchema)
|
|
51
|
+
\t\t\t\t.set(payload as any)
|
|
52
|
+
\t\t\t\t.where(eq(tableSchema.${primaryKey}, idValue as any))
|
|
53
|
+
\t\t\t\t.execute();
|
|
54
|
+
\t\t}
|
|
55
|
+
|
|
56
|
+
\t\tconst query = new URLSearchParams({
|
|
57
|
+
\t\t\tpage: getValue(body.page) || '1',
|
|
58
|
+
\t\t\tsort: getValue(body.sort) || '${defaultSort}',
|
|
59
|
+
\t\t\torder: getValue(body.order) || 'desc',
|
|
60
|
+
\t\t\tsearch: getValue(body.search) || '',
|
|
61
|
+
\t\t});
|
|
62
|
+
\t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
|
|
63
|
+
\t});
|
|
64
|
+
|
|
65
|
+
\tadminApp.post('/table/${exportName}/delete', async (c) => {
|
|
66
|
+
\t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
|
|
67
|
+
\t\tconst tableSchema = (schema as any).${exportName};
|
|
68
|
+
\t\tif (!tableSchema) return c.text('Table missing', 404);
|
|
69
|
+
|
|
70
|
+
\t\tconst body = await c.req.parseBody();
|
|
71
|
+
\t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
|
|
72
|
+
\t\tconst rawId = getValue(body['${primaryKey}']);
|
|
73
|
+
\t\tif (rawId === '') return c.text('${primaryKey} is required', 400);
|
|
74
|
+
|
|
75
|
+
\t\tlet idValue: unknown = rawId;
|
|
76
|
+
\t\t${numericIdGuard}
|
|
77
|
+
|
|
78
|
+
\t\tawait db
|
|
79
|
+
\t\t\t.delete(tableSchema)
|
|
80
|
+
\t\t\t.where(eq(tableSchema.${primaryKey}, idValue as any))
|
|
81
|
+
\t\t\t.execute();
|
|
82
|
+
|
|
83
|
+
\t\tconst query = new URLSearchParams({
|
|
84
|
+
\t\t\tpage: getValue(body.page) || '1',
|
|
85
|
+
\t\t\tsort: getValue(body.sort) || '${defaultSort}',
|
|
86
|
+
\t\t\torder: getValue(body.order) || 'desc',
|
|
87
|
+
\t\t\tsearch: getValue(body.search) || '',
|
|
88
|
+
\t\t});
|
|
89
|
+
\t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
|
|
90
|
+
\t});
|
|
91
|
+
|
|
92
|
+
\tadminApp.post('/table/${exportName}/delete-bulk', async (c) => {
|
|
93
|
+
\t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
|
|
94
|
+
\t\tconst tableSchema = (schema as any).${exportName};
|
|
95
|
+
\t\tif (!tableSchema) return c.text('Table missing', 404);
|
|
96
|
+
|
|
97
|
+
\t\tconst body = await c.req.parseBody();
|
|
98
|
+
\t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
|
|
99
|
+
\t\tconst mode = getValue(body.bulkMode);
|
|
100
|
+
\t\tconst selectedIdsRaw = getValue(body.selectedIds);
|
|
101
|
+
\t\tconst search = getValue(body.search);
|
|
102
|
+
|
|
103
|
+
\t\tif (mode === 'all-matching') {
|
|
104
|
+
\t\t\tlet deleteQuery = db.delete(tableSchema);
|
|
105
|
+
\t\t\tif (search) {
|
|
106
|
+
const searchConditions: any[] = [];
|
|
107
|
+
\t\t\t\t${searchConditions}
|
|
108
|
+
\t\t\t\tif (searchConditions.length > 0) {
|
|
109
|
+
\t\t\t\t\tdeleteQuery = deleteQuery.where(or(...searchConditions)) as any;
|
|
110
|
+
\t\t\t\t}
|
|
111
|
+
\t\t\t}
|
|
112
|
+
\t\t\tawait deleteQuery.execute();
|
|
113
|
+
\t\t} else {
|
|
114
|
+
\t\t\tconst idValues = selectedIdsRaw
|
|
115
|
+
\t\t\t\t.split(',')
|
|
116
|
+
\t\t\t\t.map((value) => value.trim())
|
|
117
|
+
\t\t\t\t.filter((value) => value.length > 0);
|
|
118
|
+
\t\t\tif (idValues.length === 0) return c.text('No rows selected', 400);
|
|
119
|
+
|
|
120
|
+
\t\t\tconst parsedIds: unknown[] = ${parseBulkIds};
|
|
121
|
+
\t\t\tif (parsedIds.length === 0) return c.text('No valid selected rows', 400);
|
|
122
|
+
|
|
123
|
+
\t\t\tawait db
|
|
124
|
+
\t\t\t\t.delete(tableSchema)
|
|
125
|
+
\t\t\t\t.where(inArray(tableSchema.${primaryKey}, parsedIds as any))
|
|
126
|
+
\t\t\t\t.execute();
|
|
127
|
+
\t\t}
|
|
128
|
+
|
|
129
|
+
\t\tconst query = new URLSearchParams({
|
|
130
|
+
\t\t\tpage: getValue(body.page) || '1',
|
|
131
|
+
\t\t\tsort: getValue(body.sort) || '${defaultSort}',
|
|
132
|
+
\t\t\torder: getValue(body.order) || 'desc',
|
|
133
|
+
\t\t\tsearch: getValue(body.search) || '',
|
|
134
|
+
\t\t});
|
|
135
|
+
\t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
|
|
136
|
+
\t});`
|
|
137
|
+
: "";
|
|
138
|
+
|
|
139
|
+
return `
|
|
140
|
+
\tadminApp.post('/table/${exportName}/create', async (c) => {
|
|
141
|
+
\t\tconst db = drizzle(c.env[options.databaseBinding], { schema });
|
|
142
|
+
\t\tconst tableSchema = (schema as any).${exportName};
|
|
143
|
+
\t\tif (!tableSchema) return c.text('Table missing', 404);
|
|
144
|
+
|
|
145
|
+
\t\tconst body = await c.req.parseBody();
|
|
146
|
+
\t\tconst getValue = (value: unknown) => (typeof value === 'string' ? value : '');
|
|
147
|
+
\t\tconst payload: Record<string, unknown> = {};
|
|
148
|
+
|
|
149
|
+
\t\t${createAssignments}
|
|
150
|
+
|
|
151
|
+
\t\tawait db.insert(tableSchema).values(payload as any).execute();
|
|
152
|
+
|
|
153
|
+
\t\tconst query = new URLSearchParams({
|
|
154
|
+
\t\t\tpage: getValue(body.page) || '1',
|
|
155
|
+
\t\t\tsort: getValue(body.sort) || '${defaultSort}',
|
|
156
|
+
\t\t\torder: getValue(body.order) || 'desc',
|
|
157
|
+
\t\t\tsearch: getValue(body.search) || '',
|
|
158
|
+
\t\t});
|
|
159
|
+
\t\treturn c.redirect('/admin/table/${exportName}?' + query.toString());
|
|
160
|
+
\t});
|
|
161
|
+
${editRoute}
|
|
162
|
+
\t`;
|
|
163
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// Re-exported from the modular table/ directory.
|
|
2
|
-
// The actual implementation has been split into focused files:
|
|
3
|
-
// - table/actions-cell.ts — edit drawer + delete dialog cell
|
|
4
|
-
// - table/get-route.ts — GET /admin/table/:tableName handler
|
|
5
|
-
// - table/post-routes.ts — POST create/edit/delete handlers
|
|
6
|
-
// - table/index.ts — orchestrating buildTableRoute()
|
|
7
|
-
export { buildTableRoute } from "./table/index";
|
|
1
|
+
// Re-exported from the modular table/ directory.
|
|
2
|
+
// The actual implementation has been split into focused files:
|
|
3
|
+
// - table/actions-cell.ts — edit drawer + delete dialog cell
|
|
4
|
+
// - table/get-route.ts — GET /admin/table/:tableName handler
|
|
5
|
+
// - table/post-routes.ts — POST create/edit/delete handlers
|
|
6
|
+
// - table/index.ts — orchestrating buildTableRoute()
|
|
7
|
+
export { buildTableRoute } from "./table/index";
|
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
import { buildUsersTableHtml } from "./html/table";
|
|
2
|
-
import { buildUsersPageHtml } from "./html/page";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Builds the GET /admin/users route handler (runtime template string).
|
|
6
|
-
* Handles search, sort, pagination, and renders the users page.
|
|
7
|
-
*/
|
|
8
|
-
export function buildUsersGetRoute(): string {
|
|
9
|
-
const tableHtml = buildUsersTableHtml();
|
|
10
|
-
const pageHtml = buildUsersPageHtml();
|
|
11
|
-
|
|
12
|
-
return `
|
|
13
|
-
\tadminApp.get('/users', async (c) => {
|
|
14
|
-
\t\tconst db = drizzle(c.env[options.databaseBinding]);
|
|
15
|
-
\t\tconst auth = createAuth({ DATABASE: c.env[options.databaseBinding] } as any, c.req.raw.cf as any);
|
|
16
|
-
\t\tconst session = await auth.api.getSession({ headers: c.req.raw.headers });
|
|
17
|
-
\t\tconst page = parseInt(c.req.query('page') || '1');
|
|
18
|
-
\t\tconst limit = 20;
|
|
19
|
-
\t\tconst offset = (page - 1) * limit;
|
|
20
|
-
\t\tconst sort = c.req.query('sort') || 'createdAt';
|
|
21
|
-
\t\tconst order = c.req.query('order') || 'desc';
|
|
22
|
-
\t\tconst search = c.req.query('search') || '';
|
|
23
|
-
\t\tconst currentUserId = session?.user?.id || '';
|
|
24
|
-
|
|
25
|
-
\t\tlet query = db.select().from(users);
|
|
26
|
-
\t\tlet countQuery = db.select({ count: sql\`count(*)\` }).from(users);
|
|
27
|
-
|
|
28
|
-
\t\tif (search) {
|
|
29
|
-
\t\t\tconst searchConditions = [
|
|
30
|
-
\t\t\t\tlike(users.name, \`%\${search}%\`),
|
|
31
|
-
\t\t\t\tlike(users.email, \`%\${search}%\`),
|
|
32
|
-
\t\t\t\tlike(users.role, \`%\${search}%\`),
|
|
33
|
-
\t\t\t];
|
|
34
|
-
\t\t\tquery = query.where(or(...searchConditions)) as any;
|
|
35
|
-
\t\t\tcountQuery = countQuery.where(or(...searchConditions)) as any;
|
|
36
|
-
\t\t}
|
|
37
|
-
|
|
38
|
-
\t\tconst sortColumns: Record<string, any> = {
|
|
39
|
-
\t\t\tid: users.id,
|
|
40
|
-
\t\t\tname: users.name,
|
|
41
|
-
\t\t\temail: users.email,
|
|
42
|
-
\t\t\trole: users.role,
|
|
43
|
-
\t\t\tcreatedAt: users.createdAt,
|
|
44
|
-
\t\t\tbanned: users.banned,
|
|
45
|
-
\t\t};
|
|
46
|
-
|
|
47
|
-
\t\tif (sortColumns[sort]) {
|
|
48
|
-
\t\t\tquery = query.orderBy(order === 'asc' ? asc(sortColumns[sort]) : desc(sortColumns[sort])) as any;
|
|
49
|
-
\t\t}
|
|
50
|
-
|
|
51
|
-
\t\tconst data = await query.limit(limit).offset(offset).execute();
|
|
52
|
-
\t\tconst totalResult = await countQuery.execute();
|
|
53
|
-
\t\tconst total = Number(totalResult[0]?.count || 0);
|
|
54
|
-
\t\tconst totalPages = Math.ceil(total / limit);
|
|
55
|
-
|
|
56
|
-
\t\t${tableHtml}
|
|
57
|
-
|
|
58
|
-
\t\t${pageHtml}
|
|
59
|
-
|
|
60
|
-
\t\tif (c.req.header('hx-request')) {
|
|
61
|
-
\t\t\treturn c.html(content);
|
|
62
|
-
\t\t}
|
|
63
|
-
|
|
64
|
-
\t\treturn c.html(Layout({
|
|
65
|
-
\t\t\ttitle: "users - Admin Dashboard",
|
|
66
|
-
\t\t\tchildren: content,
|
|
67
|
-
\t\t}));
|
|
68
|
-
\t});`;
|
|
69
|
-
}
|
|
1
|
+
import { buildUsersTableHtml } from "./html/table";
|
|
2
|
+
import { buildUsersPageHtml } from "./html/page";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Builds the GET /admin/users route handler (runtime template string).
|
|
6
|
+
* Handles search, sort, pagination, and renders the users page.
|
|
7
|
+
*/
|
|
8
|
+
export function buildUsersGetRoute(): string {
|
|
9
|
+
const tableHtml = buildUsersTableHtml();
|
|
10
|
+
const pageHtml = buildUsersPageHtml();
|
|
11
|
+
|
|
12
|
+
return `
|
|
13
|
+
\tadminApp.get('/users', async (c) => {
|
|
14
|
+
\t\tconst db = drizzle(c.env[options.databaseBinding]);
|
|
15
|
+
\t\tconst auth = createAuth({ DATABASE: c.env[options.databaseBinding] } as any, c.req.raw.cf as any);
|
|
16
|
+
\t\tconst session = await auth.api.getSession({ headers: c.req.raw.headers });
|
|
17
|
+
\t\tconst page = parseInt(c.req.query('page') || '1');
|
|
18
|
+
\t\tconst limit = 20;
|
|
19
|
+
\t\tconst offset = (page - 1) * limit;
|
|
20
|
+
\t\tconst sort = c.req.query('sort') || 'createdAt';
|
|
21
|
+
\t\tconst order = c.req.query('order') || 'desc';
|
|
22
|
+
\t\tconst search = c.req.query('search') || '';
|
|
23
|
+
\t\tconst currentUserId = session?.user?.id || '';
|
|
24
|
+
|
|
25
|
+
\t\tlet query = db.select().from(users);
|
|
26
|
+
\t\tlet countQuery = db.select({ count: sql\`count(*)\` }).from(users);
|
|
27
|
+
|
|
28
|
+
\t\tif (search) {
|
|
29
|
+
\t\t\tconst searchConditions = [
|
|
30
|
+
\t\t\t\tlike(users.name, \`%\${search}%\`),
|
|
31
|
+
\t\t\t\tlike(users.email, \`%\${search}%\`),
|
|
32
|
+
\t\t\t\tlike(users.role, \`%\${search}%\`),
|
|
33
|
+
\t\t\t];
|
|
34
|
+
\t\t\tquery = query.where(or(...searchConditions)) as any;
|
|
35
|
+
\t\t\tcountQuery = countQuery.where(or(...searchConditions)) as any;
|
|
36
|
+
\t\t}
|
|
37
|
+
|
|
38
|
+
\t\tconst sortColumns: Record<string, any> = {
|
|
39
|
+
\t\t\tid: users.id,
|
|
40
|
+
\t\t\tname: users.name,
|
|
41
|
+
\t\t\temail: users.email,
|
|
42
|
+
\t\t\trole: users.role,
|
|
43
|
+
\t\t\tcreatedAt: users.createdAt,
|
|
44
|
+
\t\t\tbanned: users.banned,
|
|
45
|
+
\t\t};
|
|
46
|
+
|
|
47
|
+
\t\tif (sortColumns[sort]) {
|
|
48
|
+
\t\t\tquery = query.orderBy(order === 'asc' ? asc(sortColumns[sort]) : desc(sortColumns[sort])) as any;
|
|
49
|
+
\t\t}
|
|
50
|
+
|
|
51
|
+
\t\tconst data = await query.limit(limit).offset(offset).execute();
|
|
52
|
+
\t\tconst totalResult = await countQuery.execute();
|
|
53
|
+
\t\tconst total = Number(totalResult[0]?.count || 0);
|
|
54
|
+
\t\tconst totalPages = Math.ceil(total / limit);
|
|
55
|
+
|
|
56
|
+
\t\t${tableHtml}
|
|
57
|
+
|
|
58
|
+
\t\t${pageHtml}
|
|
59
|
+
|
|
60
|
+
\t\tif (c.req.header('hx-request')) {
|
|
61
|
+
\t\t\treturn c.html(content);
|
|
62
|
+
\t\t}
|
|
63
|
+
|
|
64
|
+
\t\treturn c.html(Layout({
|
|
65
|
+
\t\t\ttitle: "users - Admin Dashboard",
|
|
66
|
+
\t\t\tchildren: content,
|
|
67
|
+
\t\t}));
|
|
68
|
+
\t});`;
|
|
69
|
+
}
|