nuxt-auto-crud 1.15.2 → 1.16.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
@@ -1,6 +1,6 @@
1
1
  # Nuxt Auto CRUD
2
2
 
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.
3
+ > **Note:** This module is widely used in MVP development and is rapidly maturing. While currently in **Beta**, the core API (CRUD) is stable. Breaking changes may still occur in configuration or advanced features. Production use is encouraged with version pinning.
4
4
 
5
5
  Auto-expose RESTful CRUD APIs for your **Nuxt** application based solely on your database schema. Minimal configuration required.
6
6
 
@@ -13,6 +13,7 @@ And you don't need a separate Strapi or Supabase setup to automate your CRUD pro
13
13
  While this module exposes CRUD APIs, you are expected to build your own frontend application to consume them.
14
14
 
15
15
  - [✨ Release Notes](/CHANGELOG.md)
16
+ - [🗺️ Roadmap](/ROADMAP.md)
16
17
  - [🎮 Try the Playground](/playground)
17
18
 
18
19
  ## 🚀 CRUD APIs are ready to use without code
@@ -320,33 +321,29 @@ To run the tests locally:
320
321
  npm run test
321
322
  ```
322
323
 
323
- ## 🛡️ Resource Configuration (RBAC)
324
+ ## 🛡️ Public View Configuration (Field Visibility)
324
325
 
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.
326
+ You can define which fields are visible to unauthenticated (guest) users in your `nuxt.config.ts`.
327
+ > **Note:** Access control (RBAC) - determining *who* can access *what* - is expected to be handled by your database/permissions system (using `nuxt-authorization`). This configuration only controls the *serialization* of the response for guests.
326
328
 
327
329
  ```typescript
328
- // autocrud.config.ts
329
- export default {
330
- resources: {
331
- users: {
332
- // Access Control
333
- auth: {
334
- // Admin has full access
335
- admin: true,
336
- // Public (unauthenticated) users can only list and read
337
- public: ['list', 'read'],
338
- },
339
- // Field Visibility
340
- publicColumns: ['id', 'name', 'avatar'], // Only these columns are returned to public users
330
+ // nuxt.config.ts
331
+ export default defineNuxtConfig({
332
+ autoCrud: {
333
+ resources: {
334
+ // Guest View: Only these columns are visible to unauthenticated users.
335
+ // Access control (who can list/read) is managed by your DB permissions.
336
+ users: ['id', 'name', 'avatar'],
341
337
  },
342
- }
343
- }
338
+ },
339
+ })
344
340
  ```
345
341
 
346
342
  ## ⚠️ Known Issues
347
343
 
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.
344
+ - **Automatic Relation Expansion:** The module tries to automatically expand foreign keys (e.g., `user_id` -> `user: { name: ... }`). However, this relies on the foreign key column name matching the target table name (e.g., `user_id` for `users`).
345
+ - **Limitation:** If you have custom FK names like `customer_id` or `author_id` pointing to `users`, the automatic expansion will not work yet.
346
+ - **Workaround:** Ensure your FK columns follow the `tablename_id` convention where possible for now.
350
347
 
351
348
  ## 🎮 Try the Playground
352
349
 
@@ -431,7 +428,12 @@ export default defineNuxtConfig({
431
428
  // Authentication configuration (see "Authentication Configuration" section)
432
429
  auth: {
433
430
  // ...
434
- }
431
+ },
432
+
433
+ // Public Guest View Configuration (Field Visibility)
434
+ resources: {
435
+ users: ['id', 'name', 'avatar'],
436
+ },
435
437
  },
436
438
  });
437
439
  ```
@@ -472,6 +474,7 @@ You can customize hidden fields by modifying the `modelMapper.ts` utility.
472
474
  - **YouTube (Installation):** [https://youtu.be/M9-koXmhB9k](https://youtu.be/M9-koXmhB9k)
473
475
  - **YouTube (Add Schemas):** [https://youtu.be/7gW0KW1KtN0](https://youtu.be/7gW0KW1KtN0)
474
476
  - **YouTube (Various Permissions):** [https://www.youtube.com/watch?v=Yty3OCYbwOo](https://www.youtube.com/watch?v=Yty3OCYbwOo)
477
+ - **YouTube (Dynamic RBAC):** [https://www.youtube.com/watch?v=W0ju4grRC9M](https://www.youtube.com/watch?v=W0ju4grRC9M)
475
478
  - **npm:** [https://www.npmjs.com/package/nuxt-auto-crud](https://www.npmjs.com/package/nuxt-auto-crud)
476
479
  - **Github Discussions:** [https://github.com/clifordpereira/nuxt-auto-crud/discussions/1](https://github.com/clifordpereira/nuxt-auto-crud/discussions/1)
477
480
  - **Discord:** [https://discord.gg/hGgyEaGu](https://discord.gg/hGgyEaGu)
package/dist/module.d.mts CHANGED
@@ -17,22 +17,10 @@ interface ModuleOptions {
17
17
  auth?: boolean | AuthOptions;
18
18
  /**
19
19
  * Resource-specific configuration
20
- * Define public access and column visibility
20
+ * Define column visibility for unauthenticated users
21
21
  */
22
22
  resources?: {
23
- [modelName: string]: {
24
- /**
25
- * Actions allowed without authentication
26
- * true = all actions
27
- * array = specific actions ('list', 'create', 'read', 'update', 'delete')
28
- */
29
- public?: boolean | ('list' | 'create' | 'read' | 'update' | 'delete')[];
30
- /**
31
- * Columns to return for unauthenticated requests
32
- * If not specified, all columns (except hidden ones) are returned
33
- */
34
- publicColumns?: string[];
35
- };
23
+ [modelName: string]: string[];
36
24
  };
37
25
  }
38
26
  interface AuthOptions {
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-auto-crud",
3
3
  "configKey": "autoCrud",
4
- "version": "1.15.2",
4
+ "version": "1.16.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -24,16 +24,10 @@ const module$1 = defineNuxtModule({
24
24
  nuxt.options.alias["#site/drizzle"] = drizzlePath;
25
25
  addImportsDir(resolver.resolve(nuxt.options.rootDir, "shared/utils"));
26
26
  nuxt.options.alias["#authorization"] ||= "nuxt-authorization/utils";
27
- const { loadConfig } = await import('c12');
28
- const { config: externalConfig } = await loadConfig({
29
- name: "autocrud",
30
- cwd: nuxt.options.rootDir
31
- });
32
27
  const mergedAuth = options.auth === false ? { authentication: false, authorization: false, type: "session" } : {
33
28
  authentication: true,
34
29
  authorization: options.auth === true,
35
30
  type: "session",
36
- ...typeof externalConfig?.auth === "object" ? externalConfig.auth : {},
37
31
  ...typeof options.auth === "object" ? options.auth : {}
38
32
  };
39
33
  nuxt.options.runtimeConfig.autoCrud = {
@@ -44,7 +38,6 @@ const module$1 = defineNuxtModule({
44
38
  jwtSecret: mergedAuth.jwtSecret
45
39
  },
46
40
  resources: {
47
- ...externalConfig?.resources,
48
41
  ...options.resources
49
42
  }
50
43
  };
@@ -2,20 +2,21 @@ import { createError } from "h3";
2
2
  import { useAutoCrudConfig } from "./config.js";
3
3
  import { checkAdminAccess } from "./auth.js";
4
4
  import { filterHiddenFields, filterPublicColumns } from "./modelMapper.js";
5
+ import { getUserSession } from "#imports";
5
6
  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
- }
7
+ const { auth } = useAutoCrudConfig();
8
+ const isAuthorized = await checkAdminAccess(event, model, action);
9
+ if (!isAuthorized) {
10
+ throw createError({
11
+ statusCode: 401,
12
+ message: "Unauthorized"
13
+ });
17
14
  }
18
- return isAdmin;
15
+ if (!auth?.authentication) {
16
+ return true;
17
+ }
18
+ const session = await getUserSession(event);
19
+ return !!session.user;
19
20
  }
20
21
  export function formatResourceResult(model, data, isAdmin) {
21
22
  if (isAdmin) {
@@ -93,7 +93,7 @@ export function getHiddenFields(modelName) {
93
93
  }
94
94
  export function getPublicColumns(modelName) {
95
95
  const { resources } = useRuntimeConfig().autoCrud;
96
- return resources?.[modelName]?.publicColumns;
96
+ return resources?.[modelName];
97
97
  }
98
98
  export function filterPublicColumns(modelName, data) {
99
99
  const publicColumns = getPublicColumns(modelName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-auto-crud",
3
- "version": "1.15.2",
3
+ "version": "1.16.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",
@@ -49,10 +49,10 @@
49
49
  "dependencies": {
50
50
  "@nuxt/kit": "^4.2.1",
51
51
  "@types/pluralize": "^0.0.33",
52
- "pluralize": "^8.0.0",
53
- "scule": "^1.0.0",
54
52
  "c12": "^2.0.1",
55
- "jose": "^5.9.6"
53
+ "jose": "^5.9.6",
54
+ "pluralize": "^8.0.0",
55
+ "scule": "^1.0.0"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "drizzle-orm": ">=0.30.0"
@@ -68,8 +68,9 @@
68
68
  "@nuxt/schema": "^4.2.1",
69
69
  "@nuxt/test-utils": "^3.20.1",
70
70
  "@nuxthub/core": "^0.9.1",
71
+ "@types/better-sqlite3": "^7.6.13",
71
72
  "@types/node": "latest",
72
- "better-sqlite3": "^12.4.6",
73
+ "better-sqlite3": "^12.5.0",
73
74
  "changelogen": "^0.6.2",
74
75
  "drizzle-kit": "^0.31.7",
75
76
  "drizzle-orm": "^0.38.3",
@@ -4,26 +4,29 @@ import { useAutoCrudConfig } from './config'
4
4
  import { checkAdminAccess } from './auth'
5
5
  import { filterHiddenFields, filterPublicColumns } from './modelMapper'
6
6
 
7
+ // @ts-expect-error - #imports is available in runtime
8
+ import { getUserSession } from '#imports'
9
+
7
10
  export async function ensureResourceAccess(event: H3Event, model: string, action: string): Promise<boolean> {
8
- const { resources } = useAutoCrudConfig()
9
- const isAdmin = await checkAdminAccess(event, model, action)
11
+ const { auth } = useAutoCrudConfig()
10
12
 
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))
13
+ // This throws 403 if not authorized
14
+ const isAuthorized = await checkAdminAccess(event, model, action)
15
+ if (!isAuthorized) {
16
+ throw createError({
17
+ statusCode: 401,
18
+ message: 'Unauthorized',
19
+ })
20
+ }
17
21
 
18
- if (!isPublic) {
19
- throw createError({
20
- statusCode: 401,
21
- message: 'Unauthorized',
22
- })
23
- }
22
+ // If authentication is disabled, treated as fully inclusive access
23
+ if (!auth?.authentication) {
24
+ return true
24
25
  }
25
26
 
26
- return isAdmin
27
+ // Check if user is authenticated
28
+ const session = await getUserSession(event)
29
+ return !!session.user
27
30
  }
28
31
 
29
32
  export function formatResourceResult(model: string, data: Record<string, unknown>, isAdmin: boolean) {
@@ -213,7 +213,8 @@ export function getHiddenFields(modelName: string): string[] {
213
213
  */
214
214
  export function getPublicColumns(modelName: string): string[] | undefined {
215
215
  const { resources } = useRuntimeConfig().autoCrud
216
- return resources?.[modelName]?.publicColumns
216
+ // Runtime config structure now matches simple key-value
217
+ return resources?.[modelName]
217
218
  }
218
219
 
219
220
  /**