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.
Files changed (121) hide show
  1. package/cjs/index.js +10 -5
  2. package/cjs/index.js.map +1 -1
  3. package/create-app.js +0 -0
  4. package/esm/index.js +10 -5
  5. package/esm/index.js.map +1 -1
  6. package/package.json +32 -38
  7. package/templates/warlock/docs/new-module.md +123 -61
  8. package/templates/warlock/package.json +60 -67
  9. package/templates/warlock/src/app/auth/controllers/forgot-password.controller.ts +3 -2
  10. package/templates/warlock/src/app/auth/controllers/login.controller.ts +1 -4
  11. package/templates/warlock/src/app/auth/main.ts +6 -3
  12. package/templates/warlock/src/app/auth/models/otp/migrations/22-12-2025_10-30-20.otp-migration.ts +13 -16
  13. package/templates/warlock/src/app/auth/models/otp/otp.model.ts +26 -31
  14. package/templates/warlock/src/app/auth/requests/login.request.ts +1 -1
  15. package/templates/warlock/src/app/auth/services/auth.service.ts +10 -5
  16. package/templates/warlock/src/app/auth/services/otp.service.ts +2 -12
  17. package/templates/warlock/src/app/auth/services/reset-password.service.ts +1 -1
  18. package/templates/warlock/src/app/posts/controllers/create-new-post.controller.ts +20 -0
  19. package/templates/warlock/src/app/posts/controllers/update-post.controller.ts +24 -0
  20. package/templates/warlock/src/app/posts/models/post/psot.model.ts +33 -0
  21. package/templates/warlock/src/app/posts/routes.ts +11 -0
  22. package/templates/warlock/src/app/shared/utils/global-columns-schema.ts +8 -0
  23. package/templates/warlock/src/app/shared/utils/locales.ts +4 -0
  24. package/templates/warlock/src/app/uploads/controllers/fetch-uploaded-file.controller.ts +32 -0
  25. package/templates/warlock/src/app/uploads/routes.ts +4 -0
  26. package/templates/warlock/src/app/users/controllers/create-new-user.controller.ts +28 -0
  27. package/templates/warlock/src/app/users/controllers/get-users.controller.ts +7 -2
  28. package/templates/warlock/src/app/users/events/inject-created-by-user.into-model.event.ts +32 -0
  29. package/templates/warlock/src/app/users/events/sync.ts +5 -0
  30. package/templates/warlock/src/app/users/main.ts +5 -0
  31. package/templates/warlock/src/app/users/models/user/migrations/11-12-2025_23-58-03-user.migration.ts +12 -12
  32. package/templates/warlock/src/app/users/models/user/user.model.ts +54 -27
  33. package/templates/warlock/src/app/users/repositories/users.repository.ts +16 -20
  34. package/templates/warlock/src/app/users/routes.ts +7 -1
  35. package/templates/warlock/src/app/users/seeds/users.seed.ts +21 -0
  36. package/templates/warlock/src/app/utils/cloud-upload.middleware.ts +14 -0
  37. package/templates/warlock/src/app/utils/router.ts +11 -1
  38. package/templates/warlock/src/config/auth.ts +2 -0
  39. package/templates/warlock/src/config/database.ts +24 -7
  40. package/templates/warlock/src/config/storage.ts +17 -8
  41. package/templates/warlock/tsconfig.json +6 -5
  42. package/templates/warlock/yarn.lock +4831 -0
  43. package/cjs/commands/create-new-app/get-app-path.d.ts +0 -2
  44. package/cjs/commands/create-new-app/get-app-path.d.ts.map +0 -1
  45. package/cjs/commands/create-new-app/get-app-path.js +0 -8
  46. package/cjs/commands/create-new-app/get-app-path.js.map +0 -1
  47. package/cjs/commands/create-new-app/index.d.ts +0 -2
  48. package/cjs/commands/create-new-app/index.d.ts.map +0 -1
  49. package/cjs/commands/create-new-app/index.js +0 -50
  50. package/cjs/commands/create-new-app/index.js.map +0 -1
  51. package/cjs/commands/create-new-app/select-app-type.d.ts +0 -2
  52. package/cjs/commands/create-new-app/select-app-type.d.ts.map +0 -1
  53. package/cjs/commands/create-new-app/types.d.ts +0 -9
  54. package/cjs/commands/create-new-app/types.d.ts.map +0 -1
  55. package/cjs/commands/create-warlock-app/index.d.ts +0 -3
  56. package/cjs/commands/create-warlock-app/index.d.ts.map +0 -1
  57. package/cjs/commands/create-warlock-app/index.js +0 -51
  58. package/cjs/commands/create-warlock-app/index.js.map +0 -1
  59. package/cjs/helpers/app.d.ts +0 -54
  60. package/cjs/helpers/app.d.ts.map +0 -1
  61. package/cjs/helpers/app.js +0 -126
  62. package/cjs/helpers/app.js.map +0 -1
  63. package/cjs/helpers/exec.d.ts +0 -10
  64. package/cjs/helpers/exec.d.ts.map +0 -1
  65. package/cjs/helpers/exec.js +0 -69
  66. package/cjs/helpers/exec.js.map +0 -1
  67. package/cjs/helpers/package-manager.d.ts +0 -6
  68. package/cjs/helpers/package-manager.d.ts.map +0 -1
  69. package/cjs/helpers/package-manager.js +0 -22
  70. package/cjs/helpers/package-manager.js.map +0 -1
  71. package/cjs/helpers/paths.d.ts +0 -4
  72. package/cjs/helpers/paths.d.ts.map +0 -1
  73. package/cjs/helpers/paths.js +0 -8
  74. package/cjs/helpers/paths.js.map +0 -1
  75. package/cjs/helpers/project-builder-helpers.d.ts +0 -6
  76. package/cjs/helpers/project-builder-helpers.d.ts.map +0 -1
  77. package/cjs/helpers/project-builder-helpers.js +0 -18
  78. package/cjs/helpers/project-builder-helpers.js.map +0 -1
  79. package/cjs/index.d.ts +0 -2
  80. package/cjs/index.d.ts.map +0 -1
  81. package/esm/commands/create-new-app/get-app-path.d.ts +0 -2
  82. package/esm/commands/create-new-app/get-app-path.d.ts.map +0 -1
  83. package/esm/commands/create-new-app/get-app-path.js +0 -8
  84. package/esm/commands/create-new-app/get-app-path.js.map +0 -1
  85. package/esm/commands/create-new-app/index.d.ts +0 -2
  86. package/esm/commands/create-new-app/index.d.ts.map +0 -1
  87. package/esm/commands/create-new-app/index.js +0 -50
  88. package/esm/commands/create-new-app/index.js.map +0 -1
  89. package/esm/commands/create-new-app/select-app-type.d.ts +0 -2
  90. package/esm/commands/create-new-app/select-app-type.d.ts.map +0 -1
  91. package/esm/commands/create-new-app/types.d.ts +0 -9
  92. package/esm/commands/create-new-app/types.d.ts.map +0 -1
  93. package/esm/commands/create-warlock-app/index.d.ts +0 -3
  94. package/esm/commands/create-warlock-app/index.d.ts.map +0 -1
  95. package/esm/commands/create-warlock-app/index.js +0 -51
  96. package/esm/commands/create-warlock-app/index.js.map +0 -1
  97. package/esm/helpers/app.d.ts +0 -54
  98. package/esm/helpers/app.d.ts.map +0 -1
  99. package/esm/helpers/app.js +0 -126
  100. package/esm/helpers/app.js.map +0 -1
  101. package/esm/helpers/exec.d.ts +0 -10
  102. package/esm/helpers/exec.d.ts.map +0 -1
  103. package/esm/helpers/exec.js +0 -69
  104. package/esm/helpers/exec.js.map +0 -1
  105. package/esm/helpers/package-manager.d.ts +0 -6
  106. package/esm/helpers/package-manager.d.ts.map +0 -1
  107. package/esm/helpers/package-manager.js +0 -22
  108. package/esm/helpers/package-manager.js.map +0 -1
  109. package/esm/helpers/paths.d.ts +0 -4
  110. package/esm/helpers/paths.d.ts.map +0 -1
  111. package/esm/helpers/paths.js +0 -8
  112. package/esm/helpers/paths.js.map +0 -1
  113. package/esm/helpers/project-builder-helpers.d.ts +0 -6
  114. package/esm/helpers/project-builder-helpers.d.ts.map +0 -1
  115. package/esm/helpers/project-builder-helpers.js +0 -18
  116. package/esm/helpers/project-builder-helpers.js.map +0 -1
  117. package/esm/index.d.ts +0 -2
  118. package/esm/index.d.ts.map +0 -1
  119. package/templates/warlock/src/app/users/repositories/users-repository.ts +0 -66
  120. package/templates/warlock/src/app/users/services/get-new-customers.ts +0 -5
  121. package/templates/warlock/src/app/utils/output.ts +0 -5
@@ -1,35 +1,34 @@
1
- import { Model, type Casts, type Document } from "@warlock.js/cascade";
1
+ import { Model, RegisterModel } from "@warlock.js/cascade";
2
+ import { type Infer, v } from "@warlock.js/seal";
3
+ import { globalColumnsSchema } from "app/shared/utils/global-columns-schema";
2
4
 
3
- export class OTP extends Model {
4
- /**
5
- * Collection name
6
- */
7
- public static collection = "otps";
5
+ const otpSchema = globalColumnsSchema.extend({
6
+ code: v.string().required(),
7
+ type: v.string().required(),
8
+ target: v.string().required(),
9
+ channel: v.string().required(),
10
+ userId: v.number().required(),
11
+ userType: v.string().required(),
12
+ expiresAt: v.date().required(),
13
+ usedAt: v.date().optional(),
14
+ attempts: v.number().default(0),
15
+ maxAttempts: v.number().default(5),
16
+ metadata: v.record(v.any()).optional(),
17
+ });
18
+
19
+ type OTPSchema = Infer<typeof otpSchema>;
8
20
 
21
+ @RegisterModel()
22
+ export class OTP extends Model<OTPSchema> {
9
23
  /**
10
- * Default value for model data
24
+ * Table name
11
25
  */
12
- public defaultValue: Document = {
13
- attempts: 0,
14
- maxAttempts: 5,
15
- };
26
+ public static table = "otps";
16
27
 
17
28
  /**
18
- * Cast data types before saving
29
+ * Model schema
19
30
  */
20
- protected casts: Casts = {
21
- code: "string",
22
- type: "string",
23
- target: "string",
24
- channel: "string",
25
- userId: "number",
26
- userType: "string",
27
- expiresAt: "date",
28
- usedAt: "date",
29
- attempts: "number",
30
- maxAttempts: "number",
31
- metadata: "object",
32
- };
31
+ public static schema = otpSchema;
33
32
 
34
33
  /**
35
34
  * Check if OTP is valid (not expired, not used, not max attempts)
@@ -59,17 +58,13 @@ export class OTP extends Model {
59
58
  * Mark OTP as used
60
59
  */
61
60
  public async markAsUsed(): Promise<this> {
62
- return this.save({
63
- usedAt: new Date(),
64
- });
61
+ return this.set("usedAt", new Date()).save();
65
62
  }
66
63
 
67
64
  /**
68
65
  * Increment failed attempt
69
66
  */
70
67
  public async incrementAttempt(): Promise<this> {
71
- return this.save({
72
- attempts: this.get("attempts") + 1,
73
- });
68
+ return this.set("attempts", this.get("attempts") + 1).save();
74
69
  }
75
70
  }
@@ -1,7 +1,7 @@
1
1
  import { v, type Infer, type Request } from "@warlock.js/core";
2
2
 
3
3
  export const loginSchema = v.object({
4
- email: v.string().email().required(),
4
+ email: v.email().required(),
5
5
  password: v.string().required(),
6
6
  });
7
7
 
@@ -1,16 +1,21 @@
1
1
  import type { Auth } from "@warlock.js/auth";
2
2
  import { authService, type DeviceInfo, type TokenPair } from "@warlock.js/auth";
3
- import { User } from "app/users/models/user";
3
+ import { User } from "app/users/models/user/user.model";
4
4
 
5
5
  export type LoginCredentials = {
6
6
  email: string;
7
7
  password: string;
8
8
  };
9
9
 
10
- export type LoginResult = {
11
- user: Auth;
12
- tokens: TokenPair;
13
- };
10
+ export type LoginResult =
11
+ | {
12
+ user: Auth;
13
+ tokens: TokenPair;
14
+ }
15
+ | {
16
+ user: Auth;
17
+ accessToken: string;
18
+ };
14
19
 
15
20
  /**
16
21
  * Login with email and password
@@ -120,14 +120,6 @@ export async function verifyOtpService(
120
120
  });
121
121
  }
122
122
 
123
- // Verify code matches
124
- if (otp.get("code") !== code) {
125
- await otp.incrementAttempt();
126
- throw new ForbiddenError(t("auth.otpInvalid"), {
127
- errorCode: AuthErrorCode.OTP_INVALID,
128
- });
129
- }
130
-
131
123
  // Mark as used
132
124
  await otp.markAsUsed();
133
125
 
@@ -164,11 +156,9 @@ export async function resendOtpService(
164
156
  * Cleanup expired OTPs
165
157
  */
166
158
  export async function cleanupExpiredOtpsService(): Promise<number> {
167
- const expiredOtps = await OTP.aggregate().where("expiresAt", "<", new Date()).get();
159
+ const expiredOtps = await OTP.query().where("expiresAt", "<", new Date()).get();
168
160
 
169
- for (const otp of expiredOtps) {
170
- await otp.destroy();
171
- }
161
+ await Promise.all(expiredOtps.map((otp) => otp.destroy()));
172
162
 
173
163
  return expiredOtps.length;
174
164
  }
@@ -25,7 +25,7 @@ export async function resetPasswordService(
25
25
  }
26
26
 
27
27
  // Update password
28
- await user.save({ password: newPassword });
28
+ await user.set("password", newPassword).save();
29
29
 
30
30
  // Revoke all tokens (force re-login)
31
31
  // Or make it an option through user decision.
@@ -0,0 +1,20 @@
1
+ import { type RequestHandler, v } from "@warlock.js/core";
2
+ import { Post } from "../models/post/psot.model";
3
+
4
+ export const createNewPostController: RequestHandler = async (request, response) => {
5
+ const post = await Post.create({
6
+ ...request.validated(),
7
+ });
8
+
9
+ return response.success({
10
+ post,
11
+ });
12
+ };
13
+
14
+ createNewPostController.validation = {
15
+ schema: v.object({
16
+ title: v.string().required(),
17
+ description: v.string(),
18
+ image: v.file().image().maxSize({ size: 2, unit: "MB" }).saveTo("posts"),
19
+ }),
20
+ };
@@ -0,0 +1,24 @@
1
+ import { type RequestHandler, v } from "@warlock.js/core";
2
+ import { Post } from "../models/post/psot.model";
3
+
4
+ export const updatePostController: RequestHandler = async (request, response) => {
5
+ const post = await Post.find(request.int("id"));
6
+
7
+ if (!post) {
8
+ return response.notFound();
9
+ }
10
+
11
+ await post.save({ merge: request.validated() });
12
+
13
+ return response.success({
14
+ post,
15
+ });
16
+ };
17
+
18
+ updatePostController.validation = {
19
+ schema: v.object({
20
+ title: v.string().required(),
21
+ description: v.string(),
22
+ image: v.file().image().maxSize({ size: 2, unit: "MB" }).saveTo("posts"),
23
+ }),
24
+ };
@@ -0,0 +1,33 @@
1
+ import { Model, RegisterModel } from "@warlock.js/cascade";
2
+ import { defineResource, useComputedSlug } from "@warlock.js/core";
3
+ import { type Infer, v } from "@warlock.js/seal";
4
+ import { globalColumnsSchema } from "app/shared/utils/global-columns-schema";
5
+
6
+ export const postSchema = globalColumnsSchema.extend({
7
+ title: v.string().required(),
8
+ description: v.string().required(),
9
+ slug: v.computed(useComputedSlug()),
10
+ image: v.string().required(),
11
+ });
12
+
13
+ export type PostSchema = Infer<typeof postSchema>;
14
+
15
+ @RegisterModel()
16
+ export class Post extends Model<PostSchema> {
17
+ public static table = "posts";
18
+
19
+ public static schema = postSchema;
20
+
21
+ public static resource = defineResource({
22
+ schema: {
23
+ id: "number",
24
+ slug: "string",
25
+ title: "string",
26
+ description: "string",
27
+ image: "storageUrl",
28
+ createdBy: "object",
29
+ updatedBy: "object",
30
+ isActive: "boolean",
31
+ },
32
+ });
33
+ }
@@ -0,0 +1,11 @@
1
+ import { router } from "@warlock.js/core";
2
+ import { cloudUpload, guarded } from "app/utils/router";
3
+ import { createNewPostController } from "./controllers/create-new-post.controller";
4
+ import { updatePostController } from "./controllers/update-post.controller";
5
+
6
+ guarded(() => {
7
+ cloudUpload(() => {
8
+ router.post("/posts", createNewPostController);
9
+ });
10
+ router.put("/posts/:id", updatePostController);
11
+ });
@@ -0,0 +1,8 @@
1
+ import { v } from "@warlock.js/seal";
2
+
3
+ export const globalColumnsSchema = v.object({
4
+ createdBy: v.embed("User"),
5
+ updatedBy: v.embed("User"),
6
+ deletedBy: v.embed("User"),
7
+ isActive: v.boolean().default(true),
8
+ });
@@ -725,4 +725,8 @@ groupedTranslations("attributes", {
725
725
  en: "Name",
726
726
  ar: "الاسم",
727
727
  },
728
+ image: {
729
+ en: "Image",
730
+ ar: "الصورة",
731
+ },
728
732
  });
@@ -0,0 +1,32 @@
1
+ import { fileExistsAsync } from "@mongez/fs";
2
+ import { CACHE_FOR } from "@warlock.js/cache";
3
+ import { Image, type RequestHandler, storage, v } from "@warlock.js/core";
4
+
5
+ export const fetchUploadedFileController: RequestHandler = async (request, response) => {
6
+ const absolutePath = storage.root(request.input("*"));
7
+
8
+ const { w: width, h: height } = request.validated();
9
+
10
+ if (!(await fileExistsAsync(absolutePath))) {
11
+ return response.notFound();
12
+ }
13
+
14
+ if (width || height) {
15
+ const image = new Image(absolutePath);
16
+ image.resize({
17
+ width,
18
+ height,
19
+ });
20
+
21
+ return response.sendImage(image, CACHE_FOR.ONE_DAY);
22
+ }
23
+
24
+ return response.sendFile(absolutePath, CACHE_FOR.ONE_YEAR);
25
+ };
26
+
27
+ fetchUploadedFileController.validation = {
28
+ schema: v.object({
29
+ w: v.numeric().min(1),
30
+ h: v.numeric().min(1),
31
+ }),
32
+ };
@@ -0,0 +1,4 @@
1
+ import { router } from "@warlock.js/core";
2
+ import { fetchUploadedFileController } from "./controllers/fetch-uploaded-file.controller";
3
+
4
+ router.get("/uploads/*", fetchUploadedFileController);
@@ -0,0 +1,28 @@
1
+ import { v, type RequestHandler } from "@warlock.js/core";
2
+ import { User } from "../models/user";
3
+
4
+ export const createNewUserController: RequestHandler = async (request, response) => {
5
+ const file = request.file("image")!;
6
+
7
+ const output = await file.save("images");
8
+
9
+ const user = await User.create({
10
+ ...request.validated(["name", "email", "password"]),
11
+ image: output.path,
12
+ imageMetadata: await file.metadata(),
13
+ });
14
+
15
+ return response.success({
16
+ message: "File uploaded successfully",
17
+ user,
18
+ });
19
+ };
20
+
21
+ createNewUserController.validation = {
22
+ schema: v.object({
23
+ name: v.string().required(),
24
+ email: v.email().required().unique(User),
25
+ password: v.string().min(6),
26
+ image: v.file().image().required().maxSize({ unit: "MB", size: 1.5 }),
27
+ }),
28
+ };
@@ -1,9 +1,14 @@
1
1
  import { type Request, type RequestHandler, type Response } from "@warlock.js/core";
2
- import { getUsersService } from "../services/get-users.service";
2
+ import { usersRepository } from "../repositories/users.repository";
3
3
 
4
4
  export const getUsersController: RequestHandler = async (request: Request, response: Response) => {
5
+ const users = await usersRepository.listCached({
6
+ ...request.all(),
7
+ simpleSelect: true,
8
+ });
9
+
5
10
  return response.success({
6
- users: await getUsersService(),
11
+ users,
7
12
  });
8
13
  };
9
14
 
@@ -0,0 +1,32 @@
1
+ import { Model } from "@warlock.js/cascade";
2
+ import { useCurrentUser } from "@warlock.js/core";
3
+
4
+ const globalEvents = Model.globalEvents();
5
+
6
+ const saveSubscription = globalEvents.onSaving(async (model, { isInsert }) => {
7
+ const user = useCurrentUser();
8
+
9
+ if (!user) return;
10
+
11
+ if (isInsert) {
12
+ if (model.schemaHas("createdBy")) {
13
+ model.set("createdBy", user);
14
+ }
15
+ }
16
+
17
+ if (model.schemaHas("updatedBy")) {
18
+ model.set("updatedBy", user);
19
+ }
20
+ });
21
+
22
+ const deleteSubscription = globalEvents.onDeleting(async (model) => {
23
+ const user = useCurrentUser();
24
+
25
+ if (!user) return;
26
+
27
+ if (model.schemaHas("deletedBy")) {
28
+ model.set("deletedBy", user);
29
+ }
30
+ });
31
+
32
+ export const cleanup = [saveSubscription, deleteSubscription];
@@ -0,0 +1,5 @@
1
+ import { modelSync } from "@warlock.js/cascade";
2
+ import { Post } from "app/posts/models/post/psot.model";
3
+ import { User } from "../models/user";
4
+
5
+ modelSync.sync(User, Post, "createdBy");
@@ -0,0 +1,5 @@
1
+ import { onceConnected } from "@warlock.js/cascade";
2
+
3
+ onceConnected(async () => {
4
+ // TODO: Review cloud storage deleteDirectory not working as expected
5
+ });
@@ -1,14 +1,14 @@
1
- import { migrationOffice, type Blueprint } from "@warlock.js/cascade";
1
+ import { Migration } from "@warlock.js/cascade";
2
2
  import { User } from "../user.model";
3
3
 
4
- export default migrationOffice.register({
5
- name: "userMigration",
6
- createdAt: "11-12-2025_23-58-03",
7
- blueprint: User.blueprint(),
8
- up: (blueprint: Blueprint) => {
9
- blueprint.unique("id");
10
- },
11
- down: (blueprint: Blueprint) => {
12
- blueprint.dropUniqueIndex("id");
13
- },
14
- });
4
+ export default class UsersMigration extends Migration.for(User) {
5
+ public up() {
6
+ this.int("id").unique().autoIncrement();
7
+ this.unique("email");
8
+ }
9
+
10
+ public down() {
11
+ this.dropIndex("id");
12
+ this.dropUnique("email");
13
+ }
14
+ }
@@ -1,46 +1,73 @@
1
1
  import { Auth } from "@warlock.js/auth";
2
- import { type Casts, type Document, type ModelSync } from "@warlock.js/cascade";
2
+ import { RegisterModel } from "@warlock.js/cascade";
3
+ import { defineResource, uploadedFileMetadataSchema, useHashedPassword } from "@warlock.js/core";
4
+ import { type Infer, v } from "@warlock.js/seal";
5
+ import { globalColumnsSchema } from "app/shared/utils/global-columns-schema";
3
6
 
4
- export class User extends Auth {
7
+ const UserResource = defineResource({
8
+ schema: {
9
+ id: "number",
10
+ name: "string",
11
+ email: "string",
12
+ image: "storageUrl",
13
+ createdAt: "date",
14
+ updatedAt: "date",
15
+ isActive: "boolean",
16
+ type: () => "user",
17
+ },
18
+ });
19
+
20
+ export const userSchema = globalColumnsSchema.extend({
21
+ name: v.string().required().trim(),
22
+ email: v.email().requiredIfEmpty("id"),
23
+ image: v.string(),
24
+ imageMetadata: uploadedFileMetadataSchema,
25
+ password: v.string().min(6).requiredIfEmpty("id").addTransformer(useHashedPassword()),
26
+ });
27
+
28
+ export type UserSchema = Infer<typeof userSchema>;
29
+
30
+ @RegisterModel()
31
+ export class User extends Auth<UserSchema> {
5
32
  /**
6
33
  * Collection name
7
34
  */
8
- public static collection = "users";
35
+ public static table = "users";
9
36
 
10
37
  /**
11
- * List of models to sync with
12
- * To sync with a single embedded document use: [User.sync("city")],
13
- * this will update the city sub-document to all users when city model is updated.
14
- * To sync with multiple embedded documents use: [Post.syncMany("keywords")],
15
- * This will update the keywords sub-document to all posts when keywords model is updated.
38
+ * Model Schema
16
39
  */
17
- public syncWith: ModelSync[] = [];
40
+ public static schema = userSchema;
18
41
 
19
42
  /**
20
- * Default value for model data
21
- * Works only when creating new records
43
+ * Embed fields when saving in another model
22
44
  */
23
- public defaultValue: Document = {};
45
+ public static embed = ["id", "name"];
24
46
 
25
47
  /**
26
- * User type
48
+ * Resource to be used when converting the model to JSON
27
49
  */
28
- public userType = "user";
50
+ public static resource = UserResource;
29
51
 
30
52
  /**
31
- * Cast data types before saving documents into database
53
+ * User type identifier
32
54
  */
33
- protected casts: Casts = {
34
- name: "string",
35
- age: "number",
36
- email: "string",
37
- isActive: "boolean",
38
- };
55
+ public get userType(): string {
56
+ return "user";
57
+ }
39
58
 
40
- /**
41
- * Define what columns should be used when model document is embedded in another document.
42
- * Make sure to set only the needed columns to reduce the document size.
43
- * This is useful for better performance when fetching data from database.
44
- */
45
- public embedded = ["id", "name"];
59
+ static {
60
+ // Local scopes
61
+ this.addScope("active", (query) => {
62
+ query.where("isActive", true);
63
+ });
64
+
65
+ this.addScope("admins", (query) => {
66
+ query.where("role", "admin");
67
+ });
68
+
69
+ this.addScope("verified", (query) => {
70
+ query.where("emailVerified", true);
71
+ });
72
+ }
46
73
  }
@@ -1,27 +1,23 @@
1
- import { RepositoryManager, type FilterByOptions, type RepositoryOptions } from "@warlock.js/core";
2
- import { User } from "./../models/user";
1
+ import type { FilterRules, RepositoryOptions } from "@warlock.js/core";
2
+ import { RepositoryManager } from "@warlock.js/core";
3
+ import { User } from "../models/user";
3
4
 
4
- export class UsersRepository extends RepositoryManager<User> {
5
- /**
6
- * {@inheritDoc}
7
- */
8
- public model = User;
5
+ class UsersRepository extends RepositoryManager<User> {
6
+ public source = User;
9
7
 
10
- /**
11
- * Simple columns selections
12
- * Set the columns that need to be selected when passing 'simple' option with 'true'
13
- */
14
- public simpleSelectColumns = ["id"];
8
+ public simpleSelectColumns: string[] = ["id", "name", "createdAt"];
15
9
 
16
- /**
17
- * List default options
18
- */
19
- protected defaultOptions: RepositoryOptions = this.withDefaultOptions({});
10
+ public filterBy: FilterRules = {
11
+ id: "int",
12
+ name: "like",
13
+ email: "=",
14
+ };
20
15
 
21
- /**
22
- * Filter By options
23
- */
24
- protected filterBy: FilterByOptions = this.withDefaultFilters({});
16
+ public defaultOptions: RepositoryOptions = {
17
+ orderBy: {
18
+ createdAt: "desc",
19
+ },
20
+ };
25
21
  }
26
22
 
27
23
  export const usersRepository = new UsersRepository();
@@ -1,4 +1,10 @@
1
1
  import { router } from "@warlock.js/core";
2
+ import { guarded } from "app/utils/router";
3
+ import { createNewUserController } from "./controllers/create-new-user.controller";
2
4
  import { getUsersController } from "./controllers/get-users.controller";
3
5
 
4
- router.get("/users", getUsersController);
6
+ router.post("/users", createNewUserController);
7
+
8
+ guarded(() => {
9
+ router.get("/users", getUsersController);
10
+ });
@@ -0,0 +1,21 @@
1
+ import { Random } from "@mongez/reinforcements";
2
+ import { seeder } from "@warlock.js/core";
3
+ import { User } from "../models/user";
4
+
5
+ export default seeder({
6
+ name: "Seed Users",
7
+ once: true,
8
+ run: async () => {
9
+ for (let i = 0; i < 10; i++) {
10
+ await User.create({
11
+ name: `User ${Random.int()}`,
12
+ email: `user${Random.int()}@gmail.com`,
13
+ password: `password-${Random.int()}`,
14
+ });
15
+ }
16
+
17
+ return {
18
+ recordsCreated: 10,
19
+ };
20
+ },
21
+ });
@@ -0,0 +1,14 @@
1
+ import { type Request, type Response, storage, storageDriverContext } from "@warlock.js/core";
2
+
3
+ export async function cloudUploadMiddleware(request: Request, _response: Response) {
4
+ if (request.path !== "/posts") return;
5
+
6
+ // Example 1: Set driver with tenant-specific prefix
7
+ storageDriverContext.setDriver(storage.getDriver("r2"), {
8
+ prefix: `tenant`,
9
+ metadata: { tenantId: request.user?.id },
10
+ });
11
+
12
+ // Example 2: Or just set prefix for same driver
13
+ // storageDriverContext.setPrefix(`tenant-${request.user?.id}`);
14
+ }
@@ -1,5 +1,6 @@
1
1
  import { authMiddleware } from "@warlock.js/auth";
2
2
  import { router } from "@warlock.js/core";
3
+ import { cloudUploadMiddleware } from "./cloud-upload.middleware";
3
4
 
4
5
  export function publicRoutes(callback: () => void) {
5
6
  router.group(
@@ -23,7 +24,16 @@ export function guardedAdmin(callback: () => void) {
23
24
  export function guarded(callback: () => void) {
24
25
  router.group(
25
26
  {
26
- middleware: [authMiddleware()],
27
+ middleware: [authMiddleware("user")],
28
+ },
29
+ callback,
30
+ );
31
+ }
32
+
33
+ export function cloudUpload(callback: () => void) {
34
+ router.group(
35
+ {
36
+ middleware: [authMiddleware("user"), cloudUploadMiddleware],
27
37
  },
28
38
  callback,
29
39
  );
@@ -11,6 +11,8 @@ const authConfigurations: AuthConfigurations = {
11
11
  expiresIn: NO_EXPIRATION,
12
12
  refresh: {
13
13
  expiresIn: "7d",
14
+ enabled: false,
15
+ secret: env("JWT_REFRESH_SECRET"),
14
16
  },
15
17
  },
16
18
  };