create-warlock 4.0.93 → 4.0.95
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/package.json +7 -7
- package/templates/warlock/src/app/auth/models/otp/migrations/22-12-2025_10-30-20.otp-migration.ts +33 -5
- package/templates/warlock/src/app/posts/controllers/create-new-post.controller.ts +1 -0
- package/templates/warlock/src/app/posts/models/post/migrations/09-01-2026_02-07-51-post.migration.ts +36 -0
- package/templates/warlock/src/app/posts/models/post/psot.model.ts +10 -1
- package/templates/warlock/src/app/posts/routes.ts +2 -5
- package/templates/warlock/src/app/users/events/sync.ts +1 -1
- package/templates/warlock/src/app/users/main.ts +19 -0
- package/templates/warlock/src/app/users/models/user/migrations/11-12-2025_23-58-03-user.migration.ts +25 -4
- package/templates/warlock/src/app/users/models/user/user.model.ts +13 -2
- package/templates/warlock/src/app/users/routes.ts +1 -3
- package/templates/warlock/src/app/utils/cloud-upload.middleware.ts +1 -1
- package/templates/warlock/src/config/database.ts +31 -2
- package/templates/warlock/warlock.config.ts +8 -1
package/package.json
CHANGED
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
"@mongez/reinforcements": "^2.3.17",
|
|
25
25
|
"@mongez/localization": "^3.2.1",
|
|
26
26
|
"@mongez/supportive-is": "^2.0.4",
|
|
27
|
-
"@warlock.js/auth": "4.0.
|
|
28
|
-
"@warlock.js/cache": "4.0.
|
|
29
|
-
"@warlock.js/cascade": "4.0.
|
|
30
|
-
"@warlock.js/scheduler": "4.0.
|
|
31
|
-
"@warlock.js/core": "4.0.
|
|
32
|
-
"@warlock.js/logger": "4.0.
|
|
33
|
-
"@warlock.js/seal": "4.0.
|
|
27
|
+
"@warlock.js/auth": "4.0.95",
|
|
28
|
+
"@warlock.js/cache": "4.0.95",
|
|
29
|
+
"@warlock.js/cascade": "4.0.95",
|
|
30
|
+
"@warlock.js/scheduler": "4.0.95",
|
|
31
|
+
"@warlock.js/core": "4.0.95",
|
|
32
|
+
"@warlock.js/logger": "4.0.95",
|
|
33
|
+
"@warlock.js/seal": "4.0.95",
|
|
34
34
|
"dayjs": "^1.11.19"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
package/templates/warlock/src/app/auth/models/otp/migrations/22-12-2025_10-30-20.otp-migration.ts
CHANGED
|
@@ -3,17 +3,45 @@ import { OTP } from "../otp.model";
|
|
|
3
3
|
|
|
4
4
|
export default migrate(OTP, {
|
|
5
5
|
name: "otp",
|
|
6
|
-
createdAt: "2025-12-22T10:30:20",
|
|
6
|
+
createdAt: "2025-12-22T10:30:20",
|
|
7
7
|
up() {
|
|
8
|
+
// Create table
|
|
9
|
+
this.createTableIfNotExists();
|
|
10
|
+
|
|
11
|
+
// Primary key
|
|
12
|
+
this.id();
|
|
13
|
+
|
|
14
|
+
// OTP fields
|
|
15
|
+
this.string("code", 20);
|
|
16
|
+
this.string("type", 50);
|
|
17
|
+
this.string("target", 255);
|
|
18
|
+
this.string("channel", 50);
|
|
19
|
+
this.integer("userId");
|
|
20
|
+
this.string("userType", 50);
|
|
21
|
+
this.timestamp("expiresAt");
|
|
22
|
+
this.timestamp("usedAt").nullable();
|
|
23
|
+
this.integer("attempts").default(0);
|
|
24
|
+
this.integer("maxAttempts").default(5);
|
|
25
|
+
this.json("metadata").nullable();
|
|
26
|
+
|
|
27
|
+
// Status
|
|
28
|
+
this.boolean("isActive").default(true);
|
|
29
|
+
|
|
30
|
+
// Embedded user references (JSONB)
|
|
31
|
+
this.json("createdBy").nullable();
|
|
32
|
+
this.json("updatedBy").nullable();
|
|
33
|
+
this.json("deletedBy").nullable();
|
|
34
|
+
|
|
35
|
+
// Timestamps
|
|
36
|
+
this.timestamps();
|
|
37
|
+
|
|
38
|
+
// Indexes for common queries
|
|
8
39
|
this.index("code");
|
|
9
40
|
this.index(["target", "type"]);
|
|
10
41
|
this.index("expiresAt");
|
|
11
42
|
this.index("userId");
|
|
12
43
|
},
|
|
13
44
|
down() {
|
|
14
|
-
this.
|
|
15
|
-
this.dropIndex(["target", "type"]);
|
|
16
|
-
this.dropIndex("expiresAt");
|
|
17
|
-
this.dropIndex("userId");
|
|
45
|
+
this.dropTableIfExists();
|
|
18
46
|
},
|
|
19
47
|
});
|
package/templates/warlock/src/app/posts/models/post/migrations/09-01-2026_02-07-51-post.migration.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Migration } from "@warlock.js/cascade";
|
|
2
|
+
import { Post } from "../psot.model";
|
|
3
|
+
|
|
4
|
+
export default class PostsMigration extends Migration.for(Post) {
|
|
5
|
+
public up() {
|
|
6
|
+
// Create table
|
|
7
|
+
this.createTableIfNotExists();
|
|
8
|
+
|
|
9
|
+
// Primary key
|
|
10
|
+
this.id();
|
|
11
|
+
|
|
12
|
+
// Post fields
|
|
13
|
+
this.string("title", 255);
|
|
14
|
+
this.text("description");
|
|
15
|
+
this.string("slug", 255).unique();
|
|
16
|
+
this.string("image", 500).nullable();
|
|
17
|
+
|
|
18
|
+
// Status
|
|
19
|
+
this.boolean("isActive").default(true);
|
|
20
|
+
|
|
21
|
+
this.int("authorId").notNullable();
|
|
22
|
+
|
|
23
|
+
// Embedded user references (JSONB for PostgreSQL)
|
|
24
|
+
this.json("createdBy").nullable();
|
|
25
|
+
this.json("updatedBy").nullable();
|
|
26
|
+
this.json("deletedBy").nullable();
|
|
27
|
+
|
|
28
|
+
// Timestamps
|
|
29
|
+
this.timestamps();
|
|
30
|
+
this.timestamp("deletedAt").nullable();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public down() {
|
|
34
|
+
this.dropTableIfExists();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { Model, RegisterModel } from "@warlock.js/cascade";
|
|
1
|
+
import { belongsTo, Model, RegisterModel } from "@warlock.js/cascade";
|
|
2
2
|
import { defineResource, useComputedSlug } from "@warlock.js/core";
|
|
3
3
|
import { type Infer, v } from "@warlock.js/seal";
|
|
4
4
|
import { globalColumnsSchema } from "app/shared/utils/global-columns-schema";
|
|
5
|
+
import { type User } from "app/users/models/user";
|
|
5
6
|
|
|
6
7
|
export const postSchema = globalColumnsSchema.extend({
|
|
7
8
|
title: v.string().required(),
|
|
8
9
|
description: v.string().required(),
|
|
9
10
|
slug: v.computed(useComputedSlug()),
|
|
10
11
|
image: v.string().required(),
|
|
12
|
+
authorId: v.int().required(),
|
|
11
13
|
});
|
|
12
14
|
|
|
13
15
|
export type PostSchema = Infer<typeof postSchema>;
|
|
@@ -18,6 +20,12 @@ export class Post extends Model<PostSchema> {
|
|
|
18
20
|
|
|
19
21
|
public static schema = postSchema;
|
|
20
22
|
|
|
23
|
+
public author?: User;
|
|
24
|
+
|
|
25
|
+
public static relations = {
|
|
26
|
+
author: belongsTo("User"),
|
|
27
|
+
};
|
|
28
|
+
|
|
21
29
|
public static resource = defineResource({
|
|
22
30
|
schema: {
|
|
23
31
|
id: "number",
|
|
@@ -25,6 +33,7 @@ export class Post extends Model<PostSchema> {
|
|
|
25
33
|
title: "string",
|
|
26
34
|
description: "string",
|
|
27
35
|
image: "storageUrl",
|
|
36
|
+
authorId: "number",
|
|
28
37
|
createdBy: "object",
|
|
29
38
|
updatedBy: "object",
|
|
30
39
|
isActive: "boolean",
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { router } from "@warlock.js/core";
|
|
2
|
-
import {
|
|
2
|
+
import { guarded } from "app/utils/router";
|
|
3
3
|
import { createNewPostController } from "./controllers/create-new-post.controller";
|
|
4
4
|
import { updatePostController } from "./controllers/update-post.controller";
|
|
5
5
|
|
|
6
6
|
guarded(() => {
|
|
7
|
-
|
|
8
|
-
router.post("/posts", createNewPostController);
|
|
9
|
-
});
|
|
10
|
-
router.put("/posts/:id", updatePostController);
|
|
7
|
+
router.route("/posts").create(createNewPostController).update(updatePostController);
|
|
11
8
|
});
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { onceConnected } from "@warlock.js/cascade";
|
|
2
|
+
import { Post } from "app/posts/models/post/psot.model";
|
|
2
3
|
|
|
3
4
|
onceConnected(async () => {
|
|
4
5
|
// TODO: Review cloud storage deleteDirectory not working as expected
|
|
6
|
+
|
|
7
|
+
// // Lazy loading
|
|
8
|
+
// const user = (await User.first())!;
|
|
9
|
+
// await user.load("posts");
|
|
10
|
+
|
|
11
|
+
// console.log(user.posts!.length);
|
|
12
|
+
|
|
13
|
+
// // Eager loading
|
|
14
|
+
// const user2 = await User.with("posts").first();
|
|
15
|
+
|
|
16
|
+
// console.log(user2?.posts?.length);
|
|
17
|
+
|
|
18
|
+
// const post = await Post.with("author").first();
|
|
19
|
+
|
|
20
|
+
// console.log(post?.author);
|
|
21
|
+
const post = await Post.joinWith("author").first();
|
|
22
|
+
console.log(post?.data); // { id, title, authorId, ... } - no author
|
|
23
|
+
console.log(post?.author); // User model instance
|
|
5
24
|
});
|
package/templates/warlock/src/app/users/models/user/migrations/11-12-2025_23-58-03-user.migration.ts
CHANGED
|
@@ -3,12 +3,33 @@ import { User } from "../user.model";
|
|
|
3
3
|
|
|
4
4
|
export default class UsersMigration extends Migration.for(User) {
|
|
5
5
|
public up() {
|
|
6
|
-
|
|
7
|
-
this.
|
|
6
|
+
// Create table
|
|
7
|
+
this.createTableIfNotExists();
|
|
8
|
+
|
|
9
|
+
// Primary key
|
|
10
|
+
this.id(); // Creates "id" SERIAL PRIMARY KEY
|
|
11
|
+
|
|
12
|
+
// User fields
|
|
13
|
+
this.string("name", 255);
|
|
14
|
+
this.string("email", 255).unique();
|
|
15
|
+
this.string("password", 255);
|
|
16
|
+
this.string("image", 500).nullable();
|
|
17
|
+
this.json("imageMetadata").nullable();
|
|
18
|
+
|
|
19
|
+
// Status
|
|
20
|
+
this.boolean("isActive").default(true);
|
|
21
|
+
|
|
22
|
+
// Embedded user references (JSONB for PostgreSQL)
|
|
23
|
+
this.json("createdBy").nullable();
|
|
24
|
+
this.json("updatedBy").nullable();
|
|
25
|
+
this.json("deletedBy").nullable();
|
|
26
|
+
|
|
27
|
+
// Timestamps (auto-managed by the model)
|
|
28
|
+
this.timestamps(); // Creates createdAt and updatedAt
|
|
29
|
+
this.timestamp("deletedAt").nullable(); // Soft deletes
|
|
8
30
|
}
|
|
9
31
|
|
|
10
32
|
public down() {
|
|
11
|
-
this.
|
|
12
|
-
this.dropUnique("email");
|
|
33
|
+
this.dropTableIfExists();
|
|
13
34
|
}
|
|
14
35
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Auth } from "@warlock.js/auth";
|
|
2
|
-
import { RegisterModel } from "@warlock.js/cascade";
|
|
2
|
+
import { hasMany, RegisterModel } from "@warlock.js/cascade";
|
|
3
3
|
import { defineResource, uploadedFileMetadataSchema, useHashedPassword } from "@warlock.js/core";
|
|
4
4
|
import { type Infer, v } from "@warlock.js/seal";
|
|
5
|
+
import { type Post } from "app/posts/models/post/psot.model";
|
|
5
6
|
import { globalColumnsSchema } from "app/shared/utils/global-columns-schema";
|
|
6
7
|
|
|
7
8
|
const UserResource = defineResource({
|
|
@@ -19,7 +20,7 @@ const UserResource = defineResource({
|
|
|
19
20
|
|
|
20
21
|
export const userSchema = globalColumnsSchema.extend({
|
|
21
22
|
name: v.string().required().trim(),
|
|
22
|
-
email: v.email().requiredIfEmpty("id"),
|
|
23
|
+
email: v.email().requiredIfEmpty("id").unique("User"),
|
|
23
24
|
image: v.string(),
|
|
24
25
|
imageMetadata: uploadedFileMetadataSchema,
|
|
25
26
|
password: v.string().min(6).requiredIfEmpty("id").addTransformer(useHashedPassword()),
|
|
@@ -39,6 +40,12 @@ export class User extends Auth<UserSchema> {
|
|
|
39
40
|
*/
|
|
40
41
|
public static schema = userSchema;
|
|
41
42
|
|
|
43
|
+
public static relations = {
|
|
44
|
+
posts: hasMany("Post", { foreignKey: "authorId" }),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
public posts?: Post[];
|
|
48
|
+
|
|
42
49
|
/**
|
|
43
50
|
* Embed fields when saving in another model
|
|
44
51
|
*/
|
|
@@ -69,5 +76,9 @@ export class User extends Auth<UserSchema> {
|
|
|
69
76
|
this.addScope("verified", (query) => {
|
|
70
77
|
query.where("emailVerified", true);
|
|
71
78
|
});
|
|
79
|
+
|
|
80
|
+
// this.addGlobalScope("active", (query) => {
|
|
81
|
+
// query.where("isActive", true);
|
|
82
|
+
// });
|
|
72
83
|
}
|
|
73
84
|
}
|
|
@@ -3,8 +3,6 @@ import { guarded } from "app/utils/router";
|
|
|
3
3
|
import { createNewUserController } from "./controllers/create-new-user.controller";
|
|
4
4
|
import { getUsersController } from "./controllers/get-users.controller";
|
|
5
5
|
|
|
6
|
-
router.post("/users", createNewUserController);
|
|
7
|
-
|
|
8
6
|
guarded(() => {
|
|
9
|
-
router.
|
|
7
|
+
router.route("/users").get(getUsersController).post(createNewUserController);
|
|
10
8
|
});
|
|
@@ -10,5 +10,5 @@ export async function cloudUploadMiddleware(request: Request, _response: Respons
|
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
// Example 2: Or just set prefix for same driver
|
|
13
|
-
|
|
13
|
+
storageDriverContext.setPrefix(`tenant-${request.user?.id}`);
|
|
14
14
|
}
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
} from "@warlock.js/cascade";
|
|
7
7
|
|
|
8
8
|
const databaseConfigurations: ConnectionOptions<MongoDriverOptions, MongoClientOptions> = {
|
|
9
|
-
driver: "
|
|
9
|
+
driver: "postgres",
|
|
10
10
|
name: "default",
|
|
11
11
|
database: env("DB_NAME"),
|
|
12
12
|
host: env("DB_HOST", "localhost"),
|
|
@@ -21,15 +21,44 @@ const databaseConfigurations: ConnectionOptions<MongoDriverOptions, MongoClientO
|
|
|
21
21
|
counterCollection: "counters",
|
|
22
22
|
},
|
|
23
23
|
|
|
24
|
-
defaultDeleteStrategy: "
|
|
24
|
+
defaultDeleteStrategy: "permanent",
|
|
25
25
|
|
|
26
26
|
clientOptions: {
|
|
27
27
|
replicaSet: env("DB_REPLICA_SET"),
|
|
28
28
|
},
|
|
29
29
|
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Model Defaults Configuration
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// These settings override driver defaults and apply to all models.
|
|
34
|
+
// Individual models can override these by setting static properties.
|
|
35
|
+
//
|
|
36
|
+
// Configuration hierarchy (highest to lowest):
|
|
37
|
+
// 1. Model static property (e.g., User.createdAtColumn = "creation_date")
|
|
38
|
+
// 2. modelOptions (below) - Database-wide overrides
|
|
39
|
+
// 3. Driver defaults (PostgreSQL: snake_case, MongoDB: camelCase)
|
|
40
|
+
// 4. Framework defaults
|
|
41
|
+
// ============================================================================
|
|
30
42
|
modelOptions: {
|
|
43
|
+
// ID Generation (for MongoDB)
|
|
31
44
|
randomIncrement: true,
|
|
32
45
|
initialId: 1,
|
|
46
|
+
|
|
47
|
+
// Timestamps - PostgreSQL driver already defaults to snake_case
|
|
48
|
+
// (created_at, updated_at) so these are optional unless overriding
|
|
49
|
+
timestamps: true,
|
|
50
|
+
// createdAtColumn: "created_at", // Already driver default
|
|
51
|
+
// updatedAtColumn: "updated_at", // Already driver default
|
|
52
|
+
|
|
53
|
+
// Deletion Strategy
|
|
54
|
+
// deleteStrategy: "soft", // Uncomment to enable soft deletes globally
|
|
55
|
+
// deletedAtColumn: "deleted_at", // Already driver default
|
|
56
|
+
|
|
57
|
+
// Validation
|
|
58
|
+
strictMode: "strip",
|
|
59
|
+
|
|
60
|
+
// Naming Convention
|
|
61
|
+
// namingConvention: "snake_case", // Already driver default for PostgreSQL
|
|
33
62
|
},
|
|
34
63
|
};
|
|
35
64
|
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
authMigrations,
|
|
3
|
+
registerAuthCleanupCommand,
|
|
4
|
+
registerJWTSecretGeneratorCommand,
|
|
5
|
+
} from "@warlock.js/auth";
|
|
2
6
|
import { defineConfig } from "@warlock.js/core";
|
|
3
7
|
|
|
4
8
|
export default defineConfig({
|
|
5
9
|
cli: {
|
|
6
10
|
commands: [registerJWTSecretGeneratorCommand(), registerAuthCleanupCommand()],
|
|
7
11
|
},
|
|
12
|
+
database: {
|
|
13
|
+
migrations: authMigrations,
|
|
14
|
+
},
|
|
8
15
|
});
|