nuxt-auto-crud 1.4.0 → 1.6.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 +113 -84
- package/dist/module.d.mts +26 -22
- package/dist/module.json +1 -1
- package/dist/module.mjs +52 -9
- package/dist/runtime/composables/useRelationDisplay.d.ts +12 -0
- package/dist/runtime/composables/useRelationDisplay.js +48 -0
- package/dist/runtime/composables/useResourceSchemas.d.ts +19 -0
- package/dist/runtime/composables/useResourceSchemas.js +17 -0
- package/dist/runtime/server/api/[model]/[id].patch.js +3 -0
- package/dist/runtime/server/api/[model]/index.get.js +2 -1
- package/dist/runtime/server/api/_relations.get.d.ts +2 -0
- package/dist/runtime/server/api/_relations.get.js +24 -0
- package/dist/runtime/server/api/_schema/[table].get.d.ts +10 -0
- package/dist/runtime/server/api/_schema/[table].get.js +32 -0
- package/dist/runtime/server/api/_schema/index.get.d.ts +2 -0
- package/dist/runtime/server/api/_schema/index.get.js +24 -0
- package/dist/runtime/server/plugins/seed.d.ts +2 -0
- package/dist/runtime/server/plugins/seed.js +36 -0
- package/dist/runtime/server/utils/auth.js +1 -1
- package/dist/runtime/server/utils/config.d.ts +2 -2
- package/dist/runtime/server/utils/modelMapper.js +16 -8
- package/dist/runtime/server/utils/schema.d.ts +20 -0
- package/dist/runtime/server/utils/schema.js +70 -0
- package/package.json +8 -5
- package/src/runtime/composables/useRelationDisplay.ts +67 -0
- package/src/runtime/composables/useResourceSchemas.ts +42 -0
- package/src/runtime/server/api/[model]/[id].patch.ts +5 -0
- package/src/runtime/server/api/[model]/index.get.ts +5 -2
- package/src/runtime/server/api/_relations.get.ts +31 -0
- package/src/runtime/server/api/_schema/[table].get.ts +41 -0
- package/src/runtime/server/api/_schema/index.get.ts +31 -0
- package/src/runtime/server/plugins/seed.ts +55 -0
- package/src/runtime/server/utils/auth.ts +1 -1
- package/src/runtime/server/utils/config.ts +3 -3
- package/src/runtime/server/utils/modelMapper.ts +25 -11
- package/src/runtime/server/utils/schema.ts +96 -0
package/README.md
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
# Nuxt Auto CRUD
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
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.
|
|
6
4
|
|
|
7
5
|
Auto-generate RESTful CRUD APIs for your **Nuxt** application based solely on your database schema. Minimal configuration required.
|
|
8
6
|
|
|
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.
|
|
9
|
+
|
|
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.
|
|
12
|
+
|
|
13
|
+
While we provide a playground with a CMS-like interface, this is primarily to demonstrate the capabilities. You are expected to build your own frontend application to consume these APIs.
|
|
14
|
+
|
|
9
15
|
- [✨ Release Notes](/CHANGELOG.md)
|
|
10
16
|
- [🎮 Try the Playground](/playground)
|
|
11
17
|
|
|
12
18
|
## 🚀 CRUD APIs are ready to use without code
|
|
13
19
|
|
|
20
|
+
Once installed, your database tables automatically become API endpoints:
|
|
21
|
+
|
|
14
22
|
- `GET /api/:model` - List all records
|
|
15
23
|
- `POST /api/:model` - Create a new record
|
|
16
24
|
- `GET /api/:model/:id` - Get record by ID
|
|
@@ -19,7 +27,7 @@ Auto-generate RESTful CRUD APIs for your **Nuxt** application based solely on yo
|
|
|
19
27
|
|
|
20
28
|
## 📦 How to install
|
|
21
29
|
|
|
22
|
-
###
|
|
30
|
+
### 1. Fullstack Template (Recommended)
|
|
23
31
|
|
|
24
32
|
Start a new project with everything pre-configured using our template:
|
|
25
33
|
|
|
@@ -31,24 +39,20 @@ bun db:generate
|
|
|
31
39
|
bun run dev
|
|
32
40
|
```
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
### Add User
|
|
37
|
-
Open Nuxt DevTools (bottom-middle icon) > `...` menu > **Database** icon to add users.
|
|
38
|
-
> **Note:** If the users table doesn't appear, restart the server (`Ctrl + C` and `bun run dev`).
|
|
42
|
+
**Template Usage Modes:**
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
1. **Fullstack App**: The template includes the `nuxt-auto-crud` module, providing both the backend APIs and the frontend UI.
|
|
45
|
+
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).
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
Visit [http://localhost:3000/api/users](http://localhost:3000/api/users).
|
|
47
|
+
Detailed instructions can be found in [https://auto-crud.clifland.in/](https://auto-crud.clifland.in/)
|
|
44
48
|
|
|
45
|
-
### Existing Project
|
|
49
|
+
### 2. Manual Setup (Existing Project)
|
|
46
50
|
|
|
47
51
|
If you want to add `nuxt-auto-crud` to an existing project, follow these steps:
|
|
48
52
|
|
|
49
53
|
> **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).
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
#### Install dependencies
|
|
52
56
|
|
|
53
57
|
```bash
|
|
54
58
|
# Install module and required dependencies
|
|
@@ -60,7 +64,7 @@ bun add nuxt-auto-crud @nuxthub/core@latest drizzle-orm
|
|
|
60
64
|
bun add --dev wrangler drizzle-kit
|
|
61
65
|
```
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
#### Configure Nuxt
|
|
64
68
|
|
|
65
69
|
Add the modules to your `nuxt.config.ts`:
|
|
66
70
|
|
|
@@ -75,11 +79,12 @@ export default defineNuxtConfig({
|
|
|
75
79
|
|
|
76
80
|
autoCrud: {
|
|
77
81
|
schemaPath: 'server/database/schema', // default value
|
|
82
|
+
auth: false, // Disable auth by default for easy testing
|
|
78
83
|
},
|
|
79
84
|
})
|
|
80
85
|
```
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
#### Configure Drizzle
|
|
83
88
|
|
|
84
89
|
Add the generation script to your `package.json`:
|
|
85
90
|
|
|
@@ -88,6 +93,7 @@ Add the generation script to your `package.json`:
|
|
|
88
93
|
"scripts": {
|
|
89
94
|
"db:generate": "drizzle-kit generate"
|
|
90
95
|
}
|
|
96
|
+
// ...
|
|
91
97
|
}
|
|
92
98
|
```
|
|
93
99
|
|
|
@@ -99,12 +105,12 @@ import { defineConfig } from 'drizzle-kit'
|
|
|
99
105
|
|
|
100
106
|
export default defineConfig({
|
|
101
107
|
dialect: 'sqlite',
|
|
102
|
-
schema: './server/database/schema.ts',
|
|
108
|
+
schema: './server/database/schema/index.ts', // Point to your schema index file
|
|
103
109
|
out: './server/database/migrations'
|
|
104
110
|
})
|
|
105
111
|
```
|
|
106
112
|
|
|
107
|
-
|
|
113
|
+
#### Setup Database Connection
|
|
108
114
|
|
|
109
115
|
Create `server/utils/drizzle.ts` to export the database instance:
|
|
110
116
|
|
|
@@ -124,12 +130,12 @@ export function useDrizzle() {
|
|
|
124
130
|
export type User = typeof schema.users.$inferSelect
|
|
125
131
|
```
|
|
126
132
|
|
|
127
|
-
|
|
133
|
+
#### Define your database schema
|
|
128
134
|
|
|
129
|
-
Create `server/database/schema.ts`:
|
|
135
|
+
Create your schema files in `server/database/schema/`. For example, `server/database/schema/users.ts`:
|
|
130
136
|
|
|
131
137
|
```typescript
|
|
132
|
-
// server/database/schema.ts
|
|
138
|
+
// server/database/schema/users.ts
|
|
133
139
|
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
|
|
134
140
|
|
|
135
141
|
export const users = sqliteTable('users', {
|
|
@@ -142,7 +148,9 @@ export const users = sqliteTable('users', {
|
|
|
142
148
|
})
|
|
143
149
|
```
|
|
144
150
|
|
|
145
|
-
|
|
151
|
+
> **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.
|
|
152
|
+
|
|
153
|
+
#### Run the project
|
|
146
154
|
|
|
147
155
|
```bash
|
|
148
156
|
cd <project-name>
|
|
@@ -150,15 +158,86 @@ bun db:generate
|
|
|
150
158
|
bun run dev
|
|
151
159
|
```
|
|
152
160
|
|
|
153
|
-
That's it! 🎉 Your CRUD APIs are now available
|
|
161
|
+
That's it! 🎉 Your CRUD APIs are now available at `/api/users`.
|
|
162
|
+
|
|
163
|
+
### 3. Backend-only App (API Mode)
|
|
164
|
+
|
|
165
|
+
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.
|
|
166
|
+
|
|
167
|
+
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.
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
export default defineNuxtConfig({
|
|
171
|
+
modules: ['nuxt-auto-crud'],
|
|
172
|
+
autoCrud: {
|
|
173
|
+
auth: false // APIs are public (or handled by your own middleware)
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## 🔐 Authentication Configuration
|
|
154
179
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
180
|
+
The module supports `auth: false` by default, which exposes all LCRUD APIs. You can enable authentication and authorization as needed.
|
|
181
|
+
|
|
182
|
+
### Session Auth (Default)
|
|
183
|
+
|
|
184
|
+
Requires `nuxt-auth-utils` and `nuxt-authorization`.
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
export default defineNuxtConfig({
|
|
188
|
+
autoCrud: {
|
|
189
|
+
auth: {
|
|
190
|
+
type: 'session',
|
|
191
|
+
authentication: true, // Enables requireUserSession() check
|
|
192
|
+
authorization: true // Enables authorize(model, action) check
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### JWT Auth
|
|
199
|
+
|
|
200
|
+
Useful for backend-only apps.
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
export default defineNuxtConfig({
|
|
204
|
+
autoCrud: {
|
|
205
|
+
auth: {
|
|
206
|
+
type: 'jwt',
|
|
207
|
+
authentication: true,
|
|
208
|
+
jwtSecret: process.env.JWT_SECRET,
|
|
209
|
+
authorization: true
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
```
|
|
160
214
|
|
|
161
|
-
|
|
215
|
+
## 🛡️ Resource Configuration (RBAC)
|
|
216
|
+
|
|
217
|
+
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.
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// autocrud.config.ts
|
|
221
|
+
export default {
|
|
222
|
+
resources: {
|
|
223
|
+
users: {
|
|
224
|
+
// Access Control
|
|
225
|
+
auth: {
|
|
226
|
+
// Admin has full access
|
|
227
|
+
admin: true,
|
|
228
|
+
// Public (unauthenticated) users can only list and read
|
|
229
|
+
public: ['list', 'read'],
|
|
230
|
+
},
|
|
231
|
+
// Field Visibility
|
|
232
|
+
publicColumns: ['id', 'name', 'avatar'], // Only these columns are returned to public users
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## ⚠️ Known Issues
|
|
239
|
+
|
|
240
|
+
- **Foreign Key Naming:** Currently, if you have multiple foreign keys referring to the same table (e.g., `customer_id` and `author_id` both referring to the `users` table), the automatic relation handling might assume `user_id` for both. This is a known limitation in the current alpha version.
|
|
162
241
|
|
|
163
242
|
## 🎮 Try the Playground
|
|
164
243
|
|
|
@@ -172,15 +251,13 @@ cd nuxt-auto-crud
|
|
|
172
251
|
# Install dependencies (parent folder)
|
|
173
252
|
bun install
|
|
174
253
|
|
|
175
|
-
# Run the playground (
|
|
176
|
-
cd playground
|
|
254
|
+
# Run the playground (Fullstack)
|
|
255
|
+
cd playground
|
|
177
256
|
bun install
|
|
178
257
|
bun db:generate
|
|
179
258
|
bun run dev
|
|
180
259
|
```
|
|
181
260
|
|
|
182
|
-
The playground includes a sample schema with users, posts, and comments tables, plus an interactive UI to explore all the features.
|
|
183
|
-
|
|
184
261
|
## 📖 Usage Examples
|
|
185
262
|
|
|
186
263
|
### Create a Record
|
|
@@ -224,57 +301,11 @@ const updated = await $fetch("/api/users/1", {
|
|
|
224
301
|
```typescript
|
|
225
302
|
await $fetch("/api/users/1", {
|
|
226
303
|
method: "DELETE",
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
## Use Cases
|
|
231
|
-
|
|
232
|
-
### 1. Full-stack App (with Auth)
|
|
233
|
-
|
|
234
|
-
If you are building a full-stack Nuxt application, you can easily integrate `nuxt-auth-utils` and `nuxt-authorization` to secure your auto-generated APIs.
|
|
235
|
-
|
|
236
|
-
First, install the modules:
|
|
237
|
-
|
|
238
|
-
```bash
|
|
239
|
-
npx nuxi@latest module add auth-utils
|
|
240
|
-
npm install nuxt-authorization
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
Then, configure `nuxt-auto-crud` in your `nuxt.config.ts`:
|
|
244
|
-
|
|
245
|
-
```ts
|
|
246
|
-
export default defineNuxtConfig({
|
|
247
|
-
modules: [
|
|
248
|
-
'nuxt-auto-crud',
|
|
249
|
-
'nuxt-auth-utils'
|
|
250
|
-
],
|
|
251
|
-
autoCrud: {
|
|
252
|
-
auth: {
|
|
253
|
-
enabled: true, // Enables requireUserSession() check
|
|
254
|
-
authorization: true // Enables authorize(model, action) check
|
|
255
|
-
}
|
|
304
|
+
headers: {
|
|
305
|
+
// If auth is enabled
|
|
306
|
+
Authorization: 'Bearer ...'
|
|
256
307
|
}
|
|
257
|
-
})
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
When `authorization` is enabled, the module will call `authorize(model, action)` where action is one of: `create`, `read`, `update`, `delete`.
|
|
261
|
-
|
|
262
|
-
### 2. Backend-only App (API Mode)
|
|
263
|
-
|
|
264
|
-
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.
|
|
265
|
-
|
|
266
|
-
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.
|
|
267
|
-
|
|
268
|
-
```ts
|
|
269
|
-
export default defineNuxtConfig({
|
|
270
|
-
modules: ['nuxt-auto-crud'],
|
|
271
|
-
autoCrud: {
|
|
272
|
-
auth: {
|
|
273
|
-
enabled: false, // Default
|
|
274
|
-
authorization: false // Default
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
})
|
|
308
|
+
});
|
|
278
309
|
```
|
|
279
310
|
|
|
280
311
|
## Configuration
|
|
@@ -327,5 +358,3 @@ Contributions are welcome! Please check out the [contribution guide](/CONTRIBUTI
|
|
|
327
358
|
## 👨💻 Author
|
|
328
359
|
|
|
329
360
|
Made with ❤️ by [Cliford Pereira](https://github.com/clifordpereira)
|
|
330
|
-
|
|
331
|
-
|
package/dist/module.d.mts
CHANGED
|
@@ -14,27 +14,7 @@ interface ModuleOptions {
|
|
|
14
14
|
/**
|
|
15
15
|
* Authentication configuration
|
|
16
16
|
*/
|
|
17
|
-
auth?:
|
|
18
|
-
/**
|
|
19
|
-
* Authentication type
|
|
20
|
-
* @default 'session'
|
|
21
|
-
*/
|
|
22
|
-
type?: 'session' | 'jwt';
|
|
23
|
-
/**
|
|
24
|
-
* JWT Secret (required if type is 'jwt')
|
|
25
|
-
*/
|
|
26
|
-
jwtSecret?: string;
|
|
27
|
-
/**
|
|
28
|
-
* Enable authentication checks (requires nuxt-auth-utils for session)
|
|
29
|
-
* @default false
|
|
30
|
-
*/
|
|
31
|
-
enabled: boolean;
|
|
32
|
-
/**
|
|
33
|
-
* Enable authorization checks (requires nuxt-authorization)
|
|
34
|
-
* @default false
|
|
35
|
-
*/
|
|
36
|
-
authorization?: boolean;
|
|
37
|
-
};
|
|
17
|
+
auth?: boolean | AuthOptions;
|
|
38
18
|
/**
|
|
39
19
|
* Resource-specific configuration
|
|
40
20
|
* Define public access and column visibility
|
|
@@ -55,10 +35,34 @@ interface ModuleOptions {
|
|
|
55
35
|
};
|
|
56
36
|
};
|
|
57
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
|
+
}
|
|
58
62
|
|
|
59
63
|
declare module '@nuxt/schema' {
|
|
60
64
|
interface RuntimeConfig {
|
|
61
|
-
autoCrud:
|
|
65
|
+
autoCrud: RuntimeModuleOptions;
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
68
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineNuxtModule, createResolver, addServerHandler, addServerImportsDir } from '@nuxt/kit';
|
|
1
|
+
import { defineNuxtModule, createResolver, addServerHandler, addServerImportsDir, addImportsDir, addServerPlugin } from '@nuxt/kit';
|
|
2
2
|
|
|
3
3
|
const module$1 = defineNuxtModule({
|
|
4
4
|
meta: {
|
|
@@ -7,7 +7,8 @@ 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
13
|
async setup(options, nuxt) {
|
|
13
14
|
const resolver = createResolver(import.meta.url);
|
|
@@ -27,17 +28,45 @@ const module$1 = defineNuxtModule({
|
|
|
27
28
|
name: "autocrud",
|
|
28
29
|
cwd: nuxt.options.rootDir
|
|
29
30
|
});
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
let mergedAuth = {
|
|
32
|
+
authentication: false,
|
|
33
|
+
authorization: false,
|
|
34
|
+
type: "session"
|
|
33
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
|
+
}
|
|
34
63
|
const mergedResources = {
|
|
35
64
|
...externalConfig?.resources,
|
|
36
65
|
...options.resources
|
|
37
66
|
};
|
|
38
67
|
nuxt.options.runtimeConfig.autoCrud = {
|
|
39
68
|
auth: {
|
|
40
|
-
|
|
69
|
+
authentication: mergedAuth.authentication ?? false,
|
|
41
70
|
authorization: mergedAuth.authorization ?? false,
|
|
42
71
|
type: mergedAuth.type ?? "session",
|
|
43
72
|
jwtSecret: mergedAuth.jwtSecret
|
|
@@ -45,6 +74,21 @@ const module$1 = defineNuxtModule({
|
|
|
45
74
|
resources: mergedResources || {}
|
|
46
75
|
};
|
|
47
76
|
const apiDir = resolver.resolve("./runtime/server/api");
|
|
77
|
+
addServerHandler({
|
|
78
|
+
route: "/api/_schema",
|
|
79
|
+
method: "get",
|
|
80
|
+
handler: resolver.resolve(apiDir, "_schema/index.get")
|
|
81
|
+
});
|
|
82
|
+
addServerHandler({
|
|
83
|
+
route: "/api/_schema/:table",
|
|
84
|
+
method: "get",
|
|
85
|
+
handler: resolver.resolve(apiDir, "_schema/[table].get")
|
|
86
|
+
});
|
|
87
|
+
addServerHandler({
|
|
88
|
+
route: "/api/_relations",
|
|
89
|
+
method: "get",
|
|
90
|
+
handler: resolver.resolve(apiDir, "_relations.get")
|
|
91
|
+
});
|
|
48
92
|
addServerHandler({
|
|
49
93
|
route: "/api/:model",
|
|
50
94
|
method: "get",
|
|
@@ -71,9 +115,8 @@ const module$1 = defineNuxtModule({
|
|
|
71
115
|
handler: resolver.resolve(apiDir, "[model]/[id].delete")
|
|
72
116
|
});
|
|
73
117
|
addServerImportsDir(resolver.resolve("./runtime/server/utils"));
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
console.log(` - API: /api/[model]`);
|
|
118
|
+
addImportsDir(resolver.resolve("./runtime/composables"));
|
|
119
|
+
addServerPlugin(resolver.resolve("./runtime/server/plugins/seed"));
|
|
77
120
|
}
|
|
78
121
|
});
|
|
79
122
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const useRelationDisplay: (schema: {
|
|
2
|
+
resource: string;
|
|
3
|
+
fields: {
|
|
4
|
+
name: string;
|
|
5
|
+
type: string;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
}[];
|
|
8
|
+
}) => {
|
|
9
|
+
fetchRelations: () => Promise<void>;
|
|
10
|
+
getDisplayValue: (key: string, value: unknown) => unknown;
|
|
11
|
+
relationsMap: import("vue").Ref<Record<string, Record<string, string>>, Record<string, Record<string, string>>>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ref, useFetch, useRequestHeaders } from "#imports";
|
|
2
|
+
export const useRelationDisplay = (schema) => {
|
|
3
|
+
const resourceName = schema.resource;
|
|
4
|
+
const relationsMap = ref({});
|
|
5
|
+
const displayValues = ref({});
|
|
6
|
+
const headers = useRequestHeaders(["cookie"]);
|
|
7
|
+
const fetchRelations = async () => {
|
|
8
|
+
const { data: relations } = await useFetch("/api/_relations");
|
|
9
|
+
if (relations.value) {
|
|
10
|
+
relationsMap.value = relations.value;
|
|
11
|
+
}
|
|
12
|
+
const resourceRelations = relationsMap.value[resourceName] || {};
|
|
13
|
+
const relationFields = Object.keys(resourceRelations);
|
|
14
|
+
if (relationFields.length === 0) return;
|
|
15
|
+
await Promise.all(
|
|
16
|
+
relationFields.map(async (fieldName) => {
|
|
17
|
+
const targetTable = resourceRelations[fieldName];
|
|
18
|
+
try {
|
|
19
|
+
const relatedData = await $fetch(`/api/${targetTable}`, { headers });
|
|
20
|
+
if (relatedData) {
|
|
21
|
+
displayValues.value[fieldName] = relatedData.reduce(
|
|
22
|
+
(acc, item) => {
|
|
23
|
+
const id = item.id;
|
|
24
|
+
const label = item.name || item.title || item.email || item.username || `#${item.id}`;
|
|
25
|
+
acc[id] = label;
|
|
26
|
+
return acc;
|
|
27
|
+
},
|
|
28
|
+
{}
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error(`Failed to fetch relation data for ${targetTable}:`, error);
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
const getDisplayValue = (key, value) => {
|
|
38
|
+
if (displayValues.value[key] && (typeof value === "number" || typeof value === "string")) {
|
|
39
|
+
return displayValues.value[key][value] || value;
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
fetchRelations,
|
|
45
|
+
getDisplayValue,
|
|
46
|
+
relationsMap
|
|
47
|
+
};
|
|
48
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Ref } from 'vue';
|
|
2
|
+
export interface ResourceField {
|
|
3
|
+
name: string;
|
|
4
|
+
type: string;
|
|
5
|
+
required?: boolean;
|
|
6
|
+
selectOptions?: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface ResourceSchema {
|
|
9
|
+
resource: string;
|
|
10
|
+
fields: ResourceField[];
|
|
11
|
+
}
|
|
12
|
+
export type ResourceSchemas = Record<string, ResourceSchema>;
|
|
13
|
+
export declare const useResourceSchemas: () => Promise<{
|
|
14
|
+
schemas: Ref<ResourceSchemas | null | undefined>;
|
|
15
|
+
getSchema: (resource: string) => ResourceSchema | undefined;
|
|
16
|
+
status: Ref<"idle" | "pending" | "success" | "error">;
|
|
17
|
+
error: Ref<any>;
|
|
18
|
+
refresh: () => Promise<void>;
|
|
19
|
+
}>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useAsyncData, useRequestHeaders } from "#imports";
|
|
2
|
+
export const useResourceSchemas = async () => {
|
|
3
|
+
const { data: schemas, status, error, refresh } = await useAsyncData("resource-schemas", () => $fetch("/api/_schema", {
|
|
4
|
+
headers: useRequestHeaders(["cookie"])
|
|
5
|
+
}));
|
|
6
|
+
const getSchema = (resource) => {
|
|
7
|
+
if (!schemas.value) return void 0;
|
|
8
|
+
return schemas.value[resource];
|
|
9
|
+
};
|
|
10
|
+
return {
|
|
11
|
+
schemas,
|
|
12
|
+
getSchema,
|
|
13
|
+
status,
|
|
14
|
+
error,
|
|
15
|
+
refresh
|
|
16
|
+
};
|
|
17
|
+
};
|
|
@@ -21,6 +21,9 @@ export default eventHandler(async (event) => {
|
|
|
21
21
|
const table = getTableForModel(model);
|
|
22
22
|
const body = await readBody(event);
|
|
23
23
|
const payload = filterUpdatableFields(model, body);
|
|
24
|
+
if ("updatedAt" in table) {
|
|
25
|
+
payload.updatedAt = /* @__PURE__ */ new Date();
|
|
26
|
+
}
|
|
24
27
|
const updatedRecord = await useDrizzle().update(table).set(payload).where(eq(table.id, Number(id))).returning().get();
|
|
25
28
|
if (!updatedRecord) {
|
|
26
29
|
throw createError({
|
|
@@ -3,6 +3,7 @@ import { getTableForModel, filterHiddenFields, filterPublicColumns } from "../..
|
|
|
3
3
|
import { useDrizzle } from "#site/drizzle";
|
|
4
4
|
import { useAutoCrudConfig } from "../../utils/config.js";
|
|
5
5
|
import { checkAdminAccess } from "../../utils/auth.js";
|
|
6
|
+
import { desc } from "drizzle-orm";
|
|
6
7
|
export default eventHandler(async (event) => {
|
|
7
8
|
console.log("[GET] Request received", event.path);
|
|
8
9
|
const { resources } = useAutoCrudConfig();
|
|
@@ -19,7 +20,7 @@ export default eventHandler(async (event) => {
|
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
const table = getTableForModel(model);
|
|
22
|
-
const results = await useDrizzle().select().from(table).all();
|
|
23
|
+
const results = await useDrizzle().select().from(table).orderBy(desc(table.id)).all();
|
|
23
24
|
return results.map((item) => {
|
|
24
25
|
if (isAdmin) {
|
|
25
26
|
return filterHiddenFields(model, item);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { eventHandler, createError } from "h3";
|
|
2
|
+
import { getRelations } from "../utils/schema.js";
|
|
3
|
+
import { useAutoCrudConfig } from "../utils/config.js";
|
|
4
|
+
import { verifyJwtToken } from "../utils/jwt.js";
|
|
5
|
+
export default eventHandler(async (event) => {
|
|
6
|
+
const { auth } = useAutoCrudConfig();
|
|
7
|
+
if (auth?.authentication) {
|
|
8
|
+
let isAuthenticated = false;
|
|
9
|
+
if (auth.type === "jwt" && auth.jwtSecret) {
|
|
10
|
+
isAuthenticated = await verifyJwtToken(event, auth.jwtSecret);
|
|
11
|
+
} else {
|
|
12
|
+
try {
|
|
13
|
+
await requireUserSession(event);
|
|
14
|
+
isAuthenticated = true;
|
|
15
|
+
} catch {
|
|
16
|
+
isAuthenticated = false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (!isAuthenticated) {
|
|
20
|
+
throw createError({ statusCode: 401, message: "Unauthorized" });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return getRelations();
|
|
24
|
+
});
|