appflare 0.2.48 → 0.2.49
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 +898 -898
- package/cli/commands/index.ts +247 -247
- package/cli/generate.ts +360 -360
- package/cli/index.ts +120 -120
- package/cli/load-config.ts +184 -184
- package/cli/schema-compiler.ts +1366 -1366
- 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 +763 -763
- 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 +195 -195
- package/cli/templates/core/client/types.ts +187 -187
- 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 +271 -271
- package/cli/templates/dashboard/builders/functions/render-page/result-panel.ts +85 -85
- package/cli/templates/dashboard/builders/functions/render-page/scripts.ts +703 -703
- package/cli/templates/dashboard/builders/functions/tree-builder.ts +47 -47
- package/cli/templates/dashboard/builders/navigation.ts +155 -155
- 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 +257 -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 +420 -420
- 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 +44 -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 +82 -82
- package/cli/templates/handlers/generators/context/storage-helpers.ts +59 -59
- package/cli/templates/handlers/generators/context/types.ts +40 -40
- 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 +14 -14
- package/cli/templates/handlers/generators/registration/modules/cron.ts +35 -35
- 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 +510 -510
- package/cli/templates/handlers/generators/registration/modules/scheduler.ts +65 -65
- package/cli/templates/handlers/generators/registration/modules/storage.ts +199 -199
- package/cli/templates/handlers/generators/registration/sections.ts +210 -210
- package/cli/templates/handlers/generators/types/context.ts +121 -121
- package/cli/templates/handlers/generators/types/core.ts +108 -108
- package/cli/templates/handlers/generators/types/operations.ts +135 -135
- package/cli/templates/handlers/generators/types/query-definitions/filter-and-where-types.ts +291 -291
- 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 +1382 -1382
- package/cli/templates/handlers/generators/types/query-definitions/schema-and-table-types.ts +278 -278
- 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 +156 -156
- package/cli/templates/handlers/generators/types/query-runtime/runtime-setup.ts +45 -45
- package/cli/templates/handlers/generators/types/query-runtime/runtime-write.ts +958 -958
- package/cli/templates/handlers/generators/types/query-runtime.ts +15 -15
- package/cli/templates/handlers/index.ts +47 -47
- package/cli/templates/handlers/operations.ts +116 -116
- package/cli/templates/handlers/registration.ts +91 -91
- package/cli/templates/handlers/types.ts +17 -17
- package/cli/templates/handlers/utils.ts +48 -48
- package/cli/types.ts +110 -110
- package/cli/utils/handler-discovery.ts +501 -501
- package/cli/utils/json-utils.ts +24 -24
- package/cli/utils/path-utils.ts +19 -19
- package/cli/utils/schema-discovery.ts +402 -399
- package/dist/cli/index.js +77 -55
- package/dist/cli/index.mjs +77 -55
- package/index.ts +18 -18
- package/package.json +58 -58
- package/react/index.ts +5 -5
- package/react/use-infinite-query.ts +255 -255
- package/react/use-mutation.ts +89 -89
- package/react/use-query.ts +210 -210
- package/schema.ts +641 -641
- package/test-better-auth-hash.ts +2 -2
- package/tsconfig.json +6 -6
- package/tsup.config.ts +82 -82
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import { DiscoveredHandlerOperation } from "../../../../utils/handler-discovery";
|
|
2
|
-
|
|
3
|
-
export type TreeNode = {
|
|
4
|
-
name: string;
|
|
5
|
-
type: "folder";
|
|
6
|
-
children: TreeNode[];
|
|
7
|
-
handlers: DiscoveredHandlerOperation[];
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export function buildFunctionTree(
|
|
11
|
-
handlers: DiscoveredHandlerOperation[],
|
|
12
|
-
): TreeNode[] {
|
|
13
|
-
const root: TreeNode = {
|
|
14
|
-
name: "root",
|
|
15
|
-
type: "folder",
|
|
16
|
-
children: [],
|
|
17
|
-
handlers: [],
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
for (const handler of handlers) {
|
|
21
|
-
const segments = handler.clientSegments ?? [handler.exportName];
|
|
22
|
-
let current = root;
|
|
23
|
-
|
|
24
|
-
for (let i = 0; i < segments.length - 1; i++) {
|
|
25
|
-
const segment = segments[i];
|
|
26
|
-
let child = current.children.find(
|
|
27
|
-
(c) => c.name === segment && c.type === "folder",
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
if (!child) {
|
|
31
|
-
child = {
|
|
32
|
-
name: segment,
|
|
33
|
-
type: "folder",
|
|
34
|
-
children: [],
|
|
35
|
-
handlers: [],
|
|
36
|
-
};
|
|
37
|
-
current.children.push(child);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
current = child;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
current.handlers.push(handler);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return root.children;
|
|
47
|
-
}
|
|
1
|
+
import { DiscoveredHandlerOperation } from "../../../../utils/handler-discovery";
|
|
2
|
+
|
|
3
|
+
export type TreeNode = {
|
|
4
|
+
name: string;
|
|
5
|
+
type: "folder";
|
|
6
|
+
children: TreeNode[];
|
|
7
|
+
handlers: DiscoveredHandlerOperation[];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function buildFunctionTree(
|
|
11
|
+
handlers: DiscoveredHandlerOperation[],
|
|
12
|
+
): TreeNode[] {
|
|
13
|
+
const root: TreeNode = {
|
|
14
|
+
name: "root",
|
|
15
|
+
type: "folder",
|
|
16
|
+
children: [],
|
|
17
|
+
handlers: [],
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
for (const handler of handlers) {
|
|
21
|
+
const segments = handler.clientSegments ?? [handler.exportName];
|
|
22
|
+
let current = root;
|
|
23
|
+
|
|
24
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
25
|
+
const segment = segments[i];
|
|
26
|
+
let child = current.children.find(
|
|
27
|
+
(c) => c.name === segment && c.type === "folder",
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
if (!child) {
|
|
31
|
+
child = {
|
|
32
|
+
name: segment,
|
|
33
|
+
type: "folder",
|
|
34
|
+
children: [],
|
|
35
|
+
handlers: [],
|
|
36
|
+
};
|
|
37
|
+
current.children.push(child);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
current = child;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
current.handlers.push(handler);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return root.children;
|
|
47
|
+
}
|
|
@@ -1,155 +1,155 @@
|
|
|
1
|
-
import { DiscoveredSchema } from "../../../utils/schema-discovery";
|
|
2
|
-
import { DiscoveredHandlerOperation } from "../../../utils/handler-discovery";
|
|
3
|
-
import { TableInfo } from "../types";
|
|
4
|
-
import { buildFunctionTree, TreeNode } from "./functions/tree-builder";
|
|
5
|
-
|
|
6
|
-
export function collectTablesInfo(schema: DiscoveredSchema): TableInfo[] {
|
|
7
|
-
return schema.tables.map((table) => ({
|
|
8
|
-
exportName: table.exportName,
|
|
9
|
-
tableName: table.tableName,
|
|
10
|
-
columns: table.columns.map((column) => column.name),
|
|
11
|
-
}));
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** Generates the right-pane list items: users entry first, then schema tables */
|
|
15
|
-
export function buildSidebarTableList(tablesInfo: TableInfo[]): string {
|
|
16
|
-
const tableItems = tablesInfo
|
|
17
|
-
.map(
|
|
18
|
-
(table) =>
|
|
19
|
-
`<li data-name="${table.tableName}">
|
|
20
|
-
\t\t\t\t\t\t<a href="/admin/table/${table.exportName}" hx-get="/admin/table/${table.exportName}" hx-target="#main-content" hx-push-url="true" hx-swap="outerHTML" class="sidebar-link flex items-center gap-2 px-3 py-2 text-sm rounded-lg w-full">
|
|
21
|
-
\t\t\t\t\t\t\t<iconify-icon icon="mdi:table" width="16" height="16" class="opacity-50 shrink-0"></iconify-icon>
|
|
22
|
-
\t\t\t\t\t\t\t<span class="truncate">${table.tableName}</span>
|
|
23
|
-
\t\t\t\t\t\t</a>
|
|
24
|
-
\t\t\t\t\t</li>`,
|
|
25
|
-
)
|
|
26
|
-
.join("\n\t\t\t\t\t");
|
|
27
|
-
|
|
28
|
-
return `<li data-name="users">
|
|
29
|
-
\t\t\t\t\t\t<a href="/admin/users" hx-get="/admin/users" hx-target="#main-content" hx-push-url="true" hx-swap="outerHTML" class="sidebar-link flex items-center gap-2 px-3 py-2 text-sm rounded-lg w-full">
|
|
30
|
-
\t\t\t\t\t\t\t<iconify-icon icon="mdi:account-group" width="16" height="16" class="opacity-50 shrink-0"></iconify-icon>
|
|
31
|
-
\t\t\t\t\t\t\t<span class="truncate">users</span>
|
|
32
|
-
\t\t\t\t\t\t</a>
|
|
33
|
-
\t\t\t\t\t</li>
|
|
34
|
-
\t\t\t\t\t${tableItems}`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function renderTreeNode(node: TreeNode, depth: number): string {
|
|
38
|
-
const indent = " ".repeat(depth);
|
|
39
|
-
const hasChildren = node.children.length > 0;
|
|
40
|
-
const hasHandlers = node.handlers.length > 0;
|
|
41
|
-
const isExpandable = hasChildren || hasHandlers;
|
|
42
|
-
|
|
43
|
-
let html = "";
|
|
44
|
-
|
|
45
|
-
if (isExpandable) {
|
|
46
|
-
const folderId = `folder-${node.name.replace(/[^a-zA-Z0-9]/g, "-")}-${depth}`;
|
|
47
|
-
html += `\n${indent}<li data-name="${node.name}" class="folder-item">`;
|
|
48
|
-
html += `\n${indent} <details class="group/folder" open>`;
|
|
49
|
-
html += `\n${indent} <summary class="sidebar-link flex items-center gap-2 px-3 py-2 text-sm rounded-lg w-full cursor-pointer list-none">`;
|
|
50
|
-
html += `\n${indent} <iconify-icon icon="solar:folder-bold-duotone" width="16" height="16" class="opacity-50 shrink-0 transition-transform group-open/folder:rotate-0"></iconify-icon>`;
|
|
51
|
-
html += `\n${indent} <span class="truncate font-medium">${node.name}</span>`;
|
|
52
|
-
html += `\n${indent} </summary>`;
|
|
53
|
-
html += `\n${indent} <ul class="flex flex-col gap-0.5 ml-4 border-l border-base-200 pl-2">`;
|
|
54
|
-
|
|
55
|
-
for (const child of node.children) {
|
|
56
|
-
html += renderTreeNode(child, depth + 2);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
for (const handler of node.handlers) {
|
|
60
|
-
const icon = handler.kind === "query" ? "solar:reorder-linear" : "solar:bolt-linear";
|
|
61
|
-
html += `\n${indent} <li data-name="${handler.exportName}">`;
|
|
62
|
-
html += `\n${indent} <a href="/admin/functions${handler.routePath}" hx-get="/admin/functions${handler.routePath}" hx-target="#main-content" hx-push-url="true" hx-swap="outerHTML" class="sidebar-link flex items-center gap-2 px-3 py-2 text-sm rounded-lg w-full">`;
|
|
63
|
-
html += `\n${indent} <iconify-icon icon="${icon}" width="16" height="16" class="opacity-50 shrink-0"></iconify-icon>`;
|
|
64
|
-
html += `\n${indent} <span class="truncate">${handler.exportName}</span>`;
|
|
65
|
-
html += `\n${indent} </a>`;
|
|
66
|
-
html += `\n${indent} </li>`;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
html += `\n${indent} </ul>`;
|
|
70
|
-
html += `\n${indent} </details>`;
|
|
71
|
-
html += `\n${indent}</li>`;
|
|
72
|
-
} else {
|
|
73
|
-
for (const handler of node.handlers) {
|
|
74
|
-
const icon = handler.kind === "query" ? "solar:reorder-linear" : "solar:bolt-linear";
|
|
75
|
-
html += `\n${indent}<li data-name="${handler.exportName}">`;
|
|
76
|
-
html += `\n${indent} <a href="/admin/functions${handler.routePath}" hx-get="/admin/functions${handler.routePath}" hx-target="#main-content" hx-push-url="true" hx-swap="outerHTML" class="sidebar-link flex items-center gap-2 px-3 py-2 text-sm rounded-lg w-full">`;
|
|
77
|
-
html += `\n${indent} <iconify-icon icon="${icon}" width="16" height="16" class="opacity-50 shrink-0"></iconify-icon>`;
|
|
78
|
-
html += `\n${indent} <span class="truncate">${handler.exportName}</span>`;
|
|
79
|
-
html += `\n${indent} </a>`;
|
|
80
|
-
html += `\n${indent}</li>`;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return html;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function buildSidebarFunctionList(
|
|
88
|
-
handlers: DiscoveredHandlerOperation[],
|
|
89
|
-
): string {
|
|
90
|
-
const queries = handlers.filter((h) => h.kind === "query");
|
|
91
|
-
const mutations = handlers.filter((h) => h.kind === "mutation");
|
|
92
|
-
|
|
93
|
-
const queryTree = buildFunctionTree(queries);
|
|
94
|
-
const mutationTree = buildFunctionTree(mutations);
|
|
95
|
-
|
|
96
|
-
const queryItems = queryTree.map((node) => renderTreeNode(node, 0)).join("");
|
|
97
|
-
const mutationItems = mutationTree.map((node) => renderTreeNode(node, 0)).join("");
|
|
98
|
-
|
|
99
|
-
return `
|
|
100
|
-
<div id="pane-functions" class="flex flex-col h-full hidden">
|
|
101
|
-
<div class="px-3 pt-5 pb-3">
|
|
102
|
-
<p class="text-[10px] font-semibold uppercase tracking-widest opacity-35 mb-3 px-1">Functions</p>
|
|
103
|
-
<div class="relative">
|
|
104
|
-
<iconify-icon icon="solar:magnifer-linear" width="13" height="13" class="absolute left-2.5 top-1/2 -translate-y-1/2 opacity-35 pointer-events-none"></iconify-icon>
|
|
105
|
-
<input
|
|
106
|
-
type="text"
|
|
107
|
-
id="function-search"
|
|
108
|
-
placeholder="Search functions..."
|
|
109
|
-
class="input input-sm border border-base-200 bg-base-200/50 focus:bg-base-100 focus:border-primary focus:outline-none w-full pl-7 text-xs rounded-lg h-8"
|
|
110
|
-
onkeyup="filterFunctions(this.value)"
|
|
111
|
-
/>
|
|
112
|
-
</div>
|
|
113
|
-
</div>
|
|
114
|
-
<nav class="flex-1 overflow-y-auto px-2 pb-4">
|
|
115
|
-
${
|
|
116
|
-
queries.length > 0
|
|
117
|
-
? `<p class="text-[9px] font-bold uppercase tracking-wider opacity-25 mt-4 mb-1 px-2">Queries</p>
|
|
118
|
-
<ul class="flex flex-col gap-0.5">${queryItems}</ul>`
|
|
119
|
-
: ""
|
|
120
|
-
}
|
|
121
|
-
${
|
|
122
|
-
mutations.length > 0
|
|
123
|
-
? `<p class="text-[9px] font-bold uppercase tracking-wider opacity-25 mt-4 mb-1 px-2">Mutations</p>
|
|
124
|
-
<ul class="flex flex-col gap-0.5">${mutationItems}</ul>`
|
|
125
|
-
: ""
|
|
126
|
-
}
|
|
127
|
-
</nav>
|
|
128
|
-
</div>
|
|
129
|
-
`;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function buildDashboardCards(tablesInfo: TableInfo[]): string {
|
|
133
|
-
return tablesInfo
|
|
134
|
-
.map((table) =>
|
|
135
|
-
`
|
|
136
|
-
<a
|
|
137
|
-
href="/admin/table/${table.exportName}"
|
|
138
|
-
class="card bg-base-100 border border-base-200 hover:border-primary/30 hover:shadow-md transition-all cursor-pointer group"
|
|
139
|
-
>
|
|
140
|
-
<div class="card-body p-5">
|
|
141
|
-
<div class="flex items-center gap-3">
|
|
142
|
-
<div class="w-9 h-9 rounded-lg bg-primary/10 flex items-center justify-center">
|
|
143
|
-
<iconify-icon icon="mdi:table" width="20" height="20" class="text-primary"></iconify-icon>
|
|
144
|
-
</div>
|
|
145
|
-
<div>
|
|
146
|
-
<h2 class="font-semibold text-sm capitalize group-hover:text-primary transition-colors">${table.tableName}</h2>
|
|
147
|
-
<p class="text-xs opacity-40 mt-0.5">Manage records</p>
|
|
148
|
-
</div>
|
|
149
|
-
</div>
|
|
150
|
-
</div>
|
|
151
|
-
</a>
|
|
152
|
-
`.replace(/\n/g, "\\n"),
|
|
153
|
-
)
|
|
154
|
-
.join("");
|
|
155
|
-
}
|
|
1
|
+
import { DiscoveredSchema } from "../../../utils/schema-discovery";
|
|
2
|
+
import { DiscoveredHandlerOperation } from "../../../utils/handler-discovery";
|
|
3
|
+
import { TableInfo } from "../types";
|
|
4
|
+
import { buildFunctionTree, TreeNode } from "./functions/tree-builder";
|
|
5
|
+
|
|
6
|
+
export function collectTablesInfo(schema: DiscoveredSchema): TableInfo[] {
|
|
7
|
+
return schema.tables.map((table) => ({
|
|
8
|
+
exportName: table.exportName,
|
|
9
|
+
tableName: table.tableName,
|
|
10
|
+
columns: table.columns.map((column) => column.name),
|
|
11
|
+
}));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Generates the right-pane list items: users entry first, then schema tables */
|
|
15
|
+
export function buildSidebarTableList(tablesInfo: TableInfo[]): string {
|
|
16
|
+
const tableItems = tablesInfo
|
|
17
|
+
.map(
|
|
18
|
+
(table) =>
|
|
19
|
+
`<li data-name="${table.tableName}">
|
|
20
|
+
\t\t\t\t\t\t<a href="/admin/table/${table.exportName}" hx-get="/admin/table/${table.exportName}" hx-target="#main-content" hx-push-url="true" hx-swap="outerHTML" class="sidebar-link flex items-center gap-2 px-3 py-2 text-sm rounded-lg w-full">
|
|
21
|
+
\t\t\t\t\t\t\t<iconify-icon icon="mdi:table" width="16" height="16" class="opacity-50 shrink-0"></iconify-icon>
|
|
22
|
+
\t\t\t\t\t\t\t<span class="truncate">${table.tableName}</span>
|
|
23
|
+
\t\t\t\t\t\t</a>
|
|
24
|
+
\t\t\t\t\t</li>`,
|
|
25
|
+
)
|
|
26
|
+
.join("\n\t\t\t\t\t");
|
|
27
|
+
|
|
28
|
+
return `<li data-name="users">
|
|
29
|
+
\t\t\t\t\t\t<a href="/admin/users" hx-get="/admin/users" hx-target="#main-content" hx-push-url="true" hx-swap="outerHTML" class="sidebar-link flex items-center gap-2 px-3 py-2 text-sm rounded-lg w-full">
|
|
30
|
+
\t\t\t\t\t\t\t<iconify-icon icon="mdi:account-group" width="16" height="16" class="opacity-50 shrink-0"></iconify-icon>
|
|
31
|
+
\t\t\t\t\t\t\t<span class="truncate">users</span>
|
|
32
|
+
\t\t\t\t\t\t</a>
|
|
33
|
+
\t\t\t\t\t</li>
|
|
34
|
+
\t\t\t\t\t${tableItems}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function renderTreeNode(node: TreeNode, depth: number): string {
|
|
38
|
+
const indent = " ".repeat(depth);
|
|
39
|
+
const hasChildren = node.children.length > 0;
|
|
40
|
+
const hasHandlers = node.handlers.length > 0;
|
|
41
|
+
const isExpandable = hasChildren || hasHandlers;
|
|
42
|
+
|
|
43
|
+
let html = "";
|
|
44
|
+
|
|
45
|
+
if (isExpandable) {
|
|
46
|
+
const folderId = `folder-${node.name.replace(/[^a-zA-Z0-9]/g, "-")}-${depth}`;
|
|
47
|
+
html += `\n${indent}<li data-name="${node.name}" class="folder-item">`;
|
|
48
|
+
html += `\n${indent} <details class="group/folder" open>`;
|
|
49
|
+
html += `\n${indent} <summary class="sidebar-link flex items-center gap-2 px-3 py-2 text-sm rounded-lg w-full cursor-pointer list-none">`;
|
|
50
|
+
html += `\n${indent} <iconify-icon icon="solar:folder-bold-duotone" width="16" height="16" class="opacity-50 shrink-0 transition-transform group-open/folder:rotate-0"></iconify-icon>`;
|
|
51
|
+
html += `\n${indent} <span class="truncate font-medium">${node.name}</span>`;
|
|
52
|
+
html += `\n${indent} </summary>`;
|
|
53
|
+
html += `\n${indent} <ul class="flex flex-col gap-0.5 ml-4 border-l border-base-200 pl-2">`;
|
|
54
|
+
|
|
55
|
+
for (const child of node.children) {
|
|
56
|
+
html += renderTreeNode(child, depth + 2);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const handler of node.handlers) {
|
|
60
|
+
const icon = handler.kind === "query" ? "solar:reorder-linear" : "solar:bolt-linear";
|
|
61
|
+
html += `\n${indent} <li data-name="${handler.exportName}">`;
|
|
62
|
+
html += `\n${indent} <a href="/admin/functions${handler.routePath}" hx-get="/admin/functions${handler.routePath}" hx-target="#main-content" hx-push-url="true" hx-swap="outerHTML" class="sidebar-link flex items-center gap-2 px-3 py-2 text-sm rounded-lg w-full">`;
|
|
63
|
+
html += `\n${indent} <iconify-icon icon="${icon}" width="16" height="16" class="opacity-50 shrink-0"></iconify-icon>`;
|
|
64
|
+
html += `\n${indent} <span class="truncate">${handler.exportName}</span>`;
|
|
65
|
+
html += `\n${indent} </a>`;
|
|
66
|
+
html += `\n${indent} </li>`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
html += `\n${indent} </ul>`;
|
|
70
|
+
html += `\n${indent} </details>`;
|
|
71
|
+
html += `\n${indent}</li>`;
|
|
72
|
+
} else {
|
|
73
|
+
for (const handler of node.handlers) {
|
|
74
|
+
const icon = handler.kind === "query" ? "solar:reorder-linear" : "solar:bolt-linear";
|
|
75
|
+
html += `\n${indent}<li data-name="${handler.exportName}">`;
|
|
76
|
+
html += `\n${indent} <a href="/admin/functions${handler.routePath}" hx-get="/admin/functions${handler.routePath}" hx-target="#main-content" hx-push-url="true" hx-swap="outerHTML" class="sidebar-link flex items-center gap-2 px-3 py-2 text-sm rounded-lg w-full">`;
|
|
77
|
+
html += `\n${indent} <iconify-icon icon="${icon}" width="16" height="16" class="opacity-50 shrink-0"></iconify-icon>`;
|
|
78
|
+
html += `\n${indent} <span class="truncate">${handler.exportName}</span>`;
|
|
79
|
+
html += `\n${indent} </a>`;
|
|
80
|
+
html += `\n${indent}</li>`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return html;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function buildSidebarFunctionList(
|
|
88
|
+
handlers: DiscoveredHandlerOperation[],
|
|
89
|
+
): string {
|
|
90
|
+
const queries = handlers.filter((h) => h.kind === "query");
|
|
91
|
+
const mutations = handlers.filter((h) => h.kind === "mutation");
|
|
92
|
+
|
|
93
|
+
const queryTree = buildFunctionTree(queries);
|
|
94
|
+
const mutationTree = buildFunctionTree(mutations);
|
|
95
|
+
|
|
96
|
+
const queryItems = queryTree.map((node) => renderTreeNode(node, 0)).join("");
|
|
97
|
+
const mutationItems = mutationTree.map((node) => renderTreeNode(node, 0)).join("");
|
|
98
|
+
|
|
99
|
+
return `
|
|
100
|
+
<div id="pane-functions" class="flex flex-col h-full hidden">
|
|
101
|
+
<div class="px-3 pt-5 pb-3">
|
|
102
|
+
<p class="text-[10px] font-semibold uppercase tracking-widest opacity-35 mb-3 px-1">Functions</p>
|
|
103
|
+
<div class="relative">
|
|
104
|
+
<iconify-icon icon="solar:magnifer-linear" width="13" height="13" class="absolute left-2.5 top-1/2 -translate-y-1/2 opacity-35 pointer-events-none"></iconify-icon>
|
|
105
|
+
<input
|
|
106
|
+
type="text"
|
|
107
|
+
id="function-search"
|
|
108
|
+
placeholder="Search functions..."
|
|
109
|
+
class="input input-sm border border-base-200 bg-base-200/50 focus:bg-base-100 focus:border-primary focus:outline-none w-full pl-7 text-xs rounded-lg h-8"
|
|
110
|
+
onkeyup="filterFunctions(this.value)"
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
<nav class="flex-1 overflow-y-auto px-2 pb-4">
|
|
115
|
+
${
|
|
116
|
+
queries.length > 0
|
|
117
|
+
? `<p class="text-[9px] font-bold uppercase tracking-wider opacity-25 mt-4 mb-1 px-2">Queries</p>
|
|
118
|
+
<ul class="flex flex-col gap-0.5">${queryItems}</ul>`
|
|
119
|
+
: ""
|
|
120
|
+
}
|
|
121
|
+
${
|
|
122
|
+
mutations.length > 0
|
|
123
|
+
? `<p class="text-[9px] font-bold uppercase tracking-wider opacity-25 mt-4 mb-1 px-2">Mutations</p>
|
|
124
|
+
<ul class="flex flex-col gap-0.5">${mutationItems}</ul>`
|
|
125
|
+
: ""
|
|
126
|
+
}
|
|
127
|
+
</nav>
|
|
128
|
+
</div>
|
|
129
|
+
`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function buildDashboardCards(tablesInfo: TableInfo[]): string {
|
|
133
|
+
return tablesInfo
|
|
134
|
+
.map((table) =>
|
|
135
|
+
`
|
|
136
|
+
<a
|
|
137
|
+
href="/admin/table/${table.exportName}"
|
|
138
|
+
class="card bg-base-100 border border-base-200 hover:border-primary/30 hover:shadow-md transition-all cursor-pointer group"
|
|
139
|
+
>
|
|
140
|
+
<div class="card-body p-5">
|
|
141
|
+
<div class="flex items-center gap-3">
|
|
142
|
+
<div class="w-9 h-9 rounded-lg bg-primary/10 flex items-center justify-center">
|
|
143
|
+
<iconify-icon icon="mdi:table" width="20" height="20" class="text-primary"></iconify-icon>
|
|
144
|
+
</div>
|
|
145
|
+
<div>
|
|
146
|
+
<h2 class="font-semibold text-sm capitalize group-hover:text-primary transition-colors">${table.tableName}</h2>
|
|
147
|
+
<p class="text-xs opacity-40 mt-0.5">Manage records</p>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
</a>
|
|
152
|
+
`.replace(/\n/g, "\\n"),
|
|
153
|
+
)
|
|
154
|
+
.join("");
|
|
155
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { buildStorageRuntimeHelpers } from "./runtime/helpers";
|
|
2
|
-
import { buildStoragePageRuntime } from "./runtime/storage-page";
|
|
3
|
-
import { buildStorageRouteHandlers } from "./routes";
|
|
4
|
-
|
|
5
|
-
export function buildStorageRoutes(): string {
|
|
6
|
-
return `
|
|
7
|
-
${buildStorageRuntimeHelpers()}
|
|
8
|
-
|
|
9
|
-
${buildStoragePageRuntime()}
|
|
10
|
-
|
|
11
|
-
${buildStorageRouteHandlers()}
|
|
12
|
-
`;
|
|
13
|
-
}
|
|
1
|
+
import { buildStorageRuntimeHelpers } from "./runtime/helpers";
|
|
2
|
+
import { buildStoragePageRuntime } from "./runtime/storage-page";
|
|
3
|
+
import { buildStorageRouteHandlers } from "./routes";
|
|
4
|
+
|
|
5
|
+
export function buildStorageRoutes(): string {
|
|
6
|
+
return `
|
|
7
|
+
${buildStorageRuntimeHelpers()}
|
|
8
|
+
|
|
9
|
+
${buildStoragePageRuntime()}
|
|
10
|
+
|
|
11
|
+
${buildStorageRouteHandlers()}
|
|
12
|
+
`;
|
|
13
|
+
}
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
export function buildStorageCreateDirectoryRoute(): string {
|
|
2
|
-
return `
|
|
3
|
-
adminApp.post('/storage/directory', async (c) => {
|
|
4
|
-
const bucket = getStorageBucket(c);
|
|
5
|
-
if (!bucket) return c.text("Storage not configured", 400);
|
|
6
|
-
|
|
7
|
-
const body = await c.req.parseBody();
|
|
8
|
-
const prefix = normalizePrefix((body['prefix'] as string) || '');
|
|
9
|
-
const directory = ((body['directory'] as string) || '').trim();
|
|
10
|
-
|
|
11
|
-
if (directory) {
|
|
12
|
-
const normalizedDirectory = directory
|
|
13
|
-
.split('/')
|
|
14
|
-
.map((part) => part.trim())
|
|
15
|
-
.filter(Boolean)
|
|
16
|
-
.join('/');
|
|
17
|
-
|
|
18
|
-
if (normalizedDirectory) {
|
|
19
|
-
const markerKey = prefix + normalizedDirectory + '/.keep';
|
|
20
|
-
await bucket.put(markerKey, '', {
|
|
21
|
-
httpMetadata: { contentType: 'text/plain; charset=utf-8' },
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return c.redirect(prefixToStoragePath(prefix));
|
|
27
|
-
});
|
|
28
|
-
`;
|
|
29
|
-
}
|
|
1
|
+
export function buildStorageCreateDirectoryRoute(): string {
|
|
2
|
+
return `
|
|
3
|
+
adminApp.post('/storage/directory', async (c) => {
|
|
4
|
+
const bucket = getStorageBucket(c);
|
|
5
|
+
if (!bucket) return c.text("Storage not configured", 400);
|
|
6
|
+
|
|
7
|
+
const body = await c.req.parseBody();
|
|
8
|
+
const prefix = normalizePrefix((body['prefix'] as string) || '');
|
|
9
|
+
const directory = ((body['directory'] as string) || '').trim();
|
|
10
|
+
|
|
11
|
+
if (directory) {
|
|
12
|
+
const normalizedDirectory = directory
|
|
13
|
+
.split('/')
|
|
14
|
+
.map((part) => part.trim())
|
|
15
|
+
.filter(Boolean)
|
|
16
|
+
.join('/');
|
|
17
|
+
|
|
18
|
+
if (normalizedDirectory) {
|
|
19
|
+
const markerKey = prefix + normalizedDirectory + '/.keep';
|
|
20
|
+
await bucket.put(markerKey, '', {
|
|
21
|
+
httpMetadata: { contentType: 'text/plain; charset=utf-8' },
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return c.redirect(prefixToStoragePath(prefix));
|
|
27
|
+
});
|
|
28
|
+
`;
|
|
29
|
+
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
export function buildStorageDeleteRoute(): string {
|
|
2
|
-
return `
|
|
3
|
-
adminApp.delete('/storage/delete', async (c) => {
|
|
4
|
-
const bucket = getStorageBucket(c);
|
|
5
|
-
if (!bucket) return c.text("Storage not configured", 400);
|
|
6
|
-
|
|
7
|
-
const key = c.req.query('key');
|
|
8
|
-
const prefix = normalizePrefix(c.req.query('prefix') || '');
|
|
9
|
-
|
|
10
|
-
if (key) {
|
|
11
|
-
await bucket.delete(key);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
c.header('HX-Redirect', prefixToStoragePath(prefix));
|
|
15
|
-
return c.html('');
|
|
16
|
-
});
|
|
17
|
-
`;
|
|
18
|
-
}
|
|
1
|
+
export function buildStorageDeleteRoute(): string {
|
|
2
|
+
return `
|
|
3
|
+
adminApp.delete('/storage/delete', async (c) => {
|
|
4
|
+
const bucket = getStorageBucket(c);
|
|
5
|
+
if (!bucket) return c.text("Storage not configured", 400);
|
|
6
|
+
|
|
7
|
+
const key = c.req.query('key');
|
|
8
|
+
const prefix = normalizePrefix(c.req.query('prefix') || '');
|
|
9
|
+
|
|
10
|
+
if (key) {
|
|
11
|
+
await bucket.delete(key);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
c.header('HX-Redirect', prefixToStoragePath(prefix));
|
|
15
|
+
return c.html('');
|
|
16
|
+
});
|
|
17
|
+
`;
|
|
18
|
+
}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
export function buildStorageDownloadRoute(): string {
|
|
2
|
-
return `
|
|
3
|
-
adminApp.get('/storage/download', async (c) => {
|
|
4
|
-
const bucket = getStorageBucket(c);
|
|
5
|
-
if (!bucket) return c.text("Storage not configured", 400);
|
|
6
|
-
|
|
7
|
-
const key = c.req.query('key');
|
|
8
|
-
if (!key) return c.text("Missing key", 400);
|
|
9
|
-
|
|
10
|
-
const object = await bucket.get(key);
|
|
11
|
-
if (!object) return c.text("Not found", 404);
|
|
12
|
-
|
|
13
|
-
const headers = new Headers();
|
|
14
|
-
object.writeHttpMetadata(headers);
|
|
15
|
-
headers.set('etag', object.httpEtag);
|
|
16
|
-
|
|
17
|
-
const filename = key.split('/').pop() || 'download';
|
|
18
|
-
headers.set('Content-Disposition', \`attachment; filename="\${filename}"\`);
|
|
19
|
-
|
|
20
|
-
return new Response(object.body, { headers });
|
|
21
|
-
});
|
|
22
|
-
`;
|
|
23
|
-
}
|
|
1
|
+
export function buildStorageDownloadRoute(): string {
|
|
2
|
+
return `
|
|
3
|
+
adminApp.get('/storage/download', async (c) => {
|
|
4
|
+
const bucket = getStorageBucket(c);
|
|
5
|
+
if (!bucket) return c.text("Storage not configured", 400);
|
|
6
|
+
|
|
7
|
+
const key = c.req.query('key');
|
|
8
|
+
if (!key) return c.text("Missing key", 400);
|
|
9
|
+
|
|
10
|
+
const object = await bucket.get(key);
|
|
11
|
+
if (!object) return c.text("Not found", 404);
|
|
12
|
+
|
|
13
|
+
const headers = new Headers();
|
|
14
|
+
object.writeHttpMetadata(headers);
|
|
15
|
+
headers.set('etag', object.httpEtag);
|
|
16
|
+
|
|
17
|
+
const filename = key.split('/').pop() || 'download';
|
|
18
|
+
headers.set('Content-Disposition', \`attachment; filename="\${filename}"\`);
|
|
19
|
+
|
|
20
|
+
return new Response(object.body, { headers });
|
|
21
|
+
});
|
|
22
|
+
`;
|
|
23
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { buildStorageListRoute } from "./list-route";
|
|
2
|
-
import { buildStorageUploadRoute } from "./upload-route";
|
|
3
|
-
import { buildStorageDeleteRoute } from "./delete-route";
|
|
4
|
-
import { buildStorageCreateDirectoryRoute } from "./create-directory-route";
|
|
5
|
-
import { buildStorageDownloadRoute } from "./download-route";
|
|
6
|
-
import { buildStoragePreviewRoute } from "./preview-route";
|
|
7
|
-
|
|
8
|
-
export function buildStorageRouteHandlers(): string {
|
|
9
|
-
return `
|
|
10
|
-
${buildStorageDownloadRoute()}
|
|
11
|
-
|
|
12
|
-
${buildStoragePreviewRoute()}
|
|
13
|
-
|
|
14
|
-
${buildStorageUploadRoute()}
|
|
15
|
-
|
|
16
|
-
${buildStorageDeleteRoute()}
|
|
17
|
-
|
|
18
|
-
${buildStorageCreateDirectoryRoute()}
|
|
19
|
-
|
|
20
|
-
${buildStorageListRoute()}
|
|
21
|
-
`;
|
|
22
|
-
}
|
|
1
|
+
import { buildStorageListRoute } from "./list-route";
|
|
2
|
+
import { buildStorageUploadRoute } from "./upload-route";
|
|
3
|
+
import { buildStorageDeleteRoute } from "./delete-route";
|
|
4
|
+
import { buildStorageCreateDirectoryRoute } from "./create-directory-route";
|
|
5
|
+
import { buildStorageDownloadRoute } from "./download-route";
|
|
6
|
+
import { buildStoragePreviewRoute } from "./preview-route";
|
|
7
|
+
|
|
8
|
+
export function buildStorageRouteHandlers(): string {
|
|
9
|
+
return `
|
|
10
|
+
${buildStorageDownloadRoute()}
|
|
11
|
+
|
|
12
|
+
${buildStoragePreviewRoute()}
|
|
13
|
+
|
|
14
|
+
${buildStorageUploadRoute()}
|
|
15
|
+
|
|
16
|
+
${buildStorageDeleteRoute()}
|
|
17
|
+
|
|
18
|
+
${buildStorageCreateDirectoryRoute()}
|
|
19
|
+
|
|
20
|
+
${buildStorageListRoute()}
|
|
21
|
+
`;
|
|
22
|
+
}
|