nuxt-auto-crud 1.19.1 → 1.20.0
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/dist/module.json +1 -1
- package/dist/runtime/server/api/[model]/[id].delete.js +1 -1
- package/dist/runtime/server/api/[model]/[id].patch.js +10 -0
- package/dist/runtime/server/api/[model]/index.post.js +13 -0
- package/dist/runtime/server/utils/auth.js +12 -4
- package/package.json +1 -1
- package/src/runtime/server/api/[model]/[id].delete.ts +1 -1
- package/src/runtime/server/api/[model]/[id].patch.ts +16 -0
- package/src/runtime/server/api/[model]/index.post.ts +23 -0
- package/src/runtime/server/utils/auth.ts +21 -10
package/dist/module.json
CHANGED
|
@@ -5,7 +5,7 @@ import { db } from "hub:db";
|
|
|
5
5
|
import { ensureResourceAccess, formatResourceResult } from "../../utils/handler.js";
|
|
6
6
|
export default eventHandler(async (event) => {
|
|
7
7
|
const { model, id } = getRouterParams(event);
|
|
8
|
-
const isAdmin = await ensureResourceAccess(event, model, "delete");
|
|
8
|
+
const isAdmin = await ensureResourceAccess(event, model, "delete", { id });
|
|
9
9
|
const table = getTableForModel(model);
|
|
10
10
|
const singularName = getModelSingularName(model);
|
|
11
11
|
const deletedRecord = await db.delete(table).where(eq(table.id, Number(id))).returning().get();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { eventHandler, getRouterParams, readBody, createError } from "h3";
|
|
2
|
+
import { getUserSession } from "#imports";
|
|
2
3
|
import { eq } from "drizzle-orm";
|
|
3
4
|
import { getTableForModel, filterUpdatableFields } from "../../utils/modelMapper.js";
|
|
4
5
|
import { db } from "hub:db";
|
|
@@ -13,6 +14,15 @@ export default eventHandler(async (event) => {
|
|
|
13
14
|
if ("updatedAt" in table) {
|
|
14
15
|
payload.updatedAt = /* @__PURE__ */ new Date();
|
|
15
16
|
}
|
|
17
|
+
try {
|
|
18
|
+
const session = await getUserSession(event);
|
|
19
|
+
if (session?.user?.id) {
|
|
20
|
+
if ("updatedBy" in table) {
|
|
21
|
+
payload.updatedBy = session.user.id;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
}
|
|
16
26
|
const updatedRecord = await db.update(table).set(payload).where(eq(table.id, Number(id))).returning().get();
|
|
17
27
|
if (!updatedRecord) {
|
|
18
28
|
throw createError({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { eventHandler, getRouterParams, readBody } from "h3";
|
|
2
|
+
import { getUserSession } from "#imports";
|
|
2
3
|
import { getTableForModel, filterUpdatableFields } from "../../utils/modelMapper.js";
|
|
3
4
|
import { db } from "hub:db";
|
|
4
5
|
import { ensureResourceAccess, formatResourceResult, hashPayloadFields } from "../../utils/handler.js";
|
|
@@ -9,6 +10,18 @@ export default eventHandler(async (event) => {
|
|
|
9
10
|
const body = await readBody(event);
|
|
10
11
|
const payload = filterUpdatableFields(model, body);
|
|
11
12
|
await hashPayloadFields(payload);
|
|
13
|
+
try {
|
|
14
|
+
const session = await getUserSession(event);
|
|
15
|
+
if (session?.user?.id) {
|
|
16
|
+
if ("createdBy" in table) {
|
|
17
|
+
payload.createdBy = session.user.id;
|
|
18
|
+
}
|
|
19
|
+
if ("updatedBy" in table) {
|
|
20
|
+
payload.updatedBy = session.user.id;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
12
25
|
const newRecord = await db.insert(table).values(payload).returning().get();
|
|
13
26
|
return formatResourceResult(model, newRecord, isAdmin);
|
|
14
27
|
});
|
|
@@ -37,10 +37,18 @@ export async function checkAdminAccess(event, model, action, context) {
|
|
|
37
37
|
if (model === "users" && String(context.id) === String(user.id)) {
|
|
38
38
|
return true;
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
const hasCreatedBy = "createdBy" in table;
|
|
41
|
+
const hasUserId = "userId" in table;
|
|
42
|
+
if (hasCreatedBy || hasUserId) {
|
|
43
|
+
const query = db.select().from(table).where(eq(table.id, Number(context.id)));
|
|
44
|
+
const record = await query.get();
|
|
45
|
+
if (record) {
|
|
46
|
+
if (hasCreatedBy) {
|
|
47
|
+
if (String(record.createdBy) === String(user.id)) return true;
|
|
48
|
+
}
|
|
49
|
+
if (hasUserId) {
|
|
50
|
+
if (String(record.userId) === String(user.id)) return true;
|
|
51
|
+
}
|
|
44
52
|
}
|
|
45
53
|
}
|
|
46
54
|
} catch (e) {
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
|
|
|
9
9
|
|
|
10
10
|
export default eventHandler(async (event) => {
|
|
11
11
|
const { model, id } = getRouterParams(event) as { model: string, id: string }
|
|
12
|
-
const isAdmin = await ensureResourceAccess(event, model, 'delete')
|
|
12
|
+
const isAdmin = await ensureResourceAccess(event, model, 'delete', { id })
|
|
13
13
|
|
|
14
14
|
const table = getTableForModel(model) as TableWithId
|
|
15
15
|
const singularName = getModelSingularName(model)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// server/api/[model]/[id].patch.ts
|
|
2
2
|
import { eventHandler, getRouterParams, readBody, createError } from 'h3'
|
|
3
|
+
import type { H3Event } from 'h3'
|
|
4
|
+
import { getUserSession } from '#imports'
|
|
3
5
|
import { eq } from 'drizzle-orm'
|
|
4
6
|
import { getTableForModel, filterUpdatableFields } from '../../utils/modelMapper'
|
|
5
7
|
import type { TableWithId } from '../../types'
|
|
@@ -28,6 +30,20 @@ export default eventHandler(async (event) => {
|
|
|
28
30
|
(payload as any).updatedAt = new Date()
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
// Inject updatedBy if user is authenticated
|
|
34
|
+
try {
|
|
35
|
+
const session = await (getUserSession as (event: H3Event) => Promise<{ user: { id: string | number } | null }>)(event)
|
|
36
|
+
if (session?.user?.id) {
|
|
37
|
+
if ('updatedBy' in table) {
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
39
|
+
(payload as any).updatedBy = session.user.id
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// No session available
|
|
45
|
+
}
|
|
46
|
+
|
|
31
47
|
const updatedRecord = await db
|
|
32
48
|
.update(table)
|
|
33
49
|
.set(payload)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// server/api/[model]/index.post.ts
|
|
2
2
|
import { eventHandler, getRouterParams, readBody } from 'h3'
|
|
3
|
+
import type { H3Event } from 'h3'
|
|
4
|
+
import { getUserSession } from '#imports'
|
|
3
5
|
import { getTableForModel, filterUpdatableFields } from '../../utils/modelMapper'
|
|
4
6
|
// @ts-expect-error - hub:db is a virtual alias
|
|
5
7
|
import { db } from 'hub:db'
|
|
@@ -17,6 +19,27 @@ export default eventHandler(async (event) => {
|
|
|
17
19
|
// Auto-hash fields based on config (default: ['password'])
|
|
18
20
|
await hashPayloadFields(payload)
|
|
19
21
|
|
|
22
|
+
// Inject createdBy/updatedBy if user is authenticated
|
|
23
|
+
try {
|
|
24
|
+
const session = await (getUserSession as (event: H3Event) => Promise<{ user: { id: string | number } | null }>)(event)
|
|
25
|
+
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
|
|
29
|
+
if ('createdBy' in table) {
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
(payload as any).createdBy = session.user.id
|
|
32
|
+
}
|
|
33
|
+
if ('updatedBy' in table) {
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
(payload as any).updatedBy = session.user.id
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// No session available
|
|
41
|
+
}
|
|
42
|
+
|
|
20
43
|
const newRecord = await db.insert(table).values(payload).returning().get()
|
|
21
44
|
|
|
22
45
|
return formatResourceResult(model, newRecord as Record<string, unknown>, isAdmin)
|
|
@@ -62,16 +62,27 @@ export async function checkAdminAccess(event: H3Event, model: string, action: st
|
|
|
62
62
|
return true
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
// Standard case: Check 'userId' column for ownership
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
65
|
+
// Standard case: Check 'createdBy' or 'userId' column for ownership
|
|
66
|
+
|
|
67
|
+
// const columns = table.columns || {}
|
|
68
|
+
|
|
69
|
+
const hasCreatedBy = 'createdBy' in table
|
|
70
|
+
const hasUserId = 'userId' in table
|
|
71
|
+
|
|
72
|
+
if (hasCreatedBy || hasUserId) {
|
|
73
|
+
// @ts-expect-error - table is dynamic
|
|
74
|
+
const query = db.select().from(table).where(eq(table.id, Number(context.id)))
|
|
75
|
+
const record = await query.get()
|
|
76
|
+
|
|
77
|
+
if (record) {
|
|
78
|
+
// Check createdBy
|
|
79
|
+
if (hasCreatedBy) {
|
|
80
|
+
if (String(record.createdBy) === String(user.id)) return true
|
|
81
|
+
}
|
|
82
|
+
// Check userId (legacy)
|
|
83
|
+
if (hasUserId) {
|
|
84
|
+
if (String(record.userId) === String(user.id)) return true
|
|
85
|
+
}
|
|
75
86
|
}
|
|
76
87
|
}
|
|
77
88
|
}
|