create-warlock 4.0.30 → 4.0.39
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/cjs/index.js +10 -5
- package/cjs/index.js.map +1 -1
- package/create-app.js +0 -0
- package/esm/index.js +10 -5
- package/esm/index.js.map +1 -1
- package/package.json +32 -38
- package/templates/warlock/docs/new-module.md +123 -61
- package/templates/warlock/package.json +60 -67
- package/templates/warlock/src/app/auth/controllers/forgot-password.controller.ts +3 -2
- package/templates/warlock/src/app/auth/controllers/login.controller.ts +1 -4
- package/templates/warlock/src/app/auth/main.ts +6 -3
- package/templates/warlock/src/app/auth/models/otp/migrations/22-12-2025_10-30-20.otp-migration.ts +13 -16
- package/templates/warlock/src/app/auth/models/otp/otp.model.ts +26 -31
- package/templates/warlock/src/app/auth/requests/login.request.ts +1 -1
- package/templates/warlock/src/app/auth/services/auth.service.ts +10 -5
- package/templates/warlock/src/app/auth/services/otp.service.ts +2 -12
- package/templates/warlock/src/app/auth/services/reset-password.service.ts +1 -1
- package/templates/warlock/src/app/posts/controllers/create-new-post.controller.ts +20 -0
- package/templates/warlock/src/app/posts/controllers/update-post.controller.ts +24 -0
- package/templates/warlock/src/app/posts/models/post/psot.model.ts +33 -0
- package/templates/warlock/src/app/posts/routes.ts +11 -0
- package/templates/warlock/src/app/shared/utils/global-columns-schema.ts +8 -0
- package/templates/warlock/src/app/shared/utils/locales.ts +4 -0
- package/templates/warlock/src/app/uploads/controllers/fetch-uploaded-file.controller.ts +32 -0
- package/templates/warlock/src/app/uploads/routes.ts +4 -0
- package/templates/warlock/src/app/users/controllers/create-new-user.controller.ts +28 -0
- package/templates/warlock/src/app/users/controllers/get-users.controller.ts +7 -2
- package/templates/warlock/src/app/users/events/inject-created-by-user.into-model.event.ts +32 -0
- package/templates/warlock/src/app/users/events/sync.ts +5 -0
- package/templates/warlock/src/app/users/main.ts +5 -0
- package/templates/warlock/src/app/users/models/user/migrations/11-12-2025_23-58-03-user.migration.ts +12 -12
- package/templates/warlock/src/app/users/models/user/user.model.ts +54 -27
- package/templates/warlock/src/app/users/repositories/users.repository.ts +16 -20
- package/templates/warlock/src/app/users/routes.ts +7 -1
- package/templates/warlock/src/app/users/seeds/users.seed.ts +21 -0
- package/templates/warlock/src/app/utils/cloud-upload.middleware.ts +14 -0
- package/templates/warlock/src/app/utils/router.ts +11 -1
- package/templates/warlock/src/config/auth.ts +2 -0
- package/templates/warlock/src/config/database.ts +24 -7
- package/templates/warlock/src/config/storage.ts +17 -8
- package/templates/warlock/tsconfig.json +6 -5
- package/templates/warlock/yarn.lock +4831 -0
- package/cjs/commands/create-new-app/get-app-path.d.ts +0 -2
- package/cjs/commands/create-new-app/get-app-path.d.ts.map +0 -1
- package/cjs/commands/create-new-app/get-app-path.js +0 -8
- package/cjs/commands/create-new-app/get-app-path.js.map +0 -1
- package/cjs/commands/create-new-app/index.d.ts +0 -2
- package/cjs/commands/create-new-app/index.d.ts.map +0 -1
- package/cjs/commands/create-new-app/index.js +0 -50
- package/cjs/commands/create-new-app/index.js.map +0 -1
- package/cjs/commands/create-new-app/select-app-type.d.ts +0 -2
- package/cjs/commands/create-new-app/select-app-type.d.ts.map +0 -1
- package/cjs/commands/create-new-app/types.d.ts +0 -9
- package/cjs/commands/create-new-app/types.d.ts.map +0 -1
- package/cjs/commands/create-warlock-app/index.d.ts +0 -3
- package/cjs/commands/create-warlock-app/index.d.ts.map +0 -1
- package/cjs/commands/create-warlock-app/index.js +0 -51
- package/cjs/commands/create-warlock-app/index.js.map +0 -1
- package/cjs/helpers/app.d.ts +0 -54
- package/cjs/helpers/app.d.ts.map +0 -1
- package/cjs/helpers/app.js +0 -126
- package/cjs/helpers/app.js.map +0 -1
- package/cjs/helpers/exec.d.ts +0 -10
- package/cjs/helpers/exec.d.ts.map +0 -1
- package/cjs/helpers/exec.js +0 -69
- package/cjs/helpers/exec.js.map +0 -1
- package/cjs/helpers/package-manager.d.ts +0 -6
- package/cjs/helpers/package-manager.d.ts.map +0 -1
- package/cjs/helpers/package-manager.js +0 -22
- package/cjs/helpers/package-manager.js.map +0 -1
- package/cjs/helpers/paths.d.ts +0 -4
- package/cjs/helpers/paths.d.ts.map +0 -1
- package/cjs/helpers/paths.js +0 -8
- package/cjs/helpers/paths.js.map +0 -1
- package/cjs/helpers/project-builder-helpers.d.ts +0 -6
- package/cjs/helpers/project-builder-helpers.d.ts.map +0 -1
- package/cjs/helpers/project-builder-helpers.js +0 -18
- package/cjs/helpers/project-builder-helpers.js.map +0 -1
- package/cjs/index.d.ts +0 -2
- package/cjs/index.d.ts.map +0 -1
- package/esm/commands/create-new-app/get-app-path.d.ts +0 -2
- package/esm/commands/create-new-app/get-app-path.d.ts.map +0 -1
- package/esm/commands/create-new-app/get-app-path.js +0 -8
- package/esm/commands/create-new-app/get-app-path.js.map +0 -1
- package/esm/commands/create-new-app/index.d.ts +0 -2
- package/esm/commands/create-new-app/index.d.ts.map +0 -1
- package/esm/commands/create-new-app/index.js +0 -50
- package/esm/commands/create-new-app/index.js.map +0 -1
- package/esm/commands/create-new-app/select-app-type.d.ts +0 -2
- package/esm/commands/create-new-app/select-app-type.d.ts.map +0 -1
- package/esm/commands/create-new-app/types.d.ts +0 -9
- package/esm/commands/create-new-app/types.d.ts.map +0 -1
- package/esm/commands/create-warlock-app/index.d.ts +0 -3
- package/esm/commands/create-warlock-app/index.d.ts.map +0 -1
- package/esm/commands/create-warlock-app/index.js +0 -51
- package/esm/commands/create-warlock-app/index.js.map +0 -1
- package/esm/helpers/app.d.ts +0 -54
- package/esm/helpers/app.d.ts.map +0 -1
- package/esm/helpers/app.js +0 -126
- package/esm/helpers/app.js.map +0 -1
- package/esm/helpers/exec.d.ts +0 -10
- package/esm/helpers/exec.d.ts.map +0 -1
- package/esm/helpers/exec.js +0 -69
- package/esm/helpers/exec.js.map +0 -1
- package/esm/helpers/package-manager.d.ts +0 -6
- package/esm/helpers/package-manager.d.ts.map +0 -1
- package/esm/helpers/package-manager.js +0 -22
- package/esm/helpers/package-manager.js.map +0 -1
- package/esm/helpers/paths.d.ts +0 -4
- package/esm/helpers/paths.d.ts.map +0 -1
- package/esm/helpers/paths.js +0 -8
- package/esm/helpers/paths.js.map +0 -1
- package/esm/helpers/project-builder-helpers.d.ts +0 -6
- package/esm/helpers/project-builder-helpers.d.ts.map +0 -1
- package/esm/helpers/project-builder-helpers.js +0 -18
- package/esm/helpers/project-builder-helpers.js.map +0 -1
- package/esm/index.d.ts +0 -2
- package/esm/index.d.ts.map +0 -1
- package/templates/warlock/src/app/users/repositories/users-repository.ts +0 -66
- package/templates/warlock/src/app/users/services/get-new-customers.ts +0 -5
- package/templates/warlock/src/app/utils/output.ts +0 -5
|
@@ -12,7 +12,7 @@ src/app/[module-name]/
|
|
|
12
12
|
│ └── [module].restful.ts # RESTful resource controller (optional)
|
|
13
13
|
├── services/ # Business logic layer
|
|
14
14
|
│ ├── *.service.ts
|
|
15
|
-
|
|
15
|
+
├── mail/ # Business logic layer
|
|
16
16
|
│ └── *.ts
|
|
17
17
|
├── repositories/ # Data access layer
|
|
18
18
|
│ └── [resource].repository.ts
|
|
@@ -22,11 +22,11 @@ src/app/[module-name]/
|
|
|
22
22
|
│ ├── [model-name].model.ts
|
|
23
23
|
│ └── migrations/
|
|
24
24
|
│ └── [date]_[model-name].migration.ts
|
|
25
|
-
├──
|
|
25
|
+
├── requests/ # Request validation schemas
|
|
26
26
|
│ ├── index.ts # Simple validations (if all in one file)
|
|
27
|
-
│ └── *.
|
|
28
|
-
├──
|
|
29
|
-
│ └── *.
|
|
27
|
+
│ └── *.request.ts # Individual validation files
|
|
28
|
+
├── resources/ # Response resource transformers
|
|
29
|
+
│ └── *.resource.ts
|
|
30
30
|
├── events/ # Event handlers/listeners (auto-imported by Warlock.js)
|
|
31
31
|
│ └── *.ts
|
|
32
32
|
├── components/ # Reusable components for use within mails
|
|
@@ -41,9 +41,11 @@ src/app/[module-name]/
|
|
|
41
41
|
## Directory Structure Details
|
|
42
42
|
|
|
43
43
|
### 1. main.ts (Auto-imported)
|
|
44
|
+
|
|
44
45
|
Entry point file that is automatically imported by Warlock.js when the module loads. Use this for module initialization, event listeners, or setup code that should run when the module is loaded.
|
|
45
46
|
|
|
46
47
|
**Example:**
|
|
48
|
+
|
|
47
49
|
```typescript
|
|
48
50
|
import { onceConnected } from "@warlock.js/cascade";
|
|
49
51
|
|
|
@@ -56,9 +58,11 @@ onceConnected(async () => {
|
|
|
56
58
|
```
|
|
57
59
|
|
|
58
60
|
### 2. routes.ts (Auto-imported)
|
|
61
|
+
|
|
59
62
|
Main routing file that defines all module endpoints. This file is automatically imported by Warlock.js. Use router guards (`guarded`, `guardedAdmin`, `guardedGuest`, etc.) to protect routes.
|
|
60
63
|
|
|
61
64
|
**Example:**
|
|
65
|
+
|
|
62
66
|
```typescript
|
|
63
67
|
import { router } from "@warlock.js/core";
|
|
64
68
|
import { guarded, guardedGuest } from "app/utils/router";
|
|
@@ -75,28 +79,28 @@ guarded(() => {
|
|
|
75
79
|
```
|
|
76
80
|
|
|
77
81
|
### 3. controllers/
|
|
82
|
+
|
|
78
83
|
Request handlers that process HTTP requests. Organize controllers by feature (e.g., `auth/`, `profile/`).
|
|
79
84
|
|
|
80
85
|
**Controller Structure:**
|
|
86
|
+
|
|
81
87
|
```typescript
|
|
82
88
|
import type { Request, RequestHandler, Response } from "@warlock.js/core";
|
|
83
89
|
import { someService } from "app/[module]/services/some.service";
|
|
84
|
-
import {
|
|
90
|
+
import { someRequestSchema, type SomeRequest } from "app/[module]/requests/some.request";
|
|
85
91
|
|
|
86
|
-
export const someController: RequestHandler = async (
|
|
87
|
-
request: Request,
|
|
88
|
-
response: Response,
|
|
89
|
-
) => {
|
|
92
|
+
export const someController: RequestHandler = async (request: SomeRequest, response: Response) => {
|
|
90
93
|
const data = await someService(request.validated());
|
|
91
94
|
return response.success({ data });
|
|
92
95
|
};
|
|
93
96
|
|
|
94
97
|
someController.validation = {
|
|
95
|
-
schema:
|
|
98
|
+
schema: someRequestSchema,
|
|
96
99
|
};
|
|
97
100
|
```
|
|
98
101
|
|
|
99
102
|
**For RESTful Resources:**
|
|
103
|
+
|
|
100
104
|
```typescript
|
|
101
105
|
import { Restful, type RouteResource, v } from "@warlock.js/core";
|
|
102
106
|
import { SomeModel } from "../models/some";
|
|
@@ -104,7 +108,7 @@ import { someRepository } from "../repositories/some.repository";
|
|
|
104
108
|
|
|
105
109
|
class RestfulSome extends Restful<SomeModel> implements RouteResource {
|
|
106
110
|
protected repository = someRepository;
|
|
107
|
-
|
|
111
|
+
|
|
108
112
|
public validation: RouteResource["validation"] = {
|
|
109
113
|
create: {
|
|
110
114
|
schema: v.object({
|
|
@@ -118,31 +122,33 @@ export const restfulSome = new RestfulSome();
|
|
|
118
122
|
```
|
|
119
123
|
|
|
120
124
|
### 4. services/
|
|
125
|
+
|
|
121
126
|
Business logic layer. Services handle the core functionality and interact with repositories.
|
|
122
127
|
|
|
123
128
|
**Service Structure:**
|
|
129
|
+
|
|
124
130
|
```typescript
|
|
125
131
|
import type { SomeModel } from "app/[module]/models/some";
|
|
126
132
|
import { someRepository } from "app/[module]/repositories/some.repository";
|
|
127
133
|
|
|
128
|
-
export async function someService(
|
|
129
|
-
data: Record<string, any>,
|
|
130
|
-
): Promise<SomeModel> {
|
|
134
|
+
export async function someService(data: Record<string, any>): Promise<SomeModel> {
|
|
131
135
|
return await someRepository.create(data);
|
|
132
136
|
}
|
|
133
137
|
```
|
|
134
138
|
|
|
135
139
|
### 5. repositories/
|
|
140
|
+
|
|
136
141
|
Data access layer that extends `RepositoryManager`. Handles database queries and filtering.
|
|
137
142
|
|
|
138
143
|
**Repository Structure:**
|
|
144
|
+
|
|
139
145
|
```typescript
|
|
140
146
|
import type { FilterByOptions, RepositoryOptions } from "@warlock.js/core";
|
|
141
147
|
import { RepositoryManager } from "@warlock.js/core";
|
|
142
148
|
import { SomeModel } from "../models/some";
|
|
143
149
|
|
|
144
150
|
export class SomeRepository extends RepositoryManager<SomeModel> {
|
|
145
|
-
public
|
|
151
|
+
public source = SomeModel;
|
|
146
152
|
|
|
147
153
|
protected defaultOptions: RepositoryOptions = this.withDefaultOptions({});
|
|
148
154
|
|
|
@@ -156,46 +162,69 @@ export const someRepository = new SomeRepository();
|
|
|
156
162
|
```
|
|
157
163
|
|
|
158
164
|
### 6. models/
|
|
165
|
+
|
|
159
166
|
Database models that extend base model classes (e.g., `Model`, `Auth`). Include migrations in the `migrations/` subdirectory.
|
|
160
167
|
|
|
161
168
|
**Model Structure:**
|
|
169
|
+
|
|
162
170
|
```typescript
|
|
163
171
|
import { Model } from "@warlock.js/core";
|
|
164
|
-
import type {
|
|
165
|
-
import {
|
|
172
|
+
import type { StrictMode } from "@warlock.js/cascade";
|
|
173
|
+
import { SomeResource } from "../../resources/some.resource";
|
|
174
|
+
import { v, type Infer } from "@warlock.js/seal";
|
|
166
175
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
public syncWith = [];
|
|
176
|
+
const someModelSchema = v.object({
|
|
177
|
+
name: v.string().required(),
|
|
178
|
+
email: v.email().required(),
|
|
179
|
+
});
|
|
172
180
|
|
|
173
|
-
|
|
174
|
-
isActive: true,
|
|
175
|
-
};
|
|
181
|
+
type SomeModelType = Infer<typeof someModelSchema>;
|
|
176
182
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
183
|
+
export class SomeModel extends Model<SomeModelType> {
|
|
184
|
+
public static table = "somes";
|
|
185
|
+
public static strictMode: StrictMode = "fail";
|
|
186
|
+
public static resource = SomeResource;
|
|
187
|
+
|
|
188
|
+
public static schema = someModelSchema;
|
|
181
189
|
|
|
182
|
-
public
|
|
190
|
+
public embed = ["id", "name"];
|
|
183
191
|
}
|
|
184
192
|
```
|
|
185
193
|
|
|
186
|
-
### 7.
|
|
187
|
-
|
|
194
|
+
### 7. requests/
|
|
195
|
+
|
|
196
|
+
Request handlers that process HTTP requests. Organize controllers by feature (e.g., `auth/`, `profile/`).
|
|
197
|
+
|
|
198
|
+
**Request Structure:**
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import type { Request, RequestHandler, Response } from "@warlock.js/core";
|
|
202
|
+
import { someService } from "app/[module]/services/some.service";
|
|
203
|
+
import { someRequestSchema, type SomeRequest } from "app/[module]/requests/some.request";
|
|
204
|
+
|
|
205
|
+
export const someController: RequestHandler = async (request: SomeRequest, response: Response) => {
|
|
206
|
+
const data = await someService(request.validated());
|
|
207
|
+
return response.success({ data });
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
someController.validation = {
|
|
211
|
+
schema: someRequestSchema,
|
|
212
|
+
};
|
|
213
|
+
```
|
|
188
214
|
|
|
189
215
|
**Important Rules:**
|
|
216
|
+
|
|
190
217
|
- If a module has simple validations, use `index.ts` to export all schemas
|
|
191
|
-
- If
|
|
218
|
+
- If requests are complex or numerous, use separate `.request.ts` files
|
|
192
219
|
- Always export the schema and its inferred type
|
|
193
|
-
- Pass the inferred type as generic to `Request<Model, ValidationType>`
|
|
194
220
|
- Use `Infer<typeof schema>` (similar to Zod) to generate TypeScript types
|
|
195
221
|
|
|
196
222
|
**Simple Validation (index.ts):**
|
|
223
|
+
|
|
197
224
|
```typescript
|
|
198
|
-
import { v, type Infer } from "@warlock.js/
|
|
225
|
+
import { v, type Infer } from "@warlock.js/seal";
|
|
226
|
+
import { type Request } from "@warlock.js/core";
|
|
227
|
+
import { type User } from "app/users/models/user";
|
|
199
228
|
|
|
200
229
|
export const createSchema = v.object({
|
|
201
230
|
name: v.string().minLength(2).required(),
|
|
@@ -207,30 +236,41 @@ export const updateSchema = v.object({
|
|
|
207
236
|
|
|
208
237
|
export type CreateData = Infer<typeof createSchema>;
|
|
209
238
|
export type UpdateData = Infer<typeof updateSchema>;
|
|
239
|
+
|
|
240
|
+
export type CreatePostRequest<User, CreateData>;
|
|
241
|
+
export type UpdatePostRequest<User, UpdateData>;
|
|
210
242
|
```
|
|
211
243
|
|
|
212
244
|
**Individual Validation Files:**
|
|
245
|
+
|
|
213
246
|
```typescript
|
|
214
|
-
//
|
|
215
|
-
import { v, type Infer } from "@warlock.js/
|
|
247
|
+
// requests/create-account.request.ts
|
|
248
|
+
import { v, type Infer } from "@warlock.js/seal";
|
|
249
|
+
import { type Request } from "@warlock.js/core";
|
|
216
250
|
|
|
217
251
|
export const createAccountSchema = v.object({
|
|
218
252
|
name: v.string().minLength(2).required(),
|
|
219
|
-
email:
|
|
220
|
-
password: v.string().
|
|
253
|
+
email: vemail().required(),
|
|
254
|
+
password: v.string().required().strongPassword(),
|
|
221
255
|
});
|
|
222
256
|
|
|
223
257
|
export type CreateAccountData = Infer<typeof createAccountSchema>;
|
|
258
|
+
|
|
259
|
+
export type CreateAccountRequest<undefined, CreateAccountData>;
|
|
224
260
|
```
|
|
225
261
|
|
|
226
262
|
**Usage in Controller:**
|
|
263
|
+
|
|
227
264
|
```typescript
|
|
228
265
|
import type { Request, RequestHandler, Response } from "@warlock.js/core";
|
|
229
266
|
import type { User } from "app/[module]/models/user";
|
|
230
|
-
import {
|
|
267
|
+
import {
|
|
268
|
+
createAccountSchema,
|
|
269
|
+
type CreateAccountRequest,
|
|
270
|
+
} from "app/[module]/requests/create-account.request";
|
|
231
271
|
|
|
232
272
|
export const createAccountController: RequestHandler = async (
|
|
233
|
-
request:
|
|
273
|
+
request: CreateAccountRequest,
|
|
234
274
|
response: Response,
|
|
235
275
|
) => {
|
|
236
276
|
// request.validated() is now typed as CreateAccountData
|
|
@@ -243,26 +283,34 @@ createAccountController.validation = {
|
|
|
243
283
|
};
|
|
244
284
|
```
|
|
245
285
|
|
|
246
|
-
### 8.
|
|
247
|
-
|
|
286
|
+
### 8. resources/
|
|
287
|
+
|
|
288
|
+
Resources define how data is transformed before being sent to clients.
|
|
289
|
+
|
|
290
|
+
**Resource Structure:**
|
|
248
291
|
|
|
249
|
-
**Output Structure:**
|
|
250
292
|
```typescript
|
|
251
|
-
import {
|
|
252
|
-
import {
|
|
293
|
+
import { Resource } from "@warlock.js/core";
|
|
294
|
+
import {} from "app/utils/output";
|
|
253
295
|
|
|
254
|
-
export class
|
|
255
|
-
|
|
296
|
+
export class SomeResource extends Resource {
|
|
297
|
+
public schema = {
|
|
256
298
|
name: "string",
|
|
257
|
-
|
|
258
|
-
|
|
299
|
+
id: "int",
|
|
300
|
+
type: () => "user", // custom output value
|
|
301
|
+
image: (value) => (value.startsWith("/") ? value : "/" + value),
|
|
302
|
+
};
|
|
259
303
|
}
|
|
260
304
|
```
|
|
261
305
|
|
|
262
306
|
### 9. events/ (Auto-imported)
|
|
307
|
+
|
|
263
308
|
Event handlers and listeners for model events or application events. **All files in this folder are automatically imported by Warlock.js** - no need to manually import them elsewhere.
|
|
264
309
|
|
|
310
|
+
> During development server, It's very important to use export const cleanup for events unbinding to prevent duplicate events registery on hmr.
|
|
311
|
+
|
|
265
312
|
**Event Structure:**
|
|
313
|
+
|
|
266
314
|
```typescript
|
|
267
315
|
import { Response } from "@warlock.js/core";
|
|
268
316
|
|
|
@@ -270,21 +318,28 @@ export function someEventHandler(response: Response) {
|
|
|
270
318
|
// Event handling logic
|
|
271
319
|
}
|
|
272
320
|
|
|
273
|
-
Response.on("sending", someEventHandler);
|
|
321
|
+
const event = Response.on("sending", someEventHandler);
|
|
322
|
+
|
|
323
|
+
export const cleanup = [event];
|
|
324
|
+
// or use a callback if the event is not an unsubscribe function
|
|
325
|
+
export const cleanup = () => event.unsbcribe();
|
|
274
326
|
```
|
|
275
327
|
|
|
276
328
|
**Note:** Files in the `events/` folder are auto-imported, similar to `routes.ts`, `main.ts`, and `utils/locales.ts`. Just create the file and it will be loaded automatically.
|
|
277
329
|
|
|
278
330
|
### 10. services/mail/ (or separate mail/ folder)
|
|
331
|
+
|
|
279
332
|
Email service functions for sending notifications. **Mail files should be suffixed with `.mail.ts`** (e.g., `welcome.mail.ts`, `reset-password.mail.ts`).
|
|
280
333
|
|
|
281
334
|
You can organize mail services either as:
|
|
335
|
+
|
|
282
336
|
- Files in `services/mail/` folder (if you have multiple mail-related services) - **Recommended**
|
|
283
337
|
- Individual files in `services/` folder (if you have few mail services)
|
|
284
338
|
- A separate `mail/` folder at module root (legacy pattern, still supported)
|
|
285
339
|
|
|
286
340
|
**Mail Structure:**
|
|
287
|
-
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
288
343
|
// services/mail/welcome.mail.ts
|
|
289
344
|
import { sendMail } from "@warlock.js/core";
|
|
290
345
|
import type { SomeModel } from "../models/some";
|
|
@@ -294,7 +349,7 @@ export default async function sendWelcomeEmail(model: SomeModel) {
|
|
|
294
349
|
await sendMail({
|
|
295
350
|
to: model.get("email"),
|
|
296
351
|
subject: "Subject",
|
|
297
|
-
|
|
352
|
+
component: <SomeEmailComponent name={model.get("name")} />,
|
|
298
353
|
});
|
|
299
354
|
}
|
|
300
355
|
```
|
|
@@ -302,10 +357,12 @@ export default async function sendWelcomeEmail(model: SomeModel) {
|
|
|
302
357
|
**Recommendation:** Use `services/mail/` folder for better organization when you have multiple mail templates, and always suffix mail files with `.mail.ts`.
|
|
303
358
|
|
|
304
359
|
### 11. components/
|
|
360
|
+
|
|
305
361
|
Reusable components for use within email templates. These are typically functions that return HTML strings or React-like components that can be used in mail services.
|
|
306
362
|
|
|
307
363
|
**Component Structure:**
|
|
308
|
-
|
|
364
|
+
|
|
365
|
+
```tsx
|
|
309
366
|
export function WelcomeEmailComponent(data: { name: string; email: string }) {
|
|
310
367
|
return `
|
|
311
368
|
<div>
|
|
@@ -317,7 +374,8 @@ export function WelcomeEmailComponent(data: { name: string; email: string }) {
|
|
|
317
374
|
```
|
|
318
375
|
|
|
319
376
|
**Usage in Mail:**
|
|
320
|
-
|
|
377
|
+
|
|
378
|
+
```tsx
|
|
321
379
|
import { sendMail } from "@warlock.js/core";
|
|
322
380
|
import { WelcomeEmailComponent } from "../components/welcome-email.component";
|
|
323
381
|
|
|
@@ -325,22 +383,22 @@ export default async function sendWelcomeEmail(user: User) {
|
|
|
325
383
|
await sendMail({
|
|
326
384
|
to: user.get("email"),
|
|
327
385
|
subject: "Welcome",
|
|
328
|
-
|
|
329
|
-
name: user.get("name"),
|
|
330
|
-
email: user.get("email")
|
|
331
|
-
}),
|
|
386
|
+
component: <WelcomeEmailComponent name={user.get("name")} email={user.get("email")} />,
|
|
332
387
|
});
|
|
333
388
|
}
|
|
334
389
|
```
|
|
335
390
|
|
|
336
391
|
### 12. types/
|
|
392
|
+
|
|
337
393
|
TypeScript type definitions specific to the module. Use this folder for:
|
|
394
|
+
|
|
338
395
|
- Request/response types
|
|
339
396
|
- Service parameter types
|
|
340
397
|
- Module-specific interfaces
|
|
341
398
|
- Type utilities
|
|
342
399
|
|
|
343
400
|
**Types Structure:**
|
|
401
|
+
|
|
344
402
|
```typescript
|
|
345
403
|
// types/user.types.ts
|
|
346
404
|
export interface UserPreferences {
|
|
@@ -352,9 +410,11 @@ export type UserRole = "admin" | "user" | "guest";
|
|
|
352
410
|
```
|
|
353
411
|
|
|
354
412
|
### 13. utils/
|
|
413
|
+
|
|
355
414
|
Module-specific utility functions. **Must include `locales.ts`** which is automatically imported by Warlock.js.
|
|
356
415
|
|
|
357
416
|
**utils/locales.ts (Auto-imported):**
|
|
417
|
+
|
|
358
418
|
```typescript
|
|
359
419
|
import { groupedTranslations } from "@mongez/localization";
|
|
360
420
|
|
|
@@ -371,6 +431,7 @@ groupedTranslations("moduleName", {
|
|
|
371
431
|
```
|
|
372
432
|
|
|
373
433
|
**utils/flags.ts (Constants):**
|
|
434
|
+
|
|
374
435
|
```typescript
|
|
375
436
|
// Module-specific constants and flags
|
|
376
437
|
export const USER_STATUS = {
|
|
@@ -387,6 +448,7 @@ export const USER_ROLES = {
|
|
|
387
448
|
```
|
|
388
449
|
|
|
389
450
|
**Other Utilities:**
|
|
451
|
+
|
|
390
452
|
```typescript
|
|
391
453
|
// utils/helpers.ts or similar
|
|
392
454
|
export function formatUserName(user: User): string {
|
|
@@ -444,4 +506,4 @@ export function formatUserName(user: User): string {
|
|
|
444
506
|
- Keep mail services organized: use `services/mail/` folder if multiple, or individual files in `services/` if few
|
|
445
507
|
- Use `components/` folder for reusable email components
|
|
446
508
|
- Define module-specific types in `types/` folder
|
|
447
|
-
- Use `utils/flags.ts` for constants and configuration flags
|
|
509
|
+
- Use `utils/flags.ts` for constants and configuration flags
|
|
@@ -1,69 +1,62 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
"huskier": {
|
|
61
|
-
"hooks": {
|
|
62
|
-
"pre-commit": [
|
|
63
|
-
"yarn format",
|
|
64
|
-
"yarn lint",
|
|
65
|
-
"yarn tsc"
|
|
66
|
-
]
|
|
67
|
-
}
|
|
2
|
+
"name": "app-name",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "warlock dev",
|
|
8
|
+
"build": "tsc && warlock build",
|
|
9
|
+
"start": "warlock start",
|
|
10
|
+
"seed": "warlock seed",
|
|
11
|
+
"migrate": "warlock migrate",
|
|
12
|
+
"migrate.fresh": "warlock migrate --fresh",
|
|
13
|
+
"migrate.list": "warlock migrate --list",
|
|
14
|
+
"jwt": "warlock jwt.generate",
|
|
15
|
+
"serve": "yarn build && nohup warlock start > /dev/null 2>&1",
|
|
16
|
+
"lint": "npx eslint --fix ./src --max-warnings=0",
|
|
17
|
+
"format": "npx prettier --write ./src/**/*.{js,jsx,ts,tsx,css,md,json} --config ./.prettierrc.json",
|
|
18
|
+
"tsc": "npx tsc --noEmit",
|
|
19
|
+
"prepare": "huskier-init && husky install",
|
|
20
|
+
"lf": "find . -type d ( -name 'node_modules' -o -name '.git' ) -prune -o -type f -exec dos2unix {} ;"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@mongez/fs": "^3.0.5",
|
|
24
|
+
"@mongez/reinforcements": "^2.3.17",
|
|
25
|
+
"@mongez/localization": "^3.2.1",
|
|
26
|
+
"@mongez/supportive-is": "^2.0.4",
|
|
27
|
+
"@warlock.js/auth": "4.0.32",
|
|
28
|
+
"@warlock.js/cache": "4.0.32",
|
|
29
|
+
"@warlock.js/cascade": "4.0.32",
|
|
30
|
+
"@warlock.js/scheduler": "4.0.32",
|
|
31
|
+
"@warlock.js/core": "4.0.32",
|
|
32
|
+
"@warlock.js/logger": "4.0.32",
|
|
33
|
+
"@warlock.js/seal": "4.0.32",
|
|
34
|
+
"dayjs": "^1.11.19"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@mongez/huskier": "^3.0.0",
|
|
38
|
+
"@types/mime": "^3.0.4",
|
|
39
|
+
"@types/node": "^25.0.3",
|
|
40
|
+
"@types/prettier": "^3.0.0",
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "^8.50.0",
|
|
42
|
+
"@typescript-eslint/parser": "^8.50.0",
|
|
43
|
+
"eslint": "^9.39.2",
|
|
44
|
+
"eslint-config-prettier": "^10.1.8",
|
|
45
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
46
|
+
"eslint-plugin-unused-imports": "^4.3.0",
|
|
47
|
+
"npm-check-updates": "^19.2.0",
|
|
48
|
+
"prettier": "^3.7.4",
|
|
49
|
+
"prettier-plugin-organize-imports": "^4.3.0",
|
|
50
|
+
"tsx": "^4.21.0",
|
|
51
|
+
"typescript": "^5.9.3"
|
|
52
|
+
},
|
|
53
|
+
"huskier": {
|
|
54
|
+
"hooks": {
|
|
55
|
+
"pre-commit": [
|
|
56
|
+
"yarn format",
|
|
57
|
+
"yarn lint",
|
|
58
|
+
"yarn tsc"
|
|
59
|
+
]
|
|
68
60
|
}
|
|
69
|
-
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -13,8 +13,9 @@ export const forgotPassword: RequestHandler = async (request: Request, response:
|
|
|
13
13
|
const user = await usersRepository.first({ email });
|
|
14
14
|
|
|
15
15
|
if (!user) {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
// Silent success - don't reveal if email exists
|
|
17
|
+
return response.success({
|
|
18
|
+
message: t("auth.otpSent"),
|
|
18
19
|
});
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -18,10 +18,7 @@ export const login: RequestHandler = async (request: LoginRequest, response: Res
|
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
return response.success(
|
|
22
|
-
user: result.user,
|
|
23
|
-
...result.tokens,
|
|
24
|
-
});
|
|
21
|
+
return response.success(result);
|
|
25
22
|
};
|
|
26
23
|
|
|
27
24
|
login.description = "User Login";
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { authService } from "@warlock.js/auth";
|
|
1
2
|
import { onceConnected } from "@warlock.js/cascade";
|
|
2
|
-
import { Job } from "@warlock.js/scheduler";
|
|
3
3
|
import { scheduler } from "app/shared/services/scheduler.service";
|
|
4
4
|
import { cleanupExpiredOtpsService } from "./services/otp.service";
|
|
5
5
|
|
|
6
6
|
onceConnected(() => {
|
|
7
|
-
|
|
8
|
-
scheduler.
|
|
7
|
+
// Cleanup expired OTPs every hour
|
|
8
|
+
scheduler.newJob("cleanup-expired-otps", cleanupExpiredOtpsService).everyHour();
|
|
9
|
+
|
|
10
|
+
// Cleanup expired refresh tokens every hour
|
|
11
|
+
scheduler.newJob("cleanup-expired-tokens", () => authService.cleanupExpiredTokens()).everyHour();
|
|
9
12
|
});
|
package/templates/warlock/src/app/auth/models/otp/migrations/22-12-2025_10-30-20.otp-migration.ts
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { migrate } from "@warlock.js/cascade";
|
|
2
2
|
import { OTP } from "../otp.model";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export default migrationOffice.register({
|
|
4
|
+
export default migrate(OTP, {
|
|
7
5
|
name: "otp",
|
|
8
|
-
createdAt: "
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
await otpBlueprint.index("userId");
|
|
6
|
+
createdAt: "2025-12-22T10:30:20", // ISO Date
|
|
7
|
+
up() {
|
|
8
|
+
this.index("code");
|
|
9
|
+
this.index(["target", "type"]);
|
|
10
|
+
this.index("expiresAt");
|
|
11
|
+
this.index("userId");
|
|
15
12
|
},
|
|
16
|
-
down
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
down() {
|
|
14
|
+
this.dropIndex("code");
|
|
15
|
+
this.dropIndex(["target", "type"]);
|
|
16
|
+
this.dropIndex("expiresAt");
|
|
17
|
+
this.dropIndex("userId");
|
|
21
18
|
},
|
|
22
19
|
});
|