jcc-express-mvc 1.9.4 → 1.9.6
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/final-documentation/Digging Deeper/Helpers.md +8 -7
- package/final-documentation/Digging Deeper/Monitor.md +30 -7
- package/final-documentation/Getting-Started/Frontend.md +12 -3
- package/final-documentation/Getting-Started/Installation.md +11 -3
- package/final-documentation/JCC-Eloquent/Defining-Model.md +1 -1
- package/final-documentation/JCC-Eloquent/JCC-Eloquent-Introduction.md +1 -1
- package/final-documentation/JCC-Eloquent/Pagination.md +13 -10
- package/final-documentation/JCC-Eloquent/Query-Builder.md +2 -2
- package/final-documentation/Security/Authentication.md +39 -5
- package/final-documentation/The Basics/Artisan-Node.md +19 -2
- package/final-documentation/The Basics/Asset-bundling.md +5 -4
- package/lib/Application/ApplicationBuilder.d.ts.map +1 -1
- package/lib/Application/ApplicationBuilder.js +2 -0
- package/lib/Auth/index.d.ts +2 -0
- package/lib/Auth/index.d.ts.map +1 -1
- package/lib/Auth/index.js +15 -16
- package/lib/Auth/type.d.ts +11 -0
- package/lib/Auth/type.d.ts.map +1 -0
- package/lib/Auth/type.js +2 -0
- package/lib/Command-Line/MakeCommand.d.ts +1 -0
- package/lib/Command-Line/MakeCommand.d.ts.map +1 -1
- package/lib/Command-Line/MakeCommand.js +62 -0
- package/lib/Command-Line/NodeArtisanCommand.d.ts +2 -0
- package/lib/Command-Line/NodeArtisanCommand.d.ts.map +1 -1
- package/lib/Command-Line/NodeArtisanCommand.js +19 -0
- package/lib/Command-Line/PrismaCommand.d.ts +9 -0
- package/lib/Command-Line/PrismaCommand.d.ts.map +1 -0
- package/lib/Command-Line/PrismaCommand.js +46 -0
- package/lib/Command-Line/files/Resource.d.ts +8 -0
- package/lib/Command-Line/files/Resource.d.ts.map +1 -0
- package/lib/Command-Line/files/Resource.js +25 -0
- package/lib/Database/Database.d.ts +1 -6
- package/lib/Database/Database.d.ts.map +1 -1
- package/lib/Database/Database.js +22 -9
- package/lib/Database/DatabaseServiceProvider.d.ts.map +1 -1
- package/lib/Database/DatabaseServiceProvider.js +4 -0
- package/lib/Database/Drivers/Prisma/adapter.d.ts +14 -0
- package/lib/Database/Drivers/Prisma/adapter.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/adapter.js +50 -0
- package/lib/Database/Drivers/Prisma/adapters/mariadb.d.ts +6 -0
- package/lib/Database/Drivers/Prisma/adapters/mariadb.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/adapters/mariadb.js +28 -0
- package/lib/Database/Drivers/Prisma/adapters/postgres.d.ts +4 -0
- package/lib/Database/Drivers/Prisma/adapters/postgres.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/adapters/postgres.js +30 -0
- package/lib/Database/Drivers/Prisma/adapters/sqlite.d.ts +6 -0
- package/lib/Database/Drivers/Prisma/adapters/sqlite.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/adapters/sqlite.js +60 -0
- package/lib/Database/Drivers/Prisma/connection.d.ts +5 -0
- package/lib/Database/Drivers/Prisma/connection.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/connection.js +14 -0
- package/lib/Database/Drivers/Prisma/readConnectionEnv.d.ts +3 -0
- package/lib/Database/Drivers/Prisma/readConnectionEnv.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/readConnectionEnv.js +15 -0
- package/lib/Database/Drivers/Prisma/register.d.ts +7 -0
- package/lib/Database/Drivers/Prisma/register.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/register.js +43 -0
- package/lib/Database/Drivers/Prisma/types.d.ts +18 -0
- package/lib/Database/Drivers/Prisma/types.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/types.js +2 -0
- package/lib/Database/Drivers/PrismaDriver.d.ts +24 -0
- package/lib/Database/Drivers/PrismaDriver.d.ts.map +1 -0
- package/lib/Database/Drivers/PrismaDriver.js +41 -0
- package/lib/Database/Prisma/connection.d.ts +6 -0
- package/lib/Database/Prisma/connection.d.ts.map +1 -0
- package/lib/Database/Prisma/connection.js +19 -0
- package/lib/Database/PrismaServiceProvider.d.ts +12 -0
- package/lib/Database/PrismaServiceProvider.d.ts.map +1 -0
- package/lib/Database/PrismaServiceProvider.js +45 -0
- package/lib/Database/index.d.ts +4 -0
- package/lib/Database/index.d.ts.map +1 -1
- package/lib/Database/index.js +13 -1
- package/lib/Database/interface.d.ts +1 -1
- package/lib/Database/isPrismaOrm.d.ts +3 -0
- package/lib/Database/isPrismaOrm.d.ts.map +1 -0
- package/lib/Database/isPrismaOrm.js +7 -0
- package/lib/Database/type.d.ts +1 -1
- package/lib/Database/type.d.ts.map +1 -1
- package/lib/Error/public/419.html +1 -1
- package/lib/Global/helpers.d.ts.map +1 -1
- package/lib/Global/helpers.js +18 -5
- package/lib/Jcc-eloquent/lib/Builder.d.ts +3 -4
- package/lib/Jcc-eloquent/lib/Builder.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Builder.js +29 -23
- package/lib/Jcc-eloquent/lib/Model.d.ts +3 -4
- package/lib/Jcc-eloquent/lib/Model.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/Model.js +32 -31
- package/lib/Jcc-eloquent/lib/QueryBuilder.d.ts +1 -2
- package/lib/Jcc-eloquent/lib/QueryBuilder.d.ts.map +1 -1
- package/lib/Jcc-eloquent/lib/QueryBuilder.js +2 -2
- package/lib/Middleware/index.d.ts.map +1 -1
- package/lib/Middleware/index.js +0 -1
- package/lib/Resources/JsonResources.d.ts +27 -0
- package/lib/Resources/JsonResources.d.ts.map +1 -0
- package/lib/Resources/JsonResources.js +137 -0
- package/lib/Resources/ResourceCollection.d.ts +15 -0
- package/lib/Resources/ResourceCollection.d.ts.map +1 -0
- package/lib/Resources/ResourceCollection.js +32 -0
- package/lib/Resources/helpers.d.ts +8 -0
- package/lib/Resources/helpers.d.ts.map +1 -0
- package/lib/Resources/helpers.js +38 -0
- package/lib/Resources/index.d.ts +5 -0
- package/lib/Resources/index.d.ts.map +1 -0
- package/lib/Resources/index.js +9 -0
- package/lib/Resources/types.d.ts +10 -0
- package/lib/Resources/types.d.ts.map +1 -0
- package/lib/Resources/types.js +5 -0
- package/lib/util/index.d.ts +8 -0
- package/lib/util/index.d.ts.map +1 -1
- package/lib/util/index.js +25 -1
- package/package.json +1 -1
- package/lib/Auth/Auth.d.ts +0 -8
- package/lib/Auth/Auth.d.ts.map +0 -1
- package/lib/Auth/Auth.js +0 -22
- package/lib/Inertia/old-index.d.ts +0 -7
- package/lib/Inertia/old-index.d.ts.map +0 -1
- package/lib/Inertia/old-index.js +0 -55
|
@@ -21,21 +21,22 @@ These helpers are available during request handling and in `tinker` (with contai
|
|
|
21
21
|
|
|
22
22
|
## HTTP context helpers
|
|
23
23
|
|
|
24
|
-
- `request()` -> current `AppRequest`
|
|
25
|
-
- `response()` -> current `AppResponse`
|
|
26
|
-
- `next()` -> current `next` function
|
|
24
|
+
- `request()` -> current `AppRequest` (returns `{}` outside an HTTP request)
|
|
25
|
+
- `response()` -> current `AppResponse` (returns `{}` outside an HTTP request)
|
|
27
26
|
- `view(view, options?, callback?)`
|
|
28
27
|
- `inertia(component, props?, option?)`
|
|
29
28
|
- `redirect(url, status?)`
|
|
30
29
|
- `back(url?)`
|
|
31
30
|
|
|
32
|
-
These depend on per-request container bindings, so they are request-lifecycle helpers.
|
|
31
|
+
These depend on per-request container bindings, so they are request-lifecycle helpers. `request()` and `response()` are safe to call from code that may run outside middleware (for example pagination link builders) — they return an empty object when no request is bound.
|
|
32
|
+
|
|
33
|
+
`next()` is **not** exposed as a global helper. Use the `next` argument in middleware and route handlers directly.
|
|
33
34
|
|
|
34
35
|
---
|
|
35
36
|
|
|
36
37
|
## Auth and authorization helpers
|
|
37
38
|
|
|
38
|
-
- `auth()` ->
|
|
39
|
+
- `auth()` -> `Authentication` class (`Auth.attempt`, `Auth.apiAttempt`, `Auth.logout`, etc.)
|
|
39
40
|
- `Gate` -> `GateFacade`
|
|
40
41
|
- `can(user, ability, model?, ...args)`
|
|
41
42
|
- `authorize(user, ability, ...args)`
|
|
@@ -98,8 +99,8 @@ Global helper types are declared in `jcc-express-mvc/global.d.ts`, so TypeScript
|
|
|
98
99
|
|
|
99
100
|
## Notes
|
|
100
101
|
|
|
101
|
-
- `
|
|
102
|
-
-
|
|
102
|
+
- Helpers that rely on request bindings (`request`, `response`, `validate`, `session`, etc.) are not meaningful before HTTP middleware bootstraps those bindings.
|
|
103
|
+
- `request()` and `response()` catch missing bindings and return `{}` instead of throwing.
|
|
103
104
|
|
|
104
105
|
---
|
|
105
106
|
|
|
@@ -157,8 +157,14 @@ export const monitor: Partial<MonitorConfig> = {
|
|
|
157
157
|
},
|
|
158
158
|
system: { enabled: true, intervalMs: 15_000 },
|
|
159
159
|
errors: { enabled: true },
|
|
160
|
-
database: { enabled:
|
|
161
|
-
queue: { enabled:
|
|
160
|
+
database: { enabled: true, slowQueryMs: 200 },
|
|
161
|
+
queue: { enabled: true },
|
|
162
|
+
commands: { enabled: true },
|
|
163
|
+
schedule: { enabled: true },
|
|
164
|
+
mail: { enabled: true },
|
|
165
|
+
cache: { enabled: true },
|
|
166
|
+
outgoing: { enabled: true },
|
|
167
|
+
logs: { enabled: true },
|
|
162
168
|
},
|
|
163
169
|
|
|
164
170
|
exporters: {
|
|
@@ -187,6 +193,22 @@ export const monitor: Partial<MonitorConfig> = {
|
|
|
187
193
|
recentErrors: 100,
|
|
188
194
|
recentJobs: 100,
|
|
189
195
|
recentQueries: 100,
|
|
196
|
+
recentCommands: 100,
|
|
197
|
+
recentScheduledTasks: 100,
|
|
198
|
+
recentNotifications: 100,
|
|
199
|
+
recentMail: 100,
|
|
200
|
+
recentCacheOps: 100,
|
|
201
|
+
recentOutgoingRequests: 100,
|
|
202
|
+
recentLogs: 100,
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
storage: {
|
|
206
|
+
driver: config.get("MONITOR_STORAGE", "memory") as
|
|
207
|
+
| "memory"
|
|
208
|
+
| "file"
|
|
209
|
+
| "redis"
|
|
210
|
+
| "database",
|
|
211
|
+
file: { path: config.get("MONITOR_STORAGE_PATH", "storage/monitor") },
|
|
190
212
|
},
|
|
191
213
|
|
|
192
214
|
alerts: {
|
|
@@ -258,13 +280,14 @@ The `/_monitor` path is excluded from request metric collection so the dashboard
|
|
|
258
280
|
| --------------- | ------------------------------------------------------------------- |
|
|
259
281
|
| Requests | Auto — `MonitorMiddleware` records every request |
|
|
260
282
|
| Exceptions | Auto — `ErrorCollector` + `MonitorErrorMiddleware` |
|
|
261
|
-
| Queries |
|
|
262
|
-
| Jobs |
|
|
283
|
+
| Queries | Auto when `collectors.database.enabled` — ORM query hooks |
|
|
284
|
+
| Jobs | Auto when `collectors.queue.enabled` — `Queue` records job events |
|
|
285
|
+
| Commands | Auto when `collectors.commands.enabled` — console kernel records runs |
|
|
286
|
+
| Scheduled Tasks | Auto when `collectors.schedule.enabled` — scheduler records runs |
|
|
263
287
|
| Mail | Auto when `collectors.mail.enabled` — wraps the `Mail` facade |
|
|
264
288
|
| Cache | Auto when `collectors.cache.enabled` — wraps `Cache.get/put/forget` |
|
|
265
|
-
| Outgoing | Auto when `collectors.outgoing.enabled` —
|
|
266
|
-
|
|
|
267
|
-
| Scheduled Tasks | Manual — `Monitor.recordScheduledTask({ ... })` from the runner |
|
|
289
|
+
| Outgoing | Auto when `collectors.outgoing.enabled` — axios + `fetch` patch |
|
|
290
|
+
| Logs | Auto when `collectors.logs.enabled` — framework logger integration |
|
|
268
291
|
| Notifications | Manual — `Monitor.recordNotification({ ... })` after dispatch |
|
|
269
292
|
|
|
270
293
|
---
|
|
@@ -156,11 +156,20 @@ Vite is the default bundler: fast builds and Hot Module Replacement (HMR) during
|
|
|
156
156
|
|
|
157
157
|
For Blade tags, `public/hot`, manifest resolution, and troubleshooting, see [Asset-bundling.md](../The Basics/Asset-bundling.md).
|
|
158
158
|
|
|
159
|
-
Local workflow —
|
|
159
|
+
Local workflow — use two terminals:
|
|
160
160
|
|
|
161
161
|
```bash
|
|
162
|
-
|
|
163
|
-
bun
|
|
162
|
+
# Terminal 1 — backend (recommended: Laravel-style watcher)
|
|
163
|
+
bun artisanNode serve
|
|
164
|
+
|
|
165
|
+
# Terminal 2 — Vite assets (writes public/hot)
|
|
166
|
+
bun run watch # or: npm run watch
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Alternative backend command:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
bun run dev # or: npm run dev → bun --watch server.ts (simpler, no serve watcher)
|
|
164
173
|
```
|
|
165
174
|
|
|
166
175
|
Production-style build:
|
|
@@ -224,6 +224,14 @@ This starts Vite in watch mode (React/Vue, Tailwind, hot reload, etc.). Let it k
|
|
|
224
224
|
|
|
225
225
|
In a second terminal, from the same project root:
|
|
226
226
|
|
|
227
|
+
```bash
|
|
228
|
+
bun artisanNode serve
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
This is the recommended dev server: it watches backend files, reloads silently, keeps the same port, and reacts to `public/hot` when Vite starts.
|
|
232
|
+
|
|
233
|
+
Alternative (simpler, without the Laravel-style watcher):
|
|
234
|
+
|
|
227
235
|
```bash
|
|
228
236
|
bun run dev
|
|
229
237
|
```
|
|
@@ -234,7 +242,7 @@ or:
|
|
|
234
242
|
npm run dev
|
|
235
243
|
```
|
|
236
244
|
|
|
237
|
-
This
|
|
245
|
+
This runs `bun --watch server.ts`, which boots Express and your bootstrap pipeline with Bun's built-in file watcher.
|
|
238
246
|
|
|
239
247
|
### 7.3 Open the app
|
|
240
248
|
|
|
@@ -267,7 +275,7 @@ Use `bun artisanNode list` or `bun artisanNode help [command]` to see commands (
|
|
|
267
275
|
- [ ] `.env` exists and is filled in for your machine (starter projects already have the file; cloned repos may need `cp .env.example .env`)
|
|
268
276
|
- [ ] `bun artisanNode key:generate` has set `JWT_SECRET` (required for JWT auth)
|
|
269
277
|
- [ ] `bun artisanNode migrate` completed (or you intentionally skip DB for now)
|
|
270
|
-
- [ ] `watch` then `dev` both run without crashing (`bun run` / `npm run` as you prefer)
|
|
278
|
+
- [ ] `watch` then `serve` (or `dev`) both run without crashing (`bun run` / `npm run` as you prefer)
|
|
271
279
|
- [ ] Browser loads `http://localhost:<PORT>`
|
|
272
280
|
|
|
273
281
|
---
|
|
@@ -287,7 +295,7 @@ Use `bun artisanNode list` or `bun artisanNode help [command]` to see commands (
|
|
|
287
295
|
|
|
288
296
|
- [Configuration.md](./Configuration.md) — `.env`, `app/Config`, and how values are loaded at runtime
|
|
289
297
|
- [Directory-structure.md](./Directory-structure.md) — Project folders and what each is for
|
|
290
|
-
- [Frontend.md](./Frontend.md) — Why `watch` runs
|
|
298
|
+
- [Frontend.md](./Frontend.md) — Why `watch` runs alongside `serve` (Vite + Inertia)
|
|
291
299
|
- [Deployment.md](./Deployment.md) — Production `.env`, builds, migrations, and process management
|
|
292
300
|
- `docs/docs-for-dev.md` — broad framework guide (routing, Eloquent, validation, queues, etc.)
|
|
293
301
|
- `docs/getting-started.md` — shorter orientation
|
|
@@ -279,7 +279,7 @@ Route.middleware(["apiAuth"]).get("/me", async (req) => {
|
|
|
279
279
|
If `hasToken` is not enabled on the model:
|
|
280
280
|
|
|
281
281
|
```text
|
|
282
|
-
ModelTokenError: User Missing required token. Please set 'protected hasToken = true' .
|
|
282
|
+
ModelTokenError: User Model Missing required token. Please set 'protected hasToken = true' .
|
|
283
283
|
```
|
|
284
284
|
|
|
285
285
|
The global error handler returns `500` with the error message for `ModelTokenError`.
|
|
@@ -111,7 +111,7 @@ export class User extends Model {
|
|
|
111
111
|
Model-level pagination helper exists:
|
|
112
112
|
|
|
113
113
|
```typescript
|
|
114
|
-
const page = await User.paginate(
|
|
114
|
+
const page = await User.paginate(15);
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
This returns `data` plus pagination metadata/links.
|
|
@@ -4,16 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
JCC-Eloquent provides pagination at both model and builder level.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Pagination reads the current HTTP request via the global `request()` helper — you do **not** pass `req` as the first argument. Page number comes from `request().query.page` (default `1`). Link URLs use `request().protocol`, host, and path.
|
|
8
|
+
|
|
9
|
+
Both methods return `data` + pagination metadata.
|
|
8
10
|
|
|
9
11
|
---
|
|
10
12
|
|
|
11
13
|
## Model pagination
|
|
12
14
|
|
|
13
|
-
`Model.paginate(
|
|
15
|
+
`Model.paginate(perPage?, page?)`:
|
|
14
16
|
|
|
15
17
|
```typescript
|
|
16
|
-
const paginatedUsers = await User.paginate(
|
|
18
|
+
const paginatedUsers = await User.paginate(15);
|
|
17
19
|
```
|
|
18
20
|
|
|
19
21
|
This returns:
|
|
@@ -31,7 +33,7 @@ Builder pagination works on filtered queries:
|
|
|
31
33
|
```typescript
|
|
32
34
|
const page = await User.where("active", true)
|
|
33
35
|
.orderBy("created_at", "desc")
|
|
34
|
-
.paginate(
|
|
36
|
+
.paginate(20);
|
|
35
37
|
```
|
|
36
38
|
|
|
37
39
|
Use this when pagination must respect custom constraints.
|
|
@@ -43,7 +45,7 @@ Use this when pagination must respect custom constraints.
|
|
|
43
45
|
`simplePaginate` uses offset pagination without running a `COUNT(*)` query — faster for large tables when you don't need a total count:
|
|
44
46
|
|
|
45
47
|
```typescript
|
|
46
|
-
const page = await User.where("active", true).simplePaginate(
|
|
48
|
+
const page = await User.where("active", true).simplePaginate(15);
|
|
47
49
|
```
|
|
48
50
|
|
|
49
51
|
Response shape:
|
|
@@ -70,7 +72,7 @@ No `total` or `total_pages` — just whether there is a next page.
|
|
|
70
72
|
`cursorPaginate` implements keyset (cursor-based) pagination — stable and O(1) for any page depth, regardless of table size:
|
|
71
73
|
|
|
72
74
|
```typescript
|
|
73
|
-
const page = await User.orderBy("id").cursorPaginate(
|
|
75
|
+
const page = await User.orderBy("id").cursorPaginate(20);
|
|
74
76
|
```
|
|
75
77
|
|
|
76
78
|
Response shape:
|
|
@@ -112,7 +114,7 @@ Pass the next page by forwarding `?cursor=<token>` from `meta.next_cursor`.
|
|
|
112
114
|
For full manual control:
|
|
113
115
|
|
|
114
116
|
```typescript
|
|
115
|
-
const currentPage = Number(
|
|
117
|
+
const currentPage = Number(request().query?.page || 1);
|
|
116
118
|
const perPage = 20;
|
|
117
119
|
const offset = (currentPage - 1) * perPage;
|
|
118
120
|
|
|
@@ -131,7 +133,8 @@ const rows = await User.orderBy("id", "desc").offset(offset).limit(perPage).get(
|
|
|
131
133
|
|
|
132
134
|
## Summary
|
|
133
135
|
|
|
134
|
-
- Use `paginate(
|
|
135
|
-
- Use `simplePaginate(
|
|
136
|
-
- Use `cursorPaginate(
|
|
136
|
+
- Use `paginate(perPage)` for standard pagination with total counts and page links.
|
|
137
|
+
- Use `simplePaginate(perPage)` for large tables where you don't need a total count.
|
|
138
|
+
- Use `cursorPaginate(perPage)` for high-performance, stable pagination on large or frequently-updated tables.
|
|
139
|
+
- Pagination uses the global `request()` helper for page number and link generation — call it inside a route handler or middleware context.
|
|
137
140
|
- Use `offset` + `limit` only for fully custom paging flows.
|
|
@@ -258,7 +258,7 @@ await User.where("id", 1).updateJson("preferences.notifications.email", false);
|
|
|
258
258
|
For large datasets where `COUNT(*)` is too slow, use cursor-based (keyset) pagination:
|
|
259
259
|
|
|
260
260
|
```typescript
|
|
261
|
-
const page = await User.orderBy("id").cursorPaginate(
|
|
261
|
+
const page = await User.orderBy("id").cursorPaginate(20);
|
|
262
262
|
```
|
|
263
263
|
|
|
264
264
|
Response shape:
|
|
@@ -291,7 +291,7 @@ Pass the next page by forwarding `?cursor=<token>` from `meta.next_cursor`.
|
|
|
291
291
|
Offset pagination without a `COUNT(*)` query — faster for large tables:
|
|
292
292
|
|
|
293
293
|
```typescript
|
|
294
|
-
const page = await User.where("active", true).simplePaginate(
|
|
294
|
+
const page = await User.where("active", true).simplePaginate(15);
|
|
295
295
|
```
|
|
296
296
|
|
|
297
297
|
Response:
|
|
@@ -101,9 +101,43 @@ Route.middleware(["loginThrottle"]).post("/auth/login", async (req, res, next) =
|
|
|
101
101
|
Route.middleware(["auth"]).get("/home", homeHandler);
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
### API token login (
|
|
104
|
+
### API token login (`apiAttempt`)
|
|
105
105
|
|
|
106
|
-
For stateless API clients,
|
|
106
|
+
For stateless API clients, use `auth().apiAttempt()` to validate credentials and issue a bearer token in one step:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
Route.post("/api/login", async () => {
|
|
110
|
+
const result = await auth().apiAttempt({ field: "email", table: "User" });
|
|
111
|
+
|
|
112
|
+
if (!result.success) {
|
|
113
|
+
return response().status(401).json({ message: result.message });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
token: result.token,
|
|
118
|
+
user: result.user,
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
Route.middleware(["apiAuth"]).get("/api/me", async (req) => {
|
|
123
|
+
return { user: req.user };
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`apiAttempt` options:
|
|
128
|
+
|
|
129
|
+
| Field | Default | Description |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| `field` | `"email"` | Request input key to match (`email`, `phone`, `username`, etc.) |
|
|
132
|
+
| `table` | `"User"` | Model name resolved via `getModel()` |
|
|
133
|
+
|
|
134
|
+
The request body must include the credential field and `password`. On success, returns `{ success: true, token, user, message }`. On failure, returns `{ success: false, token: null, user: null, message: "Invalid credentials" }`.
|
|
135
|
+
|
|
136
|
+
The model must have `protected hasToken = true`. User lookup and password verification are shared with session login via `findUserForAuth()`.
|
|
137
|
+
|
|
138
|
+
### Manual token login (`createToken`)
|
|
139
|
+
|
|
140
|
+
You can also issue a token manually after your own credential check:
|
|
107
141
|
|
|
108
142
|
```typescript
|
|
109
143
|
import { User } from "@/Model/User";
|
|
@@ -120,13 +154,13 @@ Route.middleware(["apiAuth"]).get("/api/me", async (req) => {
|
|
|
120
154
|
});
|
|
121
155
|
```
|
|
122
156
|
|
|
123
|
-
|
|
157
|
+
Full `createToken` details: [Defining-Model.md](../JCC-Eloquent/Defining-Model.md#api-tokens-createtoken).
|
|
124
158
|
|
|
125
159
|
---
|
|
126
160
|
|
|
127
161
|
## Summary
|
|
128
162
|
|
|
129
163
|
- Authentication uses JWT cookies with rotating refresh tokens.
|
|
130
|
-
- Use `Auth.attempt(next)` for login, `auth`/`apiAuth` middleware for protection.
|
|
131
|
-
- For API
|
|
164
|
+
- Use `Auth.attempt(next)` for cookie login, `auth().apiAttempt()` for API email/password + bearer token, and `auth`/`apiAuth` middleware for protection.
|
|
165
|
+
- For manual API tokens, enable `hasToken` on the model and call `createToken()` — `apiAuth` reloads the user from JWT claims.
|
|
132
166
|
- `Auth.logout()` clears auth state and redirects to login.
|
|
@@ -60,12 +60,29 @@ bun artisanNode db:wipe
|
|
|
60
60
|
## Dev server
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
|
-
bun artisanNode serve #
|
|
63
|
+
bun artisanNode serve # Laravel-style dev server with file watcher (entry: server.ts)
|
|
64
64
|
bun artisanNode serve app.ts # use a different entry file
|
|
65
65
|
bun artisanNode watch # alias of `serve`
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
`serve` (alias `watch`)
|
|
68
|
+
`serve` (alias `watch`) is the recommended backend dev command. It:
|
|
69
|
+
|
|
70
|
+
- watches backend paths (`app/`, `route/`, `jcc-express-mvc/`, `resources/views/`, etc.)
|
|
71
|
+
- reloads **silently** on code changes (no terminal clear, no restart banner)
|
|
72
|
+
- keeps the same `PORT` from `.env` across restarts
|
|
73
|
+
- watches `public/hot` so the backend picks up Vite when the dev asset server starts
|
|
74
|
+
|
|
75
|
+
Run Vite separately for frontend assets:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Terminal 1 — backend (file watcher + Express)
|
|
79
|
+
bun artisanNode serve
|
|
80
|
+
|
|
81
|
+
# Terminal 2 — frontend (Vite HMR, writes public/hot)
|
|
82
|
+
npm run watch
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`npm run dev` (`bun --watch server.ts`) is a simpler alternative without the Laravel-style watcher. Prefer `bun artisanNode serve` when you want silent reloads, stable ports, and `public/hot` integration.
|
|
69
86
|
|
|
70
87
|
---
|
|
71
88
|
|
|
@@ -15,11 +15,12 @@ For Inertia, SSR, and the HTTP kernel’s `inertia()` middleware, see `Getting-S
|
|
|
15
15
|
Typical `package.json` scripts:
|
|
16
16
|
|
|
17
17
|
- `watch` — Runs `vite` (dev server, HMR, writes `public/hot`).
|
|
18
|
-
- `dev` — Runs
|
|
18
|
+
- `dev` — Runs `bun --watch server.ts` (simple backend reload; no Laravel-style watcher).
|
|
19
|
+
- `serve` — Run via `bun artisanNode serve` (recommended backend dev server; see `Artisan-Node.md`).
|
|
19
20
|
- `vite-build` — `vite build` plus `vite build --ssr` when you ship SSR (adjust to your app).
|
|
20
21
|
- `build` — TypeScript compile (`tsc`) for the server codebase; not a substitute for `vite-build`.
|
|
21
22
|
|
|
22
|
-
Start Vite before hitting pages that use `@vite`, or
|
|
23
|
+
Start Vite before hitting pages that use `@vite`, or refresh after `public/hot` appears. The template layer reads `public/hot` on each request in development.
|
|
23
24
|
|
|
24
25
|
---
|
|
25
26
|
|
|
@@ -41,13 +42,13 @@ Add or change `input` when you create new entrypoints. Tailwind and React (or Vu
|
|
|
41
42
|
|
|
42
43
|
## `public/hot` and the dev server
|
|
43
44
|
|
|
44
|
-
In non-production, `EngineHelpers` reads `public/hot` (first line is the Vite origin, e.g. `http://127.0.0.1:5173`). That value becomes `viteHost` for:
|
|
45
|
+
In non-production, `EngineHelpers` reads `public/hot` on **each render** (first line is the Vite origin, e.g. `http://127.0.0.1:5173`). That value becomes `viteHost` for:
|
|
45
46
|
|
|
46
47
|
- Module `<script>` tags pointing at your entries.
|
|
47
48
|
- Optional link to the CSS entry when you pass more than one resource in `@vite` (first entry is treated as CSS in dev in that case).
|
|
48
49
|
- Injection of `/@vite/client` for HMR.
|
|
49
50
|
|
|
50
|
-
If `hot` is missing, tags may point at an empty base—run `npm run watch` / `bun run watch`.
|
|
51
|
+
If `hot` is missing, tags may point at an empty base — run `npm run watch` / `bun run watch`. After Vite creates `public/hot`, refresh the page (or use `bun artisanNode serve`, which watches `public/hot` and reloads the backend when Vite starts).
|
|
51
52
|
|
|
52
53
|
---
|
|
53
54
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApplicationBuilder.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Application/ApplicationBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"ApplicationBuilder.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Application/ApplicationBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAI/D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAU3C,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,qBAAa,kBAAkB;IACtB,GAAG,EAAE,WAAW,CAAC;gBAEZ,GAAG,EAAE,WAAW;IAIrB,UAAU,CAAC,MAAM,EAAE,GAAG;IAKtB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAMtC,WAAW,CAAC,WAAW,EAAE,WAAW,EAAE;IAOtC,aAAa,CAClB,SAAS,EAAE,KAAK,CAAC,KAAK,GAAG,EAAE,WAAW,KAAK,eAAe,CAAC;IA4B7D,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,gBAAgB;IAIjB,WAAW;IAKX,cAAc,CAAC,oBAAoB,GAAE,cAAc,EAAO;IAY1D,MAAM;CAKd"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ApplicationBuilder = void 0;
|
|
4
4
|
const DatabaseServiceProvider_1 = require("../Database/DatabaseServiceProvider");
|
|
5
|
+
const PrismaServiceProvider_1 = require("../Database/PrismaServiceProvider");
|
|
5
6
|
const Middleware_1 = require("../Middleware");
|
|
6
7
|
const NodeArtisanCommand_1 = require("../Command-Line/NodeArtisanCommand");
|
|
7
8
|
const helpers_1 = require("../Global/helpers");
|
|
@@ -40,6 +41,7 @@ class ApplicationBuilder {
|
|
|
40
41
|
const chain = [
|
|
41
42
|
LogServiceProvider_1.LogServiceProvider,
|
|
42
43
|
DatabaseServiceProvider_1.DatabaseServiceProvider,
|
|
44
|
+
PrismaServiceProvider_1.PrismaServiceProvider,
|
|
43
45
|
SessionServiceProvider_1.SessionServiceProvider,
|
|
44
46
|
CacheServiceProvider_1.CacheServiceProvider,
|
|
45
47
|
...ordered,
|
package/lib/Auth/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AppRequest, AppResponse, AppNext } from "../Interface";
|
|
2
2
|
import { type IRefreshTokenStore } from "./refreshTokenStore";
|
|
3
|
+
import { ApiAuthResponse, ApiAuthType } from "./type";
|
|
3
4
|
export declare class Authentication {
|
|
4
5
|
private static refreshStore;
|
|
5
6
|
/** Use a shared store (e.g. Redis) when running multiple app instances. */
|
|
@@ -12,6 +13,7 @@ export declare class Authentication {
|
|
|
12
13
|
private static setTokens;
|
|
13
14
|
/** Handle user login attempt */
|
|
14
15
|
static attempt(next: AppNext, redirect?: string): Promise<void>;
|
|
16
|
+
static apiAttempt(data: ApiAuthType): Promise<ApiAuthResponse>;
|
|
15
17
|
/**
|
|
16
18
|
* After the user is resolved (e.g. OAuth via Socialite), issue JWT cookies
|
|
17
19
|
* and redirect or JSON response like {@link Authentication.attempt}.
|
package/lib/Auth/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAkBhE,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAsBtD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,YAAY,CAAgD;IAE3E,2EAA2E;IAC3E,MAAM,CAAC,oBAAoB,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI;IAI5D,qDAAqD;IACrD,OAAO,CAAC,MAAM,CAAC,cAAc;IAc7B,0DAA0D;mBACrC,OAAO;IAS5B,wFAAwF;IACxF,OAAO,CAAC,MAAM,CAAC,SAAS;IA0BxB,gCAAgC;WACnB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,GAAE,MAAgB;WA2BjD,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC;IAwBpE;;;OAGG;WACU,aAAa,CACxB,GAAG,EAAE,UAAU,EACf,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,QAAQ,GAAE,MAAgB,GACzB,OAAO,CAAC,IAAI,CAAC;IA4BhB,+BAA+B;WAClB,YAAY,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO;IAsC1E,qBAAqB;IACrB,MAAM,CAAC,MAAM;IAsBb,4FAA4F;IAC5F,MAAM,CAAC,KAAK,IAAI,OAAO;IAWvB,MAAM,CAAC,IAAI;IAIX,MAAM,CAAC,EAAE;WAII,WAAW,CACtB,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,QAAQ,GAAE,MAAgB;CAI7B"}
|
package/lib/Auth/index.js
CHANGED
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Authentication = void 0;
|
|
4
4
|
const util_1 = require("../util");
|
|
5
|
-
const Config_1 = require("../Config/Config");
|
|
6
5
|
const ValidationException_v2_1 = require("../Error/ValidationException-v2");
|
|
7
|
-
const Jcc_eloquent_1 = require("../Jcc-eloquent");
|
|
8
6
|
const refreshTokenStore_1 = require("./refreshTokenStore");
|
|
9
7
|
const REFRESH_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
10
8
|
const ACCESS_MAX_AGE_MS = 60 * 60 * 1000;
|
|
@@ -47,20 +45,7 @@ class Authentication {
|
|
|
47
45
|
const field = this.getCredentials(data);
|
|
48
46
|
if (!Object.keys(field).length)
|
|
49
47
|
return { user: null, field: "email" };
|
|
50
|
-
let user =
|
|
51
|
-
const orm = Config_1.config.get("DB_ORM");
|
|
52
|
-
if (orm === "mongodb" || orm === "mongoose") {
|
|
53
|
-
user = await User.findOne(field).select("+password");
|
|
54
|
-
}
|
|
55
|
-
else if (orm === "sequelize") {
|
|
56
|
-
user = await User.findOne({ where: field });
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
// JCC ORM
|
|
60
|
-
user = await Jcc_eloquent_1.DB.table("users")
|
|
61
|
-
.where(Object.keys(field)[0], Object.values(field)[0])
|
|
62
|
-
.first();
|
|
63
|
-
}
|
|
48
|
+
let user = await (0, util_1.findUserForAuth)(User, field);
|
|
64
49
|
return { user, field: Object.keys(field)[0] || "email" };
|
|
65
50
|
}
|
|
66
51
|
/** Generate and attach tokens to cookies (refresh is rotated server-side via `jti`). */
|
|
@@ -100,6 +85,20 @@ class Authentication {
|
|
|
100
85
|
next(error);
|
|
101
86
|
}
|
|
102
87
|
}
|
|
88
|
+
static async apiAttempt(data) {
|
|
89
|
+
data = data || { field: "email", model: "User" };
|
|
90
|
+
const input = request().input(data.field || "");
|
|
91
|
+
const password = request().input("password");
|
|
92
|
+
const { [data.model]: Model } = (0, util_1.getModel)(data.model);
|
|
93
|
+
let user = await (0, util_1.findUserForAuth)(Model, { [data.field]: input });
|
|
94
|
+
if (!user) {
|
|
95
|
+
return (0, util_1.authResponseMessages)(false, "Invalid credentials", null, null);
|
|
96
|
+
}
|
|
97
|
+
if (!(await (0, util_1.verifyHash)(password, user?.password || ""))) {
|
|
98
|
+
return (0, util_1.authResponseMessages)(false, "Invalid credentials", null, null);
|
|
99
|
+
}
|
|
100
|
+
return (0, util_1.authResponseMessages)(true, "Login successful", user?.toJSON(), user.createToken());
|
|
101
|
+
}
|
|
103
102
|
/**
|
|
104
103
|
* After the user is resolved (e.g. OAuth via Socialite), issue JWT cookies
|
|
105
104
|
* and redirect or JSON response like {@link Authentication.attempt}.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Auth/type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IACjC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC"}
|
package/lib/Auth/type.js
ADDED
|
@@ -3,6 +3,7 @@ export declare class MakeCommand {
|
|
|
3
3
|
createController(controllerName: string, modelName?: boolean): any;
|
|
4
4
|
createApiController(controllerName: string, modelName?: boolean): any;
|
|
5
5
|
createModel(modelName: string): any;
|
|
6
|
+
createResource(name: string): void;
|
|
6
7
|
createRequest(requestName: string): any;
|
|
7
8
|
createMigration(migrationName: string, optionalTable: string): any;
|
|
8
9
|
createSeeder(name: string): any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MakeCommand.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Command-Line/MakeCommand.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MakeCommand.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Command-Line/MakeCommand.ts"],"names":[],"mappings":"AAoCA,qBAAa,WAAW;IACtB,OAAO,CAAC,aAAa,CAA0C;IAE/D,gBAAgB,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,GAAG;IAsChE,mBAAmB,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,GAAG;IAkDnE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG;IAuBnC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAuClC,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,GAAG;IA4BvC,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,GAAG;IA4BlE,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG;IA2B/B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG;IA8B/B,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG;IA8B3B,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,GAAG;IAmC1D,gBAAgB;IA+BhB,kBAAkB;IAyBlB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,GAAG;IAiCjD,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,GAAG;IAmC/C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,GAAG;IAmC1D,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IA+BjC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IA+BpD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAiC7B,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IA8BhC,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG;CAkCrC"}
|
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -28,6 +61,7 @@ const Service_1 = __importDefault(require("./files/Service"));
|
|
|
28
61
|
const Repository_1 = __importDefault(require("./files/Repository"));
|
|
29
62
|
const Dto_1 = __importDefault(require("./files/Dto"));
|
|
30
63
|
const Action_1 = __importDefault(require("./files/Action"));
|
|
64
|
+
const Resource_1 = __importStar(require("./files/Resource"));
|
|
31
65
|
const Config_1 = require("../Config/Config");
|
|
32
66
|
const rootPath = app_root_path_1.default.path;
|
|
33
67
|
/** Convert PascalCase to kebab-case (e.g. SendEmails -> send:emails) */
|
|
@@ -115,6 +149,34 @@ class MakeCommand {
|
|
|
115
149
|
return console.log(colors_1.default.red(`${modelName} model not added`)); // Log error if model addition fails
|
|
116
150
|
}
|
|
117
151
|
}
|
|
152
|
+
createResource(name) {
|
|
153
|
+
try {
|
|
154
|
+
if (!name) {
|
|
155
|
+
console.log(colors_1.default.red("Please give a name for your resource"));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const { className, filePath } = (0, Resource_1.parseResourceName)(name);
|
|
159
|
+
const resourcePath = path_1.default.resolve(`${rootPath}/app/Http/Resources`);
|
|
160
|
+
if (!fs_1.default.existsSync(resourcePath)) {
|
|
161
|
+
fs_1.default.mkdirSync(resourcePath, { recursive: true });
|
|
162
|
+
}
|
|
163
|
+
const targetDir = path_1.default.dirname(path_1.default.resolve(resourcePath, filePath));
|
|
164
|
+
if (!fs_1.default.existsSync(targetDir)) {
|
|
165
|
+
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
166
|
+
}
|
|
167
|
+
const resourceFile = path_1.default.resolve(resourcePath, `${filePath}.${this.fileExtension}`);
|
|
168
|
+
if (fs_1.default.existsSync(resourceFile)) {
|
|
169
|
+
console.log(colors_1.default.yellow(`${className} already exists`));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
fs_1.default.writeFileSync(resourceFile, (0, Resource_1.default)(className));
|
|
173
|
+
console.log(colors_1.default.green(`${className} created successfully [${resourceFile}]`));
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
console.log(colors_1.default.red(`${name} resource not created`));
|
|
177
|
+
console.log(err.message);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
118
180
|
createRequest(requestName) {
|
|
119
181
|
try {
|
|
120
182
|
if (!requestName) {
|
|
@@ -12,6 +12,7 @@ export declare class ConsoleKernel {
|
|
|
12
12
|
private _watch;
|
|
13
13
|
private _app;
|
|
14
14
|
private _cache;
|
|
15
|
+
private _prisma;
|
|
15
16
|
private get migrate();
|
|
16
17
|
private get db();
|
|
17
18
|
private get make();
|
|
@@ -19,6 +20,7 @@ export declare class ConsoleKernel {
|
|
|
19
20
|
private get schedule();
|
|
20
21
|
private get watch();
|
|
21
22
|
private get cache();
|
|
23
|
+
private get prisma();
|
|
22
24
|
private app;
|
|
23
25
|
constructor();
|
|
24
26
|
/** Load user commands from app/Console/Command (guarded, non-blocking for missing dir) */
|