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.
Files changed (31) hide show
  1. package/README.md +114 -37
  2. package/dist/module.d.mts +53 -1
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +54 -2
  5. package/dist/runtime/server/api/[model]/[id].delete.d.ts +1 -1
  6. package/dist/runtime/server/api/[model]/[id].delete.js +20 -2
  7. package/dist/runtime/server/api/[model]/[id].get.d.ts +1 -1
  8. package/dist/runtime/server/api/[model]/[id].get.js +21 -4
  9. package/dist/runtime/server/api/[model]/[id].patch.d.ts +1 -1
  10. package/dist/runtime/server/api/[model]/[id].patch.js +29 -5
  11. package/dist/runtime/server/api/[model]/index.get.js +25 -4
  12. package/dist/runtime/server/api/[model]/index.post.d.ts +1 -1
  13. package/dist/runtime/server/api/[model]/index.post.js +23 -8
  14. package/dist/runtime/server/utils/auth.d.ts +2 -0
  15. package/dist/runtime/server/utils/auth.js +39 -0
  16. package/dist/runtime/server/utils/config.d.ts +2 -0
  17. package/dist/runtime/server/utils/config.js +4 -0
  18. package/dist/runtime/server/utils/jwt.d.ts +2 -0
  19. package/dist/runtime/server/utils/jwt.js +19 -0
  20. package/dist/runtime/server/utils/modelMapper.d.ts +31 -0
  21. package/dist/runtime/server/utils/modelMapper.js +38 -0
  22. package/package.json +13 -3
  23. package/src/runtime/server/api/[model]/[id].delete.ts +29 -3
  24. package/src/runtime/server/api/[model]/[id].get.ts +29 -5
  25. package/src/runtime/server/api/[model]/[id].patch.ts +40 -9
  26. package/src/runtime/server/api/[model]/index.get.ts +33 -5
  27. package/src/runtime/server/api/[model]/index.post.ts +32 -15
  28. package/src/runtime/server/utils/auth.ts +55 -0
  29. package/src/runtime/server/utils/config.ts +6 -0
  30. package/src/runtime/server/utils/jwt.ts +23 -0
  31. 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
- [![npm version][npm-version-src]][npm-version-href]
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. No configuration needed!
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
- ### New Project (Recommended)
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
- ### Add User
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
- ### Test API
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
- ### 1. Install dependencies
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
- ### 2. Configure Nuxt
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
- ### 3. Configure Drizzle
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
- ### 4. Setup Database Connection
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
- ### 5. Define your database schema
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
- ### 6. Run the project
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
- - `GET /api/users` - List all users
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
- _(Same endpoints for all your tables!)_
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
- The playground includes a sample schema with users, posts, and comments tables, plus an interactive UI to explore all the features.
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
- ## ⚙️ Configuration
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
- - NuxtHub (for database functionality)
249
- - Drizzle ORM
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-auto-crud",
3
3
  "configKey": "autoCrud",
4
- "version": "1.3.0",
4
+ "version": "1.5.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
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<any>>;
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
- return deletedRecord;
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<any>>;
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, getModelSingularName } from "../../utils/modelMapper.js";
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: `${singularName} not found`
26
+ message: "Record not found"
14
27
  });
15
28
  }
16
- return record;
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<any>>;
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 updateData = filterUpdatableFields(model, body);
10
- const record = await useDrizzle().update(table).set(updateData).where(eq(table.id, Number(id))).returning().get();
11
- return record;
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 records = await useDrizzle().select().from(table).all();
8
- return records;
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<any>>;
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 values = {
9
- ...body,
10
- createdAt: /* @__PURE__ */ new Date()
11
- };
12
- const record = await useDrizzle().insert(table).values(values).returning().get();
13
- return record;
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,2 @@
1
+ import type { H3Event } from 'h3';
2
+ export declare function checkAdminAccess(event: H3Event, model: string, action: string): Promise<boolean>;
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ import type { RuntimeModuleOptions } from '../../../types.js';
2
+ export declare const useAutoCrudConfig: () => RuntimeModuleOptions;
@@ -0,0 +1,4 @@
1
+ import { useRuntimeConfig } from "#imports";
2
+ export const useAutoCrudConfig = () => {
3
+ return useRuntimeConfig().autoCrud;
4
+ };
@@ -0,0 +1,2 @@
1
+ import type { H3Event } from 'h3';
2
+ export declare function verifyJwtToken(event: H3Event, secret: string): Promise<boolean>;