create-warlock 4.0.30 → 4.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/index.js +10 -5
- package/cjs/index.js.map +1 -1
- package/create-app.js +0 -0
- package/esm/index.js +10 -5
- package/esm/index.js.map +1 -1
- package/package.json +32 -38
- package/templates/warlock/docs/new-module.md +123 -61
- package/templates/warlock/package.json +60 -67
- package/templates/warlock/src/app/auth/controllers/forgot-password.controller.ts +3 -2
- package/templates/warlock/src/app/auth/controllers/login.controller.ts +1 -4
- package/templates/warlock/src/app/auth/main.ts +6 -3
- package/templates/warlock/src/app/auth/models/otp/migrations/22-12-2025_10-30-20.otp-migration.ts +13 -16
- package/templates/warlock/src/app/auth/models/otp/otp.model.ts +26 -31
- package/templates/warlock/src/app/auth/requests/login.request.ts +1 -1
- package/templates/warlock/src/app/auth/services/auth.service.ts +10 -5
- package/templates/warlock/src/app/auth/services/otp.service.ts +2 -12
- package/templates/warlock/src/app/auth/services/reset-password.service.ts +1 -1
- package/templates/warlock/src/app/posts/controllers/create-new-post.controller.ts +20 -0
- package/templates/warlock/src/app/posts/controllers/update-post.controller.ts +24 -0
- package/templates/warlock/src/app/posts/models/post/psot.model.ts +33 -0
- package/templates/warlock/src/app/posts/routes.ts +11 -0
- package/templates/warlock/src/app/shared/utils/global-columns-schema.ts +8 -0
- package/templates/warlock/src/app/shared/utils/locales.ts +4 -0
- package/templates/warlock/src/app/uploads/controllers/fetch-uploaded-file.controller.ts +32 -0
- package/templates/warlock/src/app/uploads/routes.ts +4 -0
- package/templates/warlock/src/app/users/controllers/create-new-user.controller.ts +28 -0
- package/templates/warlock/src/app/users/controllers/get-users.controller.ts +7 -2
- package/templates/warlock/src/app/users/events/inject-created-by-user.into-model.event.ts +32 -0
- package/templates/warlock/src/app/users/events/sync.ts +5 -0
- package/templates/warlock/src/app/users/main.ts +5 -0
- package/templates/warlock/src/app/users/models/user/migrations/11-12-2025_23-58-03-user.migration.ts +12 -12
- package/templates/warlock/src/app/users/models/user/user.model.ts +54 -27
- package/templates/warlock/src/app/users/repositories/users.repository.ts +16 -20
- package/templates/warlock/src/app/users/routes.ts +7 -1
- package/templates/warlock/src/app/users/seeds/users.seed.ts +21 -0
- package/templates/warlock/src/app/utils/cloud-upload.middleware.ts +14 -0
- package/templates/warlock/src/app/utils/router.ts +11 -1
- package/templates/warlock/src/config/auth.ts +2 -0
- package/templates/warlock/src/config/database.ts +24 -7
- package/templates/warlock/src/config/storage.ts +17 -8
- package/templates/warlock/tsconfig.json +6 -5
- package/templates/warlock/yarn.lock +4831 -0
- package/cjs/commands/create-new-app/get-app-path.d.ts +0 -2
- package/cjs/commands/create-new-app/get-app-path.d.ts.map +0 -1
- package/cjs/commands/create-new-app/get-app-path.js +0 -8
- package/cjs/commands/create-new-app/get-app-path.js.map +0 -1
- package/cjs/commands/create-new-app/index.d.ts +0 -2
- package/cjs/commands/create-new-app/index.d.ts.map +0 -1
- package/cjs/commands/create-new-app/index.js +0 -50
- package/cjs/commands/create-new-app/index.js.map +0 -1
- package/cjs/commands/create-new-app/select-app-type.d.ts +0 -2
- package/cjs/commands/create-new-app/select-app-type.d.ts.map +0 -1
- package/cjs/commands/create-new-app/types.d.ts +0 -9
- package/cjs/commands/create-new-app/types.d.ts.map +0 -1
- package/cjs/commands/create-warlock-app/index.d.ts +0 -3
- package/cjs/commands/create-warlock-app/index.d.ts.map +0 -1
- package/cjs/commands/create-warlock-app/index.js +0 -51
- package/cjs/commands/create-warlock-app/index.js.map +0 -1
- package/cjs/helpers/app.d.ts +0 -54
- package/cjs/helpers/app.d.ts.map +0 -1
- package/cjs/helpers/app.js +0 -126
- package/cjs/helpers/app.js.map +0 -1
- package/cjs/helpers/exec.d.ts +0 -10
- package/cjs/helpers/exec.d.ts.map +0 -1
- package/cjs/helpers/exec.js +0 -69
- package/cjs/helpers/exec.js.map +0 -1
- package/cjs/helpers/package-manager.d.ts +0 -6
- package/cjs/helpers/package-manager.d.ts.map +0 -1
- package/cjs/helpers/package-manager.js +0 -22
- package/cjs/helpers/package-manager.js.map +0 -1
- package/cjs/helpers/paths.d.ts +0 -4
- package/cjs/helpers/paths.d.ts.map +0 -1
- package/cjs/helpers/paths.js +0 -8
- package/cjs/helpers/paths.js.map +0 -1
- package/cjs/helpers/project-builder-helpers.d.ts +0 -6
- package/cjs/helpers/project-builder-helpers.d.ts.map +0 -1
- package/cjs/helpers/project-builder-helpers.js +0 -18
- package/cjs/helpers/project-builder-helpers.js.map +0 -1
- package/cjs/index.d.ts +0 -2
- package/cjs/index.d.ts.map +0 -1
- package/esm/commands/create-new-app/get-app-path.d.ts +0 -2
- package/esm/commands/create-new-app/get-app-path.d.ts.map +0 -1
- package/esm/commands/create-new-app/get-app-path.js +0 -8
- package/esm/commands/create-new-app/get-app-path.js.map +0 -1
- package/esm/commands/create-new-app/index.d.ts +0 -2
- package/esm/commands/create-new-app/index.d.ts.map +0 -1
- package/esm/commands/create-new-app/index.js +0 -50
- package/esm/commands/create-new-app/index.js.map +0 -1
- package/esm/commands/create-new-app/select-app-type.d.ts +0 -2
- package/esm/commands/create-new-app/select-app-type.d.ts.map +0 -1
- package/esm/commands/create-new-app/types.d.ts +0 -9
- package/esm/commands/create-new-app/types.d.ts.map +0 -1
- package/esm/commands/create-warlock-app/index.d.ts +0 -3
- package/esm/commands/create-warlock-app/index.d.ts.map +0 -1
- package/esm/commands/create-warlock-app/index.js +0 -51
- package/esm/commands/create-warlock-app/index.js.map +0 -1
- package/esm/helpers/app.d.ts +0 -54
- package/esm/helpers/app.d.ts.map +0 -1
- package/esm/helpers/app.js +0 -126
- package/esm/helpers/app.js.map +0 -1
- package/esm/helpers/exec.d.ts +0 -10
- package/esm/helpers/exec.d.ts.map +0 -1
- package/esm/helpers/exec.js +0 -69
- package/esm/helpers/exec.js.map +0 -1
- package/esm/helpers/package-manager.d.ts +0 -6
- package/esm/helpers/package-manager.d.ts.map +0 -1
- package/esm/helpers/package-manager.js +0 -22
- package/esm/helpers/package-manager.js.map +0 -1
- package/esm/helpers/paths.d.ts +0 -4
- package/esm/helpers/paths.d.ts.map +0 -1
- package/esm/helpers/paths.js +0 -8
- package/esm/helpers/paths.js.map +0 -1
- package/esm/helpers/project-builder-helpers.d.ts +0 -6
- package/esm/helpers/project-builder-helpers.d.ts.map +0 -1
- package/esm/helpers/project-builder-helpers.js +0 -18
- package/esm/helpers/project-builder-helpers.js.map +0 -1
- package/esm/index.d.ts +0 -2
- package/esm/index.d.ts.map +0 -1
- package/templates/warlock/src/app/users/repositories/users-repository.ts +0 -66
- package/templates/warlock/src/app/users/services/get-new-customers.ts +0 -5
- package/templates/warlock/src/app/utils/output.ts +0 -5
|
@@ -1,35 +1,34 @@
|
|
|
1
|
-
import { Model,
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
*
|
|
24
|
+
* Table name
|
|
11
25
|
*/
|
|
12
|
-
public
|
|
13
|
-
attempts: 0,
|
|
14
|
-
maxAttempts: 5,
|
|
15
|
-
};
|
|
26
|
+
public static table = "otps";
|
|
16
27
|
|
|
17
28
|
/**
|
|
18
|
-
*
|
|
29
|
+
* Model schema
|
|
19
30
|
*/
|
|
20
|
-
|
|
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,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
|
-
|
|
12
|
-
|
|
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.
|
|
159
|
+
const expiredOtps = await OTP.query().where("expiresAt", "<", new Date()).get();
|
|
168
160
|
|
|
169
|
-
|
|
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.
|
|
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,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,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 {
|
|
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
|
|
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];
|
package/templates/warlock/src/app/users/models/user/migrations/11-12-2025_23-58-03-user.migration.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Migration } from "@warlock.js/cascade";
|
|
2
2
|
import { User } from "../user.model";
|
|
3
3
|
|
|
4
|
-
export default
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
|
35
|
+
public static table = "users";
|
|
9
36
|
|
|
10
37
|
/**
|
|
11
|
-
*
|
|
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
|
|
40
|
+
public static schema = userSchema;
|
|
18
41
|
|
|
19
42
|
/**
|
|
20
|
-
*
|
|
21
|
-
* Works only when creating new records
|
|
43
|
+
* Embed fields when saving in another model
|
|
22
44
|
*/
|
|
23
|
-
public
|
|
45
|
+
public static embed = ["id", "name"];
|
|
24
46
|
|
|
25
47
|
/**
|
|
26
|
-
*
|
|
48
|
+
* Resource to be used when converting the model to JSON
|
|
27
49
|
*/
|
|
28
|
-
public
|
|
50
|
+
public static resource = UserResource;
|
|
29
51
|
|
|
30
52
|
/**
|
|
31
|
-
*
|
|
53
|
+
* User type identifier
|
|
32
54
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
email: "string",
|
|
37
|
-
isActive: "boolean",
|
|
38
|
-
};
|
|
55
|
+
public get userType(): string {
|
|
56
|
+
return "user";
|
|
57
|
+
}
|
|
39
58
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
public filterBy: FilterRules = {
|
|
11
|
+
id: "int",
|
|
12
|
+
name: "like",
|
|
13
|
+
email: "=",
|
|
14
|
+
};
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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.
|
|
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
|
);
|