nuxt-auto-crud 1.24.0 → 1.26.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 CHANGED
@@ -1,373 +1,43 @@
1
1
  # Nuxt Auto CRUD
2
2
 
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.
3
+ **Nuxt Auto CRUD is a headless, zero-codegen CRUD engine that transforms Drizzle ORM schemas into fully functional RESTful APIs for Nuxt 4.**
4
4
 
5
- Auto-expose RESTful CRUD APIs for your **Nuxt** application based solely on your database schema. Minimal configuration required.
5
+ | Specification | Details |
6
+ | :--- | :--- |
7
+ | **Runtime** | Nuxt 4 (`app/` directory), Nitro |
8
+ | **Persistence** | SQLite / libSQL (Optimized for Cloudflare D1) |
9
+ | **ORM & SSOT** | Drizzle ORM (Schema-driven) |
10
+ | **Validation** | `drizzle-zod` (Dynamic derivation) |
6
11
 
7
- **Core Philosophy:**
8
- The main objective of this module is to **expose CRUD APIs without the need for writing code**. You define your database schema, and `nuxt-auto-crud` handles the rest.
12
+ ## 🛠 Architectural Logic: Zero-Codegen
13
+ NAC treats your Drizzle schema as the **Single Source of Truth (SSOT)**. Unlike traditional scaffolds, it does not generate physical files; it mounts dynamic Nitro handlers at runtime.
9
14
 
10
- You don't need to setup an extra server or database to create an MVP of an application. The Nuxt (Nitro) server and SQLite can save you time and money.
11
- And you don't need a separate Strapi or Supabase setup to automate your CRUD process. `nuxt-auto-crud` will help you with that and accelerate your development exponentially.
15
+ * **Dynamic Routing**: Automatically maps `GET|POST|PATCH|DELETE` to your Drizzle tables.
16
+ * **Agentic Compatibility**: Built with an MCP-friendly structure to allow AI Agents to interact directly with the schema-driven API.
12
17
 
13
- While this module exposes CRUD APIs, you are expected to build your own frontend application to consume them.
18
+ ## 🔐 RBAC & Permissions
19
+ Integrates with `nuxt-authorization` for database-driven Role-Based Access Control.
20
+ * **Ownership Logic**: Supports `update_own` and `delete_own` via `createdBy` column reflection.
21
+ * **Granular Scopes**: Fine-grained control over `list` vs `list_all` (drafts/soft-deleted).
14
22
 
15
- - [✨ Release Notes](/CHANGELOG.md)
16
- - [🗺️ Roadmap](/ROADMAP.md)
17
- - [🎮 Try the Playground](/playground)
23
+ ## 🌐 Endpoints
24
+ | Method | Endpoint | Description |
25
+ | :--- | :--- | :--- |
26
+ | `GET` | `/api/:model` | List with filtering/paging |
27
+ | `POST` | `/api/:model` | Validated creation |
28
+ | `GET` | `/api/:model/:id` | Single record retrieval |
29
+ | `PATCH` | `/api/:model/:id` | Partial validated update |
30
+ | `DELETE` | `/api/:model/:id` | Soft/Hard deletion |
18
31
 
19
- ## 🚀 CRUD APIs are ready to use without code
32
+ ---
20
33
 
21
- Once installed, your database tables' CRUD APIs are exposed in a controlled manner:
34
+ ## Installation
35
+ It is highly recommended to use the [Template](https://auto-crud.clifland.in/docs/auto-crud) for new installations.
22
36
 
23
- - `GET /api/:model` - List all records
24
- - `POST /api/:model` - Create a new record
25
- - `GET /api/:model/:id` - Get record by ID
26
- - `PATCH /api/:model/:id` - Update record
27
- - `DELETE /api/:model/:id` - Delete record
37
+ If you are adding it to an existing application, refer to the [Manual Installation](https://auto-crud.clifland.in/docs/manual-installation) guide.
28
38
 
29
- ## 📦 How to install
39
+ [YouTube Walkthrough](https://www.youtube.com/watch?v=_o0cddJUU50&list=PLnbvxcojhIixqM1J08Tnm7vmMdx2wsy4B)
30
40
 
31
- ### 1. Fullstack Template (Recommended)
41
+ [NPM Package](https://www.npmjs.com/package/nuxt-auto-crud)
32
42
 
33
- Start a new project with everything pre-configured using our template:
34
-
35
- ```bash
36
- npx nuxi init -t gh:clifordpereira/nuxt-auto-crud_template <project-name>
37
- cd <project-name>
38
- bun install
39
- bun db:generate
40
- bun run dev
41
- ```
42
-
43
- **Template Usage Modes:**
44
-
45
- 1. **Fullstack App**: The template includes the `nuxt-auto-crud` module, providing both the backend APIs and the frontend UI. [Watch Demo](https://youtu.be/M9-koXmhB9k)
46
- 2. **Frontend Only**: You can use the template just for the frontend. In this case, you don't need to install the module in the frontend app. Instead, you would install `nuxt-auto-crud` in a separate backend setup (e.g., another Nuxt project acting as the API).
47
-
48
- Detailed instructions can be found in [https://auto-crud.clifland.in/docs/auto-crud](https://auto-crud.clifland.in/docs/auto-crud)
49
-
50
- ### 2. Manual Setup (Existing Project)
51
-
52
- If you want to add `nuxt-auto-crud` to an existing project, follow these steps:
53
-
54
- > **Note:** These instructions have been simplified for NuxtHub.
55
-
56
- #### Install dependencies
57
-
58
- ```bash
59
- npm install nuxt-auto-crud @nuxthub/core@^0.10.0 drizzle-orm
60
- npm install nuxt-auth-utils nuxt-authorization # Optional: for authentication
61
- npm install --save-dev wrangler drizzle-kit
62
- ```
63
-
64
- > You can also use `bun` or `pnpm` instead of `npm`.
65
-
66
- #### Configure Nuxt
67
-
68
- Add the modules to your `nuxt.config.ts`:
69
-
70
- ```typescript
71
- // nuxt.config.ts
72
- export default defineNuxtConfig({
73
- modules: ['@nuxthub/core', 'nuxt-auto-crud'],
74
-
75
- hub: {
76
- db: 'sqlite',
77
- },
78
-
79
- autoCrud: {
80
- schemaPath: 'server/db/schema',
81
- // auth: false,
82
- auth: {
83
- type: 'session', // for Normal Authentication with nuxt-auth-utils
84
- authentication: true,
85
- authorization: true,
86
- },
87
- },
88
- })
89
- ```
90
-
91
- #### Configure Drizzle
92
-
93
- Add the generation script to your `package.json`:
94
-
95
- ```json
96
- {
97
- "scripts": {
98
- "db:generate": "nuxt db generate"
99
- }
100
- // ...
101
- }
102
- ```
103
-
104
- Create `drizzle.config.ts` in your project root:
105
-
106
- ```typescript
107
- // drizzle.config.ts
108
- import { defineConfig } from 'drizzle-kit'
109
-
110
- export default defineConfig({
111
- dialect: 'sqlite',
112
- schema: './server/db/schema/index.ts', // Point to your schema index file
113
- out: './server/db/migrations'
114
- })
115
- ```
116
-
117
-
118
-
119
- #### Define your database schema
120
-
121
- Create your schema files in `server/db/schema/`. For example, `server/db/schema/users.ts`:
122
-
123
- ```typescript
124
- // server/db/schema/users.ts
125
- import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
126
-
127
- export const users = sqliteTable('users', {
128
- id: integer('id').primaryKey({ autoIncrement: true }),
129
- name: text('name').notNull(),
130
- email: text('email').notNull().unique(),
131
- password: text('password').notNull(),
132
- avatar: text('avatar').notNull(),
133
- createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
134
- })
135
- ```
136
- #### Run the project
137
-
138
- ```bash
139
- cd <project-name>
140
- bun db:generate
141
- bun run dev
142
- ```
143
-
144
- That's it! 🎉 Your CRUD APIs are now available at `/api/users`.
145
-
146
-
147
- #### Adding New Schemas
148
-
149
- To add a new table (e.g., `posts`), simply create a new file in your schema directory:
150
-
151
- ```typescript
152
- // server/db/schema/posts.ts
153
- import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
154
- import { users } from './users'
155
-
156
- export const posts = sqliteTable('posts', {
157
- id: integer('id').primaryKey({ autoIncrement: true }),
158
- title: text('title').notNull(),
159
- content: text('content').notNull(),
160
- authorId: integer('author_id').references(() => users.id),
161
- createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
162
- })
163
- ```
164
-
165
- Then, ensure it is exported in your `server/db/schema/index.ts` (if you are using an index file) or that your `drizzle.config.ts` is pointing to the correct location.
166
-
167
- ```typescript
168
- // server/db/schema/index.ts
169
- export * from './users'
170
- export * from './posts'
171
- ```
172
-
173
- After adding the file, run the generation script:
174
-
175
- ```bash
176
- bun db:generate
177
- ```
178
-
179
- The new API endpoints (e.g., `/api/posts`) will be automatically available. [Watch Demo](https://youtu.be/7gW0KW1KtN0)
180
-
181
-
182
- > **Note:** The `organization.ts` and `cms.ts` files you might see in the playground are just examples and are commented out by default. You should implement a robust schema tailored to your production needs.
183
-
184
- ### 3. Backend-only App (API Mode)
185
-
186
- 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 expose REST APIs.
187
-
188
- 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.
189
-
190
- ```typescript
191
- // nuxt.config.ts
192
- export default defineNuxtConfig({
193
- modules: ['nuxt-auto-crud'],
194
- autoCrud: {
195
- schemaPath: 'server/db/schema',
196
- // auth: false, // Uncomment this line for testing APIs without auth
197
- auth: {
198
- type: 'jwt', // for app providing backend apis only
199
- authentication: true,
200
- authorization: true,
201
- jwtSecret: process.env.NUXT_JWT_SECRET || 'test-secret-key-123',
202
- },
203
- },
204
- })
205
- ```
206
-
207
- **Note:** Remember to add your `NUXT_JWT_SECRET` in `.env`.
208
-
209
- You should also configure `drizzle.config.ts` correctly:
210
-
211
- ```typescript
212
- // drizzle.config.ts
213
- import { defineConfig } from 'drizzle-kit'
214
-
215
- export default defineConfig({
216
- dialect: 'sqlite',
217
- schema: './server/db/schema/index.ts',
218
- out: './server/db/migrations',
219
- tablesFilter: ['!_hub_migrations'],
220
- })
221
- ```
222
-
223
- ## 🔐 Authentication
224
-
225
- Authentication is enabled by default using **Session Auth** (requires `nuxt-auth-utils` and `nuxt-authorization`).
226
-
227
- To disable auth for testing:
228
- ```typescript
229
- autoCrud: { auth: false }
230
- ```
231
-
232
- For **JWT Auth** (backend-only apps) or advanced configuration, see the [Authentication docs](https://auto-crud.clifland.in/docs/configuration/authentication).
233
-
234
-
235
-
236
- ## 🛡️ Public View Configuration (Field Visibility)
237
-
238
- You can define which fields are visible to unauthenticated (guest) users in your `nuxt.config.ts`.
239
- > **Note:** Access control (RBAC) - determining *who* can access *what* - is expected to be handled by your database/permissions system (using `nuxt-authorization`). This configuration only controls the *serialization* of the response for guests.
240
-
241
- ```typescript
242
- // nuxt.config.ts
243
- export default defineNuxtConfig({
244
- autoCrud: {
245
- resources: {
246
- // Guest View: Only these columns are visible to unauthenticated users.
247
- // Access control (who can list/read) is managed by your DB permissions.
248
- users: ['id', 'name', 'avatar'],
249
- },
250
- },
251
- })
252
- ```
253
-
254
-
255
- ## 👤 Owner-based Permissions (RBAC)
256
-
257
- In addition to standard `create`, `read`, `update`, and `delete` permissions, you can assign **Ownership Permissions**:
258
-
259
- - `list`: Allows a user to view a list of active records (status='active').
260
- - `list_all`: Allows a user to view **all** records, including inactive ones (e.g., status='inactive', 'draft').
261
- - `update_own`: Allows a user to update a record **only if they created it**.
262
- - `delete_own`: Allows a user to delete a record **only if they created it**.
263
-
264
- **How it works:**
265
- The module checks for ownership using the following logic:
266
- 1. **Standard Tables:** Checks if the record has a `createdBy` (or `userId`) column that matches the logged-in user's ID.
267
- 2. **Users Table:** Checks if the record being accessed is the user's own profile (`id` matches).
268
-
269
- **Prerequisites:**
270
- Ensure your schema includes a `createdBy` field for resources where you want this behavior:
271
-
272
- ```typescript
273
- export const posts = sqliteTable('posts', {
274
- // ...
275
- createdBy: integer('created_by'), // Recommended
276
- })
277
- ```
278
-
279
- ## 🎮 Try the Playground
280
-
281
- Want to see it in action? Clone this repo and try the playground:
282
-
283
- ```bash
284
- # Clone the repository
285
- git clone https://github.com/clifordpereira/nuxt-auto-crud.git
286
- cd nuxt-auto-crud
287
-
288
- # Install dependencies (parent folder)
289
- bun install
290
-
291
- # Run the playground (Fullstack)
292
- cd playground
293
- bun install
294
- bun db:generate
295
- bun run dev
296
- ```
297
-
298
- ## 📖 Usage Examples
299
-
300
- ### Create a Record
301
-
302
- ```typescript
303
- const user = await $fetch("/api/users", {
304
- method: "POST",
305
- body: {
306
- name: "Cliford Pereira",
307
- email: "clifordpereira@gmail.com",
308
- bio: "Full-Stack Developer",
309
- },
310
- });
311
- ```
312
-
313
- ### Get All Records
314
-
315
- ```typescript
316
- const users = await $fetch("/api/users");
317
- ```
318
-
319
- ### Get Record by ID
320
-
321
- ```typescript
322
- const user = await $fetch("/api/users/1");
323
- ```
324
-
325
- ### Update a Record
326
-
327
- ```typescript
328
- const updated = await $fetch("/api/users/1", {
329
- method: "PATCH",
330
- body: {
331
- bio: "Updated bio",
332
- },
333
- });
334
- ```
335
-
336
- ### Delete a Record
337
-
338
- ```typescript
339
- await $fetch("/api/users/1", {
340
- method: "DELETE",
341
- });
342
- ```
343
-
344
- > **Note:** If authentication is enabled (default):
345
- > - **Fullstack App:** The module integrates with `nuxt-auth-utils`, so session cookies are handled automatically.
346
- > - **Backend-only App:** You must include the `Authorization: Bearer <token>` header in your requests.
347
-
348
-
349
-
350
- ## 🔧 Requirements
351
-
352
- - Nuxt 3 or 4
353
- - Drizzle ORM (SQLite)
354
- - NuxtHub >= 0.10.0
355
-
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)
362
-
363
- ## 🤝 Contributing
364
-
365
- Contributions are welcome! Please check out the [contribution guide](/CONTRIBUTING.md).
366
-
367
- ## 📝 License
368
-
369
- [MIT License](./LICENSE)
370
-
371
- ## 👨‍💻 Author
372
-
373
- Made with ❤️ by [Cliford Pereira](https://github.com/clifordpereira)
43
+ [Creator: Clifland](https://www.clifland.in/)
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-auto-crud",
3
3
  "configKey": "autoCrud",
4
- "version": "1.24.0",
4
+ "version": "1.26.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -67,6 +67,11 @@ const module$1 = defineNuxtModule({
67
67
  method: "get",
68
68
  handler: resolver.resolve(apiDir, "_relations.get")
69
69
  });
70
+ addServerHandler({
71
+ route: "/api/_meta",
72
+ method: "get",
73
+ handler: resolver.resolve(apiDir, "_meta.get")
74
+ });
70
75
  addServerHandler({
71
76
  route: "/api/:model",
72
77
  method: "get",
@@ -1,7 +1,7 @@
1
1
  import { eventHandler, getRouterParams, readBody } from "h3";
2
2
  import { getUserSession } from "#imports";
3
3
  import { eq } from "drizzle-orm";
4
- import { getTableForModel, filterUpdatableFields } from "../../utils/modelMapper.js";
4
+ import { getTableForModel, getZodSchema } from "../../utils/modelMapper.js";
5
5
  import { db } from "hub:db";
6
6
  import { ensureResourceAccess, formatResourceResult, hashPayloadFields } from "../../utils/handler.js";
7
7
  import { RecordNotFoundError } from "../../exceptions.js";
@@ -10,7 +10,8 @@ export default eventHandler(async (event) => {
10
10
  const isAdmin = await ensureResourceAccess(event, model, "update", { id });
11
11
  const table = getTableForModel(model);
12
12
  const body = await readBody(event);
13
- const payload = filterUpdatableFields(model, body);
13
+ const schema = getZodSchema(model, "patch");
14
+ const payload = await schema.parseAsync(body);
14
15
  if ("status" in payload) {
15
16
  const { checkAdminAccess } = await import("../../utils/auth.js");
16
17
  const hasStatusPermission = await checkAdminAccess(event, model, "update_status", { id });
@@ -1,6 +1,6 @@
1
1
  import { eventHandler, getRouterParams, readBody } from "h3";
2
2
  import { getUserSession } from "#imports";
3
- import { getTableForModel, filterUpdatableFields } from "../../utils/modelMapper.js";
3
+ import { getTableForModel, getZodSchema } from "../../utils/modelMapper.js";
4
4
  import { db } from "hub:db";
5
5
  import { ensureResourceAccess, formatResourceResult, hashPayloadFields } from "../../utils/handler.js";
6
6
  export default eventHandler(async (event) => {
@@ -8,7 +8,8 @@ export default eventHandler(async (event) => {
8
8
  const isAdmin = await ensureResourceAccess(event, model, "create");
9
9
  const table = getTableForModel(model);
10
10
  const body = await readBody(event);
11
- const payload = filterUpdatableFields(model, body);
11
+ const schema = getZodSchema(model, "insert");
12
+ const payload = await schema.parseAsync(body);
12
13
  if ("status" in payload) {
13
14
  const { checkAdminAccess } = await import("../../utils/auth.js");
14
15
  const hasStatusPermission = await checkAdminAccess(event, model, "update_status");
@@ -0,0 +1,21 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | {
2
+ architecture: string;
3
+ version: string;
4
+ resources: ({
5
+ resource: string;
6
+ endpoint: string;
7
+ labelField: string;
8
+ methods: string[];
9
+ fields: {
10
+ name: string;
11
+ type: any;
12
+ required: boolean;
13
+ isEnum: boolean;
14
+ options: any;
15
+ references: any;
16
+ isRelation: boolean;
17
+ isReadOnly: boolean;
18
+ }[];
19
+ } | null)[];
20
+ }>>;
21
+ export default _default;
@@ -0,0 +1,91 @@
1
+ import { eventHandler, getQuery, getHeader } from "h3";
2
+ import { getTableForModel, getAvailableModels } from "../utils/modelMapper.js";
3
+ import { getTableColumns as getDrizzleTableColumns } from "drizzle-orm";
4
+ import { getTableConfig } from "drizzle-orm/sqlite-core";
5
+ import { PROTECTED_FIELDS, HIDDEN_FIELDS } from "../utils/constants.js";
6
+ import { db } from "hub:db";
7
+ import { ensureAuthenticated } from "../utils/auth.js";
8
+ export default eventHandler(async (event) => {
9
+ await ensureAuthenticated(event);
10
+ const query = getQuery(event);
11
+ const acceptHeader = getHeader(event, "accept") || "";
12
+ const models = getAvailableModels().length > 0 ? getAvailableModels() : Object.keys(db?.query || {});
13
+ const resources = models.map((model) => {
14
+ try {
15
+ const table = getTableForModel(model);
16
+ const columns = getDrizzleTableColumns(table);
17
+ const config = getTableConfig(table);
18
+ const fields = Object.entries(columns).filter(([name]) => !HIDDEN_FIELDS.includes(name)).map(([name, col]) => {
19
+ let references = null;
20
+ const fk = config?.foreignKeys.find((f) => f.reference().columns[0].name === col.name);
21
+ if (fk) {
22
+ references = fk.reference().foreignTable[Symbol.for("drizzle:Name")];
23
+ } else if (col.referenceConfig?.foreignTable) {
24
+ const foreignTable = col.referenceConfig.foreignTable;
25
+ references = foreignTable[Symbol.for("drizzle:Name")] || foreignTable.name;
26
+ }
27
+ const semanticType = col.columnType.toLowerCase().replace("sqlite", "");
28
+ return {
29
+ name,
30
+ type: semanticType,
31
+ required: col.notNull || false,
32
+ isEnum: !!col.enumValues,
33
+ options: col.enumValues || null,
34
+ references,
35
+ isRelation: !!references,
36
+ // Agentic Hint: Is this field writable by the user/agent?
37
+ isReadOnly: PROTECTED_FIELDS.includes(name)
38
+ };
39
+ });
40
+ const fieldNames = fields.map((f) => f.name);
41
+ const labelField = fieldNames.find((n) => n === "name") || fieldNames.find((n) => n === "title") || fieldNames.find((n) => n === "email") || "id";
42
+ return {
43
+ resource: model,
44
+ endpoint: `/api/${model}`,
45
+ labelField,
46
+ methods: ["GET", "POST", "PATCH", "DELETE"],
47
+ fields
48
+ };
49
+ } catch {
50
+ return null;
51
+ }
52
+ }).filter(Boolean);
53
+ const payload = {
54
+ architecture: "Clifland-NAC",
55
+ version: "1.0.0-agentic",
56
+ resources
57
+ };
58
+ const currentToken = getQuery(event).token || getHeader(event, "authorization")?.split(" ")[1];
59
+ const tokenSuffix = currentToken ? `?token=${currentToken}` : "";
60
+ if (query.format === "md" || acceptHeader.includes("text/markdown")) {
61
+ let markdown = `# ${payload.architecture} API Manifest (v${payload.version})
62
+
63
+ `;
64
+ payload.resources.forEach((res) => {
65
+ if (!res) return;
66
+ markdown += `### Resource: ${res.resource}
67
+ `;
68
+ markdown += `- **Endpoint**: \`${res.endpoint}${tokenSuffix}\`
69
+ `;
70
+ markdown += `- **Methods**: ${res.methods.join(", ")}
71
+ `;
72
+ markdown += `- **Primary Label**: \`${res.labelField}\`
73
+
74
+ `;
75
+ markdown += `| Field | Type | Required | Writable | Details |
76
+ `;
77
+ markdown += `| :--- | :--- | :--- | :--- | :--- |
78
+ `;
79
+ res.fields.forEach((f) => {
80
+ const details = f.isEnum && f.options ? `Options: ${f.options.join(", ")}` : f.references ? `Refs: ${f.references}` : "-";
81
+ markdown += `| ${f.name} | ${f.type} | ${f.required ? "\u2705" : "\u274C"} | ${f.isReadOnly ? "\u274C" : "\u2705"} | ${details} |
82
+ `;
83
+ });
84
+ markdown += `
85
+ ---
86
+ `;
87
+ });
88
+ return markdown;
89
+ }
90
+ return payload;
91
+ });
@@ -1,5 +1,6 @@
1
1
  declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
2
2
  resource: string;
3
+ labelField: string;
3
4
  fields: import("../../utils/schema.js").Field[];
4
5
  }>>;
5
6
  export default _default;
@@ -7,6 +7,13 @@ export async function checkAdminAccess(event, model, action, context) {
7
7
  if (!auth?.authentication) {
8
8
  return true;
9
9
  }
10
+ const authHeader = getHeader(event, "authorization");
11
+ const query = getQuery(event);
12
+ const apiToken = useRuntimeConfig(event).apiSecretToken;
13
+ const token = (authHeader?.startsWith("Bearer ") ? authHeader.split(" ")[1] : null) || query.token;
14
+ if (token && apiToken && token === apiToken) {
15
+ return true;
16
+ }
10
17
  if (auth.type === "jwt") {
11
18
  if (!auth.jwtSecret) {
12
19
  console.warn("JWT Secret is not configured but auth type is jwt");
@@ -40,8 +47,8 @@ export async function checkAdminAccess(event, model, action, context) {
40
47
  const hasCreatedBy = "createdBy" in table;
41
48
  const hasUserId = "userId" in table;
42
49
  if (hasCreatedBy || hasUserId) {
43
- const query = db.select().from(table).where(eq(table.id, Number(context.id)));
44
- const record = await query.get();
50
+ const query2 = db.select().from(table).where(eq(table.id, Number(context.id)));
51
+ const record = await query2.get();
45
52
  if (record) {
46
53
  if (hasCreatedBy) {
47
54
  if (String(record.createdBy) === String(user.id)) return true;
@@ -73,12 +80,18 @@ export async function checkAdminAccess(event, model, action, context) {
73
80
  }
74
81
  export async function ensureAuthenticated(event) {
75
82
  const { auth } = useAutoCrudConfig();
83
+ const runtimeConfig = useRuntimeConfig(event);
76
84
  if (!auth?.authentication) return;
77
- if (auth.type === "jwt" && auth.jwtSecret) {
78
- if (!await verifyJwtToken(event, auth.jwtSecret)) {
79
- throw createError({ statusCode: 401, message: "Unauthorized" });
80
- }
85
+ const authHeader = getHeader(event, "authorization");
86
+ const query = getQuery(event);
87
+ const apiToken = runtimeConfig.apiSecretToken;
88
+ const token = (authHeader?.startsWith("Bearer ") ? authHeader.split(" ")[1] : null) || query.token;
89
+ if (token && apiToken && token === apiToken) {
81
90
  return;
82
91
  }
92
+ if (auth.type === "jwt" && auth.jwtSecret) {
93
+ if (await verifyJwtToken(event, auth.jwtSecret)) return;
94
+ throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
95
+ }
83
96
  await requireUserSession(event);
84
97
  }
@@ -0,0 +1,2 @@
1
+ export declare const PROTECTED_FIELDS: string[];
2
+ export declare const HIDDEN_FIELDS: string[];
@@ -0,0 +1,36 @@
1
+ export const PROTECTED_FIELDS = [
2
+ "id",
3
+ "createdAt",
4
+ "updatedAt",
5
+ "deletedAt",
6
+ "createdBy",
7
+ "updatedBy",
8
+ "deletedBy",
9
+ "created_at",
10
+ "updated_at",
11
+ "deleted_at",
12
+ "created_by",
13
+ "updated_by",
14
+ "deleted_by"
15
+ ];
16
+ export const HIDDEN_FIELDS = [
17
+ // Sensitive Auth
18
+ "password",
19
+ "resetToken",
20
+ "reset_token",
21
+ "resetExpires",
22
+ "reset_expires",
23
+ "githubId",
24
+ "github_id",
25
+ "googleId",
26
+ "google_id",
27
+ "secret",
28
+ "token",
29
+ // System Fields (Leakage prevention)
30
+ "deletedAt",
31
+ "createdBy",
32
+ "updatedBy",
33
+ "deleted_at",
34
+ "created_by",
35
+ "updated_by"
36
+ ];