create-warlock 3.0.16 → 3.0.18
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/package.json +1 -1
- package/templates/warlock/AGENTS.md +11 -0
- package/templates/warlock/docs/new-module.md +447 -0
- package/templates/warlock/package.json +7 -6
- package/templates/warlock/src/app/posts/controllers/create-post.controller.ts +22 -0
- package/templates/warlock/src/app/posts/controllers/get-all-posts.controller.ts +13 -0
- package/templates/warlock/src/app/posts/controllers/get-post.controller.ts +28 -0
- package/templates/warlock/src/app/posts/models/index.ts +1 -1
- package/templates/warlock/src/app/posts/models/post/index.ts +1 -0
- package/templates/warlock/src/app/posts/models/{post.model.ts → post/post.model.ts} +11 -1
- package/templates/warlock/src/app/posts/output/post.output.ts +4 -5
- package/templates/warlock/src/app/posts/repositories/posts.repository.ts +26 -0
- package/templates/warlock/src/app/posts/routes.ts +7 -7
- package/templates/warlock/src/app/posts/services/posts.service.ts +28 -0
- package/templates/warlock/src/app/posts/types/index.ts +6 -0
- package/templates/warlock/src/app/posts/utils/locales.ts +8 -0
- package/templates/warlock/src/app/posts/validation/create-post.validation.ts +8 -0
- package/templates/warlock/src/app/posts/validation/get-post.validation.ts +7 -0
- package/templates/warlock/src/app/users/controllers/auth/{activate-account.ts → activate-account.controller.ts} +18 -17
- package/templates/warlock/src/app/users/controllers/auth/admin-login.controller.ts +39 -0
- package/templates/warlock/src/app/users/controllers/auth/create-account.controller.ts +23 -0
- package/templates/warlock/src/app/users/controllers/auth/login.controller.ts +32 -0
- package/templates/warlock/src/app/users/controllers/profile/{change-password.ts → change-password.controller.ts} +1 -1
- package/templates/warlock/src/app/users/routes.ts +17 -17
- package/templates/warlock/src/app/users/services/create-account.service.ts +16 -0
- package/templates/warlock/src/app/users/services/login.service.ts +16 -0
- package/templates/warlock/src/app/users/validation/activate-account.validation.ts +8 -0
- package/templates/warlock/src/app/users/validation/admin-login.validation.ts +8 -0
- package/templates/warlock/src/app/users/validation/create-account.validation.ts +10 -0
- package/templates/warlock/src/app/users/validation/login.validation.ts +8 -0
- package/templates/warlock/src/config/validation.ts +1 -1
- package/templates/warlock/src/app/posts/controllers/create-new-post.request.ts +0 -26
- package/templates/warlock/src/app/posts/controllers/get-all-posts.request.ts +0 -17
- package/templates/warlock/src/app/posts/controllers/get-post.request.ts +0 -37
- package/templates/warlock/src/app/uploads/routes.ts +0 -27
- package/templates/warlock/src/app/users/controllers/auth/admin-login.ts +0 -40
- package/templates/warlock/src/app/users/controllers/auth/login.request.ts +0 -28
- package/templates/warlock/src/app/users/controllers/auth/register.request.ts +0 -24
- /package/templates/warlock/src/app/users/controllers/auth/{create-account.ts → create-account-simple.controller.ts} +0 -0
- /package/templates/warlock/src/app/users/controllers/auth/{forget-password.ts → forget-password.controller.ts} +0 -0
- /package/templates/warlock/src/app/users/controllers/auth/{logout.ts → logout.controller.ts} +0 -0
- /package/templates/warlock/src/app/users/controllers/auth/{resend-activation-code.ts → resend-activation-code.controller.ts} +0 -0
- /package/templates/warlock/src/app/users/controllers/auth/{reset-password.ts → reset-password.controller.ts} +0 -0
- /package/templates/warlock/src/app/users/controllers/auth/{verify-forget-password-code.ts → verify-forget-password-code.controller.ts} +0 -0
- /package/templates/warlock/src/app/users/controllers/profile/{my-profile.ts → my-profile.controller.ts} +0 -0
- /package/templates/warlock/src/app/users/controllers/profile/{update-profile.ts → update-profile.controller.ts} +0 -0
- /package/templates/warlock/src/app/users/controllers/{restful-users.ts → users.restful.ts} +0 -0
package/package.json
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Agent Guidelines
|
|
2
|
+
|
|
3
|
+
## Module Creation
|
|
4
|
+
|
|
5
|
+
When creating a new module, refer to the comprehensive guide in [docs/new-module.md](../docs/new-module.md) for:
|
|
6
|
+
- Complete directory structure
|
|
7
|
+
- Detailed explanations of each folder and file
|
|
8
|
+
- Code examples and best practices
|
|
9
|
+
- Auto-imported files (main.ts, routes.ts, utils/locales.ts, events/)
|
|
10
|
+
- Validation patterns with TypeScript type inference
|
|
11
|
+
- Controller, service, repository patterns
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
# Create a new module
|
|
2
|
+
|
|
3
|
+
Each module should consist of the following structure:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
src/app/[module-name]/
|
|
7
|
+
├── main.ts # Entry point (auto-imported by Warlock.js)
|
|
8
|
+
├── routes.ts # Main routing file (auto-imported by Warlock.js)
|
|
9
|
+
├── controllers/ # Request handlers
|
|
10
|
+
│ ├── [feature]/ # Grouped by feature (e.g., auth, profile)
|
|
11
|
+
│ │ └── *.controller.ts
|
|
12
|
+
│ └── [module].restful.ts # RESTful resource controller (optional)
|
|
13
|
+
├── services/ # Business logic layer
|
|
14
|
+
│ ├── *.service.ts
|
|
15
|
+
│ └── mail/ # Email services (optional, can be separate folder or files)
|
|
16
|
+
│ └── *.ts
|
|
17
|
+
├── repositories/ # Data access layer
|
|
18
|
+
│ └── [resource].repository.ts
|
|
19
|
+
├── models/ # Database models
|
|
20
|
+
│ └── [model-name]/
|
|
21
|
+
│ ├── index.ts
|
|
22
|
+
│ ├── [model-name].model.ts
|
|
23
|
+
│ └── migrations/
|
|
24
|
+
│ └── [date]_[model-name].migration.ts
|
|
25
|
+
├── validation/ # Request validation schemas
|
|
26
|
+
│ ├── index.ts # Simple validations (if all in one file)
|
|
27
|
+
│ └── *.validation.ts # Individual validation files
|
|
28
|
+
├── output/ # Response output transformers
|
|
29
|
+
│ └── *.output.ts
|
|
30
|
+
├── events/ # Event handlers/listeners (auto-imported by Warlock.js)
|
|
31
|
+
│ └── *.ts
|
|
32
|
+
├── components/ # Reusable components for use within mails
|
|
33
|
+
│ └── *.ts
|
|
34
|
+
├── types/ # TypeScript type definitions
|
|
35
|
+
│ └── *.ts
|
|
36
|
+
└── utils/ # Module-specific utilities
|
|
37
|
+
├── locales.ts # Translations (auto-imported by Warlock.js)
|
|
38
|
+
└── flags.ts # Constants/flags
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Directory Structure Details
|
|
42
|
+
|
|
43
|
+
### 1. main.ts (Auto-imported)
|
|
44
|
+
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
|
+
**Example:**
|
|
47
|
+
```typescript
|
|
48
|
+
import { onceConnected } from "@warlock.js/cascade";
|
|
49
|
+
|
|
50
|
+
// This function will be called once the app is connected to the database
|
|
51
|
+
onceConnected(async () => {
|
|
52
|
+
// Module initialization code
|
|
53
|
+
// Register event listeners
|
|
54
|
+
// Setup module-specific configurations
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2. routes.ts (Auto-imported)
|
|
59
|
+
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
|
+
|
|
61
|
+
**Example:**
|
|
62
|
+
```typescript
|
|
63
|
+
import { router } from "@warlock.js/core";
|
|
64
|
+
import { guarded, guardedGuest } from "app/utils/router";
|
|
65
|
+
import { createAccountController } from "./controllers/auth/create-account.controller";
|
|
66
|
+
import { myProfile } from "./controllers/profile/my-profile.controller";
|
|
67
|
+
|
|
68
|
+
guardedGuest(() => {
|
|
69
|
+
router.post("/register", createAccountController);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
guarded(() => {
|
|
73
|
+
router.get("/me", myProfile);
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 3. controllers/
|
|
78
|
+
Request handlers that process HTTP requests. Organize controllers by feature (e.g., `auth/`, `profile/`).
|
|
79
|
+
|
|
80
|
+
**Controller Structure:**
|
|
81
|
+
```typescript
|
|
82
|
+
import type { Request, RequestHandler, Response } from "@warlock.js/core";
|
|
83
|
+
import { someService } from "app/[module]/services/some.service";
|
|
84
|
+
import { someSchema } from "app/[module]/validation/some.validation";
|
|
85
|
+
|
|
86
|
+
export const someController: RequestHandler = async (
|
|
87
|
+
request: Request,
|
|
88
|
+
response: Response,
|
|
89
|
+
) => {
|
|
90
|
+
const data = await someService(request.validated());
|
|
91
|
+
return response.success({ data });
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
someController.validation = {
|
|
95
|
+
schema: someSchema,
|
|
96
|
+
};
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**For RESTful Resources:**
|
|
100
|
+
```typescript
|
|
101
|
+
import { Restful, type RouteResource, v } from "@warlock.js/core";
|
|
102
|
+
import { SomeModel } from "../models/some";
|
|
103
|
+
import { someRepository } from "../repositories/some.repository";
|
|
104
|
+
|
|
105
|
+
class RestfulSome extends Restful<SomeModel> implements RouteResource {
|
|
106
|
+
protected repository = someRepository;
|
|
107
|
+
|
|
108
|
+
public validation: RouteResource["validation"] = {
|
|
109
|
+
create: {
|
|
110
|
+
schema: v.object({
|
|
111
|
+
name: v.string().required(),
|
|
112
|
+
}),
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const restfulSome = new RestfulSome();
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 4. services/
|
|
121
|
+
Business logic layer. Services handle the core functionality and interact with repositories.
|
|
122
|
+
|
|
123
|
+
**Service Structure:**
|
|
124
|
+
```typescript
|
|
125
|
+
import type { SomeModel } from "app/[module]/models/some";
|
|
126
|
+
import { someRepository } from "app/[module]/repositories/some.repository";
|
|
127
|
+
|
|
128
|
+
export async function someService(
|
|
129
|
+
data: Record<string, any>,
|
|
130
|
+
): Promise<SomeModel> {
|
|
131
|
+
return await someRepository.create(data);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 5. repositories/
|
|
136
|
+
Data access layer that extends `RepositoryManager`. Handles database queries and filtering.
|
|
137
|
+
|
|
138
|
+
**Repository Structure:**
|
|
139
|
+
```typescript
|
|
140
|
+
import type { FilterByOptions, RepositoryOptions } from "@warlock.js/core";
|
|
141
|
+
import { RepositoryManager } from "@warlock.js/core";
|
|
142
|
+
import { SomeModel } from "../models/some";
|
|
143
|
+
|
|
144
|
+
export class SomeRepository extends RepositoryManager<SomeModel> {
|
|
145
|
+
public model = SomeModel;
|
|
146
|
+
|
|
147
|
+
protected defaultOptions: RepositoryOptions = this.withDefaultOptions({});
|
|
148
|
+
|
|
149
|
+
protected filterBy: FilterByOptions = this.withDefaultFilters({
|
|
150
|
+
name: "like",
|
|
151
|
+
isActive: "bool",
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const someRepository = new SomeRepository();
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 6. models/
|
|
159
|
+
Database models that extend base model classes (e.g., `Model`, `Auth`). Include migrations in the `migrations/` subdirectory.
|
|
160
|
+
|
|
161
|
+
**Model Structure:**
|
|
162
|
+
```typescript
|
|
163
|
+
import { Model } from "@warlock.js/core";
|
|
164
|
+
import type { Casts, Document } from "@warlock.js/core";
|
|
165
|
+
import { SomeOutput } from "../../output/some.output";
|
|
166
|
+
|
|
167
|
+
export class SomeModel extends Model {
|
|
168
|
+
public static collection = "somes";
|
|
169
|
+
public static output = SomeOutput;
|
|
170
|
+
|
|
171
|
+
public syncWith = [];
|
|
172
|
+
|
|
173
|
+
public defaultValue: Document = {
|
|
174
|
+
isActive: true,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
protected casts: Casts = {
|
|
178
|
+
name: "string",
|
|
179
|
+
isActive: "boolean",
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
public embedded = ["id", "name"];
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 7. validation/
|
|
187
|
+
Validation schemas using the `@warlock.js/core` validation system. **All validation schemas must be in the validation folder** to be used with `request.validated()` and TypeScript type inference.
|
|
188
|
+
|
|
189
|
+
**Important Rules:**
|
|
190
|
+
- If a module has simple validations, use `index.ts` to export all schemas
|
|
191
|
+
- If validations are complex or numerous, use separate `.validation.ts` files
|
|
192
|
+
- Always export the schema and its inferred type
|
|
193
|
+
- Pass the inferred type as generic to `Request<Model, ValidationType>`
|
|
194
|
+
- Use `Infer<typeof schema>` (similar to Zod) to generate TypeScript types
|
|
195
|
+
|
|
196
|
+
**Simple Validation (index.ts):**
|
|
197
|
+
```typescript
|
|
198
|
+
import { v, type Infer } from "@warlock.js/core";
|
|
199
|
+
|
|
200
|
+
export const createSchema = v.object({
|
|
201
|
+
name: v.string().minLength(2).required(),
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
export const updateSchema = v.object({
|
|
205
|
+
name: v.string().minLength(2).optional(),
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
export type CreateData = Infer<typeof createSchema>;
|
|
209
|
+
export type UpdateData = Infer<typeof updateSchema>;
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Individual Validation Files:**
|
|
213
|
+
```typescript
|
|
214
|
+
// validation/create-account.validation.ts
|
|
215
|
+
import { v, type Infer } from "@warlock.js/core";
|
|
216
|
+
|
|
217
|
+
export const createAccountSchema = v.object({
|
|
218
|
+
name: v.string().minLength(2).required(),
|
|
219
|
+
email: v.string().email().required(),
|
|
220
|
+
password: v.string().minLength(8).required().strongPassword(),
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
export type CreateAccountData = Infer<typeof createAccountSchema>;
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Usage in Controller:**
|
|
227
|
+
```typescript
|
|
228
|
+
import type { Request, RequestHandler, Response } from "@warlock.js/core";
|
|
229
|
+
import type { User } from "app/[module]/models/user";
|
|
230
|
+
import { createAccountSchema, type CreateAccountData } from "app/[module]/validation/create-account.validation";
|
|
231
|
+
|
|
232
|
+
export const createAccountController: RequestHandler = async (
|
|
233
|
+
request: Request<User, CreateAccountData>,
|
|
234
|
+
response: Response,
|
|
235
|
+
) => {
|
|
236
|
+
// request.validated() is now typed as CreateAccountData
|
|
237
|
+
const data = request.validated();
|
|
238
|
+
// ...
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
createAccountController.validation = {
|
|
242
|
+
schema: createAccountSchema,
|
|
243
|
+
};
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### 8. output/
|
|
247
|
+
Response output transformers that define what data is returned to clients.
|
|
248
|
+
|
|
249
|
+
**Output Structure:**
|
|
250
|
+
```typescript
|
|
251
|
+
import { Output, type FinalOutput } from "@warlock.js/core";
|
|
252
|
+
import { withBaseOutputDetails } from "app/utils/output";
|
|
253
|
+
|
|
254
|
+
export class SomeOutput extends Output {
|
|
255
|
+
protected output: FinalOutput = withBaseOutputDetails({
|
|
256
|
+
name: "string",
|
|
257
|
+
email: "string",
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### 9. events/ (Auto-imported)
|
|
263
|
+
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
|
+
|
|
265
|
+
**Event Structure:**
|
|
266
|
+
```typescript
|
|
267
|
+
import { Response } from "@warlock.js/core";
|
|
268
|
+
|
|
269
|
+
export function someEventHandler(response: Response) {
|
|
270
|
+
// Event handling logic
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
Response.on("sending", someEventHandler);
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**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
|
+
|
|
278
|
+
### 10. services/mail/ (or separate mail/ folder)
|
|
279
|
+
Email service functions for sending notifications. **Mail files should be suffixed with `.mail.ts`** (e.g., `welcome.mail.ts`, `reset-password.mail.ts`).
|
|
280
|
+
|
|
281
|
+
You can organize mail services either as:
|
|
282
|
+
- Files in `services/mail/` folder (if you have multiple mail-related services) - **Recommended**
|
|
283
|
+
- Individual files in `services/` folder (if you have few mail services)
|
|
284
|
+
- A separate `mail/` folder at module root (legacy pattern, still supported)
|
|
285
|
+
|
|
286
|
+
**Mail Structure:**
|
|
287
|
+
```typescript
|
|
288
|
+
// services/mail/welcome.mail.ts
|
|
289
|
+
import { sendMail } from "@warlock.js/core";
|
|
290
|
+
import type { SomeModel } from "../models/some";
|
|
291
|
+
import { SomeEmailComponent } from "../components/some-email.component";
|
|
292
|
+
|
|
293
|
+
export default async function sendWelcomeEmail(model: SomeModel) {
|
|
294
|
+
await sendMail({
|
|
295
|
+
to: model.get("email"),
|
|
296
|
+
subject: "Subject",
|
|
297
|
+
html: SomeEmailComponent({ name: model.get("name") }),
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Recommendation:** Use `services/mail/` folder for better organization when you have multiple mail templates, and always suffix mail files with `.mail.ts`.
|
|
303
|
+
|
|
304
|
+
### 11. components/
|
|
305
|
+
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
|
+
|
|
307
|
+
**Component Structure:**
|
|
308
|
+
```typescript
|
|
309
|
+
export function WelcomeEmailComponent(data: { name: string; email: string }) {
|
|
310
|
+
return `
|
|
311
|
+
<div>
|
|
312
|
+
<h1>Welcome, ${data.name}!</h1>
|
|
313
|
+
<p>Your email: ${data.email}</p>
|
|
314
|
+
</div>
|
|
315
|
+
`;
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**Usage in Mail:**
|
|
320
|
+
```typescript
|
|
321
|
+
import { sendMail } from "@warlock.js/core";
|
|
322
|
+
import { WelcomeEmailComponent } from "../components/welcome-email.component";
|
|
323
|
+
|
|
324
|
+
export default async function sendWelcomeEmail(user: User) {
|
|
325
|
+
await sendMail({
|
|
326
|
+
to: user.get("email"),
|
|
327
|
+
subject: "Welcome",
|
|
328
|
+
html: WelcomeEmailComponent({
|
|
329
|
+
name: user.get("name"),
|
|
330
|
+
email: user.get("email")
|
|
331
|
+
}),
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### 12. types/
|
|
337
|
+
TypeScript type definitions specific to the module. Use this folder for:
|
|
338
|
+
- Request/response types
|
|
339
|
+
- Service parameter types
|
|
340
|
+
- Module-specific interfaces
|
|
341
|
+
- Type utilities
|
|
342
|
+
|
|
343
|
+
**Types Structure:**
|
|
344
|
+
```typescript
|
|
345
|
+
// types/user.types.ts
|
|
346
|
+
export interface UserPreferences {
|
|
347
|
+
theme: "light" | "dark";
|
|
348
|
+
notifications: boolean;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export type UserRole = "admin" | "user" | "guest";
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### 13. utils/
|
|
355
|
+
Module-specific utility functions. **Must include `locales.ts`** which is automatically imported by Warlock.js.
|
|
356
|
+
|
|
357
|
+
**utils/locales.ts (Auto-imported):**
|
|
358
|
+
```typescript
|
|
359
|
+
import { groupedTranslations } from "@mongez/localization";
|
|
360
|
+
|
|
361
|
+
groupedTranslations("moduleName", {
|
|
362
|
+
key1: {
|
|
363
|
+
en: "English translation",
|
|
364
|
+
ar: "Arabic translation",
|
|
365
|
+
},
|
|
366
|
+
invalidCredentials: {
|
|
367
|
+
en: "Invalid credentials",
|
|
368
|
+
ar: "بيانات الدخول غير صحيحة",
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**utils/flags.ts (Constants):**
|
|
374
|
+
```typescript
|
|
375
|
+
// Module-specific constants and flags
|
|
376
|
+
export const USER_STATUS = {
|
|
377
|
+
ACTIVE: "active",
|
|
378
|
+
INACTIVE: "inactive",
|
|
379
|
+
PENDING: "pending",
|
|
380
|
+
} as const;
|
|
381
|
+
|
|
382
|
+
export const USER_ROLES = {
|
|
383
|
+
ADMIN: "admin",
|
|
384
|
+
USER: "user",
|
|
385
|
+
GUEST: "guest",
|
|
386
|
+
} as const;
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**Other Utilities:**
|
|
390
|
+
```typescript
|
|
391
|
+
// utils/helpers.ts or similar
|
|
392
|
+
export function formatUserName(user: User): string {
|
|
393
|
+
return `${user.get("firstName")} ${user.get("lastName")}`;
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**Note:** The `utils/locales.ts` file is automatically imported by Warlock.js, similar to `routes.ts`, `main.ts`, and files in the `events/` folder.
|
|
398
|
+
|
|
399
|
+
## Best Practices
|
|
400
|
+
|
|
401
|
+
1. **Naming Conventions:**
|
|
402
|
+
- Use kebab-case for file names
|
|
403
|
+
- Use PascalCase for class names
|
|
404
|
+
- Use camelCase for functions and variables
|
|
405
|
+
|
|
406
|
+
2. **Controller Organization:**
|
|
407
|
+
- Group related controllers in subdirectories (e.g., `auth/`, `profile/`)
|
|
408
|
+
- Keep controllers thin - delegate business logic to services
|
|
409
|
+
|
|
410
|
+
3. **Service Layer:**
|
|
411
|
+
- Services should contain reusable business logic
|
|
412
|
+
- Services interact with repositories, not directly with models
|
|
413
|
+
|
|
414
|
+
4. **Repository Pattern:**
|
|
415
|
+
- All database operations should go through repositories
|
|
416
|
+
- Define filter options in the repository class
|
|
417
|
+
|
|
418
|
+
5. **Validation:**
|
|
419
|
+
- **All validation schemas must be in the `validation/` folder**
|
|
420
|
+
- Always attach validation to controllers using `controller.validation = { schema }`
|
|
421
|
+
- Export TypeScript types from validation files using `Infer<typeof schema>`
|
|
422
|
+
- Use the inferred type as a generic parameter in `Request<Model, ValidationType>`
|
|
423
|
+
- Use `index.ts` for simple validations, separate `.validation.ts` files for complex ones
|
|
424
|
+
|
|
425
|
+
6. **Models:**
|
|
426
|
+
- Define casts for all fields
|
|
427
|
+
- Specify embedded fields for performance
|
|
428
|
+
- Include default values when needed
|
|
429
|
+
|
|
430
|
+
7. **Routes:**
|
|
431
|
+
- Use appropriate guards (`guarded`, `guardedAdmin`, `guardedGuest`)
|
|
432
|
+
- Group related routes together
|
|
433
|
+
- Use RESTful resources when appropriate
|
|
434
|
+
- Routes file is auto-imported by Warlock.js
|
|
435
|
+
|
|
436
|
+
8. **Auto-imported Files:**
|
|
437
|
+
- `main.ts` - Module entry point
|
|
438
|
+
- `routes.ts` - Route definitions
|
|
439
|
+
- `utils/locales.ts` - Translations
|
|
440
|
+
- All files in `events/` folder - Event handlers
|
|
441
|
+
- No need to manually import these files elsewhere
|
|
442
|
+
|
|
443
|
+
9. **Organization:**
|
|
444
|
+
- Keep mail services organized: use `services/mail/` folder if multiple, or individual files in `services/` if few
|
|
445
|
+
- Use `components/` folder for reusable email components
|
|
446
|
+
- Define module-specific types in `types/` folder
|
|
447
|
+
- Use `utils/flags.ts` for constants and configuration flags
|
|
@@ -25,16 +25,17 @@
|
|
|
25
25
|
"@mongez/collection": "^1.2.0",
|
|
26
26
|
"@mongez/encryption": "^1.0.4",
|
|
27
27
|
"@mongez/fs": "^3.0.5",
|
|
28
|
-
"@mongez/reinforcements": "^2.3.
|
|
28
|
+
"@mongez/reinforcements": "^2.3.12",
|
|
29
29
|
"@mongez/localization": "^3.0.0",
|
|
30
30
|
"@mongez/dotenv": "^1.1.9",
|
|
31
31
|
"@mongez/config": "^1.0.26",
|
|
32
32
|
"@mongez/supportive-is": "^2.0.4",
|
|
33
|
-
"@warlock.js/auth": "3.0.
|
|
34
|
-
"@warlock.js/cache": "3.0.
|
|
35
|
-
"@warlock.js/cascade": "3.0.
|
|
36
|
-
"@warlock.js/core": "3.0.
|
|
37
|
-
"@warlock.js/logger": "3.0.
|
|
33
|
+
"@warlock.js/auth": "3.0.18",
|
|
34
|
+
"@warlock.js/cache": "3.0.18",
|
|
35
|
+
"@warlock.js/cascade": "3.0.18",
|
|
36
|
+
"@warlock.js/core": "3.0.18",
|
|
37
|
+
"@warlock.js/logger": "3.0.18",
|
|
38
|
+
"@warlock.js/seal": "3.0.18",
|
|
38
39
|
"@faker-js/faker": "^9.2.0",
|
|
39
40
|
"dayjs": "^1.11.13"
|
|
40
41
|
},
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Request, RequestHandler, Response } from "@warlock.js/core";
|
|
2
|
+
import type { Post } from "app/posts/models/post";
|
|
3
|
+
import { createPostService } from "app/posts/services/posts.service";
|
|
4
|
+
import {
|
|
5
|
+
createPostSchema,
|
|
6
|
+
type CreatePostData,
|
|
7
|
+
} from "app/posts/validation/create-post.validation";
|
|
8
|
+
|
|
9
|
+
export const createPostController: RequestHandler = async (
|
|
10
|
+
request: Request<Post, CreatePostData>,
|
|
11
|
+
response: Response,
|
|
12
|
+
) => {
|
|
13
|
+
const post = await createPostService(request.validated());
|
|
14
|
+
|
|
15
|
+
return response.success({
|
|
16
|
+
post,
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
createPostController.validation = {
|
|
21
|
+
schema: createPostSchema,
|
|
22
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RequestHandler, Response } from "@warlock.js/core";
|
|
2
|
+
import { getAllPostsService } from "app/posts/services/posts.service";
|
|
3
|
+
|
|
4
|
+
export const getAllPostsController: RequestHandler = async (
|
|
5
|
+
request,
|
|
6
|
+
response: Response,
|
|
7
|
+
) => {
|
|
8
|
+
const posts = await getAllPostsService();
|
|
9
|
+
|
|
10
|
+
return response.success({
|
|
11
|
+
posts,
|
|
12
|
+
});
|
|
13
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Request, RequestHandler, Response } from "@warlock.js/core";
|
|
2
|
+
import type { Post } from "app/posts/models/post";
|
|
3
|
+
import { getPostByIdService } from "app/posts/services/posts.service";
|
|
4
|
+
import {
|
|
5
|
+
getPostSchema,
|
|
6
|
+
type GetPostData,
|
|
7
|
+
} from "app/posts/validation/get-post.validation";
|
|
8
|
+
|
|
9
|
+
export const getPostController: RequestHandler = async (
|
|
10
|
+
request: Request<Post, GetPostData>,
|
|
11
|
+
response: Response,
|
|
12
|
+
) => {
|
|
13
|
+
const post = await getPostByIdService(request.int("id"));
|
|
14
|
+
|
|
15
|
+
if (!post) {
|
|
16
|
+
return response.notFound({
|
|
17
|
+
error: "Post Not found",
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return response.success({
|
|
22
|
+
post,
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
getPostController.validation = {
|
|
27
|
+
schema: getPostSchema,
|
|
28
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "./post
|
|
1
|
+
export * from "./post";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./post.model";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Model, type Casts } from "@warlock.js/cascade";
|
|
2
|
-
import { PostOutput } from "
|
|
2
|
+
import { PostOutput } from "../../output/post.output";
|
|
3
3
|
|
|
4
4
|
export class Post extends Model {
|
|
5
5
|
/**
|
|
@@ -12,6 +12,11 @@ export class Post extends Model {
|
|
|
12
12
|
*/
|
|
13
13
|
public static output = PostOutput;
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* {@inheritDoc}
|
|
17
|
+
*/
|
|
18
|
+
public syncWith = [];
|
|
19
|
+
|
|
15
20
|
/**
|
|
16
21
|
* Casts
|
|
17
22
|
*/
|
|
@@ -19,4 +24,9 @@ export class Post extends Model {
|
|
|
19
24
|
title: "string",
|
|
20
25
|
content: "string",
|
|
21
26
|
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* {@inheritDoc}
|
|
30
|
+
*/
|
|
31
|
+
public embedded = ["id", "title"];
|
|
22
32
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { type FinalOutput
|
|
1
|
+
import { Output, type FinalOutput } from "@warlock.js/core";
|
|
2
|
+
import { withBaseOutputDetails } from "app/utils/output";
|
|
2
3
|
|
|
3
4
|
export class PostOutput extends Output {
|
|
4
5
|
/**
|
|
5
6
|
* {@inheritdoc}
|
|
6
7
|
*/
|
|
7
|
-
protected output: FinalOutput = {
|
|
8
|
-
id: "int",
|
|
8
|
+
protected output: FinalOutput = withBaseOutputDetails({
|
|
9
9
|
title: "string",
|
|
10
10
|
content: "string",
|
|
11
|
-
|
|
12
|
-
};
|
|
11
|
+
});
|
|
13
12
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FilterByOptions, RepositoryOptions } from "@warlock.js/core";
|
|
2
|
+
import { RepositoryManager } from "@warlock.js/core";
|
|
3
|
+
|
|
4
|
+
import { Post } from "../models/post";
|
|
5
|
+
|
|
6
|
+
export class PostsRepository extends RepositoryManager<Post> {
|
|
7
|
+
/**
|
|
8
|
+
* {@inheritDoc}
|
|
9
|
+
*/
|
|
10
|
+
public model = Post;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* List default options
|
|
14
|
+
*/
|
|
15
|
+
protected defaultOptions: RepositoryOptions = this.withDefaultOptions({});
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Filter By options
|
|
19
|
+
*/
|
|
20
|
+
protected filterBy: FilterByOptions = this.withDefaultFilters({
|
|
21
|
+
title: "like",
|
|
22
|
+
author: ["int", "createdBy.id"],
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const postsRepository = new PostsRepository();
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { router } from "@warlock.js/core";
|
|
2
|
-
import { guarded } from "
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { guarded } from "app/utils/router";
|
|
3
|
+
import { createPostController } from "./controllers/create-post.controller";
|
|
4
|
+
import { getAllPostsController } from "./controllers/get-all-posts.controller";
|
|
5
|
+
import { getPostController } from "./controllers/get-post.controller";
|
|
6
6
|
|
|
7
|
-
router.get("/posts",
|
|
8
|
-
router.get("/posts/:id",
|
|
7
|
+
router.get("/posts", getAllPostsController);
|
|
8
|
+
router.get("/posts/:id", getPostController);
|
|
9
9
|
|
|
10
10
|
guarded(() => {
|
|
11
|
-
router.post("/posts",
|
|
11
|
+
router.post("/posts", createPostController);
|
|
12
12
|
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { PaginationListing } from "@warlock.js/cascade";
|
|
2
|
+
import type { Post } from "app/posts/models/post";
|
|
3
|
+
import { postsRepository } from "app/posts/repositories/posts.repository";
|
|
4
|
+
import type { CreatePostData } from "app/posts/validation/create-post.validation";
|
|
5
|
+
import type { PostsListsParams } from "../types";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create a new post
|
|
9
|
+
*/
|
|
10
|
+
export async function createPostService(data: CreatePostData): Promise<Post> {
|
|
11
|
+
return await postsRepository.create(data);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get all posts
|
|
16
|
+
*/
|
|
17
|
+
export async function getAllPostsService(
|
|
18
|
+
params: PostsListsParams = {},
|
|
19
|
+
): Promise<PaginationListing<Post>> {
|
|
20
|
+
return await postsRepository.list(params);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get a single post by ID
|
|
25
|
+
*/
|
|
26
|
+
export async function getPostByIdService(id: number): Promise<Post | null> {
|
|
27
|
+
return await postsRepository.findActiveCached(id);
|
|
28
|
+
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { Request, Response } from "@warlock.js/core";
|
|
2
2
|
import { usersRepository } from "app/users/repositories/users.repository";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { loginUserService } from "app/users/services/login.service";
|
|
4
|
+
import {
|
|
5
|
+
activateAccountSchema,
|
|
6
|
+
type ActivateAccountData,
|
|
7
|
+
} from "app/users/validation/activate-account.validation";
|
|
8
|
+
|
|
9
|
+
export default async function activateAccountController(
|
|
10
|
+
request: Request<ActivateAccountData>,
|
|
6
11
|
response: Response,
|
|
7
12
|
) {
|
|
8
13
|
const currentUser = request.user;
|
|
@@ -14,25 +19,15 @@ export default async function activateAccount(
|
|
|
14
19
|
activatedAt: new Date(),
|
|
15
20
|
});
|
|
16
21
|
|
|
17
|
-
const
|
|
22
|
+
const loginData = await loginUserService(currentUser);
|
|
18
23
|
|
|
19
|
-
return response.success(
|
|
20
|
-
user: {
|
|
21
|
-
...(await currentUser.toJSON()),
|
|
22
|
-
accessToken: accessToken,
|
|
23
|
-
userType: currentUser.userType,
|
|
24
|
-
},
|
|
25
|
-
});
|
|
24
|
+
return response.success(loginData);
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
code: ["required"],
|
|
31
|
-
email: ["required", "email"],
|
|
32
|
-
},
|
|
27
|
+
activateAccountController.validation = {
|
|
28
|
+
schema: activateAccountSchema,
|
|
33
29
|
validate: async (request: Request, response: Response) => {
|
|
34
30
|
const user = await usersRepository.first({
|
|
35
|
-
isActive: false,
|
|
36
31
|
email: request.input("email"),
|
|
37
32
|
activationCode: request.int("code"),
|
|
38
33
|
});
|
|
@@ -43,6 +38,12 @@ activateAccount.validation = {
|
|
|
43
38
|
});
|
|
44
39
|
}
|
|
45
40
|
|
|
41
|
+
if (user.isActive) {
|
|
42
|
+
return response.badRequest({
|
|
43
|
+
error: "User already activated",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
46
47
|
request.user = user;
|
|
47
48
|
},
|
|
48
49
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Request, Response } from "@warlock.js/core";
|
|
2
|
+
import { User } from "app/users/models/user";
|
|
3
|
+
import { loginUserService } from "app/users/services/login.service";
|
|
4
|
+
import {
|
|
5
|
+
adminLoginSchema,
|
|
6
|
+
type AdminLoginData,
|
|
7
|
+
} from "app/users/validation/admin-login.validation";
|
|
8
|
+
|
|
9
|
+
export default async function adminLoginController(
|
|
10
|
+
request: Request<AdminLoginData>,
|
|
11
|
+
response: Response,
|
|
12
|
+
) {
|
|
13
|
+
const user = request.user;
|
|
14
|
+
|
|
15
|
+
const loginData = await loginUserService(user);
|
|
16
|
+
|
|
17
|
+
return response.success(loginData);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
adminLoginController.validation = {
|
|
21
|
+
schema: adminLoginSchema,
|
|
22
|
+
validate: async (request: Request, response: Response) => {
|
|
23
|
+
const user = await User.attempt(request.validated());
|
|
24
|
+
|
|
25
|
+
if (!user) {
|
|
26
|
+
return response.badRequest({
|
|
27
|
+
error: "Invalid credentials",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!user.get("isActive")) {
|
|
32
|
+
return response.badRequest({
|
|
33
|
+
error: "Your account is suspended!",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
request.user = user;
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Request, RequestHandler, Response } from "@warlock.js/core";
|
|
2
|
+
import type { User } from "app/users/models/user";
|
|
3
|
+
import { createAccountService } from "app/users/services/create-account.service";
|
|
4
|
+
import { loginUserService } from "app/users/services/login.service";
|
|
5
|
+
import { createAccountSchema } from "app/users/validation/create-account.validation";
|
|
6
|
+
|
|
7
|
+
export const createAccountController: RequestHandler = async (
|
|
8
|
+
request: Request<User>,
|
|
9
|
+
response: Response,
|
|
10
|
+
) => {
|
|
11
|
+
const user = await createAccountService(request.validated()); //
|
|
12
|
+
|
|
13
|
+
request.guest = request.user;
|
|
14
|
+
request.user = user;
|
|
15
|
+
|
|
16
|
+
return response.success({
|
|
17
|
+
user: await loginUserService(user),
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
createAccountController.validation = {
|
|
22
|
+
schema: createAccountSchema,
|
|
23
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Request,
|
|
3
|
+
type RequestHandler,
|
|
4
|
+
type Response,
|
|
5
|
+
} from "@warlock.js/core";
|
|
6
|
+
import { User } from "app/users/models/user";
|
|
7
|
+
import { loginUserService } from "app/users/services/login.service";
|
|
8
|
+
import {
|
|
9
|
+
loginSchema,
|
|
10
|
+
type LoginData,
|
|
11
|
+
} from "app/users/validation/login.validation";
|
|
12
|
+
|
|
13
|
+
export const loginController: RequestHandler = async (
|
|
14
|
+
request: Request<LoginData>,
|
|
15
|
+
response: Response,
|
|
16
|
+
) => {
|
|
17
|
+
const user = await User.attempt(request.validated());
|
|
18
|
+
|
|
19
|
+
if (!user) {
|
|
20
|
+
return response.badRequest({
|
|
21
|
+
error: "Invalid credentials",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const loginData = await loginUserService(user);
|
|
26
|
+
|
|
27
|
+
return response.success(loginData);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
loginController.validation = {
|
|
31
|
+
schema: loginSchema,
|
|
32
|
+
};
|
|
@@ -17,7 +17,7 @@ export default async function changePassword(
|
|
|
17
17
|
changePassword.validation = {
|
|
18
18
|
schema: v.object({
|
|
19
19
|
password: v.string().minLength(8).required(),
|
|
20
|
-
confirmPassword: v.string().required().
|
|
20
|
+
confirmPassword: v.string().required().sameAs("password").omit(),
|
|
21
21
|
}),
|
|
22
22
|
validate: (request: Request<User>, response: Response) => {
|
|
23
23
|
const user = request.user;
|
|
@@ -5,23 +5,23 @@ import {
|
|
|
5
5
|
guardedGuest,
|
|
6
6
|
guardedGuestAdmin,
|
|
7
7
|
} from "app/utils/router";
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import resendActivationCode from "./controllers/auth/resend-activation-code";
|
|
15
|
-
import resetPassword from "./controllers/auth/reset-password";
|
|
16
|
-
import verifyForgetPasswordCode from "./controllers/auth/verify-forget-password-code";
|
|
17
|
-
import changePassword from "./controllers/profile/change-password";
|
|
18
|
-
import myProfile from "./controllers/profile/my-profile";
|
|
19
|
-
import updateProfile from "./controllers/profile/update-profile";
|
|
20
|
-
import { restfulUsers } from "./controllers/restful
|
|
8
|
+
import activateAccountController from "./controllers/auth/activate-account.controller";
|
|
9
|
+
import adminLoginController from "./controllers/auth/admin-login.controller";
|
|
10
|
+
import { createAccountController } from "./controllers/auth/create-account.controller";
|
|
11
|
+
import forgetPassword from "./controllers/auth/forget-password.controller";
|
|
12
|
+
import { loginController } from "./controllers/auth/login.controller";
|
|
13
|
+
import logout from "./controllers/auth/logout.controller";
|
|
14
|
+
import resendActivationCode from "./controllers/auth/resend-activation-code.controller";
|
|
15
|
+
import resetPassword from "./controllers/auth/reset-password.controller";
|
|
16
|
+
import verifyForgetPasswordCode from "./controllers/auth/verify-forget-password-code.controller";
|
|
17
|
+
import changePassword from "./controllers/profile/change-password.controller";
|
|
18
|
+
import myProfile from "./controllers/profile/my-profile.controller";
|
|
19
|
+
import updateProfile from "./controllers/profile/update-profile.controller";
|
|
20
|
+
import { restfulUsers } from "./controllers/users.restful";
|
|
21
21
|
|
|
22
22
|
// admin auth
|
|
23
23
|
guardedGuestAdmin(() => {
|
|
24
|
-
router.post("/login",
|
|
24
|
+
router.post("/login", adminLoginController);
|
|
25
25
|
router.post("/forget-password", forgetPassword);
|
|
26
26
|
router.post("/reset-password", resetPassword);
|
|
27
27
|
});
|
|
@@ -33,9 +33,9 @@ guardedAdmin(() => {
|
|
|
33
33
|
|
|
34
34
|
// user auth
|
|
35
35
|
guardedGuest(() => {
|
|
36
|
-
router.post("/login",
|
|
37
|
-
router.post("/register",
|
|
38
|
-
router.post("/register/verify",
|
|
36
|
+
router.post("/login", loginController);
|
|
37
|
+
router.post("/register", createAccountController);
|
|
38
|
+
router.post("/register/verify", activateAccountController);
|
|
39
39
|
router.post("/resend-activation-code", resendActivationCode);
|
|
40
40
|
router.post("/forget-password", forgetPassword);
|
|
41
41
|
router.post("/forget-password/verify-code", verifyForgetPasswordCode);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Random } from "@mongez/reinforcements";
|
|
2
|
+
import type { User } from "app/users/models/user";
|
|
3
|
+
import { usersRepository } from "app/users/repositories/users.repository";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Create a new user account
|
|
7
|
+
*/
|
|
8
|
+
export async function createAccountService(
|
|
9
|
+
profileData: Record<string, any>,
|
|
10
|
+
): Promise<User> {
|
|
11
|
+
return await usersRepository.create({
|
|
12
|
+
isCustomer: true,
|
|
13
|
+
...profileData,
|
|
14
|
+
activationCode: Random.int(100000, 999999),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { User } from "app/users/models/user";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Login user and return user data with access token
|
|
5
|
+
*/
|
|
6
|
+
export async function loginUserService(user: User): Promise<{
|
|
7
|
+
user: any;
|
|
8
|
+
accessToken: string;
|
|
9
|
+
}> {
|
|
10
|
+
const accessToken = await user.generateAccessToken();
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
user: await user.toJSON(),
|
|
14
|
+
accessToken: accessToken,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { v, type Infer } from "@warlock.js/core";
|
|
2
|
+
|
|
3
|
+
export const createAccountSchema = v.object({
|
|
4
|
+
name: v.string().minLength(2).required(),
|
|
5
|
+
email: v.string().email().required(),
|
|
6
|
+
password: v.string().minLength(8).required().strongPassword(),
|
|
7
|
+
confirmPassword: v.string().minLength(8).required().sameAs("password").omit(),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export type CreateAccountData = Infer<typeof createAccountSchema>;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
v,
|
|
3
|
-
type Request,
|
|
4
|
-
type RequestHandler,
|
|
5
|
-
type Response,
|
|
6
|
-
} from "@warlock.js/core";
|
|
7
|
-
import { Post } from "../models";
|
|
8
|
-
|
|
9
|
-
export const createNewPostRequest: RequestHandler = async (
|
|
10
|
-
request: Request,
|
|
11
|
-
response: Response,
|
|
12
|
-
) => {
|
|
13
|
-
const post = await Post.create(request.validated());
|
|
14
|
-
|
|
15
|
-
return response.success({
|
|
16
|
-
message: "Post created successfully",
|
|
17
|
-
post,
|
|
18
|
-
});
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
createNewPostRequest.validation = {
|
|
22
|
-
schema: v.object({
|
|
23
|
-
title: v.string().required().minLength(4),
|
|
24
|
-
content: v.string().required(),
|
|
25
|
-
}),
|
|
26
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type Request,
|
|
3
|
-
type RequestHandler,
|
|
4
|
-
type Response,
|
|
5
|
-
} from "@warlock.js/core";
|
|
6
|
-
import { Post } from "../models";
|
|
7
|
-
|
|
8
|
-
export const getAllPostsRequest: RequestHandler = async (
|
|
9
|
-
request: Request,
|
|
10
|
-
response: Response,
|
|
11
|
-
) => {
|
|
12
|
-
const posts = await Post.list();
|
|
13
|
-
|
|
14
|
-
return response.success({
|
|
15
|
-
posts,
|
|
16
|
-
});
|
|
17
|
-
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
v,
|
|
3
|
-
type Request,
|
|
4
|
-
type RequestHandler,
|
|
5
|
-
type Response,
|
|
6
|
-
} from "@warlock.js/core";
|
|
7
|
-
import { Post } from "../models";
|
|
8
|
-
|
|
9
|
-
export const getPostRequest: RequestHandler = async (
|
|
10
|
-
request: Request,
|
|
11
|
-
response: Response,
|
|
12
|
-
) => {
|
|
13
|
-
// 👇 look at the custom validation function
|
|
14
|
-
const post: Post = request.post;
|
|
15
|
-
|
|
16
|
-
return response.success({
|
|
17
|
-
post,
|
|
18
|
-
});
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
getPostRequest.validation = {
|
|
22
|
-
schema: v.object({
|
|
23
|
-
id: v.int().required(),
|
|
24
|
-
}),
|
|
25
|
-
validate: async (request: Request, response: Response) => {
|
|
26
|
-
const post = await Post.find(request.int("id"));
|
|
27
|
-
|
|
28
|
-
if (!post) {
|
|
29
|
-
return response.notFound({
|
|
30
|
-
error: "Post Not found",
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// inject the post into the request object
|
|
35
|
-
request.post = post;
|
|
36
|
-
},
|
|
37
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
deleteFile,
|
|
3
|
-
getUploadedFile,
|
|
4
|
-
getUploadedFileUsingHash,
|
|
5
|
-
router,
|
|
6
|
-
uploadChunkedFiles,
|
|
7
|
-
uploadFiles,
|
|
8
|
-
} from "@warlock.js/core";
|
|
9
|
-
import { adminPath, guarded } from "app/utils/router";
|
|
10
|
-
|
|
11
|
-
guarded(() => {
|
|
12
|
-
// Upload files
|
|
13
|
-
router.post(["/uploads", adminPath("/uploads")], uploadFiles);
|
|
14
|
-
// Upload chunked files
|
|
15
|
-
router.post(
|
|
16
|
-
["/uploads/chunks", adminPath("/uploads/chunks")],
|
|
17
|
-
uploadChunkedFiles,
|
|
18
|
-
);
|
|
19
|
-
// Delete file by hash from the database
|
|
20
|
-
router.delete(["/uploads/:hash", adminPath("/uploads/:hash")], deleteFile);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
// Please note that the uploaded files should not be grouped in protected middleware i.e guarded with JWT
|
|
24
|
-
// Get uploaded file using the file path directly
|
|
25
|
-
router.get("/uploads/*", getUploadedFile);
|
|
26
|
-
// Get uploaded file using hash
|
|
27
|
-
router.get("/uploads/:hash", getUploadedFileUsingHash);
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { Request, Response } from "@warlock.js/core";
|
|
2
|
-
import { User } from "app/users/models/user";
|
|
3
|
-
|
|
4
|
-
export default async function adminLogin(request: Request, response: Response) {
|
|
5
|
-
const user = request.user;
|
|
6
|
-
|
|
7
|
-
const auth = await user.generateAccessToken();
|
|
8
|
-
|
|
9
|
-
return response.success({
|
|
10
|
-
user: {
|
|
11
|
-
...(await user.toJSON()),
|
|
12
|
-
accessToken: auth,
|
|
13
|
-
userType: user.userType,
|
|
14
|
-
},
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
adminLogin.validation = {
|
|
19
|
-
rules: {
|
|
20
|
-
password: ["required"],
|
|
21
|
-
email: ["required", "email"],
|
|
22
|
-
},
|
|
23
|
-
validate: async (request: Request, response: Response) => {
|
|
24
|
-
const user = await User.attempt(request.only(["email", "password"]));
|
|
25
|
-
|
|
26
|
-
if (!user) {
|
|
27
|
-
return response.badRequest({
|
|
28
|
-
error: "Invalid credentials",
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (!user.get("isActive")) {
|
|
33
|
-
return response.badRequest({
|
|
34
|
-
error: "Your account is suspended!",
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
request.user = user;
|
|
39
|
-
},
|
|
40
|
-
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type Request,
|
|
3
|
-
type RequestHandler,
|
|
4
|
-
type Response,
|
|
5
|
-
} from "@warlock.js/core";
|
|
6
|
-
import { User } from "app/users/models/user";
|
|
7
|
-
|
|
8
|
-
export const loginRequest: RequestHandler = async (
|
|
9
|
-
request: Request,
|
|
10
|
-
response: Response,
|
|
11
|
-
) => {
|
|
12
|
-
const user = await User.attempt(request.only(["email", "password"]));
|
|
13
|
-
|
|
14
|
-
if (!user) {
|
|
15
|
-
return response.badRequest({
|
|
16
|
-
message: "Invalid credentials",
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// generate a JWT token for the logged-in user
|
|
21
|
-
const token = await user.generateAccessToken();
|
|
22
|
-
|
|
23
|
-
return response.success({
|
|
24
|
-
message: "User logged in successfully",
|
|
25
|
-
user,
|
|
26
|
-
accessToken: token,
|
|
27
|
-
});
|
|
28
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Random } from "@mongez/reinforcements";
|
|
2
|
-
import type { RequestHandler } from "@warlock.js/core";
|
|
3
|
-
import { v, type Request, type Response } from "@warlock.js/core";
|
|
4
|
-
import { User } from "app/users/models/user";
|
|
5
|
-
|
|
6
|
-
export const registerRequest: RequestHandler = async (
|
|
7
|
-
request: Request,
|
|
8
|
-
response: Response,
|
|
9
|
-
) => {
|
|
10
|
-
User.create({
|
|
11
|
-
...request.validated(),
|
|
12
|
-
activationCode: Random.int(100000, 999999),
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
return response.success();
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
registerRequest.validation = {
|
|
19
|
-
schema: v.object({
|
|
20
|
-
name: v.string().minLength(2).required(),
|
|
21
|
-
email: v.string().email().required().unique(User),
|
|
22
|
-
password: v.string().minLength(8).required(),
|
|
23
|
-
}),
|
|
24
|
-
};
|
|
File without changes
|
|
File without changes
|
/package/templates/warlock/src/app/users/controllers/auth/{logout.ts → logout.controller.ts}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|