nuxt-auto-crud 1.10.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +47 -2
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +6 -2
  4. package/dist/runtime/auth.d.ts +6 -0
  5. package/dist/runtime/server/api/[model]/[id].delete.js +4 -20
  6. package/dist/runtime/server/api/[model]/[id].get.js +4 -20
  7. package/dist/runtime/server/api/[model]/[id].patch.js +4 -20
  8. package/dist/runtime/server/api/[model]/index.get.js +5 -23
  9. package/dist/runtime/server/api/[model]/index.post.js +5 -21
  10. package/dist/runtime/server/api/_relations.get.js +3 -21
  11. package/dist/runtime/server/api/_schema/[table].get.d.ts +1 -1
  12. package/dist/runtime/server/api/_schema/[table].get.js +2 -20
  13. package/dist/runtime/server/api/_schema/index.get.js +3 -21
  14. package/dist/runtime/server/utils/auth.d.ts +2 -0
  15. package/dist/runtime/server/utils/auth.js +29 -9
  16. package/dist/runtime/server/utils/handler.d.ts +3 -0
  17. package/dist/runtime/server/utils/handler.js +26 -0
  18. package/dist/runtime/server/utils/schema.d.ts +2 -2
  19. package/dist/runtime/server/utils/schema.js +20 -12
  20. package/package.json +1 -1
  21. package/src/runtime/auth.d.ts +6 -0
  22. package/src/runtime/server/api/[model]/[id].delete.ts +4 -28
  23. package/src/runtime/server/api/[model]/[id].get.ts +4 -27
  24. package/src/runtime/server/api/[model]/[id].patch.ts +6 -28
  25. package/src/runtime/server/api/[model]/index.get.ts +5 -31
  26. package/src/runtime/server/api/[model]/index.post.ts +5 -28
  27. package/src/runtime/server/api/_relations.get.ts +4 -27
  28. package/src/runtime/server/api/_schema/[table].get.ts +3 -25
  29. package/src/runtime/server/api/_schema/index.get.ts +4 -27
  30. package/src/runtime/server/utils/auth.ts +35 -9
  31. package/src/runtime/server/utils/handler.ts +36 -0
  32. package/src/runtime/server/utils/schema.ts +30 -17
  33. package/dist/runtime/server/plugins/seed.d.ts +0 -2
  34. package/dist/runtime/server/plugins/seed.js +0 -29
  35. package/src/runtime/server/plugins/seed.ts +0 -41
package/README.md CHANGED
@@ -57,10 +57,15 @@ If you want to add `nuxt-auto-crud` to an existing project, follow these steps:
57
57
  ```bash
58
58
  # Install module and required dependencies
59
59
  npm install nuxt-auto-crud @nuxthub/core@latest drizzle-orm
60
+
61
+ # Optional: Install auth dependencies if using Session Auth (Recommended)
62
+ npm install nuxt-auth-utils nuxt-authorization
63
+
60
64
  npm install --save-dev wrangler drizzle-kit
61
65
 
62
66
  # Or using bun
63
67
  bun add nuxt-auto-crud @nuxthub/core@latest drizzle-orm
68
+ bun add nuxt-auth-utils nuxt-authorization
64
69
  bun add --dev wrangler drizzle-kit
65
70
  ```
66
71
 
@@ -245,10 +250,19 @@ The module enables authentication by default. To test APIs without authenticatio
245
250
 
246
251
  ### Session Auth (Default)
247
252
 
248
- Requires `nuxt-auth-utils` and `nuxt-authorization`.
253
+ Requires `nuxt-auth-utils` and `nuxt-authorization` to be installed in your project.
254
+
255
+ ```bash
256
+ npm install nuxt-auth-utils nuxt-authorization
257
+ ```
249
258
 
250
259
  ```typescript
251
260
  export default defineNuxtConfig({
261
+ modules: [
262
+ 'nuxt-auth-utils',
263
+ 'nuxt-authorization',
264
+ 'nuxt-auto-crud'
265
+ ],
252
266
  autoCrud: {
253
267
  auth: {
254
268
  type: 'session',
@@ -261,7 +275,7 @@ export default defineNuxtConfig({
261
275
 
262
276
  ### JWT Auth
263
277
 
264
- Useful for backend-only apps.
278
+ Useful for backend-only apps. Does **not** require `nuxt-auth-utils`.
265
279
 
266
280
  ```typescript
267
281
  export default defineNuxtConfig({
@@ -276,6 +290,36 @@ export default defineNuxtConfig({
276
290
  })
277
291
  ```
278
292
 
293
+ ### Disabling Auth
294
+
295
+ You can disable authentication entirely for testing or public APIs.
296
+
297
+ ```typescript
298
+ export default defineNuxtConfig({
299
+ autoCrud: {
300
+ auth: {
301
+ authentication: false,
302
+ authorization: false
303
+ }
304
+ // OR simply:
305
+ // auth: false
306
+ }
307
+ })
308
+ ```
309
+
310
+ ## 🧪 Testing
311
+
312
+ This module is tested using **Vitest**.
313
+
314
+ - **Unit Tests:** We cover utility functions and helpers.
315
+ - **E2E Tests:** We verify the API endpoints using a real Nuxt server instance.
316
+
317
+ To run the tests locally:
318
+
319
+ ```bash
320
+ npm run test
321
+ ```
322
+
279
323
  ## 🛡️ Resource Configuration (RBAC)
280
324
 
281
325
  You can define fine-grained access control and resource policies using `autocrud.config.ts` in your project root. This file is optional and useful when you need specific rules per resource.
@@ -302,6 +346,7 @@ export default {
302
346
  ## ⚠️ Known Issues
303
347
 
304
348
  - **Foreign Key Naming:** Currently, if you have multiple foreign keys referring to the same table (e.g., `customer_id` and `author_id` both referring to the `users` table), the automatic relation handling might assume `user_id` for both. This is a known limitation in the current alpha version.
349
+ - **Sidebar Visibility:** For non-admin users, the sidebar resource list might not be fully visible or populated in the current playground implementation. This is a known limitation.
305
350
 
306
351
  ## 🎮 Try the Playground
307
352
 
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-auto-crud",
3
3
  "configKey": "autoCrud",
4
- "version": "1.10.0",
4
+ "version": "1.12.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { defineNuxtModule, createResolver, addServerHandler, addServerImportsDir, addImportsDir, addServerPlugin } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, addServerHandler, addServerImportsDir, addImportsDir } from '@nuxt/kit';
2
2
 
3
3
  const module$1 = defineNuxtModule({
4
4
  meta: {
@@ -22,6 +22,11 @@ const module$1 = defineNuxtModule({
22
22
  options.drizzlePath
23
23
  );
24
24
  nuxt.options.alias["#site/drizzle"] = drizzlePath;
25
+ const abilityPath = resolver.resolve(
26
+ nuxt.options.rootDir,
27
+ "server/utils/ability"
28
+ );
29
+ nuxt.options.alias["#site/ability"] = abilityPath;
25
30
  nuxt.options.alias["#authorization"] = nuxt.options.alias["#authorization"] || "nuxt-authorization/utils";
26
31
  const { loadConfig } = await import('c12');
27
32
  const { config: externalConfig } = await loadConfig({
@@ -116,7 +121,6 @@ const module$1 = defineNuxtModule({
116
121
  });
117
122
  addServerImportsDir(resolver.resolve("./runtime/server/utils"));
118
123
  addImportsDir(resolver.resolve("./runtime/composables"));
119
- addServerPlugin(resolver.resolve("./runtime/server/plugins/seed"));
120
124
  }
121
125
  });
122
126
 
@@ -0,0 +1,6 @@
1
+ declare module '#site/ability' {
2
+ import type { Ability } from 'nuxt-authorization/utils'
3
+
4
+ const ability: Ability
5
+ export default ability
6
+ }
@@ -1,23 +1,11 @@
1
1
  import { eventHandler, getRouterParams, createError } from "h3";
2
2
  import { eq } from "drizzle-orm";
3
- import { getTableForModel, getModelSingularName, filterHiddenFields, filterPublicColumns } from "../../utils/modelMapper.js";
3
+ import { getTableForModel, getModelSingularName } from "../../utils/modelMapper.js";
4
4
  import { useDrizzle } from "#site/drizzle";
5
- import { useAutoCrudConfig } from "../../utils/config.js";
6
- import { checkAdminAccess } from "../../utils/auth.js";
5
+ import { ensureResourceAccess, formatResourceResult } from "../../utils/handler.js";
7
6
  export default eventHandler(async (event) => {
8
- const { resources } = useAutoCrudConfig();
9
7
  const { model, id } = getRouterParams(event);
10
- const isAdmin = await checkAdminAccess(event, model, "delete");
11
- if (!isAdmin) {
12
- const resourceConfig = resources?.[model];
13
- const isPublic = resourceConfig?.public === true || Array.isArray(resourceConfig?.public) && resourceConfig.public.includes("delete");
14
- if (!isPublic) {
15
- throw createError({
16
- statusCode: 401,
17
- message: "Unauthorized"
18
- });
19
- }
20
- }
8
+ const isAdmin = await ensureResourceAccess(event, model, "delete");
21
9
  const table = getTableForModel(model);
22
10
  const singularName = getModelSingularName(model);
23
11
  const deletedRecord = await useDrizzle().delete(table).where(eq(table.id, Number(id))).returning().get();
@@ -27,9 +15,5 @@ export default eventHandler(async (event) => {
27
15
  message: `${singularName} not found`
28
16
  });
29
17
  }
30
- if (isAdmin) {
31
- return filterHiddenFields(model, deletedRecord);
32
- } else {
33
- return filterPublicColumns(model, deletedRecord);
34
- }
18
+ return formatResourceResult(model, deletedRecord, isAdmin);
35
19
  });
@@ -1,23 +1,11 @@
1
1
  import { eventHandler, getRouterParams, createError } from "h3";
2
2
  import { eq } from "drizzle-orm";
3
- import { getTableForModel, filterHiddenFields, filterPublicColumns } from "../../utils/modelMapper.js";
3
+ import { getTableForModel } from "../../utils/modelMapper.js";
4
4
  import { useDrizzle } from "#site/drizzle";
5
- import { useAutoCrudConfig } from "../../utils/config.js";
6
- import { checkAdminAccess } from "../../utils/auth.js";
5
+ import { ensureResourceAccess, formatResourceResult } from "../../utils/handler.js";
7
6
  export default eventHandler(async (event) => {
8
- const { resources } = useAutoCrudConfig();
9
7
  const { model, id } = getRouterParams(event);
10
- const isAdmin = await checkAdminAccess(event, model, "read");
11
- if (!isAdmin) {
12
- const resourceConfig = resources?.[model];
13
- const isPublic = resourceConfig?.public === true || Array.isArray(resourceConfig?.public) && resourceConfig.public.includes("read");
14
- if (!isPublic) {
15
- throw createError({
16
- statusCode: 401,
17
- message: "Unauthorized"
18
- });
19
- }
20
- }
8
+ const isAdmin = await ensureResourceAccess(event, model, "read");
21
9
  const table = getTableForModel(model);
22
10
  const record = await useDrizzle().select().from(table).where(eq(table.id, Number(id))).get();
23
11
  if (!record) {
@@ -26,9 +14,5 @@ export default eventHandler(async (event) => {
26
14
  message: "Record not found"
27
15
  });
28
16
  }
29
- if (isAdmin) {
30
- return filterHiddenFields(model, record);
31
- } else {
32
- return filterPublicColumns(model, record);
33
- }
17
+ return formatResourceResult(model, record, isAdmin);
34
18
  });
@@ -1,23 +1,11 @@
1
1
  import { eventHandler, getRouterParams, readBody, createError } from "h3";
2
2
  import { eq } from "drizzle-orm";
3
- import { getTableForModel, filterUpdatableFields, filterHiddenFields, filterPublicColumns } from "../../utils/modelMapper.js";
3
+ import { getTableForModel, filterUpdatableFields } from "../../utils/modelMapper.js";
4
4
  import { useDrizzle } from "#site/drizzle";
5
- import { useAutoCrudConfig } from "../../utils/config.js";
6
- import { checkAdminAccess } from "../../utils/auth.js";
5
+ import { ensureResourceAccess, formatResourceResult } from "../../utils/handler.js";
7
6
  export default eventHandler(async (event) => {
8
- const { resources } = useAutoCrudConfig();
9
7
  const { model, id } = getRouterParams(event);
10
- const isAdmin = await checkAdminAccess(event, model, "update");
11
- if (!isAdmin) {
12
- const resourceConfig = resources?.[model];
13
- const isPublic = resourceConfig?.public === true || Array.isArray(resourceConfig?.public) && resourceConfig.public.includes("update");
14
- if (!isPublic) {
15
- throw createError({
16
- statusCode: 401,
17
- message: "Unauthorized"
18
- });
19
- }
20
- }
8
+ const isAdmin = await ensureResourceAccess(event, model, "update");
21
9
  const table = getTableForModel(model);
22
10
  const body = await readBody(event);
23
11
  const payload = filterUpdatableFields(model, body);
@@ -31,9 +19,5 @@ export default eventHandler(async (event) => {
31
19
  message: "Record not found"
32
20
  });
33
21
  }
34
- if (isAdmin) {
35
- return filterHiddenFields(model, updatedRecord);
36
- } else {
37
- return filterPublicColumns(model, updatedRecord);
38
- }
22
+ return formatResourceResult(model, updatedRecord, isAdmin);
39
23
  });
@@ -1,31 +1,13 @@
1
- import { eventHandler, getRouterParams, createError } from "h3";
2
- import { getTableForModel, filterHiddenFields, filterPublicColumns } from "../../utils/modelMapper.js";
1
+ import { eventHandler, getRouterParams } from "h3";
2
+ import { getTableForModel } from "../../utils/modelMapper.js";
3
3
  import { useDrizzle } from "#site/drizzle";
4
- import { useAutoCrudConfig } from "../../utils/config.js";
5
- import { checkAdminAccess } from "../../utils/auth.js";
6
4
  import { desc } from "drizzle-orm";
5
+ import { ensureResourceAccess, formatResourceResult } from "../../utils/handler.js";
7
6
  export default eventHandler(async (event) => {
8
7
  console.log("[GET] Request received", event.path);
9
- const { resources } = useAutoCrudConfig();
10
8
  const { model } = getRouterParams(event);
11
- const isAdmin = await checkAdminAccess(event, model, "list");
12
- if (!isAdmin) {
13
- const resourceConfig = resources?.[model];
14
- const isPublic = resourceConfig?.public === true || Array.isArray(resourceConfig?.public) && resourceConfig.public.includes("list");
15
- if (!isPublic) {
16
- throw createError({
17
- statusCode: 401,
18
- message: "Unauthorized"
19
- });
20
- }
21
- }
9
+ const isAdmin = await ensureResourceAccess(event, model, "list");
22
10
  const table = getTableForModel(model);
23
11
  const results = await useDrizzle().select().from(table).orderBy(desc(table.id)).all();
24
- return results.map((item) => {
25
- if (isAdmin) {
26
- return filterHiddenFields(model, item);
27
- } else {
28
- return filterPublicColumns(model, item);
29
- }
30
- });
12
+ return results.map((item) => formatResourceResult(model, item, isAdmin));
31
13
  });
@@ -1,29 +1,13 @@
1
- import { eventHandler, getRouterParams, readBody, createError } from "h3";
2
- import { getTableForModel, filterHiddenFields, filterUpdatableFields, filterPublicColumns } from "../../utils/modelMapper.js";
1
+ import { eventHandler, getRouterParams, readBody } from "h3";
2
+ import { getTableForModel, filterUpdatableFields } from "../../utils/modelMapper.js";
3
3
  import { useDrizzle } from "#site/drizzle";
4
- import { useAutoCrudConfig } from "../../utils/config.js";
5
- import { checkAdminAccess } from "../../utils/auth.js";
4
+ import { ensureResourceAccess, formatResourceResult } from "../../utils/handler.js";
6
5
  export default eventHandler(async (event) => {
7
- const { resources } = useAutoCrudConfig();
8
6
  const { model } = getRouterParams(event);
9
- const isAdmin = await checkAdminAccess(event, model, "create");
10
- if (!isAdmin) {
11
- const resourceConfig = resources?.[model];
12
- const isPublic = resourceConfig?.public === true || Array.isArray(resourceConfig?.public) && resourceConfig.public.includes("create");
13
- if (!isPublic) {
14
- throw createError({
15
- statusCode: 401,
16
- message: "Unauthorized"
17
- });
18
- }
19
- }
7
+ const isAdmin = await ensureResourceAccess(event, model, "create");
20
8
  const table = getTableForModel(model);
21
9
  const body = await readBody(event);
22
10
  const payload = filterUpdatableFields(model, body);
23
11
  const newRecord = await useDrizzle().insert(table).values(payload).returning().get();
24
- if (isAdmin) {
25
- return filterHiddenFields(model, newRecord);
26
- } else {
27
- return filterPublicColumns(model, newRecord);
28
- }
12
+ return formatResourceResult(model, newRecord, isAdmin);
29
13
  });
@@ -1,25 +1,7 @@
1
- import { eventHandler, createError } from "h3";
2
- import { requireUserSession } from "#imports";
1
+ import { eventHandler } from "h3";
3
2
  import { getRelations } from "../utils/schema.js";
4
- import { useAutoCrudConfig } from "../utils/config.js";
5
- import { verifyJwtToken } from "../utils/jwt.js";
3
+ import { ensureAuthenticated } from "../utils/auth.js";
6
4
  export default eventHandler(async (event) => {
7
- const { auth } = useAutoCrudConfig();
8
- if (auth?.authentication) {
9
- let isAuthenticated = false;
10
- if (auth.type === "jwt" && auth.jwtSecret) {
11
- isAuthenticated = await verifyJwtToken(event, auth.jwtSecret);
12
- } else {
13
- try {
14
- await requireUserSession(event);
15
- isAuthenticated = true;
16
- } catch {
17
- isAuthenticated = false;
18
- }
19
- }
20
- if (!isAuthenticated) {
21
- throw createError({ statusCode: 401, message: "Unauthorized" });
22
- }
23
- }
5
+ await ensureAuthenticated(event);
24
6
  return getRelations();
25
7
  });
@@ -4,7 +4,7 @@ declare const _default: import("h3").EventHandler<import("h3").EventHandlerReque
4
4
  name: string;
5
5
  type: string;
6
6
  required: any;
7
- selectOptions: undefined;
7
+ selectOptions: string[] | undefined;
8
8
  }[];
9
9
  }>>;
10
10
  export default _default;
@@ -1,27 +1,9 @@
1
1
  import { eventHandler, createError, getRouterParam } from "h3";
2
- import { requireUserSession } from "#imports";
3
2
  import { getSchema } from "../../utils/schema.js";
4
- import { useAutoCrudConfig } from "../../utils/config.js";
5
- import { verifyJwtToken } from "../../utils/jwt.js";
3
+ import { ensureAuthenticated } from "../../utils/auth.js";
6
4
  export default eventHandler(async (event) => {
7
- const { auth } = useAutoCrudConfig();
5
+ await ensureAuthenticated(event);
8
6
  const tableName = getRouterParam(event, "table");
9
- if (auth?.authentication) {
10
- let isAuthenticated = false;
11
- if (auth.type === "jwt" && auth.jwtSecret) {
12
- isAuthenticated = await verifyJwtToken(event, auth.jwtSecret);
13
- } else {
14
- try {
15
- await requireUserSession(event);
16
- isAuthenticated = true;
17
- } catch {
18
- isAuthenticated = false;
19
- }
20
- }
21
- if (!isAuthenticated) {
22
- throw createError({ statusCode: 401, message: "Unauthorized" });
23
- }
24
- }
25
7
  if (!tableName) {
26
8
  throw createError({ statusCode: 400, message: "Table name is required" });
27
9
  }
@@ -1,25 +1,7 @@
1
- import { eventHandler, createError } from "h3";
2
- import { requireUserSession } from "#imports";
1
+ import { eventHandler } from "h3";
3
2
  import { getAllSchemas } from "../../utils/schema.js";
4
- import { useAutoCrudConfig } from "../../utils/config.js";
5
- import { verifyJwtToken } from "../../utils/jwt.js";
3
+ import { ensureAuthenticated } from "../../utils/auth.js";
6
4
  export default eventHandler(async (event) => {
7
- const { auth } = useAutoCrudConfig();
8
- if (auth?.authentication) {
9
- let isAuthenticated = false;
10
- if (auth.type === "jwt" && auth.jwtSecret) {
11
- isAuthenticated = await verifyJwtToken(event, auth.jwtSecret);
12
- } else {
13
- try {
14
- await requireUserSession(event);
15
- isAuthenticated = true;
16
- } catch {
17
- isAuthenticated = false;
18
- }
19
- }
20
- if (!isAuthenticated) {
21
- throw createError({ statusCode: 401, message: "Unauthorized" });
22
- }
23
- }
5
+ await ensureAuthenticated(event);
24
6
  return getAllSchemas();
25
7
  });
@@ -1,2 +1,4 @@
1
+ import '../../auth.d.ts.js';
1
2
  import type { H3Event } from 'h3';
2
3
  export declare function checkAdminAccess(event: H3Event, model: string, action: string): Promise<boolean>;
4
+ export declare function ensureAuthenticated(event: H3Event): Promise<void>;
@@ -1,5 +1,7 @@
1
+ import "../../auth.d.ts";
1
2
  import { createError } from "h3";
2
- import { requireUserSession } from "#imports";
3
+ import globalAbility from "#site/ability";
4
+ import { requireUserSession, allows } from "#imports";
3
5
  import { useAutoCrudConfig } from "./config.js";
4
6
  import { verifyJwtToken } from "./jwt.js";
5
7
  export async function checkAdminAccess(event, model, action) {
@@ -17,14 +19,12 @@ export async function checkAdminAccess(event, model, action) {
17
19
  try {
18
20
  await requireUserSession(event);
19
21
  if (auth.authorization) {
20
- if (event.context.ability) {
21
- const can = event.context.ability.can(action, model);
22
- if (!can) {
23
- throw createError({
24
- statusCode: 403,
25
- message: "Forbidden"
26
- });
27
- }
22
+ const allowed = await allows(event, globalAbility, model, action);
23
+ if (!allowed) {
24
+ throw createError({
25
+ statusCode: 403,
26
+ message: "Forbidden"
27
+ });
28
28
  }
29
29
  }
30
30
  return true;
@@ -35,3 +35,23 @@ export async function checkAdminAccess(event, model, action) {
35
35
  return false;
36
36
  }
37
37
  }
38
+ export async function ensureAuthenticated(event) {
39
+ const { auth } = useAutoCrudConfig();
40
+ if (!auth?.authentication) {
41
+ return;
42
+ }
43
+ let isAuthenticated = false;
44
+ if (auth.type === "jwt" && auth.jwtSecret) {
45
+ isAuthenticated = await verifyJwtToken(event, auth.jwtSecret);
46
+ } else {
47
+ try {
48
+ await requireUserSession(event);
49
+ isAuthenticated = true;
50
+ } catch {
51
+ isAuthenticated = false;
52
+ }
53
+ }
54
+ if (!isAuthenticated) {
55
+ throw createError({ statusCode: 401, message: "Unauthorized" });
56
+ }
57
+ }
@@ -0,0 +1,3 @@
1
+ import type { H3Event } from 'h3';
2
+ export declare function ensureResourceAccess(event: H3Event, model: string, action: string): Promise<boolean>;
3
+ export declare function formatResourceResult(model: string, data: Record<string, unknown>, isAdmin: boolean): Record<string, unknown>;
@@ -0,0 +1,26 @@
1
+ import { createError } from "h3";
2
+ import { useAutoCrudConfig } from "./config.js";
3
+ import { checkAdminAccess } from "./auth.js";
4
+ import { filterHiddenFields, filterPublicColumns } from "./modelMapper.js";
5
+ export async function ensureResourceAccess(event, model, action) {
6
+ const { resources } = useAutoCrudConfig();
7
+ const isAdmin = await checkAdminAccess(event, model, action);
8
+ if (!isAdmin) {
9
+ const resourceConfig = resources?.[model];
10
+ const isPublic = resourceConfig?.public === true || Array.isArray(resourceConfig?.public) && resourceConfig.public.includes(action);
11
+ if (!isPublic) {
12
+ throw createError({
13
+ statusCode: 401,
14
+ message: "Unauthorized"
15
+ });
16
+ }
17
+ }
18
+ return isAdmin;
19
+ }
20
+ export function formatResourceResult(model, data, isAdmin) {
21
+ if (isAdmin) {
22
+ return filterHiddenFields(model, data);
23
+ } else {
24
+ return filterPublicColumns(model, data);
25
+ }
26
+ }
@@ -4,7 +4,7 @@ export declare function drizzleTableToFields(table: any, resourceName: string):
4
4
  name: string;
5
5
  type: string;
6
6
  required: any;
7
- selectOptions: undefined;
7
+ selectOptions: string[] | undefined;
8
8
  }[];
9
9
  };
10
10
  export declare function getRelations(): Promise<Record<string, Record<string, string>>>;
@@ -15,6 +15,6 @@ export declare function getSchema(tableName: string): Promise<{
15
15
  name: string;
16
16
  type: string;
17
17
  required: any;
18
- selectOptions: undefined;
18
+ selectOptions: string[] | undefined;
19
19
  }[];
20
20
  } | undefined>;
@@ -7,18 +7,7 @@ export function drizzleTableToFields(table, resourceName) {
7
7
  for (const [key, col] of Object.entries(columns)) {
8
8
  const column = col;
9
9
  const isRequired = column.notNull;
10
- let type = "string";
11
- const selectOptions = void 0;
12
- if (column.dataType === "number" || column.columnType === "SQLiteInteger" || column.columnType === "SQLiteReal") {
13
- type = "number";
14
- if (column.name.endsWith("_at") || column.name.endsWith("At")) {
15
- type = "date";
16
- }
17
- } else if (column.dataType === "boolean") {
18
- type = "boolean";
19
- } else if (column.dataType === "date" || column.dataType === "string" && (column.name.endsWith("_at") || column.name.endsWith("At"))) {
20
- type = "date";
21
- }
10
+ const { type, selectOptions } = mapColumnType(column);
22
11
  fields.push({
23
12
  name: key,
24
13
  type,
@@ -31,6 +20,25 @@ export function drizzleTableToFields(table, resourceName) {
31
20
  fields
32
21
  };
33
22
  }
23
+ function mapColumnType(column) {
24
+ const enumValues = column.enumValues || column.config?.enumValues;
25
+ if (enumValues) {
26
+ return { type: "enum", selectOptions: enumValues };
27
+ }
28
+ if (column.dataType === "boolean") {
29
+ return { type: "boolean" };
30
+ }
31
+ if (column.dataType === "date" || column.dataType === "string" && (column.name.endsWith("_at") || column.name.endsWith("At"))) {
32
+ return { type: "date" };
33
+ }
34
+ if (column.dataType === "number" || column.columnType === "SQLiteInteger" || column.columnType === "SQLiteReal") {
35
+ if (column.name.endsWith("_at") || column.name.endsWith("At")) {
36
+ return { type: "date" };
37
+ }
38
+ return { type: "number" };
39
+ }
40
+ return { type: "string" };
41
+ }
34
42
  export async function getRelations() {
35
43
  const relations = {};
36
44
  for (const [tableName, table] of Object.entries(modelTableMap)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-auto-crud",
3
- "version": "1.10.0",
3
+ "version": "1.12.0",
4
4
  "description": "Exposes RESTful CRUD APIs for your Nuxt app based solely on your database migrations.",
5
5
  "author": "Cliford Pereira",
6
6
  "license": "MIT",
@@ -0,0 +1,6 @@
1
+ declare module '#site/ability' {
2
+ import type { Ability } from 'nuxt-authorization/utils'
3
+
4
+ const ability: Ability
5
+ export default ability
6
+ }
@@ -1,36 +1,17 @@
1
1
  // server/api/[model]/[id].delete.ts
2
2
  import { eventHandler, getRouterParams, createError } from 'h3'
3
3
  import { eq } from 'drizzle-orm'
4
- import { getTableForModel, getModelSingularName, filterHiddenFields, filterPublicColumns } from '../../utils/modelMapper'
5
-
4
+ import { getTableForModel, getModelSingularName } from '../../utils/modelMapper'
6
5
  import type { TableWithId } from '../../types'
7
6
  // @ts-expect-error - #site/drizzle is an alias defined by the module
8
7
  import { useDrizzle } from '#site/drizzle'
9
-
10
- import { useAutoCrudConfig } from '../../utils/config'
11
- import { checkAdminAccess } from '../../utils/auth'
8
+ import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
12
9
 
13
10
  export default eventHandler(async (event) => {
14
- const { resources } = useAutoCrudConfig()
15
11
  const { model, id } = getRouterParams(event) as { model: string, id: string }
16
-
17
- const isAdmin = await checkAdminAccess(event, model, 'delete')
18
-
19
- // Check public access if not admin
20
- if (!isAdmin) {
21
- const resourceConfig = resources?.[model]
22
- const isPublic = resourceConfig?.public === true || (Array.isArray(resourceConfig?.public) && resourceConfig.public.includes('delete'))
23
-
24
- if (!isPublic) {
25
- throw createError({
26
- statusCode: 401,
27
- message: 'Unauthorized',
28
- })
29
- }
30
- }
12
+ const isAdmin = await ensureResourceAccess(event, model, 'delete')
31
13
 
32
14
  const table = getTableForModel(model) as TableWithId
33
-
34
15
  const singularName = getModelSingularName(model)
35
16
 
36
17
  const deletedRecord = await useDrizzle()
@@ -46,10 +27,5 @@ export default eventHandler(async (event) => {
46
27
  })
47
28
  }
48
29
 
49
- if (isAdmin) {
50
- return filterHiddenFields(model, deletedRecord as Record<string, unknown>)
51
- }
52
- else {
53
- return filterPublicColumns(model, deletedRecord as Record<string, unknown>)
54
- }
30
+ return formatResourceResult(model, deletedRecord as Record<string, unknown>, isAdmin)
55
31
  })
@@ -1,33 +1,15 @@
1
1
  // server/api/[model]/[id].get.ts
2
2
  import { eventHandler, getRouterParams, createError } from 'h3'
3
3
  import { eq } from 'drizzle-orm'
4
- import { getTableForModel, filterHiddenFields, filterPublicColumns } from '../../utils/modelMapper'
5
-
4
+ import { getTableForModel } from '../../utils/modelMapper'
6
5
  import type { TableWithId } from '../../types'
7
6
  // @ts-expect-error - #site/drizzle is an alias defined by the module
8
7
  import { useDrizzle } from '#site/drizzle'
9
-
10
- import { useAutoCrudConfig } from '../../utils/config'
11
- import { checkAdminAccess } from '../../utils/auth'
8
+ import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
12
9
 
13
10
  export default eventHandler(async (event) => {
14
- const { resources } = useAutoCrudConfig()
15
11
  const { model, id } = getRouterParams(event) as { model: string, id: string }
16
-
17
- const isAdmin = await checkAdminAccess(event, model, 'read')
18
-
19
- // Check public access if not admin
20
- if (!isAdmin) {
21
- const resourceConfig = resources?.[model]
22
- const isPublic = resourceConfig?.public === true || (Array.isArray(resourceConfig?.public) && resourceConfig.public.includes('read'))
23
-
24
- if (!isPublic) {
25
- throw createError({
26
- statusCode: 401,
27
- message: 'Unauthorized',
28
- })
29
- }
30
- }
12
+ const isAdmin = await ensureResourceAccess(event, model, 'read')
31
13
 
32
14
  const table = getTableForModel(model) as TableWithId
33
15
 
@@ -44,10 +26,5 @@ export default eventHandler(async (event) => {
44
26
  })
45
27
  }
46
28
 
47
- if (isAdmin) {
48
- return filterHiddenFields(model, record as Record<string, unknown>)
49
- }
50
- else {
51
- return filterPublicColumns(model, record as Record<string, unknown>)
52
- }
29
+ return formatResourceResult(model, record as Record<string, unknown>, isAdmin)
53
30
  })
@@ -1,33 +1,15 @@
1
1
  // server/api/[model]/[id].patch.ts
2
2
  import { eventHandler, getRouterParams, readBody, createError } from 'h3'
3
3
  import { eq } from 'drizzle-orm'
4
- import { getTableForModel, filterUpdatableFields, filterHiddenFields, filterPublicColumns } from '../../utils/modelMapper'
5
-
4
+ import { getTableForModel, filterUpdatableFields } from '../../utils/modelMapper'
6
5
  import type { TableWithId } from '../../types'
7
6
  // @ts-expect-error - #site/drizzle is an alias defined by the module
8
7
  import { useDrizzle } from '#site/drizzle'
9
-
10
- import { useAutoCrudConfig } from '../../utils/config'
11
- import { checkAdminAccess } from '../../utils/auth'
8
+ import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
12
9
 
13
10
  export default eventHandler(async (event) => {
14
- const { resources } = useAutoCrudConfig()
15
11
  const { model, id } = getRouterParams(event) as { model: string, id: string }
16
-
17
- const isAdmin = await checkAdminAccess(event, model, 'update')
18
-
19
- // Check public access if not admin
20
- if (!isAdmin) {
21
- const resourceConfig = resources?.[model]
22
- const isPublic = resourceConfig?.public === true || (Array.isArray(resourceConfig?.public) && resourceConfig.public.includes('update'))
23
-
24
- if (!isPublic) {
25
- throw createError({
26
- statusCode: 401,
27
- message: 'Unauthorized',
28
- })
29
- }
30
- }
12
+ const isAdmin = await ensureResourceAccess(event, model, 'update')
31
13
 
32
14
  const table = getTableForModel(model) as TableWithId
33
15
 
@@ -36,7 +18,8 @@ export default eventHandler(async (event) => {
36
18
 
37
19
  // Automatically update updatedAt if it exists
38
20
  if ('updatedAt' in table) {
39
- payload.updatedAt = new Date()
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ (payload as any).updatedAt = new Date()
40
23
  }
41
24
 
42
25
  const updatedRecord = await useDrizzle()
@@ -53,10 +36,5 @@ export default eventHandler(async (event) => {
53
36
  })
54
37
  }
55
38
 
56
- if (isAdmin) {
57
- return filterHiddenFields(model, updatedRecord as Record<string, unknown>)
58
- }
59
- else {
60
- return filterPublicColumns(model, updatedRecord as Record<string, unknown>)
61
- }
39
+ return formatResourceResult(model, updatedRecord as Record<string, unknown>, isAdmin)
62
40
  })
@@ -1,46 +1,20 @@
1
1
  // server/api/[model]/index.get.ts
2
- import { eventHandler, getRouterParams, createError } from 'h3'
3
- import { getTableForModel, filterHiddenFields, filterPublicColumns } from '../../utils/modelMapper'
4
-
2
+ import { eventHandler, getRouterParams } from 'h3'
3
+ import { getTableForModel } from '../../utils/modelMapper'
5
4
  // @ts-expect-error - #site/drizzle is an alias defined by the module
6
5
  import { useDrizzle } from '#site/drizzle'
7
-
8
- import { useAutoCrudConfig } from '../../utils/config'
9
- import { checkAdminAccess } from '../../utils/auth'
10
-
11
6
  import { desc } from 'drizzle-orm'
12
7
  import type { TableWithId } from '../../types'
8
+ import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
13
9
 
14
10
  export default eventHandler(async (event) => {
15
11
  console.log('[GET] Request received', event.path)
16
- const { resources } = useAutoCrudConfig()
17
12
  const { model } = getRouterParams(event) as { model: string }
18
-
19
- const isAdmin = await checkAdminAccess(event, model, 'list')
20
-
21
- // Check public access if not admin
22
- if (!isAdmin) {
23
- const resourceConfig = resources?.[model]
24
- const isPublic = resourceConfig?.public === true || (Array.isArray(resourceConfig?.public) && resourceConfig.public.includes('list'))
25
-
26
- if (!isPublic) {
27
- throw createError({
28
- statusCode: 401,
29
- message: 'Unauthorized',
30
- })
31
- }
32
- }
13
+ const isAdmin = await ensureResourceAccess(event, model, 'list')
33
14
 
34
15
  const table = getTableForModel(model) as TableWithId
35
16
 
36
17
  const results = await useDrizzle().select().from(table).orderBy(desc(table.id)).all()
37
18
 
38
- return results.map((item: Record<string, unknown>) => {
39
- if (isAdmin) {
40
- return filterHiddenFields(model, item as Record<string, unknown>)
41
- }
42
- else {
43
- return filterPublicColumns(model, item as Record<string, unknown>)
44
- }
45
- })
19
+ return results.map((item: Record<string, unknown>) => formatResourceResult(model, item, isAdmin))
46
20
  })
@@ -1,31 +1,13 @@
1
1
  // server/api/[model]/index.post.ts
2
- import { eventHandler, getRouterParams, readBody, createError } from 'h3'
3
- import { getTableForModel, filterHiddenFields, filterUpdatableFields, filterPublicColumns } from '../../utils/modelMapper'
4
-
2
+ import { eventHandler, getRouterParams, readBody } from 'h3'
3
+ import { getTableForModel, filterUpdatableFields } from '../../utils/modelMapper'
5
4
  // @ts-expect-error - #site/drizzle is an alias defined by the module
6
5
  import { useDrizzle } from '#site/drizzle'
7
-
8
- import { useAutoCrudConfig } from '../../utils/config'
9
- import { checkAdminAccess } from '../../utils/auth'
6
+ import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
10
7
 
11
8
  export default eventHandler(async (event) => {
12
- const { resources } = useAutoCrudConfig()
13
9
  const { model } = getRouterParams(event) as { model: string }
14
-
15
- const isAdmin = await checkAdminAccess(event, model, 'create')
16
-
17
- // Check public access if not admin
18
- if (!isAdmin) {
19
- const resourceConfig = resources?.[model]
20
- const isPublic = resourceConfig?.public === true || (Array.isArray(resourceConfig?.public) && resourceConfig.public.includes('create'))
21
-
22
- if (!isPublic) {
23
- throw createError({
24
- statusCode: 401,
25
- message: 'Unauthorized',
26
- })
27
- }
28
- }
10
+ const isAdmin = await ensureResourceAccess(event, model, 'create')
29
11
 
30
12
  const table = getTableForModel(model)
31
13
 
@@ -34,10 +16,5 @@ export default eventHandler(async (event) => {
34
16
 
35
17
  const newRecord = await useDrizzle().insert(table).values(payload).returning().get()
36
18
 
37
- if (isAdmin) {
38
- return filterHiddenFields(model, newRecord as Record<string, unknown>)
39
- }
40
- else {
41
- return filterPublicColumns(model, newRecord as Record<string, unknown>)
42
- }
19
+ return formatResourceResult(model, newRecord as Record<string, unknown>, isAdmin)
43
20
  })
@@ -1,32 +1,9 @@
1
- import { eventHandler, createError } from 'h3'
2
- // @ts-expect-error - #imports is available in runtime
3
- import { requireUserSession } from '#imports'
1
+ import { eventHandler } from 'h3'
2
+
4
3
  import { getRelations } from '../utils/schema'
5
- import { useAutoCrudConfig } from '../utils/config'
6
- import { verifyJwtToken } from '../utils/jwt'
4
+ import { ensureAuthenticated } from '../utils/auth'
7
5
 
8
6
  export default eventHandler(async (event) => {
9
- const { auth } = useAutoCrudConfig()
10
-
11
- if (auth?.authentication) {
12
- let isAuthenticated = false
13
- if (auth.type === 'jwt' && auth.jwtSecret) {
14
- isAuthenticated = await verifyJwtToken(event, auth.jwtSecret)
15
- }
16
- else {
17
- try {
18
- await requireUserSession(event)
19
- isAuthenticated = true
20
- }
21
- catch {
22
- isAuthenticated = false
23
- }
24
- }
25
-
26
- if (!isAuthenticated) {
27
- throw createError({ statusCode: 401, message: 'Unauthorized' })
28
- }
29
- }
30
-
7
+ await ensureAuthenticated(event)
31
8
  return getRelations()
32
9
  })
@@ -1,34 +1,12 @@
1
1
  import { eventHandler, createError, getRouterParam } from 'h3'
2
- // @ts-expect-error - #imports is available in runtime
3
- import { requireUserSession } from '#imports'
2
+
4
3
  import { getSchema } from '../../utils/schema'
5
- import { useAutoCrudConfig } from '../../utils/config'
6
- import { verifyJwtToken } from '../../utils/jwt'
4
+ import { ensureAuthenticated } from '../../utils/auth'
7
5
 
8
6
  export default eventHandler(async (event) => {
9
- const { auth } = useAutoCrudConfig()
7
+ await ensureAuthenticated(event)
10
8
  const tableName = getRouterParam(event, 'table')
11
9
 
12
- if (auth?.authentication) {
13
- let isAuthenticated = false
14
- if (auth.type === 'jwt' && auth.jwtSecret) {
15
- isAuthenticated = await verifyJwtToken(event, auth.jwtSecret)
16
- }
17
- else {
18
- try {
19
- await requireUserSession(event)
20
- isAuthenticated = true
21
- }
22
- catch {
23
- isAuthenticated = false
24
- }
25
- }
26
-
27
- if (!isAuthenticated) {
28
- throw createError({ statusCode: 401, message: 'Unauthorized' })
29
- }
30
- }
31
-
32
10
  if (!tableName) {
33
11
  throw createError({ statusCode: 400, message: 'Table name is required' })
34
12
  }
@@ -1,32 +1,9 @@
1
- import { eventHandler, createError } from 'h3'
2
- // @ts-expect-error - #imports is available in runtime
3
- import { requireUserSession } from '#imports'
1
+ import { eventHandler } from 'h3'
2
+
4
3
  import { getAllSchemas } from '../../utils/schema'
5
- import { useAutoCrudConfig } from '../../utils/config'
6
- import { verifyJwtToken } from '../../utils/jwt'
4
+ import { ensureAuthenticated } from '../../utils/auth'
7
5
 
8
6
  export default eventHandler(async (event) => {
9
- const { auth } = useAutoCrudConfig()
10
-
11
- if (auth?.authentication) {
12
- let isAuthenticated = false
13
- if (auth.type === 'jwt' && auth.jwtSecret) {
14
- isAuthenticated = await verifyJwtToken(event, auth.jwtSecret)
15
- }
16
- else {
17
- try {
18
- await requireUserSession(event)
19
- isAuthenticated = true
20
- }
21
- catch {
22
- isAuthenticated = false
23
- }
24
- }
25
-
26
- if (!isAuthenticated) {
27
- throw createError({ statusCode: 401, message: 'Unauthorized' })
28
- }
29
- }
30
-
7
+ await ensureAuthenticated(event)
31
8
  return getAllSchemas()
32
9
  })
@@ -1,7 +1,9 @@
1
+ import '../../auth.d.ts'
1
2
  import type { H3Event } from 'h3'
2
3
  import { createError } from 'h3'
4
+ import globalAbility from '#site/ability'
3
5
  // @ts-expect-error - #imports is available in runtime
4
- import { requireUserSession } from '#imports'
6
+ import { requireUserSession, allows } from '#imports'
5
7
  import { useAutoCrudConfig } from './config'
6
8
  import { verifyJwtToken } from './jwt'
7
9
 
@@ -26,14 +28,12 @@ export async function checkAdminAccess(event: H3Event, model: string, action: st
26
28
 
27
29
  // Check authorization if enabled
28
30
  if (auth.authorization) {
29
- if (event.context.ability) {
30
- const can = event.context.ability.can(action, model)
31
- if (!can) {
32
- throw createError({
33
- statusCode: 403,
34
- message: 'Forbidden',
35
- })
36
- }
31
+ const allowed = await allows(event, globalAbility, model, action)
32
+ if (!allowed) {
33
+ throw createError({
34
+ statusCode: 403,
35
+ message: 'Forbidden',
36
+ })
37
37
  }
38
38
  }
39
39
 
@@ -49,3 +49,29 @@ export async function checkAdminAccess(event: H3Event, model: string, action: st
49
49
  return false
50
50
  }
51
51
  }
52
+
53
+ export async function ensureAuthenticated(event: H3Event): Promise<void> {
54
+ const { auth } = useAutoCrudConfig()
55
+
56
+ if (!auth?.authentication) {
57
+ return
58
+ }
59
+
60
+ let isAuthenticated = false
61
+ if (auth.type === 'jwt' && auth.jwtSecret) {
62
+ isAuthenticated = await verifyJwtToken(event, auth.jwtSecret)
63
+ }
64
+ else {
65
+ try {
66
+ await requireUserSession(event)
67
+ isAuthenticated = true
68
+ }
69
+ catch {
70
+ isAuthenticated = false
71
+ }
72
+ }
73
+
74
+ if (!isAuthenticated) {
75
+ throw createError({ statusCode: 401, message: 'Unauthorized' })
76
+ }
77
+ }
@@ -0,0 +1,36 @@
1
+ import { createError } from 'h3'
2
+ import type { H3Event } from 'h3'
3
+ import { useAutoCrudConfig } from './config'
4
+ import { checkAdminAccess } from './auth'
5
+ import { filterHiddenFields, filterPublicColumns } from './modelMapper'
6
+
7
+ export async function ensureResourceAccess(event: H3Event, model: string, action: string): Promise<boolean> {
8
+ const { resources } = useAutoCrudConfig()
9
+ const isAdmin = await checkAdminAccess(event, model, action)
10
+
11
+ // Check public access if not admin
12
+ if (!isAdmin) {
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ const resourceConfig = (resources as any)?.[model]
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ const isPublic = resourceConfig?.public === true || (Array.isArray(resourceConfig?.public) && (resourceConfig.public as any[]).includes(action))
17
+
18
+ if (!isPublic) {
19
+ throw createError({
20
+ statusCode: 401,
21
+ message: 'Unauthorized',
22
+ })
23
+ }
24
+ }
25
+
26
+ return isAdmin
27
+ }
28
+
29
+ export function formatResourceResult(model: string, data: Record<string, unknown>, isAdmin: boolean) {
30
+ if (isAdmin) {
31
+ return filterHiddenFields(model, data)
32
+ }
33
+ else {
34
+ return filterPublicColumns(model, data)
35
+ }
36
+ }
@@ -11,23 +11,8 @@ export function drizzleTableToFields(table: any, resourceName: string) {
11
11
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
12
  const column = col as any
13
13
  const isRequired = column.notNull
14
- let type = 'string'
15
- const selectOptions: string[] | undefined = undefined
16
-
17
- // Map Drizzle types to frontend types
18
- if (column.dataType === 'number' || column.columnType === 'SQLiteInteger' || column.columnType === 'SQLiteReal') {
19
- type = 'number'
20
- // Check if it is a timestamp
21
- if (column.name.endsWith('_at') || column.name.endsWith('At')) {
22
- type = 'date'
23
- }
24
- }
25
- else if (column.dataType === 'boolean') {
26
- type = 'boolean'
27
- }
28
- else if (column.dataType === 'date' || (column.dataType === 'string' && (column.name.endsWith('_at') || column.name.endsWith('At')))) {
29
- type = 'date'
30
- }
14
+
15
+ const { type, selectOptions } = mapColumnType(column)
31
16
 
32
17
  fields.push({
33
18
  name: key,
@@ -43,6 +28,34 @@ export function drizzleTableToFields(table: any, resourceName: string) {
43
28
  }
44
29
  }
45
30
 
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+ function mapColumnType(column: any): { type: string, selectOptions?: string[] } {
33
+ // Check for enum values (Drizzle stores them in enumValues or config.enumValues)
34
+ const enumValues = column.enumValues || column.config?.enumValues
35
+
36
+ if (enumValues) {
37
+ return { type: 'enum', selectOptions: enumValues }
38
+ }
39
+
40
+ if (column.dataType === 'boolean') {
41
+ return { type: 'boolean' }
42
+ }
43
+
44
+ if (column.dataType === 'date' || (column.dataType === 'string' && (column.name.endsWith('_at') || column.name.endsWith('At')))) {
45
+ return { type: 'date' }
46
+ }
47
+
48
+ if (column.dataType === 'number' || column.columnType === 'SQLiteInteger' || column.columnType === 'SQLiteReal') {
49
+ // Check if it is a timestamp
50
+ if (column.name.endsWith('_at') || column.name.endsWith('At')) {
51
+ return { type: 'date' }
52
+ }
53
+ return { type: 'number' }
54
+ }
55
+
56
+ return { type: 'string' }
57
+ }
58
+
46
59
  export async function getRelations() {
47
60
  const relations: Record<string, Record<string, string>> = {}
48
61
 
@@ -1,2 +0,0 @@
1
- declare const _default: any;
2
- export default _default;
@@ -1,29 +0,0 @@
1
- import { eq } from "drizzle-orm";
2
- import { useDrizzle } from "#site/drizzle";
3
- import * as tables from "#site/schema";
4
- import { useAutoCrudConfig } from "../utils/config.js";
5
- import { defineNitroPlugin, hashPassword, onHubReady } from "#imports";
6
- export default defineNitroPlugin(async () => {
7
- onHubReady(async () => {
8
- const { auth } = useAutoCrudConfig();
9
- if (!auth?.authentication || !tables.users) {
10
- return;
11
- }
12
- const db = useDrizzle();
13
- const existingAdmin = await db.select().from(tables.users).where(eq(tables.users.email, "admin@example.com")).get();
14
- if (!existingAdmin) {
15
- console.log("Seeding admin user...");
16
- const hashedPassword = await hashPassword("$1Password");
17
- await db.insert(tables.users).values({
18
- email: "admin@example.com",
19
- password: hashedPassword,
20
- name: "Admin User",
21
- avatar: "https://i.pravatar.cc/150?u=admin",
22
- role: "admin",
23
- createdAt: /* @__PURE__ */ new Date(),
24
- updatedAt: /* @__PURE__ */ new Date()
25
- });
26
- console.log("Admin user seeded.");
27
- }
28
- });
29
- });
@@ -1,41 +0,0 @@
1
- import { eq } from 'drizzle-orm'
2
- // @ts-expect-error - #site/drizzle is an alias defined by the module
3
- import { useDrizzle } from '#site/drizzle'
4
- // @ts-expect-error - #site/schema is an alias defined by the module
5
- import * as tables from '#site/schema'
6
- import { useAutoCrudConfig } from '../utils/config'
7
- // @ts-expect-error - #imports is available in runtime
8
- import { defineNitroPlugin, hashPassword, onHubReady } from '#imports'
9
-
10
- export default defineNitroPlugin(async () => {
11
- onHubReady(async () => {
12
- const { auth } = useAutoCrudConfig()
13
-
14
- // Only seed if auth is enabled and we have a users table
15
- if (!auth?.authentication || !tables.users) {
16
- return
17
- }
18
-
19
- const db = useDrizzle()
20
-
21
- // Check if admin exists
22
- const existingAdmin = await db.select().from(tables.users).where(eq(tables.users.email, 'admin@example.com')).get()
23
-
24
- if (!existingAdmin) {
25
- console.log('Seeding admin user...')
26
-
27
- const hashedPassword = await hashPassword('$1Password')
28
-
29
- await db.insert(tables.users).values({
30
- email: 'admin@example.com',
31
- password: hashedPassword,
32
- name: 'Admin User',
33
- avatar: 'https://i.pravatar.cc/150?u=admin',
34
- role: 'admin',
35
- createdAt: new Date(),
36
- updatedAt: new Date(),
37
- })
38
- console.log('Admin user seeded.')
39
- }
40
- })
41
- })