nuxt-auto-crud 1.22.0 โ†’ 1.23.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 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.
3
+ > **Note:** This module is production-ready and actively maintained. The core CRUD API is stable. Minor breaking changes may occasionally occur in advanced features or configuration. We recommend version pinning for production deployments.
4
4
 
5
5
  Auto-expose RESTful CRUD APIs for your **Nuxt** application based solely on your database schema. Minimal configuration required.
6
6
 
@@ -56,20 +56,13 @@ If you want to add `nuxt-auto-crud` to an existing project, follow these steps:
56
56
  #### Install dependencies
57
57
 
58
58
  ```bash
59
- # Install module and required dependencies
60
59
  npm install nuxt-auto-crud @nuxthub/core@^0.10.0 drizzle-orm
61
-
62
- # Optional: Install auth dependencies if using Session Auth (Recommended)
63
- npm install nuxt-auth-utils nuxt-authorization
64
-
60
+ npm install nuxt-auth-utils nuxt-authorization # Optional: for authentication
65
61
  npm install --save-dev wrangler drizzle-kit
66
-
67
- # Or using bun
68
- bun add nuxt-auto-crud @nuxthub/core@latest drizzle-orm
69
- bun add nuxt-auth-utils nuxt-authorization
70
- bun add --dev wrangler drizzle-kit
71
62
  ```
72
63
 
64
+ > You can also use `bun` or `pnpm` instead of `npm`.
65
+
73
66
  #### Configure Nuxt
74
67
 
75
68
  Add the modules to your `nuxt.config.ts`:
@@ -227,81 +220,18 @@ export default defineConfig({
227
220
  })
228
221
  ```
229
222
 
230
- ## ๐Ÿ” Authentication Configuration
223
+ ## ๐Ÿ” Authentication
231
224
 
232
- The module enables authentication by default. To test APIs without authentication, you can set `auth: false`.
233
-
234
- ### Session Auth (Default)
235
-
236
- Requires `nuxt-auth-utils` and `nuxt-authorization` to be installed in your project.
237
-
238
- ```bash
239
- npm install nuxt-auth-utils nuxt-authorization
240
- ```
225
+ Authentication is enabled by default using **Session Auth** (requires `nuxt-auth-utils` and `nuxt-authorization`).
241
226
 
227
+ To disable auth for testing:
242
228
  ```typescript
243
- export default defineNuxtConfig({
244
- modules: [
245
- 'nuxt-auth-utils',
246
- 'nuxt-authorization',
247
- 'nuxt-auto-crud'
248
- ],
249
- autoCrud: {
250
- auth: {
251
- type: 'session',
252
- authentication: true, // Enables requireUserSession() check
253
- authorization: true // Enables authorize(model, action) check
254
- }
255
- }
256
- })
229
+ autoCrud: { auth: false }
257
230
  ```
258
231
 
259
- ### JWT Auth
232
+ For **JWT Auth** (backend-only apps) or advanced configuration, see the [Authentication docs](https://auto-crud.clifland.in/docs/configuration/authentication).
260
233
 
261
- Useful for backend-only apps. Does **not** require `nuxt-auth-utils`.
262
234
 
263
- ```typescript
264
- export default defineNuxtConfig({
265
- autoCrud: {
266
- auth: {
267
- type: 'jwt',
268
- authentication: true,
269
- jwtSecret: process.env.JWT_SECRET,
270
- authorization: true
271
- }
272
- }
273
- })
274
- ```
275
-
276
- ### Disabling Auth
277
-
278
- You can disable authentication entirely for testing or public APIs.
279
-
280
- ```typescript
281
- export default defineNuxtConfig({
282
- autoCrud: {
283
- auth: {
284
- authentication: false,
285
- authorization: false
286
- }
287
- // OR simply:
288
- // auth: false
289
- }
290
- })
291
- ```
292
-
293
- ## ๐Ÿงช Testing
294
-
295
- This module is tested using **Vitest**.
296
-
297
- - **Unit Tests:** We cover utility functions and helpers.
298
- - **E2E Tests:** We verify the API endpoints using a real Nuxt server instance.
299
-
300
- To run the tests locally:
301
-
302
- ```bash
303
- npm run test
304
- ```
305
235
 
306
236
  ## ๐Ÿ›ก๏ธ Public View Configuration (Field Visibility)
307
237
 
@@ -415,51 +345,7 @@ await $fetch("/api/users/1", {
415
345
  > - **Fullstack App:** The module integrates with `nuxt-auth-utils`, so session cookies are handled automatically.
416
346
  > - **Backend-only App:** You must include the `Authorization: Bearer <token>` header in your requests.
417
347
 
418
- ## Configuration
419
-
420
- ### Module Options
421
-
422
- ```typescript
423
-
424
- export default defineNuxtConfig({
425
- autoCrud: {
426
- // Path to your database schema file (relative to project root)
427
- schemaPath: "server/db/schema", // default
428
-
429
- // Authentication configuration (see "Authentication Configuration" section)
430
- auth: {
431
- // ...
432
- },
433
-
434
- // Public Guest View Configuration (Field Visibility)
435
- resources: {
436
- users: ['id', 'name', 'avatar'],
437
- },
438
- },
439
- });
440
- ```
441
-
442
- ### Protected Fields
443
348
 
444
- By default, the following fields are protected from updates:
445
-
446
- - `id`
447
- - `createdAt`
448
- - `created_at`
449
- - `updatedAt`
450
- - `updated_at`
451
-
452
- You can customize updatable fields in your schema by modifying the `modelMapper.ts` utility.
453
-
454
- ### Hidden Fields
455
-
456
- By default, the following fields are hidden from API responses for security:
457
-
458
- - `password`
459
- - `secret`
460
- - `token`
461
-
462
- You can customize hidden fields by modifying the `modelMapper.ts` utility.
463
349
 
464
350
  ## ๐Ÿ”ง Requirements
465
351
 
@@ -467,18 +353,12 @@ You can customize hidden fields by modifying the `modelMapper.ts` utility.
467
353
  - Drizzle ORM (SQLite)
468
354
  - NuxtHub >= 0.10.0
469
355
 
470
- ## ๐Ÿ”— Other Helpful Links
471
-
472
- - **Template:** [https://github.com/clifordpereira/nuxt-auto-crud_template](https://github.com/clifordpereira/nuxt-auto-crud_template)
473
- - **Docs:** [https://auto-crud.clifland.in/docs/auto-crud](https://auto-crud.clifland.in/docs/auto-crud)
474
- - **Repo:** [https://github.com/clifordpereira/nuxt-auto-crud](https://github.com/clifordpereira/nuxt-auto-crud)
475
- - **YouTube (Installation):** [https://youtu.be/M9-koXmhB9k](https://youtu.be/M9-koXmhB9k)
476
- - **YouTube (Add Schemas):** [https://youtu.be/7gW0KW1KtN0](https://youtu.be/7gW0KW1KtN0)
477
- - **YouTube (Various Permissions):** [https://www.youtube.com/watch?v=Yty3OCYbwOo](https://www.youtube.com/watch?v=Yty3OCYbwOo)
478
- - **YouTube (Dynamic RBAC):** [https://www.youtube.com/watch?v=W0ju4grRC9M](https://www.youtube.com/watch?v=W0ju4grRC9M)
479
- - **npm:** [https://www.npmjs.com/package/nuxt-auto-crud](https://www.npmjs.com/package/nuxt-auto-crud)
480
- - **Github Discussions:** [https://github.com/clifordpereira/nuxt-auto-crud/discussions/1](https://github.com/clifordpereira/nuxt-auto-crud/discussions/1)
481
- - **Discord:** [https://discord.gg/hGgyEaGu](https://discord.gg/hGgyEaGu)
356
+ ## ๐Ÿ”— Links
357
+
358
+ - **๐Ÿ“š Documentation:** [auto-crud.clifland.in](https://auto-crud.clifland.in/docs/auto-crud)
359
+ - **๐ŸŽฌ Video Tutorials:** [YouTube Channel](https://www.youtube.com/@ClifordPereira)
360
+ - **๐Ÿ’ฌ Community:** [Discord](https://discord.gg/FBkQQfRFJM) โ€ข [GitHub Discussions](https://github.com/clifordpereira/nuxt-auto-crud/discussions/1)
361
+ - **๐Ÿ“ฆ npm:** [nuxt-auto-crud](https://www.npmjs.com/package/nuxt-auto-crud)
482
362
 
483
363
  ## ๐Ÿค Contributing
484
364
 
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-auto-crud",
3
3
  "configKey": "autoCrud",
4
- "version": "1.22.0",
4
+ "version": "1.23.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -7,15 +7,18 @@ import { checkAdminAccess } from "../../utils/auth.js";
7
7
  import { RecordNotFoundError } from "../../exceptions.js";
8
8
  export default eventHandler(async (event) => {
9
9
  const { model, id } = getRouterParams(event);
10
- const isAdmin = await ensureResourceAccess(event, model, "read");
10
+ const isAdmin = await ensureResourceAccess(event, model, "read", { id });
11
11
  const table = getTableForModel(model);
12
12
  const record = await db.select().from(table).where(eq(table.id, Number(id))).get();
13
13
  if (!record) {
14
14
  throw new RecordNotFoundError();
15
15
  }
16
16
  if ("status" in record && record.status !== "active") {
17
- const canListAll = await checkAdminAccess(event, model, "list_all");
18
- if (!canListAll) {
17
+ const [canListAll, canReadOwn] = await Promise.all([
18
+ checkAdminAccess(event, model, "list_all").catch(() => false),
19
+ checkAdminAccess(event, model, "read_own", { id }).catch(() => false)
20
+ ]);
21
+ if (!canListAll && !canReadOwn) {
19
22
  throw new RecordNotFoundError();
20
23
  }
21
24
  }
@@ -11,6 +11,13 @@ export default eventHandler(async (event) => {
11
11
  const table = getTableForModel(model);
12
12
  const body = await readBody(event);
13
13
  const payload = filterUpdatableFields(model, body);
14
+ if ("status" in payload) {
15
+ const { checkAdminAccess } = await import("../../utils/auth.js");
16
+ const hasStatusPermission = await checkAdminAccess(event, model, "update_status", { id });
17
+ if (!hasStatusPermission) {
18
+ delete payload.status;
19
+ }
20
+ }
14
21
  await hashPayloadFields(payload);
15
22
  if ("updatedAt" in table) {
16
23
  payload.updatedAt = /* @__PURE__ */ new Date();
@@ -1,25 +1,59 @@
1
1
  import { eventHandler, getRouterParams } from "h3";
2
2
  import { getTableForModel } from "../../utils/modelMapper.js";
3
3
  import { db } from "hub:db";
4
- import { desc, getTableColumns, eq } from "drizzle-orm";
5
- import { ensureResourceAccess, formatResourceResult } from "../../utils/handler.js";
4
+ import { desc, getTableColumns, eq, and, or } from "drizzle-orm";
5
+ import { formatResourceResult } from "../../utils/handler.js";
6
+ import { getUserSession } from "#imports";
6
7
  import { checkAdminAccess } from "../../utils/auth.js";
7
8
  export default eventHandler(async (event) => {
8
9
  console.log("[GET] Request received", event.path);
9
10
  const { model } = getRouterParams(event);
10
- const isAdmin = await ensureResourceAccess(event, model, "list");
11
+ let canListAny = false;
11
12
  let canListAll = false;
12
- try {
13
- canListAll = await checkAdminAccess(event, model, "list_all");
14
- } catch {
15
- canListAll = false;
13
+ let canListOwn = false;
14
+ const [anyAccess, allAccess, ownAccess] = await Promise.all([
15
+ checkAdminAccess(event, model, "list").catch(() => false),
16
+ checkAdminAccess(event, model, "list_all").catch(() => false),
17
+ checkAdminAccess(event, model, "list_own").catch(() => false)
18
+ ]);
19
+ canListAny = anyAccess;
20
+ canListAll = allAccess;
21
+ canListOwn = ownAccess;
22
+ if (!canListAny && !canListAll && !canListOwn) {
23
+ throw createError({ statusCode: 403, message: "Forbidden" });
16
24
  }
17
25
  const table = getTableForModel(model);
18
26
  const columns = getTableColumns(table);
27
+ const session = await getUserSession(event);
28
+ const userId = session?.user?.id;
19
29
  let query = db.select().from(table);
20
- if (!canListAll && "status" in columns) {
21
- query = query.where(eq(table.status, "active"));
30
+ const filters = [];
31
+ if (canListAll) {
32
+ } else if (canListAny && canListOwn && userId) {
33
+ const ownershipFilter = "createdBy" in columns ? eq(table.createdBy, userId) : "userId" in columns ? eq(table.userId, userId) : null;
34
+ const activeFilter = "status" in columns ? eq(table.status, "active") : null;
35
+ if (ownershipFilter && activeFilter) {
36
+ filters.push(or(activeFilter, ownershipFilter));
37
+ } else if (activeFilter) {
38
+ filters.push(activeFilter);
39
+ } else if (ownershipFilter) {
40
+ filters.push(ownershipFilter);
41
+ }
42
+ } else if (canListAny) {
43
+ if ("status" in columns) {
44
+ filters.push(eq(table.status, "active"));
45
+ }
46
+ } else if (canListOwn && userId) {
47
+ if ("createdBy" in columns) {
48
+ filters.push(eq(table.createdBy, userId));
49
+ } else if ("userId" in columns) {
50
+ filters.push(eq(table.userId, userId));
51
+ }
22
52
  }
53
+ if (filters.length > 0) {
54
+ query = query.where(and(...filters));
55
+ }
56
+ const isAdmin = true;
23
57
  const results = await query.orderBy(desc(table.id)).all();
24
58
  return results.map((item) => formatResourceResult(model, item, isAdmin));
25
59
  });
@@ -9,6 +9,13 @@ export default eventHandler(async (event) => {
9
9
  const table = getTableForModel(model);
10
10
  const body = await readBody(event);
11
11
  const payload = filterUpdatableFields(model, body);
12
+ if ("status" in payload) {
13
+ const { checkAdminAccess } = await import("../../utils/auth.js");
14
+ const hasStatusPermission = await checkAdminAccess(event, model, "update_status");
15
+ if (!hasStatusPermission) {
16
+ delete payload.status;
17
+ }
18
+ }
12
19
  await hashPayloadFields(payload);
13
20
  try {
14
21
  const session = await getUserSession(event);
@@ -25,7 +25,7 @@ export async function checkAdminAccess(event, model, action, context) {
25
25
  const guestCheck = !user && (typeof abilityLogic === "function" ? abilityLogic : typeof globalAbility === "function" ? globalAbility : null);
26
26
  const allowed = guestCheck ? await guestCheck(null, model, action, context) : await allows(event, globalAbility, model, action, context);
27
27
  if (!allowed) {
28
- if (user && (action === "update" || action === "delete") && context && typeof context === "object" && "id" in context) {
28
+ if (user && (action === "read" || action === "update" || action === "delete") && context && typeof context === "object" && "id" in context) {
29
29
  const ownAction = `${action}_own`;
30
30
  const userPermissions = user.permissions?.[model];
31
31
  if (userPermissions && userPermissions.includes(ownAction)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-auto-crud",
3
- "version": "1.22.0",
3
+ "version": "1.23.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",
@@ -11,7 +11,7 @@ import { RecordNotFoundError } from '../../exceptions'
11
11
 
12
12
  export default eventHandler(async (event) => {
13
13
  const { model, id } = getRouterParams(event) as { model: string, id: string }
14
- const isAdmin = await ensureResourceAccess(event, model, 'read')
14
+ const isAdmin = await ensureResourceAccess(event, model, 'read', { id })
15
15
 
16
16
  const table = getTableForModel(model) as TableWithId
17
17
 
@@ -25,11 +25,14 @@ export default eventHandler(async (event) => {
25
25
  throw new RecordNotFoundError()
26
26
  }
27
27
 
28
- // Filter inactive rows for non-admins (or those without list_all) if status field exists
28
+ // Filter inactive rows for non-admins (or those without list_all or read_own) if status field exists
29
29
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
30
  if ('status' in record && (record as any).status !== 'active') {
31
- const canListAll = await checkAdminAccess(event, model, 'list_all')
32
- if (!canListAll) {
31
+ const [canListAll, canReadOwn] = await Promise.all([
32
+ checkAdminAccess(event, model, 'list_all').catch(() => false),
33
+ checkAdminAccess(event, model, 'read_own', { id }).catch(() => false),
34
+ ])
35
+ if (!canListAll && !canReadOwn) {
33
36
  throw new RecordNotFoundError()
34
37
  }
35
38
  }
@@ -1,6 +1,7 @@
1
1
  // server/api/[model]/[id].patch.ts
2
2
  import { eventHandler, getRouterParams, readBody } from 'h3'
3
3
  import type { H3Event } from 'h3'
4
+ // @ts-expect-error - #imports is a virtual alias
4
5
  import { getUserSession } from '#imports'
5
6
  import { eq } from 'drizzle-orm'
6
7
  import { getTableForModel, filterUpdatableFields } from '../../utils/modelMapper'
@@ -21,6 +22,15 @@ export default eventHandler(async (event) => {
21
22
  const body = await readBody(event)
22
23
  const payload = filterUpdatableFields(model, body)
23
24
 
25
+ // Custom check for status update permission
26
+ if ('status' in payload) {
27
+ const { checkAdminAccess } = await import('../../utils/auth')
28
+ const hasStatusPermission = await checkAdminAccess(event, model, 'update_status', { id })
29
+ if (!hasStatusPermission) {
30
+ delete payload.status
31
+ }
32
+ }
33
+
24
34
  // Auto-hash fields based on config (default: ['password'])
25
35
  await hashPayloadFields(payload)
26
36
 
@@ -1,38 +1,91 @@
1
1
  // server/api/[model]/index.get.ts
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
3
  import { eventHandler, getRouterParams } from 'h3'
3
4
  import { getTableForModel } from '../../utils/modelMapper'
4
5
  // @ts-expect-error - hub:db is a virtual alias
5
6
  import { db } from 'hub:db'
6
- import { desc, getTableColumns, eq } from 'drizzle-orm'
7
+ import { desc, getTableColumns, eq, and, or } from 'drizzle-orm'
7
8
  import type { TableWithId } from '../../types'
8
- import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
9
+ import { formatResourceResult } from '../../utils/handler'
10
+ // @ts-expect-error - #imports is a virtual alias
11
+ import { getUserSession } from '#imports'
9
12
 
10
13
  import { checkAdminAccess } from '../../utils/auth'
11
14
 
12
15
  export default eventHandler(async (event) => {
13
16
  console.log('[GET] Request received', event.path)
14
17
  const { model } = getRouterParams(event) as { model: string }
15
- const isAdmin = await ensureResourceAccess(event, model, 'list')
16
-
18
+ let canListAny = false
17
19
  let canListAll = false
18
- try {
19
- canListAll = await checkAdminAccess(event, model, 'list_all')
20
- }
21
- catch {
22
- canListAll = false
20
+ let canListOwn = false
21
+
22
+ // 1. Check permissions
23
+ const [anyAccess, allAccess, ownAccess] = await Promise.all([
24
+ checkAdminAccess(event, model, 'list').catch(() => false),
25
+ checkAdminAccess(event, model, 'list_all').catch(() => false),
26
+ checkAdminAccess(event, model, 'list_own').catch(() => false),
27
+ ])
28
+
29
+ canListAny = anyAccess
30
+ canListAll = allAccess
31
+ canListOwn = ownAccess
32
+
33
+ if (!canListAny && !canListAll && !canListOwn) {
34
+ throw createError({ statusCode: 403, message: 'Forbidden' })
23
35
  }
24
36
 
25
37
  const table = getTableForModel(model) as TableWithId
26
38
  const columns = getTableColumns(table)
39
+ const session = await (getUserSession as any)(event)
40
+ const userId = session?.user?.id
27
41
 
28
42
  let query = db.select().from(table)
29
43
 
30
- // Filter active rows for non-admins (or those without list_all) if status field exists
44
+ // 2. Build Filters
45
+ const filters = []
31
46
 
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
47
+ if (canListAll) {
48
+ // No filters needed for List All
35
49
  }
50
+ else if (canListAny && canListOwn && userId) {
51
+ // Can see everyone's ACTIVE records OR OWN records (any status)
52
+ const ownershipFilter = 'createdBy' in columns
53
+ ? eq((table as any).createdBy, userId)
54
+ : ('userId' in columns ? eq((table as any).userId, userId) : null)
55
+
56
+ const activeFilter = 'status' in columns ? eq((table as any).status, 'active') : null
57
+
58
+ if (ownershipFilter && activeFilter) {
59
+ filters.push(or(activeFilter, ownershipFilter))
60
+ }
61
+ else if (activeFilter) {
62
+ filters.push(activeFilter)
63
+ }
64
+ else if (ownershipFilter) {
65
+ filters.push(ownershipFilter)
66
+ }
67
+ }
68
+ else if (canListAny) {
69
+ // Only Any: see everyone's ACTIVE records
70
+ if ('status' in columns) {
71
+ filters.push(eq((table as any).status, 'active'))
72
+ }
73
+ }
74
+ else if (canListOwn && userId) {
75
+ // Only Own: see ONLY own records (all statuses)
76
+ if ('createdBy' in columns) {
77
+ filters.push(eq((table as any).createdBy, userId))
78
+ }
79
+ else if ('userId' in columns) {
80
+ filters.push(eq((table as any).userId, userId))
81
+ }
82
+ }
83
+
84
+ if (filters.length > 0) {
85
+ query = query.where(and(...filters)) as any
86
+ }
87
+
88
+ const isAdmin = true // Result formatting control
36
89
 
37
90
  const results = await query.orderBy(desc(table.id)).all()
38
91
 
@@ -1,6 +1,7 @@
1
1
  // server/api/[model]/index.post.ts
2
2
  import { eventHandler, getRouterParams, readBody } from 'h3'
3
3
  import type { H3Event } from 'h3'
4
+ // @ts-expect-error - #imports is a virtual alias
4
5
  import { getUserSession } from '#imports'
5
6
  import { getTableForModel, filterUpdatableFields } from '../../utils/modelMapper'
6
7
  // @ts-expect-error - hub:db is a virtual alias
@@ -16,6 +17,15 @@ export default eventHandler(async (event) => {
16
17
  const body = await readBody(event)
17
18
  const payload = filterUpdatableFields(model, body)
18
19
 
20
+ // Custom check for status update permission (or just remove it during creation as per requirement)
21
+ if ('status' in payload) {
22
+ const { checkAdminAccess } = await import('../../utils/auth')
23
+ const hasStatusPermission = await checkAdminAccess(event, model, 'update_status')
24
+ if (!hasStatusPermission) {
25
+ delete payload.status
26
+ }
27
+ }
28
+
19
29
  // Auto-hash fields based on config (default: ['password'])
20
30
  await hashPayloadFields(payload)
21
31
 
@@ -23,9 +33,7 @@ export default eventHandler(async (event) => {
23
33
  try {
24
34
  const session = await (getUserSession as (event: H3Event) => Promise<{ user: { id: string | number } | null }>)(event)
25
35
  if (session?.user?.id) {
26
- // Check if table has columns before assigning (optional but safer if we had strict types)
27
- // Since we are passing payload to .values(), extra keys might be ignored or cause error depending on driver
28
- // Using 'in' table check is good practice
36
+ // Using 'in' table check is good practice to ensure column exists
29
37
  if ('createdBy' in table) {
30
38
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
39
  (payload as any).createdBy = session.user.id
@@ -3,6 +3,7 @@
3
3
  import type { H3Event } from 'h3'
4
4
  import { createError } from 'h3'
5
5
 
6
+ // @ts-expect-error - #imports is a virtual alias
6
7
  import { requireUserSession, allows, getUserSession, abilities as globalAbility, abilityLogic } from '#imports'
7
8
  import { useAutoCrudConfig } from './config'
8
9
  import { verifyJwtToken } from './jwt'
@@ -43,7 +44,7 @@ export async function checkAdminAccess(event: H3Event, model: string, action: st
43
44
 
44
45
  if (!allowed) {
45
46
  // Fallback: Check for "Own Record" permission (e.g. update_own, delete_own)
46
- if (user && (action === 'update' || action === 'delete') && context && typeof context === 'object' && 'id' in context) {
47
+ if (user && (action === 'read' || action === 'update' || action === 'delete') && context && typeof context === 'object' && 'id' in context) {
47
48
  const ownAction = `${action}_own`
48
49
  const userPermissions = user.permissions?.[model] as string[] | undefined
49
50
 
@@ -64,8 +65,6 @@ export async function checkAdminAccess(event: H3Event, model: string, action: st
64
65
 
65
66
  // Standard case: Check 'createdBy' or 'userId' column for ownership
66
67
 
67
- // const columns = table.columns || {}
68
-
69
68
  const hasCreatedBy = 'createdBy' in table
70
69
  const hasUserId = 'userId' in table
71
70