nuxt-auto-crud 1.3.0 → 1.5.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/README.md +114 -37
- package/dist/module.d.mts +53 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +54 -2
- package/dist/runtime/server/api/[model]/[id].delete.d.ts +1 -1
- package/dist/runtime/server/api/[model]/[id].delete.js +20 -2
- package/dist/runtime/server/api/[model]/[id].get.d.ts +1 -1
- package/dist/runtime/server/api/[model]/[id].get.js +21 -4
- package/dist/runtime/server/api/[model]/[id].patch.d.ts +1 -1
- package/dist/runtime/server/api/[model]/[id].patch.js +29 -5
- package/dist/runtime/server/api/[model]/index.get.js +25 -4
- package/dist/runtime/server/api/[model]/index.post.d.ts +1 -1
- package/dist/runtime/server/api/[model]/index.post.js +23 -8
- package/dist/runtime/server/utils/auth.d.ts +2 -0
- package/dist/runtime/server/utils/auth.js +39 -0
- package/dist/runtime/server/utils/config.d.ts +2 -0
- package/dist/runtime/server/utils/config.js +4 -0
- package/dist/runtime/server/utils/jwt.d.ts +2 -0
- package/dist/runtime/server/utils/jwt.js +19 -0
- package/dist/runtime/server/utils/modelMapper.d.ts +31 -0
- package/dist/runtime/server/utils/modelMapper.js +38 -0
- package/package.json +13 -3
- package/src/runtime/server/api/[model]/[id].delete.ts +29 -3
- package/src/runtime/server/api/[model]/[id].get.ts +29 -5
- package/src/runtime/server/api/[model]/[id].patch.ts +40 -9
- package/src/runtime/server/api/[model]/index.get.ts +33 -5
- package/src/runtime/server/api/[model]/index.post.ts +32 -15
- package/src/runtime/server/utils/auth.ts +55 -0
- package/src/runtime/server/utils/config.ts +6 -0
- package/src/runtime/server/utils/jwt.ts +23 -0
- package/src/runtime/server/utils/modelMapper.ts +83 -0
package/README.md
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
# Nuxt Auto CRUD
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
5
|
-
[![License][license-src]][license-href]
|
|
6
|
-
[![Nuxt][nuxt-src]][nuxt-href]
|
|
3
|
+
> **Note:** This module is currently in its alpha stage. However, you can use it to accelerate MVP development. It has not been tested thoroughly enough for production use; only happy-path testing is performed for each release.
|
|
7
4
|
|
|
8
|
-
Auto-generate RESTful CRUD APIs for your **Nuxt** application based solely on your database schema.
|
|
5
|
+
Auto-generate RESTful CRUD APIs for your **Nuxt** application based solely on your database schema. Minimal configuration required.
|
|
9
6
|
|
|
10
7
|
- [✨ Release Notes](/CHANGELOG.md)
|
|
11
8
|
- [🎮 Try the Playground](/playground)
|
|
@@ -20,39 +17,39 @@ Auto-generate RESTful CRUD APIs for your **Nuxt** application based solely on yo
|
|
|
20
17
|
|
|
21
18
|
## 📦 How to install
|
|
22
19
|
|
|
23
|
-
###
|
|
20
|
+
### 1. Fullstack Template (Recommended)
|
|
24
21
|
|
|
25
22
|
Start a new project with everything pre-configured using our template:
|
|
26
23
|
|
|
27
24
|
```bash
|
|
28
25
|
npx nuxi init -t gh:clifordpereira/nuxt-auto-crud_template <project-name>
|
|
29
26
|
cd <project-name>
|
|
27
|
+
bun install
|
|
30
28
|
bun db:generate
|
|
31
29
|
bun run dev
|
|
32
30
|
```
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
Open Nuxt DevTools (bottom-middle icon) > `...` menu > **Database** icon to add users.
|
|
36
|
-
> **Note:** If the users table doesn't appear, restart the server (`Ctrl + C` and `bun run dev`).
|
|
37
|
-
|
|
38
|
-
That's it! You can now access the APIs:
|
|
32
|
+
Detailed instructions can be found in [https://auto-crud.clifland.in/](https://auto-crud.clifland.in/)
|
|
39
33
|
|
|
40
|
-
###
|
|
41
|
-
Visit [http://localhost:3000/api/users](http://localhost:3000/api/users).
|
|
42
|
-
|
|
43
|
-
### Existing Project
|
|
34
|
+
### 2. Manual Setup (Existing Project)
|
|
44
35
|
|
|
45
36
|
If you want to add `nuxt-auto-crud` to an existing project, follow these steps:
|
|
46
37
|
|
|
47
|
-
|
|
38
|
+
> **Note:** These instructions assume you are using NuxtHub. If you are using a custom SQLite setup (e.g. better-sqlite3, Turso), please see [Custom Setup](./custom-setup.md).
|
|
39
|
+
|
|
40
|
+
#### Install dependencies
|
|
48
41
|
|
|
49
42
|
```bash
|
|
50
43
|
# Install module and required dependencies
|
|
44
|
+
npm install nuxt-auto-crud @nuxthub/core@latest drizzle-orm
|
|
45
|
+
npm install --save-dev wrangler drizzle-kit
|
|
46
|
+
|
|
47
|
+
# Or using bun
|
|
51
48
|
bun add nuxt-auto-crud @nuxthub/core@latest drizzle-orm
|
|
52
49
|
bun add --dev wrangler drizzle-kit
|
|
53
50
|
```
|
|
54
51
|
|
|
55
|
-
|
|
52
|
+
#### Configure Nuxt
|
|
56
53
|
|
|
57
54
|
Add the modules to your `nuxt.config.ts`:
|
|
58
55
|
|
|
@@ -67,11 +64,12 @@ export default defineNuxtConfig({
|
|
|
67
64
|
|
|
68
65
|
autoCrud: {
|
|
69
66
|
schemaPath: 'server/database/schema', // default value
|
|
67
|
+
auth: false, // Disable auth by default for easy testing
|
|
70
68
|
},
|
|
71
69
|
})
|
|
72
70
|
```
|
|
73
71
|
|
|
74
|
-
|
|
72
|
+
#### Configure Drizzle
|
|
75
73
|
|
|
76
74
|
Add the generation script to your `package.json`:
|
|
77
75
|
|
|
@@ -96,7 +94,7 @@ export default defineConfig({
|
|
|
96
94
|
})
|
|
97
95
|
```
|
|
98
96
|
|
|
99
|
-
|
|
97
|
+
#### Setup Database Connection
|
|
100
98
|
|
|
101
99
|
Create `server/utils/drizzle.ts` to export the database instance:
|
|
102
100
|
|
|
@@ -116,7 +114,7 @@ export function useDrizzle() {
|
|
|
116
114
|
export type User = typeof schema.users.$inferSelect
|
|
117
115
|
```
|
|
118
116
|
|
|
119
|
-
|
|
117
|
+
#### Define your database schema
|
|
120
118
|
|
|
121
119
|
Create `server/database/schema.ts`:
|
|
122
120
|
|
|
@@ -134,7 +132,7 @@ export const users = sqliteTable('users', {
|
|
|
134
132
|
})
|
|
135
133
|
```
|
|
136
134
|
|
|
137
|
-
|
|
135
|
+
#### Run the project
|
|
138
136
|
|
|
139
137
|
```bash
|
|
140
138
|
cd <project-name>
|
|
@@ -142,15 +140,82 @@ bun db:generate
|
|
|
142
140
|
bun run dev
|
|
143
141
|
```
|
|
144
142
|
|
|
145
|
-
That's it! 🎉 Your CRUD APIs are now available
|
|
143
|
+
That's it! 🎉 Your CRUD APIs are now available at `/api/users`.
|
|
144
|
+
|
|
145
|
+
### 3. Backend-only App (API Mode)
|
|
146
|
+
|
|
147
|
+
If you are using Nuxt as a backend for a separate client application (e.g., mobile app, SPA), you can use this module to quickly generate REST APIs.
|
|
148
|
+
|
|
149
|
+
In this case, you might handle authentication differently (e.g., validating tokens in middleware) or disable the built-in auth checks if you have a global auth middleware.
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
export default defineNuxtConfig({
|
|
153
|
+
modules: ['nuxt-auto-crud'],
|
|
154
|
+
autoCrud: {
|
|
155
|
+
auth: false // APIs are public (or handled by your own middleware)
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## 🔐 Authentication Configuration
|
|
161
|
+
|
|
162
|
+
The module supports `auth: false` by default, which exposes all LCRUD APIs. You can enable authentication and authorization as needed.
|
|
163
|
+
|
|
164
|
+
### Session Auth (Default)
|
|
165
|
+
|
|
166
|
+
Requires `nuxt-auth-utils` and `nuxt-authorization`.
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
export default defineNuxtConfig({
|
|
170
|
+
autoCrud: {
|
|
171
|
+
auth: {
|
|
172
|
+
type: 'session',
|
|
173
|
+
authentication: true, // Enables requireUserSession() check
|
|
174
|
+
authorization: true // Enables authorize(model, action) check
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### JWT Auth
|
|
146
181
|
|
|
147
|
-
|
|
148
|
-
- `POST /api/users` - Create a new user
|
|
149
|
-
- `GET /api/users/:id` - Get user by ID
|
|
150
|
-
- `PATCH /api/users/:id` - Update user
|
|
151
|
-
- `DELETE /api/users/:id` - Delete user
|
|
182
|
+
Useful for backend-only apps.
|
|
152
183
|
|
|
153
|
-
|
|
184
|
+
```typescript
|
|
185
|
+
export default defineNuxtConfig({
|
|
186
|
+
autoCrud: {
|
|
187
|
+
auth: {
|
|
188
|
+
type: 'jwt',
|
|
189
|
+
authentication: true,
|
|
190
|
+
jwtSecret: process.env.JWT_SECRET,
|
|
191
|
+
authorization: true
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## 🛡️ Resource Configuration (RBAC)
|
|
198
|
+
|
|
199
|
+
You can define fine-grained access control and resource policies using `autocrud.config.ts` in your project root. This file is optional and useful when you need specific rules per resource.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
// autocrud.config.ts
|
|
203
|
+
export default {
|
|
204
|
+
resources: {
|
|
205
|
+
users: {
|
|
206
|
+
// Access Control
|
|
207
|
+
auth: {
|
|
208
|
+
// Admin has full access
|
|
209
|
+
admin: true,
|
|
210
|
+
// Public (unauthenticated) users can only list and read
|
|
211
|
+
public: ['list', 'read'],
|
|
212
|
+
},
|
|
213
|
+
// Field Visibility
|
|
214
|
+
publicColumns: ['id', 'name', 'avatar'], // Only these columns are returned to public users
|
|
215
|
+
},
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
154
219
|
|
|
155
220
|
## 🎮 Try the Playground
|
|
156
221
|
|
|
@@ -161,17 +226,21 @@ Want to see it in action? Clone this repo and try the playground:
|
|
|
161
226
|
git clone https://github.com/clifordpereira/nuxt-auto-crud.git
|
|
162
227
|
cd nuxt-auto-crud
|
|
163
228
|
|
|
164
|
-
# Install dependencies
|
|
229
|
+
# Install dependencies (parent folder)
|
|
165
230
|
bun install
|
|
166
231
|
|
|
167
|
-
# Run the playground
|
|
232
|
+
# Run the playground (Fullstack)
|
|
168
233
|
cd playground
|
|
169
234
|
bun install
|
|
170
235
|
bun db:generate
|
|
171
236
|
bun run dev
|
|
172
|
-
```
|
|
173
237
|
|
|
174
|
-
|
|
238
|
+
# Run the playground (Backend Only)
|
|
239
|
+
cd playground-backendonly
|
|
240
|
+
bun install
|
|
241
|
+
bun db:generate
|
|
242
|
+
bun run dev
|
|
243
|
+
```
|
|
175
244
|
|
|
176
245
|
## 📖 Usage Examples
|
|
177
246
|
|
|
@@ -219,7 +288,7 @@ await $fetch("/api/users/1", {
|
|
|
219
288
|
});
|
|
220
289
|
```
|
|
221
290
|
|
|
222
|
-
##
|
|
291
|
+
## Configuration
|
|
223
292
|
|
|
224
293
|
### Module Options
|
|
225
294
|
|
|
@@ -242,11 +311,21 @@ By default, the following fields are protected from updates:
|
|
|
242
311
|
|
|
243
312
|
You can customize updatable fields in your schema by modifying the `modelMapper.ts` utility.
|
|
244
313
|
|
|
314
|
+
### Hidden Fields
|
|
315
|
+
|
|
316
|
+
By default, the following fields are hidden from API responses for security:
|
|
317
|
+
|
|
318
|
+
- `password`
|
|
319
|
+
- `secret`
|
|
320
|
+
- `token`
|
|
321
|
+
|
|
322
|
+
You can customize hidden fields by modifying the `modelMapper.ts` utility.
|
|
323
|
+
|
|
245
324
|
## 🔧 Requirements
|
|
246
325
|
|
|
247
326
|
- Nuxt 3 or 4
|
|
248
|
-
-
|
|
249
|
-
-
|
|
327
|
+
- Drizzle ORM (SQLite)
|
|
328
|
+
- NuxtHub (Recommended) or [Custom SQLite Setup](./custom-setup.md)
|
|
250
329
|
|
|
251
330
|
## 🤝 Contributing
|
|
252
331
|
|
|
@@ -259,5 +338,3 @@ Contributions are welcome! Please check out the [contribution guide](/CONTRIBUTI
|
|
|
259
338
|
## 👨💻 Author
|
|
260
339
|
|
|
261
340
|
Made with ❤️ by [Cliford Pereira](https://github.com/clifordpereira)
|
|
262
|
-
|
|
263
|
-
|
package/dist/module.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
|
|
3
3
|
interface ModuleOptions {
|
|
4
|
-
/**
|
|
5
4
|
/**
|
|
6
5
|
* Path to the database schema file
|
|
7
6
|
* @default 'server/database/schema'
|
|
@@ -12,6 +11,59 @@ interface ModuleOptions {
|
|
|
12
11
|
* @default 'server/utils/drizzle'
|
|
13
12
|
*/
|
|
14
13
|
drizzlePath?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Authentication configuration
|
|
16
|
+
*/
|
|
17
|
+
auth?: boolean | AuthOptions;
|
|
18
|
+
/**
|
|
19
|
+
* Resource-specific configuration
|
|
20
|
+
* Define public access and column visibility
|
|
21
|
+
*/
|
|
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
|
+
};
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
interface AuthOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Authentication type
|
|
41
|
+
* @default 'session'
|
|
42
|
+
*/
|
|
43
|
+
type?: 'session' | 'jwt';
|
|
44
|
+
/**
|
|
45
|
+
* JWT Secret (required if type is 'jwt')
|
|
46
|
+
*/
|
|
47
|
+
jwtSecret?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Enable authentication checks (requires nuxt-auth-utils for session)
|
|
50
|
+
* @default false
|
|
51
|
+
*/
|
|
52
|
+
authentication: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Enable authorization checks (requires nuxt-authorization)
|
|
55
|
+
* @default false
|
|
56
|
+
*/
|
|
57
|
+
authorization?: boolean;
|
|
58
|
+
}
|
|
59
|
+
interface RuntimeModuleOptions extends Omit<ModuleOptions, 'auth'> {
|
|
60
|
+
auth: AuthOptions;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
declare module '@nuxt/schema' {
|
|
64
|
+
interface RuntimeConfig {
|
|
65
|
+
autoCrud: RuntimeModuleOptions;
|
|
66
|
+
}
|
|
15
67
|
}
|
|
16
68
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
17
69
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -7,9 +7,10 @@ const module$1 = defineNuxtModule({
|
|
|
7
7
|
},
|
|
8
8
|
defaults: {
|
|
9
9
|
schemaPath: "server/database/schema",
|
|
10
|
-
drizzlePath: "server/utils/drizzle"
|
|
10
|
+
drizzlePath: "server/utils/drizzle",
|
|
11
|
+
auth: false
|
|
11
12
|
},
|
|
12
|
-
setup(options, nuxt) {
|
|
13
|
+
async setup(options, nuxt) {
|
|
13
14
|
const resolver = createResolver(import.meta.url);
|
|
14
15
|
const schemaPath = resolver.resolve(
|
|
15
16
|
nuxt.options.rootDir,
|
|
@@ -21,6 +22,57 @@ const module$1 = defineNuxtModule({
|
|
|
21
22
|
options.drizzlePath
|
|
22
23
|
);
|
|
23
24
|
nuxt.options.alias["#site/drizzle"] = drizzlePath;
|
|
25
|
+
nuxt.options.alias["#authorization"] = nuxt.options.alias["#authorization"] || "nuxt-authorization/utils";
|
|
26
|
+
const { loadConfig } = await import('c12');
|
|
27
|
+
const { config: externalConfig } = await loadConfig({
|
|
28
|
+
name: "autocrud",
|
|
29
|
+
cwd: nuxt.options.rootDir
|
|
30
|
+
});
|
|
31
|
+
let mergedAuth = {
|
|
32
|
+
authentication: false,
|
|
33
|
+
authorization: false,
|
|
34
|
+
type: "session"
|
|
35
|
+
};
|
|
36
|
+
if (options.auth === true) {
|
|
37
|
+
mergedAuth = {
|
|
38
|
+
authentication: true,
|
|
39
|
+
authorization: true,
|
|
40
|
+
type: "session",
|
|
41
|
+
...typeof externalConfig?.auth === "object" ? externalConfig.auth : {}
|
|
42
|
+
};
|
|
43
|
+
} else if (options.auth === false) {
|
|
44
|
+
mergedAuth = {
|
|
45
|
+
authentication: false,
|
|
46
|
+
authorization: false,
|
|
47
|
+
type: "session"
|
|
48
|
+
};
|
|
49
|
+
} else {
|
|
50
|
+
mergedAuth = {
|
|
51
|
+
authentication: true,
|
|
52
|
+
// Default to true if object provided? Or undefined?
|
|
53
|
+
// If options.auth is undefined, we might want defaults.
|
|
54
|
+
// But if defaults say auth: false, then options.auth might be undefined.
|
|
55
|
+
// Let's stick to the plan: default is false.
|
|
56
|
+
...typeof externalConfig?.auth === "object" ? externalConfig.auth : {},
|
|
57
|
+
...typeof options.auth === "object" ? options.auth : {}
|
|
58
|
+
};
|
|
59
|
+
if (mergedAuth.authentication === void 0) {
|
|
60
|
+
mergedAuth.authentication = false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const mergedResources = {
|
|
64
|
+
...externalConfig?.resources,
|
|
65
|
+
...options.resources
|
|
66
|
+
};
|
|
67
|
+
nuxt.options.runtimeConfig.autoCrud = {
|
|
68
|
+
auth: {
|
|
69
|
+
authentication: mergedAuth.authentication ?? false,
|
|
70
|
+
authorization: mergedAuth.authorization ?? false,
|
|
71
|
+
type: mergedAuth.type ?? "session",
|
|
72
|
+
jwtSecret: mergedAuth.jwtSecret
|
|
73
|
+
},
|
|
74
|
+
resources: mergedResources || {}
|
|
75
|
+
};
|
|
24
76
|
const apiDir = resolver.resolve("./runtime/server/api");
|
|
25
77
|
addServerHandler({
|
|
26
78
|
route: "/api/:model",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Record<string, unknown>>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import { eventHandler, getRouterParams, createError } from "h3";
|
|
2
2
|
import { eq } from "drizzle-orm";
|
|
3
|
-
import { getTableForModel, getModelSingularName } from "../../utils/modelMapper.js";
|
|
3
|
+
import { getTableForModel, getModelSingularName, filterHiddenFields, filterPublicColumns } from "../../utils/modelMapper.js";
|
|
4
4
|
import { useDrizzle } from "#site/drizzle";
|
|
5
|
+
import { useAutoCrudConfig } from "../../utils/config.js";
|
|
6
|
+
import { checkAdminAccess } from "../../utils/auth.js";
|
|
5
7
|
export default eventHandler(async (event) => {
|
|
8
|
+
const { resources } = useAutoCrudConfig();
|
|
6
9
|
const { model, id } = getRouterParams(event);
|
|
10
|
+
const isAdmin = await checkAdminAccess(event, model, "delete");
|
|
11
|
+
if (!isAdmin) {
|
|
12
|
+
const resourceConfig = resources?.[model];
|
|
13
|
+
const isPublic = resourceConfig?.public === true || Array.isArray(resourceConfig?.public) && resourceConfig.public.includes("delete");
|
|
14
|
+
if (!isPublic) {
|
|
15
|
+
throw createError({
|
|
16
|
+
statusCode: 401,
|
|
17
|
+
message: "Unauthorized"
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
7
21
|
const table = getTableForModel(model);
|
|
8
22
|
const singularName = getModelSingularName(model);
|
|
9
23
|
const deletedRecord = await useDrizzle().delete(table).where(eq(table.id, Number(id))).returning().get();
|
|
@@ -13,5 +27,9 @@ export default eventHandler(async (event) => {
|
|
|
13
27
|
message: `${singularName} not found`
|
|
14
28
|
});
|
|
15
29
|
}
|
|
16
|
-
|
|
30
|
+
if (isAdmin) {
|
|
31
|
+
return filterHiddenFields(model, deletedRecord);
|
|
32
|
+
} else {
|
|
33
|
+
return filterPublicColumns(model, deletedRecord);
|
|
34
|
+
}
|
|
17
35
|
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Record<string, unknown>>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
import { eventHandler, getRouterParams, createError } from "h3";
|
|
2
2
|
import { eq } from "drizzle-orm";
|
|
3
|
-
import { getTableForModel,
|
|
3
|
+
import { getTableForModel, filterHiddenFields, filterPublicColumns } from "../../utils/modelMapper.js";
|
|
4
4
|
import { useDrizzle } from "#site/drizzle";
|
|
5
|
+
import { useAutoCrudConfig } from "../../utils/config.js";
|
|
6
|
+
import { checkAdminAccess } from "../../utils/auth.js";
|
|
5
7
|
export default eventHandler(async (event) => {
|
|
8
|
+
const { resources } = useAutoCrudConfig();
|
|
6
9
|
const { model, id } = getRouterParams(event);
|
|
10
|
+
const isAdmin = await checkAdminAccess(event, model, "read");
|
|
11
|
+
if (!isAdmin) {
|
|
12
|
+
const resourceConfig = resources?.[model];
|
|
13
|
+
const isPublic = resourceConfig?.public === true || Array.isArray(resourceConfig?.public) && resourceConfig.public.includes("read");
|
|
14
|
+
if (!isPublic) {
|
|
15
|
+
throw createError({
|
|
16
|
+
statusCode: 401,
|
|
17
|
+
message: "Unauthorized"
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
7
21
|
const table = getTableForModel(model);
|
|
8
|
-
const singularName = getModelSingularName(model);
|
|
9
22
|
const record = await useDrizzle().select().from(table).where(eq(table.id, Number(id))).get();
|
|
10
23
|
if (!record) {
|
|
11
24
|
throw createError({
|
|
12
25
|
statusCode: 404,
|
|
13
|
-
message:
|
|
26
|
+
message: "Record not found"
|
|
14
27
|
});
|
|
15
28
|
}
|
|
16
|
-
|
|
29
|
+
if (isAdmin) {
|
|
30
|
+
return filterHiddenFields(model, record);
|
|
31
|
+
} else {
|
|
32
|
+
return filterPublicColumns(model, record);
|
|
33
|
+
}
|
|
17
34
|
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Record<string, unknown>>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,12 +1,36 @@
|
|
|
1
|
-
import { eventHandler, getRouterParams, readBody } from "h3";
|
|
1
|
+
import { eventHandler, getRouterParams, readBody, createError } from "h3";
|
|
2
2
|
import { eq } from "drizzle-orm";
|
|
3
|
-
import { getTableForModel, filterUpdatableFields } from "../../utils/modelMapper.js";
|
|
3
|
+
import { getTableForModel, filterUpdatableFields, filterHiddenFields, filterPublicColumns } from "../../utils/modelMapper.js";
|
|
4
4
|
import { useDrizzle } from "#site/drizzle";
|
|
5
|
+
import { useAutoCrudConfig } from "../../utils/config.js";
|
|
6
|
+
import { checkAdminAccess } from "../../utils/auth.js";
|
|
5
7
|
export default eventHandler(async (event) => {
|
|
8
|
+
const { resources } = useAutoCrudConfig();
|
|
6
9
|
const { model, id } = getRouterParams(event);
|
|
10
|
+
const isAdmin = await checkAdminAccess(event, model, "update");
|
|
11
|
+
if (!isAdmin) {
|
|
12
|
+
const resourceConfig = resources?.[model];
|
|
13
|
+
const isPublic = resourceConfig?.public === true || Array.isArray(resourceConfig?.public) && resourceConfig.public.includes("update");
|
|
14
|
+
if (!isPublic) {
|
|
15
|
+
throw createError({
|
|
16
|
+
statusCode: 401,
|
|
17
|
+
message: "Unauthorized"
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
7
21
|
const table = getTableForModel(model);
|
|
8
22
|
const body = await readBody(event);
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
23
|
+
const payload = filterUpdatableFields(model, body);
|
|
24
|
+
const updatedRecord = await useDrizzle().update(table).set(payload).where(eq(table.id, Number(id))).returning().get();
|
|
25
|
+
if (!updatedRecord) {
|
|
26
|
+
throw createError({
|
|
27
|
+
statusCode: 404,
|
|
28
|
+
message: "Record not found"
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (isAdmin) {
|
|
32
|
+
return filterHiddenFields(model, updatedRecord);
|
|
33
|
+
} else {
|
|
34
|
+
return filterPublicColumns(model, updatedRecord);
|
|
35
|
+
}
|
|
12
36
|
});
|
|
@@ -1,9 +1,30 @@
|
|
|
1
|
-
import { eventHandler, getRouterParams } from "h3";
|
|
2
|
-
import { getTableForModel } from "../../utils/modelMapper.js";
|
|
1
|
+
import { eventHandler, getRouterParams, createError } from "h3";
|
|
2
|
+
import { getTableForModel, filterHiddenFields, filterPublicColumns } from "../../utils/modelMapper.js";
|
|
3
3
|
import { useDrizzle } from "#site/drizzle";
|
|
4
|
+
import { useAutoCrudConfig } from "../../utils/config.js";
|
|
5
|
+
import { checkAdminAccess } from "../../utils/auth.js";
|
|
4
6
|
export default eventHandler(async (event) => {
|
|
7
|
+
console.log("[GET] Request received", event.path);
|
|
8
|
+
const { resources } = useAutoCrudConfig();
|
|
5
9
|
const { model } = getRouterParams(event);
|
|
10
|
+
const isAdmin = await checkAdminAccess(event, model, "list");
|
|
11
|
+
if (!isAdmin) {
|
|
12
|
+
const resourceConfig = resources?.[model];
|
|
13
|
+
const isPublic = resourceConfig?.public === true || Array.isArray(resourceConfig?.public) && resourceConfig.public.includes("list");
|
|
14
|
+
if (!isPublic) {
|
|
15
|
+
throw createError({
|
|
16
|
+
statusCode: 401,
|
|
17
|
+
message: "Unauthorized"
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
6
21
|
const table = getTableForModel(model);
|
|
7
|
-
const
|
|
8
|
-
return
|
|
22
|
+
const results = await useDrizzle().select().from(table).all();
|
|
23
|
+
return results.map((item) => {
|
|
24
|
+
if (isAdmin) {
|
|
25
|
+
return filterHiddenFields(model, item);
|
|
26
|
+
} else {
|
|
27
|
+
return filterPublicColumns(model, item);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
9
30
|
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Record<string, unknown>>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,14 +1,29 @@
|
|
|
1
|
-
import { eventHandler, getRouterParams, readBody } from "h3";
|
|
2
|
-
import { getTableForModel } from "../../utils/modelMapper.js";
|
|
1
|
+
import { eventHandler, getRouterParams, readBody, createError } from "h3";
|
|
2
|
+
import { getTableForModel, filterHiddenFields, filterUpdatableFields, filterPublicColumns } from "../../utils/modelMapper.js";
|
|
3
3
|
import { useDrizzle } from "#site/drizzle";
|
|
4
|
+
import { useAutoCrudConfig } from "../../utils/config.js";
|
|
5
|
+
import { checkAdminAccess } from "../../utils/auth.js";
|
|
4
6
|
export default eventHandler(async (event) => {
|
|
7
|
+
const { resources } = useAutoCrudConfig();
|
|
5
8
|
const { model } = getRouterParams(event);
|
|
9
|
+
const isAdmin = await checkAdminAccess(event, model, "create");
|
|
10
|
+
if (!isAdmin) {
|
|
11
|
+
const resourceConfig = resources?.[model];
|
|
12
|
+
const isPublic = resourceConfig?.public === true || Array.isArray(resourceConfig?.public) && resourceConfig.public.includes("create");
|
|
13
|
+
if (!isPublic) {
|
|
14
|
+
throw createError({
|
|
15
|
+
statusCode: 401,
|
|
16
|
+
message: "Unauthorized"
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
6
20
|
const table = getTableForModel(model);
|
|
7
21
|
const body = await readBody(event);
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
const payload = filterUpdatableFields(model, body);
|
|
23
|
+
const newRecord = await useDrizzle().insert(table).values(payload).returning().get();
|
|
24
|
+
if (isAdmin) {
|
|
25
|
+
return filterHiddenFields(model, newRecord);
|
|
26
|
+
} else {
|
|
27
|
+
return filterPublicColumns(model, newRecord);
|
|
28
|
+
}
|
|
14
29
|
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createError } from "h3";
|
|
2
|
+
import { useAutoCrudConfig } from "./config.js";
|
|
3
|
+
import { verifyJwtToken } from "./jwt.js";
|
|
4
|
+
export async function checkAdminAccess(event, model, action) {
|
|
5
|
+
const { auth } = useAutoCrudConfig();
|
|
6
|
+
if (!auth?.authentication) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
if (auth.type === "jwt") {
|
|
10
|
+
if (!auth.jwtSecret) {
|
|
11
|
+
console.warn("JWT Secret is not configured but auth type is jwt");
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return verifyJwtToken(event, auth.jwtSecret);
|
|
15
|
+
}
|
|
16
|
+
if (typeof requireUserSession !== "function") {
|
|
17
|
+
throw new TypeError("requireUserSession is not available");
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
await requireUserSession(event);
|
|
21
|
+
if (auth.authorization) {
|
|
22
|
+
if (event.context.ability) {
|
|
23
|
+
const can = event.context.ability.can(action, model);
|
|
24
|
+
if (!can) {
|
|
25
|
+
throw createError({
|
|
26
|
+
statusCode: 403,
|
|
27
|
+
message: "Forbidden"
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
} catch (e) {
|
|
34
|
+
if (e.statusCode === 403) {
|
|
35
|
+
throw e;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|