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.
Files changed (29) hide show
  1. package/package.json +2 -2
  2. package/template/backups/README.md +93 -0
  3. package/template/config/security.yaml +0 -1
  4. package/template/migrations/README.md +247 -0
  5. package/template/package.json +2 -6
  6. package/template/public/assets/styles/app.css +0 -0
  7. package/template/src/controller/AuthController.ts +100 -71
  8. package/template/src/controller/ErrorController.ts +126 -0
  9. package/template/src/controller/ExampleController.ts +28 -0
  10. package/template/src/controller/ExampleStaticController.ts +40 -0
  11. package/template/src/controller/UserController.ts +63 -44
  12. package/template/src/fixtures/{AppFixtures.ts → UserFixtures.ts} +4 -5
  13. package/template/src/jobs/ExampleJob.ts +63 -0
  14. package/template/src/middleware/YourMiddleware.ts +1 -1
  15. package/template/src/repository/UserRepository.ts +2 -3
  16. package/template/src/router/index.ts +3 -10
  17. package/template/src/router/routes/exampleRoutes.ts +7 -0
  18. package/template/src/router/routes/index.ts +4 -6
  19. package/template/src/server.ts +38 -27
  20. package/template/src/services/YourService.ts +3 -1
  21. package/template/src/templates/ExampleRender.tsx +20 -0
  22. package/template/src/templates/README.md +271 -0
  23. package/template/src/templates/layout/Base.tsx +21 -0
  24. package/template/src/templates/layout/Footer.tsx +7 -0
  25. package/template/src/templates/layout/Header.tsx +19 -0
  26. package/template/tsconfig.json +8 -4
  27. package/template/migrations/migration_1752052338457.sql +0 -4
  28. package/template/src/router/routes/authRoutes.ts +0 -13
  29. 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 {AccessControl, AuthenticatedRequest, UnauthorizedException, ValidationException, Validator} from "@lyra-js/core"
2
- import bcrypt from "bcrypt"
3
- import { NextFunction, Request, Response } from "express"
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
- export class UserController {
9
- static list = async (req: AuthenticatedRequest<Request>, res: Response, next: NextFunction): Promise<void> => {
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
- delete user?.password
13
- return user
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
- static read = async (req: AuthenticatedRequest<Request>, res: Response, next: NextFunction) => {
32
+ @Get({ path: "/:user", resolve: { user: User } })
33
+ async read(user: User) {
22
34
  try {
23
- const { id } = req.params
24
- const user = await userRepository.find(id)
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
- static create = async (req: AuthenticatedRequest<Request>, res: Response, next: NextFunction) => {
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
- static update = async (req: AuthenticatedRequest<Request>, res: Response, next: NextFunction) => {
92
+ @Patch({ path: "/:user", resolve: { user: User } })
93
+ async update(user: User) {
82
94
  try {
83
- const { data }: { data: User } = req.body
84
- const user = await userRepository.find(data.id)
85
- if (!user) res.status(404).json({ message: "User not found" })
86
- delete user?.password
87
- delete user?.email
88
- user.updated_at = new Date()
89
- if (AccessControl.hasRoleHigherThan(req.user, user.role)) delete user.role
90
- if (user) await userRepository.save(data)
91
- res.status(200).json({ message: "Users updated successfully" })
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
- static delete = async (req: AuthenticatedRequest<Request>, res: Response, next: NextFunction) => {
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)) throw new UnauthorizedException()
103
- if (!user?.id) res.status(400).json({ message: "Invalid user id" })
104
- if (user?.id && id) await userRepository.delete(id)
105
- res.status(200).json({ message: "User deleted successfully" })
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 bcrypt from "bcrypt"
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 AppFixtures {
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 "express"
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 { Response, Router } from "express"
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 routerBasePath = new Config().get("router.base_path")
5
+ export const router = createRouter()
7
6
 
8
- export const router = Router()
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 { Router } from "express"
1
+ import { createRouter } from "@lyra-js/core"
2
2
 
3
- import { authRoutes } from "./authRoutes"
4
- import { userRoutes } from "./userRoutes"
3
+ import { exampleRoutes } from "./exampleRoutes"
5
4
 
6
- export const routes = Router()
5
+ export const routes = createRouter()
7
6
 
8
- routes.use("/auth", authRoutes)
9
- routes.use("/user", userRoutes)
7
+ routes.use("/example", exampleRoutes)
@@ -1,12 +1,11 @@
1
- import cookieParser from "cookie-parser"
2
- import cors from "cors"
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 express from "express"
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 = express()
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
- app.set("trust proxy", false)
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
- app.use(httpRequestMiddleware)
31
- app.use(accessMiddleware)
32
- app.use(router)
33
- app.use(errorHandler)
34
-
35
- app.listen(port, (err?: Error) => {
36
- if (err) {
37
- LyraConsole.error("Error starting server:", err.message)
38
- } else {
39
- LyraConsole.info(
40
- `${params.api_name} v${params.api_version}`,
41
- `Server running at ${params.api_host}:${params.api_port}`,
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
  })
@@ -1,4 +1,6 @@
1
- export class YourService {
1
+ import { Service } from "@lyra-js/core"
2
+
3
+ export class YourService extends Service {
2
4
  exemple() {
3
5
  console.log("Here is a method of your service...")
4
6
  }
@@ -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
+ }