nuxt-auto-crud 1.14.1 → 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/api/[model]/[id].get.js +10 -0
- package/dist/runtime/server/api/[model]/index.get.js +14 -2
- package/dist/runtime/server/utils/auth.js +2 -2
- 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/api/[model]/[id].get.ts +13 -0
- package/src/runtime/server/api/[model]/index.get.ts +22 -2
- package/src/runtime/server/utils/auth.ts +3 -2
- 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
|
};
|
|
@@ -3,6 +3,7 @@ import { eq } from "drizzle-orm";
|
|
|
3
3
|
import { getTableForModel } from "../../utils/modelMapper.js";
|
|
4
4
|
import { useDrizzle } from "#site/drizzle";
|
|
5
5
|
import { ensureResourceAccess, formatResourceResult } from "../../utils/handler.js";
|
|
6
|
+
import { checkAdminAccess } from "../../utils/auth.js";
|
|
6
7
|
export default eventHandler(async (event) => {
|
|
7
8
|
const { model, id } = getRouterParams(event);
|
|
8
9
|
const isAdmin = await ensureResourceAccess(event, model, "read");
|
|
@@ -14,5 +15,14 @@ export default eventHandler(async (event) => {
|
|
|
14
15
|
message: "Record not found"
|
|
15
16
|
});
|
|
16
17
|
}
|
|
18
|
+
if ("status" in record && record.status !== "active") {
|
|
19
|
+
const canListAll = await checkAdminAccess(event, model, "list_all");
|
|
20
|
+
if (!canListAll) {
|
|
21
|
+
throw createError({
|
|
22
|
+
statusCode: 404,
|
|
23
|
+
message: "Record not found"
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
17
27
|
return formatResourceResult(model, record, isAdmin);
|
|
18
28
|
});
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import { eventHandler, getRouterParams } from "h3";
|
|
2
2
|
import { getTableForModel } from "../../utils/modelMapper.js";
|
|
3
3
|
import { useDrizzle } from "#site/drizzle";
|
|
4
|
-
import { desc } from "drizzle-orm";
|
|
4
|
+
import { desc, getTableColumns, eq } from "drizzle-orm";
|
|
5
5
|
import { ensureResourceAccess, formatResourceResult } from "../../utils/handler.js";
|
|
6
|
+
import { checkAdminAccess } from "../../utils/auth.js";
|
|
6
7
|
export default eventHandler(async (event) => {
|
|
7
8
|
console.log("[GET] Request received", event.path);
|
|
8
9
|
const { model } = getRouterParams(event);
|
|
9
10
|
const isAdmin = await ensureResourceAccess(event, model, "list");
|
|
11
|
+
let canListAll = false;
|
|
12
|
+
try {
|
|
13
|
+
canListAll = await checkAdminAccess(event, model, "list_all");
|
|
14
|
+
} catch {
|
|
15
|
+
canListAll = false;
|
|
16
|
+
}
|
|
10
17
|
const table = getTableForModel(model);
|
|
11
|
-
const
|
|
18
|
+
const columns = getTableColumns(table);
|
|
19
|
+
let query = useDrizzle().select().from(table);
|
|
20
|
+
if (!canListAll && "status" in columns) {
|
|
21
|
+
query = query.where(eq(table.status, "active"));
|
|
22
|
+
}
|
|
23
|
+
const results = await query.orderBy(desc(table.id)).all();
|
|
12
24
|
return results.map((item) => formatResourceResult(model, item, isAdmin));
|
|
13
25
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createError } from "h3";
|
|
2
|
-
import { requireUserSession, allows, getUserSession, abilities as globalAbility } from "#imports";
|
|
2
|
+
import { requireUserSession, allows, getUserSession, abilities as globalAbility, abilityLogic } from "#imports";
|
|
3
3
|
import { useAutoCrudConfig } from "./config.js";
|
|
4
4
|
import { verifyJwtToken } from "./jwt.js";
|
|
5
5
|
export async function checkAdminAccess(event, model, action) {
|
|
@@ -22,7 +22,7 @@ export async function checkAdminAccess(event, model, action) {
|
|
|
22
22
|
}
|
|
23
23
|
if (auth.authorization) {
|
|
24
24
|
try {
|
|
25
|
-
const guestCheck = !user && (typeof globalAbility === "function" ? globalAbility : null);
|
|
25
|
+
const guestCheck = !user && (typeof abilityLogic === "function" ? abilityLogic : typeof globalAbility === "function" ? globalAbility : null);
|
|
26
26
|
const allowed = guestCheck ? await guestCheck(null, model, action) : await allows(event, globalAbility, model, action);
|
|
27
27
|
if (!allowed) {
|
|
28
28
|
if (user) throw createError({ statusCode: 403, message: "Forbidden" });
|
|
@@ -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",
|
|
@@ -6,6 +6,7 @@ import type { TableWithId } from '../../types'
|
|
|
6
6
|
// @ts-expect-error - #site/drizzle is an alias defined by the module
|
|
7
7
|
import { useDrizzle } from '#site/drizzle'
|
|
8
8
|
import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
|
|
9
|
+
import { checkAdminAccess } from '../../utils/auth'
|
|
9
10
|
|
|
10
11
|
export default eventHandler(async (event) => {
|
|
11
12
|
const { model, id } = getRouterParams(event) as { model: string, id: string }
|
|
@@ -26,5 +27,17 @@ export default eventHandler(async (event) => {
|
|
|
26
27
|
})
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
// Filter inactive rows for non-admins (or those without list_all) if status field exists
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
+
if ('status' in record && (record as any).status !== 'active') {
|
|
33
|
+
const canListAll = await checkAdminAccess(event, model, 'list_all')
|
|
34
|
+
if (!canListAll) {
|
|
35
|
+
throw createError({
|
|
36
|
+
statusCode: 404,
|
|
37
|
+
message: 'Record not found',
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
29
42
|
return formatResourceResult(model, record as Record<string, unknown>, isAdmin)
|
|
30
43
|
})
|
|
@@ -3,18 +3,38 @@ import { eventHandler, getRouterParams } from 'h3'
|
|
|
3
3
|
import { getTableForModel } from '../../utils/modelMapper'
|
|
4
4
|
// @ts-expect-error - #site/drizzle is an alias defined by the module
|
|
5
5
|
import { useDrizzle } from '#site/drizzle'
|
|
6
|
-
import { desc } from 'drizzle-orm'
|
|
6
|
+
import { desc, getTableColumns, eq } from 'drizzle-orm'
|
|
7
7
|
import type { TableWithId } from '../../types'
|
|
8
8
|
import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
|
|
9
9
|
|
|
10
|
+
import { checkAdminAccess } from '../../utils/auth'
|
|
11
|
+
|
|
10
12
|
export default eventHandler(async (event) => {
|
|
11
13
|
console.log('[GET] Request received', event.path)
|
|
12
14
|
const { model } = getRouterParams(event) as { model: string }
|
|
13
15
|
const isAdmin = await ensureResourceAccess(event, model, 'list')
|
|
14
16
|
|
|
17
|
+
let canListAll = false
|
|
18
|
+
try {
|
|
19
|
+
canListAll = await checkAdminAccess(event, model, 'list_all')
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
canListAll = false
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
const table = getTableForModel(model) as TableWithId
|
|
26
|
+
const columns = getTableColumns(table)
|
|
27
|
+
|
|
28
|
+
let query = useDrizzle().select().from(table)
|
|
29
|
+
|
|
30
|
+
// Filter active rows for non-admins (or those without list_all) if status field exists
|
|
31
|
+
|
|
32
|
+
if (!canListAll && 'status' in columns) {
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
+
query = query.where(eq((table as any).status, 'active')) as any
|
|
35
|
+
}
|
|
16
36
|
|
|
17
|
-
const results = await
|
|
37
|
+
const results = await query.orderBy(desc(table.id)).all()
|
|
18
38
|
|
|
19
39
|
return results.map((item: Record<string, unknown>) => formatResourceResult(model, item, isAdmin))
|
|
20
40
|
})
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import type { H3Event } from 'h3'
|
|
4
4
|
import { createError } from 'h3'
|
|
5
5
|
// @ts-expect-error - #imports is available in runtime
|
|
6
|
-
import { requireUserSession, allows, getUserSession, abilities as globalAbility } from '#imports'
|
|
6
|
+
import { requireUserSession, allows, getUserSession, abilities as globalAbility, abilityLogic } from '#imports'
|
|
7
7
|
import { useAutoCrudConfig } from './config'
|
|
8
8
|
import { verifyJwtToken } from './jwt'
|
|
9
9
|
|
|
@@ -35,7 +35,8 @@ export async function checkAdminAccess(event: H3Event, model: string, action: st
|
|
|
35
35
|
// Check authorization if enabled
|
|
36
36
|
if (auth.authorization) {
|
|
37
37
|
try {
|
|
38
|
-
const guestCheck = !user && (typeof globalAbility === 'function' ? globalAbility : null)
|
|
38
|
+
const guestCheck = !user && (typeof abilityLogic === 'function' ? abilityLogic : (typeof globalAbility === 'function' ? globalAbility : null))
|
|
39
|
+
|
|
39
40
|
const allowed = guestCheck
|
|
40
41
|
? await guestCheck(null, model, action)
|
|
41
42
|
: await allows(event, globalAbility, model, action)
|
|
@@ -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
|
/**
|