nuxt-auto-crud 1.12.1 → 1.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **Note:** This module is currently in its alpha stage. However, you can use it to accelerate MVP development. It has not been tested thoroughly enough for production use; only happy-path testing is performed for each release.
4
4
 
5
- Auto-generate RESTful CRUD APIs for your **Nuxt** application based solely on your database schema. Minimal configuration required.
5
+ Auto-expose RESTful CRUD APIs for your **Nuxt** application based solely on your database schema. Minimal configuration required.
6
6
 
7
7
  **Core Philosophy:**
8
8
  The main objective of this module is to **expose CRUD APIs without the need for writing code**. You define your database schema, and `nuxt-auto-crud` handles the rest.
@@ -44,7 +44,7 @@ bun run dev
44
44
  1. **Fullstack App**: The template includes the `nuxt-auto-crud` module, providing both the backend APIs and the frontend UI. [Watch Demo](https://youtu.be/M9-koXmhB9k)
45
45
  2. **Frontend Only**: You can use the template just for the frontend. In this case, you don't need to install the module in the frontend app. Instead, you would install `nuxt-auto-crud` in a separate backend setup (e.g., another Nuxt project acting as the API).
46
46
 
47
- Detailed instructions can be found in [https://auto-crud.clifland.in/](https://auto-crud.clifland.in/)
47
+ Detailed instructions can be found in [https://auto-crud.clifland.in/docs](https://auto-crud.clifland.in/docs)
48
48
 
49
49
  ### 2. Manual Setup (Existing Project)
50
50
 
@@ -207,7 +207,7 @@ The new API endpoints (e.g., `/api/posts`) will be automatically available. [Wat
207
207
 
208
208
  ### 3. Backend-only App (API Mode)
209
209
 
210
- If you are using Nuxt as a backend for a separate client application (e.g., mobile app, SPA), you can use this module to quickly generate REST APIs.
210
+ If you are using Nuxt as a backend for a separate client application (e.g., mobile app, SPA), you can use this module to quickly expose REST APIs.
211
211
 
212
212
  In this case, you might handle authentication differently (e.g., validating tokens in middleware) or disable the built-in auth checks if you have a global auth middleware.
213
213
 
@@ -467,12 +467,14 @@ You can customize hidden fields by modifying the `modelMapper.ts` utility.
467
467
  ## 🔗 Other Helpful Links
468
468
 
469
469
  - **Template:** [https://github.com/clifordpereira/nuxt-auto-crud_template](https://github.com/clifordpereira/nuxt-auto-crud_template)
470
- - **Docs:** [https://auto-crud.clifland.in/](https://auto-crud.clifland.in/)
470
+ - **Docs:** [https://auto-crud.clifland.in/docs](https://auto-crud.clifland.in/docs)
471
471
  - **Repo:** [https://github.com/clifordpereira/nuxt-auto-crud](https://github.com/clifordpereira/nuxt-auto-crud)
472
- - **YouTube:** [https://youtu.be/M9-koXmhB9k](https://youtu.be/M9-koXmhB9k)
473
- - **YouTube 2:** [https://youtu.be/7gW0KW1KtN0](https://youtu.be/7gW0KW1KtN0)
472
+ - **YouTube (Installation):** [https://youtu.be/M9-koXmhB9k](https://youtu.be/M9-koXmhB9k)
473
+ - **YouTube (Add Schemas):** [https://youtu.be/7gW0KW1KtN0](https://youtu.be/7gW0KW1KtN0)
474
+ - **YouTube (Various Permissions):** [https://www.youtube.com/watch?v=Yty3OCYbwOo](https://www.youtube.com/watch?v=Yty3OCYbwOo)
474
475
  - **npm:** [https://www.npmjs.com/package/nuxt-auto-crud](https://www.npmjs.com/package/nuxt-auto-crud)
475
- - **Discuss:** [https://discord.gg/hGgyEaGu](https://discord.gg/hGgyEaGu)
476
+ - **Github Discussions:** [https://github.com/clifordpereira/nuxt-auto-crud/discussions/1](https://github.com/clifordpereira/nuxt-auto-crud/discussions/1)
477
+ - **Discord:** [https://discord.gg/hGgyEaGu](https://discord.gg/hGgyEaGu)
476
478
 
477
479
  ## 🤝 Contributing
478
480
 
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-auto-crud",
3
3
  "configKey": "autoCrud",
4
- "version": "1.12.1",
4
+ "version": "1.14.1",
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 } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, addImportsDir, addServerHandler, addServerImportsDir } from '@nuxt/kit';
2
2
 
3
3
  const module$1 = defineNuxtModule({
4
4
  meta: {
@@ -22,52 +22,19 @@ 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;
30
- nuxt.options.alias["#authorization"] = nuxt.options.alias["#authorization"] || "nuxt-authorization/utils";
25
+ addImportsDir(resolver.resolve(nuxt.options.rootDir, "shared/utils"));
26
+ nuxt.options.alias["#authorization"] ||= "nuxt-authorization/utils";
31
27
  const { loadConfig } = await import('c12');
32
28
  const { config: externalConfig } = await loadConfig({
33
29
  name: "autocrud",
34
30
  cwd: nuxt.options.rootDir
35
31
  });
36
- let mergedAuth = {
37
- authentication: false,
38
- authorization: false,
39
- type: "session"
40
- };
41
- if (options.auth === true) {
42
- mergedAuth = {
43
- authentication: true,
44
- authorization: true,
45
- type: "session",
46
- ...typeof externalConfig?.auth === "object" ? externalConfig.auth : {}
47
- };
48
- } else if (options.auth === false) {
49
- mergedAuth = {
50
- authentication: false,
51
- authorization: false,
52
- type: "session"
53
- };
54
- } else {
55
- mergedAuth = {
56
- authentication: true,
57
- // Default to true if object provided? Or undefined?
58
- // If options.auth is undefined, we might want defaults.
59
- // But if defaults say auth: false, then options.auth might be undefined.
60
- // Let's stick to the plan: default is false.
61
- ...typeof externalConfig?.auth === "object" ? externalConfig.auth : {},
62
- ...typeof options.auth === "object" ? options.auth : {}
63
- };
64
- if (mergedAuth.authentication === void 0) {
65
- mergedAuth.authentication = false;
66
- }
67
- }
68
- const mergedResources = {
69
- ...externalConfig?.resources,
70
- ...options.resources
32
+ const mergedAuth = options.auth === false ? { authentication: false, authorization: false, type: "session" } : {
33
+ authentication: true,
34
+ authorization: options.auth === true,
35
+ type: "session",
36
+ ...typeof externalConfig?.auth === "object" ? externalConfig.auth : {},
37
+ ...typeof options.auth === "object" ? options.auth : {}
71
38
  };
72
39
  nuxt.options.runtimeConfig.autoCrud = {
73
40
  auth: {
@@ -76,7 +43,10 @@ const module$1 = defineNuxtModule({
76
43
  type: mergedAuth.type ?? "session",
77
44
  jwtSecret: mergedAuth.jwtSecret
78
45
  },
79
- resources: mergedResources || {}
46
+ resources: {
47
+ ...externalConfig?.resources,
48
+ ...options.resources
49
+ }
80
50
  };
81
51
  const apiDir = resolver.resolve("./runtime/server/api");
82
52
  addServerHandler({
@@ -1,6 +1,5 @@
1
1
  import { createError } from "h3";
2
- import globalAbility from "#site/ability";
3
- import { requireUserSession, allows } from "#imports";
2
+ import { requireUserSession, allows, getUserSession, abilities as globalAbility } from "#imports";
4
3
  import { useAutoCrudConfig } from "./config.js";
5
4
  import { verifyJwtToken } from "./jwt.js";
6
5
  export async function checkAdminAccess(event, model, action) {
@@ -15,42 +14,39 @@ export async function checkAdminAccess(event, model, action) {
15
14
  }
16
15
  return verifyJwtToken(event, auth.jwtSecret);
17
16
  }
17
+ let user = null;
18
18
  try {
19
- await requireUserSession(event);
20
- if (auth.authorization) {
21
- const allowed = await allows(event, globalAbility, model, action);
19
+ const session = await getUserSession(event);
20
+ user = session.user;
21
+ } catch {
22
+ }
23
+ if (auth.authorization) {
24
+ try {
25
+ const guestCheck = !user && (typeof globalAbility === "function" ? globalAbility : null);
26
+ const allowed = guestCheck ? await guestCheck(null, model, action) : await allows(event, globalAbility, model, action);
22
27
  if (!allowed) {
23
- throw createError({
24
- statusCode: 403,
25
- message: "Forbidden"
26
- });
28
+ if (user) throw createError({ statusCode: 403, message: "Forbidden" });
29
+ return false;
27
30
  }
31
+ return true;
32
+ } catch (err) {
33
+ if (err.statusCode === 403) throw err;
34
+ return false;
28
35
  }
36
+ }
37
+ if (user) {
29
38
  return true;
30
- } catch (e) {
31
- if (e.statusCode === 403) {
32
- throw e;
33
- }
34
- return false;
35
39
  }
40
+ return false;
36
41
  }
37
42
  export async function ensureAuthenticated(event) {
38
43
  const { auth } = useAutoCrudConfig();
39
- if (!auth?.authentication) {
40
- return;
41
- }
42
- let isAuthenticated = false;
44
+ if (!auth?.authentication) return;
43
45
  if (auth.type === "jwt" && auth.jwtSecret) {
44
- isAuthenticated = await verifyJwtToken(event, auth.jwtSecret);
45
- } else {
46
- try {
47
- await requireUserSession(event);
48
- isAuthenticated = true;
49
- } catch {
50
- isAuthenticated = false;
46
+ if (!await verifyJwtToken(event, auth.jwtSecret)) {
47
+ throw createError({ statusCode: 401, message: "Unauthorized" });
51
48
  }
49
+ return;
52
50
  }
53
- if (!isAuthenticated) {
54
- throw createError({ statusCode: 401, message: "Unauthorized" });
55
- }
51
+ await requireUserSession(event);
56
52
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-auto-crud",
3
- "version": "1.12.1",
3
+ "version": "1.14.1",
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",
@@ -61,6 +61,7 @@
61
61
  "@iconify-json/heroicons": "^1.2.3",
62
62
  "@iconify-json/lucide": "^1.2.77",
63
63
  "@iconify-json/simple-icons": "^1.2.61",
64
+ "@iconify-json/vscode-icons": "^1.2.37",
64
65
  "@nuxt/devtools": "^3.1.0",
65
66
  "@nuxt/eslint-config": "^1.10.0",
66
67
  "@nuxt/module-builder": "^1.0.2",
@@ -2,9 +2,8 @@
2
2
  /// <reference path="../../auth.d.ts" />
3
3
  import type { H3Event } from 'h3'
4
4
  import { createError } from 'h3'
5
- import globalAbility from '#site/ability'
6
5
  // @ts-expect-error - #imports is available in runtime
7
- import { requireUserSession, allows } from '#imports'
6
+ import { requireUserSession, allows, getUserSession, abilities as globalAbility } from '#imports'
8
7
  import { useAutoCrudConfig } from './config'
9
8
  import { verifyJwtToken } from './jwt'
10
9
 
@@ -24,55 +23,54 @@ export async function checkAdminAccess(event: H3Event, model: string, action: st
24
23
  }
25
24
 
26
25
  // Session based (default)
26
+ let user = null
27
27
  try {
28
- await requireUserSession(event)
28
+ const session = await getUserSession(event)
29
+ user = session.user
30
+ }
31
+ catch {
32
+ // No session or error fetching session
33
+ }
34
+
35
+ // Check authorization if enabled
36
+ if (auth.authorization) {
37
+ try {
38
+ const guestCheck = !user && (typeof globalAbility === 'function' ? globalAbility : null)
39
+ const allowed = guestCheck
40
+ ? await guestCheck(null, model, action)
41
+ : await allows(event, globalAbility, model, action)
29
42
 
30
- // Check authorization if enabled
31
- if (auth.authorization) {
32
- const allowed = await allows(event, globalAbility, model, action)
33
43
  if (!allowed) {
34
- throw createError({
35
- statusCode: 403,
36
- message: 'Forbidden',
37
- })
44
+ if (user) throw createError({ statusCode: 403, message: 'Forbidden' })
45
+ return false
38
46
  }
47
+ return true
39
48
  }
49
+ catch (err) {
50
+ if ((err as { statusCode: number }).statusCode === 403) throw err
51
+ return false
52
+ }
53
+ }
40
54
 
55
+ // If authorization is NOT enabled, we rely on authentication only.
56
+ if (user) {
41
57
  return true
42
58
  }
43
- catch (e: unknown) {
44
- // If it's a 403 (Forbidden) from our ability check, rethrow it
45
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
- if ((e as any).statusCode === 403) {
47
- throw e
48
- }
49
- // Otherwise (401 from requireUserSession or function not available), return false (treat as guest)
50
- return false
51
- }
59
+
60
+ return false
52
61
  }
53
62
 
54
63
  export async function ensureAuthenticated(event: H3Event): Promise<void> {
55
64
  const { auth } = useAutoCrudConfig()
56
65
 
57
- if (!auth?.authentication) {
58
- return
59
- }
66
+ if (!auth?.authentication) return
60
67
 
61
- let isAuthenticated = false
62
68
  if (auth.type === 'jwt' && auth.jwtSecret) {
63
- isAuthenticated = await verifyJwtToken(event, auth.jwtSecret)
64
- }
65
- else {
66
- try {
67
- await requireUserSession(event)
68
- isAuthenticated = true
69
- }
70
- catch {
71
- isAuthenticated = false
69
+ if (!await verifyJwtToken(event, auth.jwtSecret)) {
70
+ throw createError({ statusCode: 401, message: 'Unauthorized' })
72
71
  }
72
+ return
73
73
  }
74
74
 
75
- if (!isAuthenticated) {
76
- throw createError({ statusCode: 401, message: 'Unauthorized' })
77
- }
75
+ await requireUserSession(event)
78
76
  }