Skip to content

Commit 4d1f211

Browse files
refactor: Centralize authorization and response formatting into new handler utilities and simplify schema type inference.
1 parent 5a686b1 commit 4d1f211

File tree

11 files changed

+127
-245
lines changed

11 files changed

+127
-245
lines changed
Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,17 @@
11
// server/api/[model]/[id].delete.ts
22
import { eventHandler, getRouterParams, createError } from 'h3'
33
import { eq } from 'drizzle-orm'
4-
import { getTableForModel, getModelSingularName, filterHiddenFields, filterPublicColumns } from '../../utils/modelMapper'
5-
4+
import { getTableForModel, getModelSingularName } from '../../utils/modelMapper'
65
import type { TableWithId } from '../../types'
76
// @ts-expect-error - #site/drizzle is an alias defined by the module
87
import { useDrizzle } from '#site/drizzle'
9-
10-
import { useAutoCrudConfig } from '../../utils/config'
11-
import { checkAdminAccess } from '../../utils/auth'
8+
import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
129

1310
export default eventHandler(async (event) => {
14-
const { resources } = useAutoCrudConfig()
1511
const { model, id } = getRouterParams(event) as { model: string, id: string }
16-
17-
const isAdmin = await checkAdminAccess(event, model, 'delete')
18-
19-
// Check public access if not admin
20-
if (!isAdmin) {
21-
const resourceConfig = resources?.[model]
22-
const isPublic = resourceConfig?.public === true || (Array.isArray(resourceConfig?.public) && resourceConfig.public.includes('delete'))
23-
24-
if (!isPublic) {
25-
throw createError({
26-
statusCode: 401,
27-
message: 'Unauthorized',
28-
})
29-
}
30-
}
12+
const isAdmin = await ensureResourceAccess(event, model, 'delete')
3113

3214
const table = getTableForModel(model) as TableWithId
33-
3415
const singularName = getModelSingularName(model)
3516

3617
const deletedRecord = await useDrizzle()
@@ -46,10 +27,5 @@ export default eventHandler(async (event) => {
4627
})
4728
}
4829

49-
if (isAdmin) {
50-
return filterHiddenFields(model, deletedRecord as Record<string, unknown>)
51-
}
52-
else {
53-
return filterPublicColumns(model, deletedRecord as Record<string, unknown>)
54-
}
30+
return formatResourceResult(model, deletedRecord as Record<string, unknown>, isAdmin)
5531
})
Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,15 @@
11
// server/api/[model]/[id].get.ts
22
import { eventHandler, getRouterParams, createError } from 'h3'
33
import { eq } from 'drizzle-orm'
4-
import { getTableForModel, filterHiddenFields, filterPublicColumns } from '../../utils/modelMapper'
5-
4+
import { getTableForModel } from '../../utils/modelMapper'
65
import type { TableWithId } from '../../types'
76
// @ts-expect-error - #site/drizzle is an alias defined by the module
87
import { useDrizzle } from '#site/drizzle'
9-
10-
import { useAutoCrudConfig } from '../../utils/config'
11-
import { checkAdminAccess } from '../../utils/auth'
8+
import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
129

1310
export default eventHandler(async (event) => {
14-
const { resources } = useAutoCrudConfig()
1511
const { model, id } = getRouterParams(event) as { model: string, id: string }
16-
17-
const isAdmin = await checkAdminAccess(event, model, 'read')
18-
19-
// Check public access if not admin
20-
if (!isAdmin) {
21-
const resourceConfig = resources?.[model]
22-
const isPublic = resourceConfig?.public === true || (Array.isArray(resourceConfig?.public) && resourceConfig.public.includes('read'))
23-
24-
if (!isPublic) {
25-
throw createError({
26-
statusCode: 401,
27-
message: 'Unauthorized',
28-
})
29-
}
30-
}
12+
const isAdmin = await ensureResourceAccess(event, model, 'read')
3113

3214
const table = getTableForModel(model) as TableWithId
3315

@@ -44,10 +26,5 @@ export default eventHandler(async (event) => {
4426
})
4527
}
4628

47-
if (isAdmin) {
48-
return filterHiddenFields(model, record as Record<string, unknown>)
49-
}
50-
else {
51-
return filterPublicColumns(model, record as Record<string, unknown>)
52-
}
29+
return formatResourceResult(model, record as Record<string, unknown>, isAdmin)
5330
})
Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,15 @@
11
// server/api/[model]/[id].patch.ts
22
import { eventHandler, getRouterParams, readBody, createError } from 'h3'
33
import { eq } from 'drizzle-orm'
4-
import { getTableForModel, filterUpdatableFields, filterHiddenFields, filterPublicColumns } from '../../utils/modelMapper'
5-
4+
import { getTableForModel, filterUpdatableFields } from '../../utils/modelMapper'
65
import type { TableWithId } from '../../types'
76
// @ts-expect-error - #site/drizzle is an alias defined by the module
87
import { useDrizzle } from '#site/drizzle'
9-
10-
import { useAutoCrudConfig } from '../../utils/config'
11-
import { checkAdminAccess } from '../../utils/auth'
8+
import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
129

1310
export default eventHandler(async (event) => {
14-
const { resources } = useAutoCrudConfig()
1511
const { model, id } = getRouterParams(event) as { model: string, id: string }
16-
17-
const isAdmin = await checkAdminAccess(event, model, 'update')
18-
19-
// Check public access if not admin
20-
if (!isAdmin) {
21-
const resourceConfig = resources?.[model]
22-
const isPublic = resourceConfig?.public === true || (Array.isArray(resourceConfig?.public) && resourceConfig.public.includes('update'))
23-
24-
if (!isPublic) {
25-
throw createError({
26-
statusCode: 401,
27-
message: 'Unauthorized',
28-
})
29-
}
30-
}
12+
const isAdmin = await ensureResourceAccess(event, model, 'update')
3113

3214
const table = getTableForModel(model) as TableWithId
3315

@@ -36,7 +18,8 @@ export default eventHandler(async (event) => {
3618

3719
// Automatically update updatedAt if it exists
3820
if ('updatedAt' in table) {
39-
payload.updatedAt = new Date()
21+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
22+
(payload as any).updatedAt = new Date()
4023
}
4124

4225
const updatedRecord = await useDrizzle()
@@ -53,10 +36,5 @@ export default eventHandler(async (event) => {
5336
})
5437
}
5538

56-
if (isAdmin) {
57-
return filterHiddenFields(model, updatedRecord as Record<string, unknown>)
58-
}
59-
else {
60-
return filterPublicColumns(model, updatedRecord as Record<string, unknown>)
61-
}
39+
return formatResourceResult(model, updatedRecord as Record<string, unknown>, isAdmin)
6240
})
Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,20 @@
11
// server/api/[model]/index.get.ts
2-
import { eventHandler, getRouterParams, createError } from 'h3'
3-
import { getTableForModel, filterHiddenFields, filterPublicColumns } from '../../utils/modelMapper'
4-
2+
import { eventHandler, getRouterParams } from 'h3'
3+
import { getTableForModel } from '../../utils/modelMapper'
54
// @ts-expect-error - #site/drizzle is an alias defined by the module
65
import { useDrizzle } from '#site/drizzle'
7-
8-
import { useAutoCrudConfig } from '../../utils/config'
9-
import { checkAdminAccess } from '../../utils/auth'
10-
116
import { desc } from 'drizzle-orm'
127
import type { TableWithId } from '../../types'
8+
import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
139

1410
export default eventHandler(async (event) => {
1511
console.log('[GET] Request received', event.path)
16-
const { resources } = useAutoCrudConfig()
1712
const { model } = getRouterParams(event) as { model: string }
18-
19-
const isAdmin = await checkAdminAccess(event, model, 'list')
20-
21-
// Check public access if not admin
22-
if (!isAdmin) {
23-
const resourceConfig = resources?.[model]
24-
const isPublic = resourceConfig?.public === true || (Array.isArray(resourceConfig?.public) && resourceConfig.public.includes('list'))
25-
26-
if (!isPublic) {
27-
throw createError({
28-
statusCode: 401,
29-
message: 'Unauthorized',
30-
})
31-
}
32-
}
13+
const isAdmin = await ensureResourceAccess(event, model, 'list')
3314

3415
const table = getTableForModel(model) as TableWithId
3516

3617
const results = await useDrizzle().select().from(table).orderBy(desc(table.id)).all()
3718

38-
return results.map((item: Record<string, unknown>) => {
39-
if (isAdmin) {
40-
return filterHiddenFields(model, item as Record<string, unknown>)
41-
}
42-
else {
43-
return filterPublicColumns(model, item as Record<string, unknown>)
44-
}
45-
})
19+
return results.map((item: Record<string, unknown>) => formatResourceResult(model, item, isAdmin))
4620
})
Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,13 @@
11
// server/api/[model]/index.post.ts
2-
import { eventHandler, getRouterParams, readBody, createError } from 'h3'
3-
import { getTableForModel, filterHiddenFields, filterUpdatableFields, filterPublicColumns } from '../../utils/modelMapper'
4-
2+
import { eventHandler, getRouterParams, readBody } from 'h3'
3+
import { getTableForModel, filterUpdatableFields } from '../../utils/modelMapper'
54
// @ts-expect-error - #site/drizzle is an alias defined by the module
65
import { useDrizzle } from '#site/drizzle'
7-
8-
import { useAutoCrudConfig } from '../../utils/config'
9-
import { checkAdminAccess } from '../../utils/auth'
6+
import { ensureResourceAccess, formatResourceResult } from '../../utils/handler'
107

118
export default eventHandler(async (event) => {
12-
const { resources } = useAutoCrudConfig()
139
const { model } = getRouterParams(event) as { model: string }
14-
15-
const isAdmin = await checkAdminAccess(event, model, 'create')
16-
17-
// Check public access if not admin
18-
if (!isAdmin) {
19-
const resourceConfig = resources?.[model]
20-
const isPublic = resourceConfig?.public === true || (Array.isArray(resourceConfig?.public) && resourceConfig.public.includes('create'))
21-
22-
if (!isPublic) {
23-
throw createError({
24-
statusCode: 401,
25-
message: 'Unauthorized',
26-
})
27-
}
28-
}
10+
const isAdmin = await ensureResourceAccess(event, model, 'create')
2911

3012
const table = getTableForModel(model)
3113

@@ -34,10 +16,5 @@ export default eventHandler(async (event) => {
3416

3517
const newRecord = await useDrizzle().insert(table).values(payload).returning().get()
3618

37-
if (isAdmin) {
38-
return filterHiddenFields(model, newRecord as Record<string, unknown>)
39-
}
40-
else {
41-
return filterPublicColumns(model, newRecord as Record<string, unknown>)
42-
}
19+
return formatResourceResult(model, newRecord as Record<string, unknown>, isAdmin)
4320
})
Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,9 @@
1-
import { eventHandler, createError } from 'h3'
2-
// @ts-expect-error - #imports is available in runtime
3-
import { requireUserSession } from '#imports'
1+
import { eventHandler } from 'h3'
2+
43
import { getRelations } from '../utils/schema'
5-
import { useAutoCrudConfig } from '../utils/config'
6-
import { verifyJwtToken } from '../utils/jwt'
4+
import { ensureAuthenticated } from '../utils/auth'
75

86
export default eventHandler(async (event) => {
9-
const { auth } = useAutoCrudConfig()
10-
11-
if (auth?.authentication) {
12-
let isAuthenticated = false
13-
if (auth.type === 'jwt' && auth.jwtSecret) {
14-
isAuthenticated = await verifyJwtToken(event, auth.jwtSecret)
15-
}
16-
else {
17-
try {
18-
await requireUserSession(event)
19-
isAuthenticated = true
20-
}
21-
catch {
22-
isAuthenticated = false
23-
}
24-
}
25-
26-
if (!isAuthenticated) {
27-
throw createError({ statusCode: 401, message: 'Unauthorized' })
28-
}
29-
}
30-
7+
await ensureAuthenticated(event)
318
return getRelations()
329
})

src/runtime/server/api/_schema/[table].get.ts

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,12 @@
11
import { eventHandler, createError, getRouterParam } from 'h3'
2-
// @ts-expect-error - #imports is available in runtime
3-
import { requireUserSession } from '#imports'
2+
43
import { getSchema } from '../../utils/schema'
5-
import { useAutoCrudConfig } from '../../utils/config'
6-
import { verifyJwtToken } from '../../utils/jwt'
4+
import { ensureAuthenticated } from '../../utils/auth'
75

86
export default eventHandler(async (event) => {
9-
const { auth } = useAutoCrudConfig()
7+
await ensureAuthenticated(event)
108
const tableName = getRouterParam(event, 'table')
119

12-
if (auth?.authentication) {
13-
let isAuthenticated = false
14-
if (auth.type === 'jwt' && auth.jwtSecret) {
15-
isAuthenticated = await verifyJwtToken(event, auth.jwtSecret)
16-
}
17-
else {
18-
try {
19-
await requireUserSession(event)
20-
isAuthenticated = true
21-
}
22-
catch {
23-
isAuthenticated = false
24-
}
25-
}
26-
27-
if (!isAuthenticated) {
28-
throw createError({ statusCode: 401, message: 'Unauthorized' })
29-
}
30-
}
31-
3210
if (!tableName) {
3311
throw createError({ statusCode: 400, message: 'Table name is required' })
3412
}
Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,9 @@
1-
import { eventHandler, createError } from 'h3'
2-
// @ts-expect-error - #imports is available in runtime
3-
import { requireUserSession } from '#imports'
1+
import { eventHandler } from 'h3'
2+
43
import { getAllSchemas } from '../../utils/schema'
5-
import { useAutoCrudConfig } from '../../utils/config'
6-
import { verifyJwtToken } from '../../utils/jwt'
4+
import { ensureAuthenticated } from '../../utils/auth'
75

86
export default eventHandler(async (event) => {
9-
const { auth } = useAutoCrudConfig()
10-
11-
if (auth?.authentication) {
12-
let isAuthenticated = false
13-
if (auth.type === 'jwt' && auth.jwtSecret) {
14-
isAuthenticated = await verifyJwtToken(event, auth.jwtSecret)
15-
}
16-
else {
17-
try {
18-
await requireUserSession(event)
19-
isAuthenticated = true
20-
}
21-
catch {
22-
isAuthenticated = false
23-
}
24-
}
25-
26-
if (!isAuthenticated) {
27-
throw createError({ statusCode: 401, message: 'Unauthorized' })
28-
}
29-
}
30-
7+
await ensureAuthenticated(event)
318
return getAllSchemas()
329
})

0 commit comments

Comments
 (0)