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 +24 -21
- package/dist/module.d.mts +2 -14
- package/dist/module.json +1 -1
- package/dist/module.mjs +0 -7
- package/dist/runtime/server/utils/handler.js +13 -12
- package/dist/runtime/server/utils/modelMapper.js +1 -1
- package/package.json +6 -5
- package/src/runtime/server/utils/handler.ts +18 -15
- package/src/runtime/server/utils/modelMapper.ts +2 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Nuxt Auto CRUD
|
|
2
2
|
|
|
3
|
-
> **Note:** This module is
|
|
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
|
-
## 🛡️
|
|
324
|
+
## 🛡️ Public View Configuration (Field Visibility)
|
|
324
325
|
|
|
325
|
-
You can define
|
|
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
|
-
//
|
|
329
|
-
export default {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
//
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
- **
|
|
349
|
-
- **
|
|
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
|
|
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
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 {
|
|
7
|
-
const
|
|
8
|
-
if (!
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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]
|
|
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.
|
|
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.
|
|
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 {
|
|
9
|
-
const isAdmin = await checkAdminAccess(event, model, action)
|
|
11
|
+
const { auth } = useAutoCrudConfig()
|
|
10
12
|
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
216
|
+
// Runtime config structure now matches simple key-value
|
|
217
|
+
return resources?.[modelName]
|
|
217
218
|
}
|
|
218
219
|
|
|
219
220
|
/**
|