create-warlock 3.0.17 → 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.
Files changed (28) hide show
  1. package/package.json +1 -1
  2. package/templates/warlock/AGENTS.md +11 -0
  3. package/templates/warlock/docs/new-module.md +447 -0
  4. package/templates/warlock/package.json +7 -7
  5. package/templates/warlock/src/app/posts/controllers/create-post.controller.ts +22 -0
  6. package/templates/warlock/src/app/posts/controllers/get-all-posts.controller.ts +13 -0
  7. package/templates/warlock/src/app/posts/controllers/get-post.controller.ts +28 -0
  8. package/templates/warlock/src/app/posts/models/index.ts +1 -1
  9. package/templates/warlock/src/app/posts/models/post/index.ts +1 -0
  10. package/templates/warlock/src/app/posts/models/{post.model.ts → post/post.model.ts} +11 -1
  11. package/templates/warlock/src/app/posts/output/post.output.ts +4 -5
  12. package/templates/warlock/src/app/posts/repositories/posts.repository.ts +26 -0
  13. package/templates/warlock/src/app/posts/routes.ts +7 -7
  14. package/templates/warlock/src/app/posts/services/posts.service.ts +28 -0
  15. package/templates/warlock/src/app/posts/types/index.ts +6 -0
  16. package/templates/warlock/src/app/posts/utils/locales.ts +8 -0
  17. package/templates/warlock/src/app/posts/validation/create-post.validation.ts +8 -0
  18. package/templates/warlock/src/app/posts/validation/get-post.validation.ts +7 -0
  19. package/templates/warlock/src/app/users/controllers/auth/create-account.controller.ts +5 -7
  20. package/templates/warlock/src/app/users/controllers/profile/change-password.controller.ts +1 -1
  21. package/templates/warlock/src/app/users/services/{account.service.ts → create-account.service.ts} +1 -1
  22. package/templates/warlock/src/app/users/services/login.service.ts +1 -3
  23. package/templates/warlock/src/app/users/validation/create-account.validation.ts +1 -1
  24. package/templates/warlock/src/config/validation.ts +1 -1
  25. package/templates/warlock/src/app/posts/controllers/create-new-post.request.ts +0 -26
  26. package/templates/warlock/src/app/posts/controllers/get-all-posts.request.ts +0 -17
  27. package/templates/warlock/src/app/posts/controllers/get-post.request.ts +0 -37
  28. package/templates/warlock/src/app/uploads/routes.ts +0 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-warlock",
3
- "version": "3.0.17",
3
+ "version": "3.0.18",
4
4
  "main": "./esm/index.js",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -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,17 +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.10",
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.17",
34
- "@warlock.js/cache": "3.0.17",
35
- "@warlock.js/cascade": "3.0.17",
36
- "@warlock.js/core": "3.0.17",
37
- "@warlock.js/logger": "3.0.17",
38
- "@warlock.js/seal": "3.0.17",
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",
39
39
  "@faker-js/faker": "^9.2.0",
40
40
  "dayjs": "^1.11.13"
41
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.model";
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 "../output/post.output";
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, Output } from "@warlock.js/core";
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
- createdAt: "date",
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 "../utils/router";
3
- import { createNewPostRequest } from "./controllers/create-new-post.request";
4
- import { getAllPostsRequest } from "./controllers/get-all-posts.request";
5
- import { getPostRequest } from "./controllers/get-post.request";
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", getAllPostsRequest);
8
- router.get("/posts/:id", getPostRequest);
7
+ router.get("/posts", getAllPostsController);
8
+ router.get("/posts/:id", getPostController);
9
9
 
10
10
  guarded(() => {
11
- router.post("/posts", createNewPostRequest);
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
+ }
@@ -0,0 +1,6 @@
1
+ export type PostsListsParams = {
2
+ page?: number;
3
+ limit?: number;
4
+ author?: number; // author id
5
+ title?: string;
6
+ };
@@ -0,0 +1,8 @@
1
+ import { groupedTranslations } from "@mongez/localization";
2
+
3
+ groupedTranslations("posts", {
4
+ postNotFound: {
5
+ en: "Post Not found",
6
+ ar: "المنشور غير موجود",
7
+ },
8
+ });
@@ -0,0 +1,8 @@
1
+ import { v, type Infer } from "@warlock.js/core";
2
+
3
+ export const createPostSchema = v.object({
4
+ title: v.string().required().minLength(4),
5
+ content: v.string().required(),
6
+ });
7
+
8
+ export type CreatePostData = Infer<typeof createPostSchema>;
@@ -0,0 +1,7 @@
1
+ import { v, type Infer } from "@warlock.js/core";
2
+
3
+ export const getPostSchema = v.object({
4
+ id: v.int().required(),
5
+ });
6
+
7
+ export type GetPostData = Infer<typeof getPostSchema>;
@@ -1,16 +1,14 @@
1
1
  import type { Request, RequestHandler, Response } from "@warlock.js/core";
2
- import { createAccountService } from "app/users/services/account.service";
2
+ import type { User } from "app/users/models/user";
3
+ import { createAccountService } from "app/users/services/create-account.service";
3
4
  import { loginUserService } from "app/users/services/login.service";
4
- import {
5
- createAccountSchema,
6
- type CreateAccountData,
7
- } from "app/users/validation/create-account.validation";
5
+ import { createAccountSchema } from "app/users/validation/create-account.validation";
8
6
 
9
7
  export const createAccountController: RequestHandler = async (
10
- request: Request<CreateAccountData>,
8
+ request: Request<User>,
11
9
  response: Response,
12
10
  ) => {
13
- const user = await createAccountService(request.validated());
11
+ const user = await createAccountService(request.validated()); //
14
12
 
15
13
  request.guest = request.user;
16
14
  request.user = user;
@@ -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().matches("password"),
20
+ confirmPassword: v.string().required().sameAs("password").omit(),
21
21
  }),
22
22
  validate: (request: Request<User>, response: Response) => {
23
23
  const user = request.user;
@@ -1,6 +1,6 @@
1
1
  import { Random } from "@mongez/reinforcements";
2
2
  import type { User } from "app/users/models/user";
3
- import usersRepository from "app/users/repositories/users-repository";
3
+ import { usersRepository } from "app/users/repositories/users.repository";
4
4
 
5
5
  /**
6
6
  * Create a new user account
@@ -6,13 +6,11 @@ import type { User } from "app/users/models/user";
6
6
  export async function loginUserService(user: User): Promise<{
7
7
  user: any;
8
8
  accessToken: string;
9
- userType: string;
10
9
  }> {
11
10
  const accessToken = await user.generateAccessToken();
12
-
11
+
13
12
  return {
14
13
  user: await user.toJSON(),
15
14
  accessToken: accessToken,
16
- userType: user.userType,
17
15
  };
18
16
  }
@@ -4,7 +4,7 @@ export const createAccountSchema = v.object({
4
4
  name: v.string().minLength(2).required(),
5
5
  email: v.string().email().required(),
6
6
  password: v.string().minLength(8).required().strongPassword(),
7
- confirmPassword: v.string().minLength(8).required().saveAs("password"),
7
+ confirmPassword: v.string().minLength(8).required().sameAs("password").omit(),
8
8
  });
9
9
 
10
10
  export type CreateAccountData = Infer<typeof createAccountSchema>;
@@ -68,7 +68,7 @@ const validationConfigurations: ValidationConfigurations = {
68
68
  image: ImageRule,
69
69
  },
70
70
  keys: {
71
- response: "messages",
71
+ response: "errors",
72
72
  inputKey: "key",
73
73
  inputError: "error",
74
74
  inputErrors: "errors",
@@ -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);