create-lyrajs 1.1.2 → 2.0.0
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 +2 -2
- package/template/backups/README.md +93 -0
- package/template/config/security.yaml +0 -1
- package/template/migrations/README.md +247 -0
- package/template/package.json +2 -6
- package/template/public/assets/styles/app.css +0 -0
- package/template/src/controller/AuthController.ts +100 -71
- package/template/src/controller/ErrorController.ts +126 -0
- package/template/src/controller/ExampleController.ts +28 -0
- package/template/src/controller/ExampleStaticController.ts +40 -0
- package/template/src/controller/UserController.ts +63 -44
- package/template/src/fixtures/{AppFixtures.ts → UserFixtures.ts} +4 -5
- package/template/src/jobs/ExampleJob.ts +63 -0
- package/template/src/middleware/YourMiddleware.ts +1 -1
- package/template/src/repository/UserRepository.ts +2 -3
- package/template/src/router/index.ts +3 -10
- package/template/src/router/routes/exampleRoutes.ts +7 -0
- package/template/src/router/routes/index.ts +4 -6
- package/template/src/server.ts +38 -27
- package/template/src/services/YourService.ts +3 -1
- package/template/src/templates/ExampleRender.tsx +20 -0
- package/template/src/templates/README.md +271 -0
- package/template/src/templates/layout/Base.tsx +21 -0
- package/template/src/templates/layout/Footer.tsx +7 -0
- package/template/src/templates/layout/Header.tsx +19 -0
- package/template/tsconfig.json +8 -4
- package/template/migrations/migration_1752052338457.sql +0 -4
- package/template/src/router/routes/authRoutes.ts +0 -13
- package/template/src/router/routes/userRoutes.ts +0 -11
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Controller, Get, HTTP_STATUS, Route } from "@lyra-js/core"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ErrorController - Handles HTTP error responses
|
|
5
|
+
*
|
|
6
|
+
* This controller provides error handling routes that can be called by the error handler.
|
|
7
|
+
* Each route can also be accessed directly for testing (e.g., GET /error/404?message=Custom+message)
|
|
8
|
+
*
|
|
9
|
+
* The errorHandler will redirect to these routes when errors occur.
|
|
10
|
+
* You can customize these methods to render custom error pages or return custom JSON responses.
|
|
11
|
+
*/
|
|
12
|
+
@Route({ path: "/error" })
|
|
13
|
+
export class ErrorController extends Controller {
|
|
14
|
+
/**
|
|
15
|
+
* Handle 404 Not Found errors
|
|
16
|
+
* Access: GET /error/404
|
|
17
|
+
*/
|
|
18
|
+
@Get("/404")
|
|
19
|
+
handle404(): void {
|
|
20
|
+
this.res.status(HTTP_STATUS.NOT_FOUND).json({
|
|
21
|
+
error: "Not Found",
|
|
22
|
+
message: "The requested resource was not found",
|
|
23
|
+
statusCode: HTTP_STATUS.NOT_FOUND,
|
|
24
|
+
path: this.req.url
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// Example: Use SSR to render a custom 404 page
|
|
28
|
+
// this.render('errors/404.ejs', {
|
|
29
|
+
// message: 'Page not found',
|
|
30
|
+
// url: this.req.url
|
|
31
|
+
// });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Handle 500 Internal Server Error
|
|
36
|
+
* Access: GET /error/500
|
|
37
|
+
*/
|
|
38
|
+
@Get("/500")
|
|
39
|
+
handle500(): void {
|
|
40
|
+
this.res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({
|
|
41
|
+
error: "Internal Server Error",
|
|
42
|
+
message: "An unexpected error occurred",
|
|
43
|
+
statusCode: HTTP_STATUS.INTERNAL_SERVER_ERROR
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// Example: Use SSR to render a custom error page
|
|
47
|
+
// this.render('errors/500.ejs', {
|
|
48
|
+
// message: 'Something went wrong'
|
|
49
|
+
// });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Handle 401 Unauthorized errors
|
|
54
|
+
* Access: GET /error/401
|
|
55
|
+
*/
|
|
56
|
+
@Get("/401")
|
|
57
|
+
handle401(): void {
|
|
58
|
+
this.res.status(HTTP_STATUS.UNAUTHORIZED).json({
|
|
59
|
+
error: "Unauthorized",
|
|
60
|
+
message: "Authentication is required",
|
|
61
|
+
statusCode: HTTP_STATUS.UNAUTHORIZED
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
// Example: Use SSR to render a custom login page
|
|
65
|
+
// this.render('errors/401.ejs', {
|
|
66
|
+
// message: 'Please log in to continue'
|
|
67
|
+
// });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Handle 403 Forbidden errors
|
|
72
|
+
* Access: GET /error/403
|
|
73
|
+
*/
|
|
74
|
+
@Get("/403")
|
|
75
|
+
handle403(): void {
|
|
76
|
+
this.res.status(HTTP_STATUS.FORBIDDEN).json({
|
|
77
|
+
error: "Forbidden",
|
|
78
|
+
message: "You don't have permission to access this resource",
|
|
79
|
+
statusCode: HTTP_STATUS.FORBIDDEN
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Example: Use SSR to render a custom forbidden page
|
|
83
|
+
// this.render('errors/403.ejs', {
|
|
84
|
+
// message: 'Access denied'
|
|
85
|
+
// });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Handle 400 Bad Request errors
|
|
90
|
+
* Access: GET /error/400
|
|
91
|
+
*/
|
|
92
|
+
@Get("/400")
|
|
93
|
+
handle400(): void {
|
|
94
|
+
this.res.status(HTTP_STATUS.BAD_REQUEST).json({
|
|
95
|
+
error: "Bad Request",
|
|
96
|
+
message: "The request was invalid",
|
|
97
|
+
statusCode: HTTP_STATUS.BAD_REQUEST
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Handle 409 Conflict errors
|
|
103
|
+
* Access: GET /error/409
|
|
104
|
+
*/
|
|
105
|
+
@Get("/409")
|
|
106
|
+
handle409(): void {
|
|
107
|
+
this.res.status(HTTP_STATUS.CONFLICT).json({
|
|
108
|
+
error: "Conflict",
|
|
109
|
+
message: "The request conflicts with current state",
|
|
110
|
+
statusCode: HTTP_STATUS.CONFLICT
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Handle 422 Unprocessable Entity errors
|
|
116
|
+
* Access: GET /error/422
|
|
117
|
+
*/
|
|
118
|
+
@Get("/422")
|
|
119
|
+
handle422(): void {
|
|
120
|
+
this.res.status(HTTP_STATUS.UNPROCESSABLE_ENTITY).json({
|
|
121
|
+
error: "Unprocessable Entity",
|
|
122
|
+
errors: "Validation failed",
|
|
123
|
+
statusCode: HTTP_STATUS.UNPROCESSABLE_ENTITY
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Controller, Get, NextFunction, Request, Response, Route } from "@lyra-js/core"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Example controller using decorator-based routing
|
|
5
|
+
* Decorators provide a modern, declarative way to define routes directly in your controller classes
|
|
6
|
+
* Routes are automatically discovered and registered - no manual route file registration needed
|
|
7
|
+
*/
|
|
8
|
+
@Route({ path: "/example" })
|
|
9
|
+
export class ExampleController extends Controller {
|
|
10
|
+
/**
|
|
11
|
+
* SSR endpoint using decorator-based routing
|
|
12
|
+
* The @Get decorator automatically registers this method as a GET route at /example/ssr
|
|
13
|
+
* Uses this.render() (instance method) for server-side rendering
|
|
14
|
+
*/
|
|
15
|
+
@Get({ path: "/ssr" })
|
|
16
|
+
async exampleSsrRouteMethod(req: Request, res: Response, next: NextFunction) {
|
|
17
|
+
try {
|
|
18
|
+
await this.render("ExampleRender.tsx", {
|
|
19
|
+
title: "Server-Side Rendering with Decorator-Based Routing",
|
|
20
|
+
content:
|
|
21
|
+
"This page is rendered using the modern decorator-based approach in LyraJS. The @Route and @Get decorators automatically register this endpoint without requiring manual route file configuration. The this.render() instance method handles server-side template rendering, generating complete HTML pages that are SEO-friendly and load faster for users. Decorators make your code cleaner, more maintainable, and self-documenting.",
|
|
22
|
+
documentationUrl: "https://lyrajs.dev/"
|
|
23
|
+
})
|
|
24
|
+
} catch (error) {
|
|
25
|
+
next(error)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Controller, NextFunction, Request, Response } from "@lyra-js/core"
|
|
2
|
+
|
|
3
|
+
export class ExampleStaticController extends Controller {
|
|
4
|
+
/**
|
|
5
|
+
* Example JSON API endpoint using static method approach
|
|
6
|
+
* This demonstrates returning JSON data from a traditional static controller method
|
|
7
|
+
*/
|
|
8
|
+
static exampleRouteMethod(req: Request, res: Response, next: NextFunction) {
|
|
9
|
+
try {
|
|
10
|
+
const exampleItem = {
|
|
11
|
+
id: 1,
|
|
12
|
+
title: "Static Controller Method Example",
|
|
13
|
+
content:
|
|
14
|
+
"This response is generated by a static controller method. Static methods are the traditional approach for defining route handlers in LyraJS, registered manually in route files. This endpoint returns JSON data, making it suitable for REST APIs consumed by frontend applications or mobile apps."
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
res.status(200).json({ message: "Item fetched successfully", exampleItem })
|
|
18
|
+
} catch (error) {
|
|
19
|
+
next(error)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Example SSR endpoint using static method approach
|
|
25
|
+
* This demonstrates server-side rendering with Controller.renderStatic()
|
|
26
|
+
* The rendered HTML is sent directly to the browser
|
|
27
|
+
*/
|
|
28
|
+
static async exampleSsrRouteMethod(req: Request, res: Response, next: NextFunction) {
|
|
29
|
+
try {
|
|
30
|
+
await Controller.renderStatic(res, "ExampleRender.tsx", {
|
|
31
|
+
title: "Server-Side Rendering with Static Controllers",
|
|
32
|
+
content:
|
|
33
|
+
"This page is rendered on the server using the Controller.renderStatic() method from a traditional static controller. The HTML is generated server-side and sent to your browser, providing faster initial page loads and better SEO. Static methods require manual route registration in your route files (e.g., src/router/routes/*.ts).",
|
|
34
|
+
documentationUrl: "https://lyrajs.dev/"
|
|
35
|
+
})
|
|
36
|
+
} catch (error) {
|
|
37
|
+
next(error)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -1,37 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
AccessControl,
|
|
3
|
+
Controller,
|
|
4
|
+
Delete,
|
|
5
|
+
Get,
|
|
6
|
+
isAuthenticated,
|
|
7
|
+
Patch,
|
|
8
|
+
Post,
|
|
9
|
+
Route,
|
|
10
|
+
UnauthorizedException,
|
|
11
|
+
ValidationException,
|
|
12
|
+
Validator
|
|
13
|
+
} from "@lyra-js/core"
|
|
4
14
|
|
|
5
15
|
import { User } from "@entity/User"
|
|
6
|
-
import { userRepository } from "@repository/UserRepository"
|
|
7
16
|
|
|
8
|
-
|
|
9
|
-
|
|
17
|
+
@Route({ path: "/user", middlewares: [isAuthenticated] })
|
|
18
|
+
export class UserController extends Controller {
|
|
19
|
+
@Get({ path: "/all" })
|
|
20
|
+
async list(): Promise<void> {
|
|
10
21
|
try {
|
|
11
|
-
const users = (await userRepository.findAll()).map((user: User) => {
|
|
12
|
-
|
|
13
|
-
return
|
|
22
|
+
const users = (await this.userRepository.findAll()).map((user: User) => {
|
|
23
|
+
const { password: _password, ...userWithoutPassword } = user
|
|
24
|
+
return userWithoutPassword
|
|
14
25
|
})
|
|
15
|
-
res.status(200).json({ message: "Users fetched successfully", users })
|
|
26
|
+
this.res.status(200).json({ message: "Users fetched successfully", users })
|
|
16
27
|
} catch (error) {
|
|
17
|
-
next(error)
|
|
28
|
+
this.next(error)
|
|
18
29
|
}
|
|
19
30
|
}
|
|
20
31
|
|
|
21
|
-
|
|
32
|
+
@Get({ path: "/:user", resolve: { user: User } })
|
|
33
|
+
async read(user: User) {
|
|
22
34
|
try {
|
|
23
|
-
const {
|
|
24
|
-
|
|
25
|
-
delete user?.password
|
|
26
|
-
res.status(200).json({ message: "User fetched successfully", user })
|
|
35
|
+
const { password: _password, ...userWithoutPassword } = user
|
|
36
|
+
this.res.status(200).json({ message: "User fetched successfully", user: userWithoutPassword })
|
|
27
37
|
} catch (error) {
|
|
28
|
-
next(error)
|
|
38
|
+
this.next(error)
|
|
29
39
|
}
|
|
30
40
|
}
|
|
31
41
|
|
|
32
|
-
|
|
42
|
+
@Post({ path: "/" })
|
|
43
|
+
async create() {
|
|
33
44
|
try {
|
|
34
|
-
const { data }: { data: User } = req.body
|
|
45
|
+
const { data }: { data: User } = this.req.body
|
|
35
46
|
|
|
36
47
|
if (!data.email || !data.password || !data.firstname || !data.lastname || !data.username) {
|
|
37
48
|
new ValidationException("All fields are required.")
|
|
@@ -51,7 +62,7 @@ export class UserController {
|
|
|
51
62
|
)
|
|
52
63
|
}
|
|
53
64
|
|
|
54
|
-
const isEmailUsed = await userRepository.findOneBy({ email: data.email })
|
|
65
|
+
const isEmailUsed = await this.userRepository.findOneBy({ email: data.email })
|
|
55
66
|
|
|
56
67
|
if (isEmailUsed) {
|
|
57
68
|
throw new Error("Email already in use")
|
|
@@ -62,7 +73,7 @@ export class UserController {
|
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
const user = new User()
|
|
65
|
-
const hashedPassword = await bcrypt.hash(data.password, 10)
|
|
76
|
+
const hashedPassword = await this.bcrypt.hash(data.password, 10)
|
|
66
77
|
|
|
67
78
|
user.username = data.username
|
|
68
79
|
user.firstname = data.firstname
|
|
@@ -71,40 +82,48 @@ export class UserController {
|
|
|
71
82
|
user.password = hashedPassword
|
|
72
83
|
user.role = "ROLE_USER"
|
|
73
84
|
|
|
74
|
-
await userRepository.save(data)
|
|
75
|
-
res.status(201).json({ message: "User created successfully" })
|
|
85
|
+
await this.userRepository.save(data)
|
|
86
|
+
this.res.status(201).json({ message: "User created successfully" })
|
|
76
87
|
} catch (error) {
|
|
77
|
-
next(error)
|
|
88
|
+
this.next(error)
|
|
78
89
|
}
|
|
79
90
|
}
|
|
80
91
|
|
|
81
|
-
|
|
92
|
+
@Patch({ path: "/:user", resolve: { user: User } })
|
|
93
|
+
async update(user: User) {
|
|
82
94
|
try {
|
|
83
|
-
const { data }: { data: User } = req.body
|
|
84
|
-
|
|
85
|
-
if (!user)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
const { data }: { data: User } = this.req.body
|
|
96
|
+
if (!user) return this.res.status(404).json({ message: "User not found" })
|
|
97
|
+
if (!AccessControl.isOwner(this.req.user, user.id) && !AccessControl.hasRoleHigherThan(this.req.user, user.role))
|
|
98
|
+
throw new UnauthorizedException()
|
|
99
|
+
|
|
100
|
+
// Remove sensitive fields from data
|
|
101
|
+
const { password: _password, email: _email, role, ...updateData } = data
|
|
102
|
+
|
|
103
|
+
// Keep role if user doesn't have higher privileges
|
|
104
|
+
const finalData = AccessControl.hasRoleHigherThan(this.req.user, user.role) ? updateData : { ...updateData, role }
|
|
105
|
+
|
|
106
|
+
finalData.updated_at = new Date()
|
|
107
|
+
await this.userRepository.save(finalData)
|
|
108
|
+
this.res.status(200).json({ message: "Users updated successfully" })
|
|
92
109
|
} catch (error) {
|
|
93
|
-
next(error)
|
|
110
|
+
this.next(error)
|
|
94
111
|
}
|
|
95
112
|
}
|
|
96
113
|
|
|
97
|
-
|
|
114
|
+
@Delete({ path: "/:id" })
|
|
115
|
+
async delete() {
|
|
98
116
|
try {
|
|
99
|
-
const { id } = req.params
|
|
100
|
-
const user = await userRepository.find(id)
|
|
101
|
-
if (!user) res.status(404).json({ message: "User not found" })
|
|
102
|
-
if (!AccessControl.isOwner(req.user, user.id) && !AccessControl.hasRoleHigherThan(req.user, user.role))
|
|
103
|
-
|
|
104
|
-
if (user?.id
|
|
105
|
-
|
|
117
|
+
const { id } = this.req.params
|
|
118
|
+
const user = await this.userRepository.find(id)
|
|
119
|
+
if (!user) return this.res.status(404).json({ message: "User not found" })
|
|
120
|
+
if (!AccessControl.isOwner(this.req.user, user.id) && !AccessControl.hasRoleHigherThan(this.req.user, user.role))
|
|
121
|
+
throw new UnauthorizedException()
|
|
122
|
+
if (!user?.id) this.res.status(400).json({ message: "Invalid user id" })
|
|
123
|
+
if (user?.id && id) await this.userRepository.delete(id)
|
|
124
|
+
this.res.status(200).json({ message: "User deleted successfully" })
|
|
106
125
|
} catch (error) {
|
|
107
|
-
next(error)
|
|
126
|
+
this.next(error)
|
|
108
127
|
}
|
|
109
128
|
}
|
|
110
129
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Fixture } from "@lyra-js/core"
|
|
2
2
|
|
|
3
3
|
import { User } from "@entity/User"
|
|
4
|
-
import { userRepository } from "@repository/UserRepository"
|
|
5
4
|
|
|
6
|
-
export class
|
|
5
|
+
export class UserFixtures extends Fixture {
|
|
7
6
|
private users = [
|
|
8
7
|
{
|
|
9
8
|
username: "Mithrandil",
|
|
@@ -34,7 +33,7 @@ export class AppFixtures {
|
|
|
34
33
|
|
|
35
34
|
private loadUsers = async () => {
|
|
36
35
|
for (const u of this.users) {
|
|
37
|
-
const hashedPassword = await bcrypt.hash(u.password, 10)
|
|
36
|
+
const hashedPassword = await this.bcrypt.hash(u.password, 10)
|
|
38
37
|
const user = new User()
|
|
39
38
|
user.username = u.username
|
|
40
39
|
user.firstname = u.firstname
|
|
@@ -43,7 +42,7 @@ export class AppFixtures {
|
|
|
43
42
|
user.password = hashedPassword
|
|
44
43
|
user.created_at = new Date()
|
|
45
44
|
user.updated_at = new Date()
|
|
46
|
-
await userRepository.save(user)
|
|
45
|
+
await this.userRepository.save(user)
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Job, JobBase, Schedule } from "@lyra-js/core"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ExampleJob - Demonstrates scheduled job functionality
|
|
5
|
+
* Jobs can have multiple scheduled methods with different recurrency patterns
|
|
6
|
+
* Access injected dependencies: this.userRepository, this.emailService, etc.
|
|
7
|
+
*/
|
|
8
|
+
@Job()
|
|
9
|
+
export class ExampleJob extends JobBase {
|
|
10
|
+
/**
|
|
11
|
+
* Example: Daily report that runs every day at 9:00 AM
|
|
12
|
+
* Uncomment to enable this scheduler
|
|
13
|
+
*/
|
|
14
|
+
@Schedule({ recurrency: "0 9 * * *", enabled: false })
|
|
15
|
+
async sendDailyReport() {
|
|
16
|
+
console.log("Sending daily report...")
|
|
17
|
+
// Your logic here
|
|
18
|
+
// Example: const users = await this.userRepository.findAll();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Example: Weekly cleanup that runs every Monday at 2:00 AM
|
|
23
|
+
* Uncomment to enable this scheduler
|
|
24
|
+
*/
|
|
25
|
+
// @Schedule({ recurrency: '0 2 * * 1', enabled: false })
|
|
26
|
+
// async weeklyCleanup() {
|
|
27
|
+
// console.log('Running weekly cleanup...');
|
|
28
|
+
// // Your logic here
|
|
29
|
+
// }
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Example: Monthly summary on the 1st of each month at midnight
|
|
33
|
+
* Uncomment to enable this scheduler
|
|
34
|
+
*/
|
|
35
|
+
// @Schedule({ recurrency: '0 0 1 * *', enabled: false })
|
|
36
|
+
// async monthlySummary() {
|
|
37
|
+
// console.log('Generating monthly summary...');
|
|
38
|
+
// // Your logic here
|
|
39
|
+
// }
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Lifecycle hook - called when the job is initialized
|
|
43
|
+
*/
|
|
44
|
+
async onInit() {
|
|
45
|
+
// console.log("ExampleJob initialized")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Lifecycle hook - called before each scheduled method execution
|
|
50
|
+
*/
|
|
51
|
+
async beforeExecute() {
|
|
52
|
+
// Add common logic before each job execution
|
|
53
|
+
// Example: Set up database transaction, log execution start, etc.
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Lifecycle hook - called after each scheduled method execution
|
|
58
|
+
*/
|
|
59
|
+
async afterExecute() {
|
|
60
|
+
// Add common logic after each job execution
|
|
61
|
+
// Example: Commit transaction, log execution end, cleanup, etc.
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NextFunction, Request, Response } from "
|
|
1
|
+
import { NextFunction, Request, Response } from "@lyra-js/core"
|
|
2
2
|
|
|
3
3
|
export const YourMiddleware = (req: Request, res: Response, next: NextFunction) => {
|
|
4
4
|
console.log("Your middleware checks or does something here...")
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { Repository } from "@lyra-js/core"
|
|
2
|
+
|
|
2
3
|
import { User } from "@entity/User"
|
|
3
4
|
|
|
4
|
-
class UserRepository extends Repository<User> {
|
|
5
|
+
export class UserRepository extends Repository<User> {
|
|
5
6
|
constructor() {
|
|
6
7
|
super(User)
|
|
7
8
|
}
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
-
export const userRepository = new UserRepository()
|
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createRouter } from "@lyra-js/core"
|
|
2
2
|
|
|
3
|
-
import { Config } from "@lyra-js/core"
|
|
4
3
|
import { routes } from "@router/routes"
|
|
5
4
|
|
|
6
|
-
const
|
|
5
|
+
export const router = createRouter()
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
router.use(routerBasePath, routes)
|
|
11
|
-
|
|
12
|
-
router.get("/", (req, res: Response) => {
|
|
13
|
-
res.send("Nothing here...")
|
|
14
|
-
})
|
|
7
|
+
router.use(routes)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ExampleStaticController as ExampleController } from "@controller/ExampleStaticController"
|
|
2
|
+
import { createRouter } from "@lyra-js/core"
|
|
3
|
+
|
|
4
|
+
export const exampleRoutes = createRouter()
|
|
5
|
+
|
|
6
|
+
exampleRoutes.get("/item", ExampleController.exampleRouteMethod)
|
|
7
|
+
exampleRoutes.get("/ssr-static", ExampleController.exampleSsrRouteMethod)
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createRouter } from "@lyra-js/core"
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { userRoutes } from "./userRoutes"
|
|
3
|
+
import { exampleRoutes } from "./exampleRoutes"
|
|
5
4
|
|
|
6
|
-
export const routes =
|
|
5
|
+
export const routes = createRouter()
|
|
7
6
|
|
|
8
|
-
routes.use("/
|
|
9
|
-
routes.use("/user", userRoutes)
|
|
7
|
+
routes.use("/example", exampleRoutes)
|
package/template/src/server.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import "reflect-metadata"
|
|
2
|
+
|
|
3
|
+
import { Config, cors, createServer, LyraConsole, SecurityConfig } from "@lyra-js/core"
|
|
4
|
+
import bcrypt from "bcrypt"
|
|
3
5
|
import * as dotenv from "dotenv"
|
|
4
|
-
import
|
|
6
|
+
import jwt from "jsonwebtoken"
|
|
5
7
|
import * as process from "node:process"
|
|
6
8
|
|
|
7
|
-
import { router } from "@app/router"
|
|
8
|
-
import { Config, SecurityConfig, LyraConsole, accessMiddleware, errorHandler, httpRequestMiddleware } from "@lyra-js/core"
|
|
9
|
-
|
|
10
9
|
dotenv.config()
|
|
11
10
|
|
|
12
11
|
process.env.TZ = process.env.TZ || "Europe/Paris"
|
|
@@ -14,32 +13,44 @@ process.env.TZ = process.env.TZ || "Europe/Paris"
|
|
|
14
13
|
const params = new Config().get("parameters")
|
|
15
14
|
const securityConfig = new SecurityConfig().getConfig()
|
|
16
15
|
|
|
17
|
-
const port = process.env.PORT
|
|
18
|
-
const app =
|
|
16
|
+
const port = process.env.PORT ? parseInt(process.env.PORT) : 3333
|
|
17
|
+
const app = createServer()
|
|
18
|
+
|
|
19
|
+
// Server settings
|
|
20
|
+
app.setSetting("trust proxy", false)
|
|
21
|
+
app.setSetting("request max size", securityConfig.limits.request_max_size || "10mb")
|
|
22
|
+
app.setSetting("ssr", {
|
|
23
|
+
engine: "jsx", // Default template engine (support tsx and jsx files)
|
|
24
|
+
templates: "./src/templates", // Path to templates directory
|
|
25
|
+
options: {} // Engine-specific options (optional)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
// Register third-party libraries for dependency injection
|
|
29
|
+
app.register(bcrypt, "bcrypt")
|
|
30
|
+
app.register(jwt, "jwt")
|
|
19
31
|
|
|
20
|
-
|
|
21
|
-
app.use(cookieParser())
|
|
22
|
-
app.use(express.json({ limit: securityConfig.limits.request_max_size || "10mb" }))
|
|
23
|
-
app.use(express.urlencoded({ limit: securityConfig.limits.request_max_size || "10mb", extended: true }))
|
|
32
|
+
// CORS middleware
|
|
24
33
|
app.use(
|
|
25
34
|
cors({
|
|
26
35
|
origin: `${process.env.CLIENT_APP_URL}`,
|
|
27
36
|
credentials: true
|
|
28
37
|
})
|
|
29
38
|
)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
|
|
40
|
+
// Enable scheduler (optional) - uncomment to activate scheduled jobs
|
|
41
|
+
// Jobs are auto-discovered from src/jobs directory
|
|
42
|
+
// Only schedules with enabled: true will run
|
|
43
|
+
// app.enableScheduler({ timezone: "UTC" })
|
|
44
|
+
|
|
45
|
+
app.serveStatic("/assets", {
|
|
46
|
+
root: "public/assets"
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
// Controllers are auto-discovered and registered from src/controller directory
|
|
50
|
+
// Repositories and Services are auto-injected via DIContainer
|
|
51
|
+
app.listen(port, () => {
|
|
52
|
+
LyraConsole.info(
|
|
53
|
+
`${params.api_name} v${params.api_version}`,
|
|
54
|
+
`Server running at ${params.api_host}:${params.api_port}`
|
|
55
|
+
)
|
|
45
56
|
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Base } from "@app/templates/layout/Base"
|
|
2
|
+
|
|
3
|
+
export default function ExampleRender({
|
|
4
|
+
title,
|
|
5
|
+
content,
|
|
6
|
+
documentationUrl
|
|
7
|
+
}: {
|
|
8
|
+
title: string
|
|
9
|
+
content: string
|
|
10
|
+
documentationUrl: string
|
|
11
|
+
}) {
|
|
12
|
+
return (
|
|
13
|
+
<Base>
|
|
14
|
+
<section>
|
|
15
|
+
<h1>{title}</h1>
|
|
16
|
+
<p>{content}</p>
|
|
17
|
+
</section>
|
|
18
|
+
</Base>
|
|
19
|
+
)
|
|
20
|
+
}
|