@cristianrg/fastpress 1.0.1

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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +180 -0
  3. package/dist/common/decorators/Body.js +20 -0
  4. package/dist/common/decorators/Param.js +23 -0
  5. package/dist/common/decorators/Query.js +20 -0
  6. package/dist/common/decorators/Req.js +32 -0
  7. package/dist/common/decorators/User.js +17 -0
  8. package/dist/common/loggers/Winston.js +37 -0
  9. package/dist/common/pipes/ParseIntPipe.js +11 -0
  10. package/dist/common/pipes/Pipe.js +2 -0
  11. package/dist/common/pipes/ZodValidationPipe.js +26 -0
  12. package/dist/common/prisma/index.js +26 -0
  13. package/dist/common/redis/index.js +40 -0
  14. package/dist/common/util/index.js +13 -0
  15. package/dist/conf/config.js +4 -0
  16. package/dist/conf/index.js +60 -0
  17. package/dist/core/decorators/Controller.js +140 -0
  18. package/dist/core/decorators/Methods.js +54 -0
  19. package/dist/core/decorators/UseGuards.js +11 -0
  20. package/dist/core/decorators/UseHooks.js +11 -0
  21. package/dist/core/decorators/UseMiddleware.js +11 -0
  22. package/dist/core/discovery/index.js +30 -0
  23. package/dist/core/discovery/utility.js +5 -0
  24. package/dist/core/loader/index.js +23 -0
  25. package/dist/core/server.js +43 -0
  26. package/dist/index.js +40 -0
  27. package/dist/modules/auth/auth.controller.js +140 -0
  28. package/dist/modules/auth/auth.service.js +84 -0
  29. package/dist/modules/auth/auth.zod.schemas.js +11 -0
  30. package/dist/shared/middlewares/Auth.js +48 -0
  31. package/dist/shared/middlewares/Sanitizer.js +45 -0
  32. package/dist/shared/models/Context.js +22 -0
  33. package/dist/shared/models/Guard.js +5 -0
  34. package/dist/shared/models/Hook.js +7 -0
  35. package/dist/shared/models/Logger.js +6 -0
  36. package/dist/shared/models/Middleware.js +5 -0
  37. package/dist/shared/models/ServerResponse.js +14 -0
  38. package/dist/shared/repository/BaseController.js +107 -0
  39. package/dist/shared/repository/Logger.js +65 -0
  40. package/dist/shared/repository/Service.js +67 -0
  41. package/package.json +48 -0
  42. package/prisma/schema.prisma +41 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 fastpress
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # Fastpress
2
+
3
+ Fastpress is an open-source project designed to empower developers to build backend applications easily. It features a clean, modern syntax inspired by major frameworks like SpringBoot, bringing that familiar structure to Node.js with TypeScript.
4
+
5
+ ## Features
6
+
7
+ - **Integrated Daemon**: Includes a development mode that monitors .ts files. The server restarts automatically upon changes, eliminating the need for manual restarts.
8
+
9
+ - **Automatic Controller Discovery**: No more manual registration. Your controllers (entry points) are automatically detected and registered by the system.
10
+
11
+ - **Decorator-based Architecture**: Use decorators to register new controllers, define HTTP methods, apply middlewares, pipes, and more.
12
+
13
+ - **Prisma Integration**: Powered by Prisma ORM for maximum flexibility and type safety. It is currently the primary database manager for the framework.
14
+
15
+ - **Base Controllers**: Jumpstart your CRUD operations by extending a **Base** class. This provides out-of-the-box functionality to get all records, find by ID, create, update, and delete without writing boilerplate code.
16
+
17
+ - **Hooks**: Events that are executed before and after a specific method is called. They allow data transformations, integration of elements into the request, and more.
18
+
19
+ - **Centralized Configuration**: Allows you to change the behavior of certain elements in the application, such as the Prisma adapter or the logging system, through a centralized configuration file.
20
+
21
+ - **Authentication**: Includes an authentication controller and middleware by default, using JWT for a token-based authentication method with refresh tokens and access tokens.
22
+
23
+
24
+ ## Usage Example
25
+
26
+ Fastpress uses decorators and Zod schemas to keep your code clean and validated:
27
+
28
+ ```typescript
29
+ const CreateUserSchema = z.object({
30
+ name: z.string().min(3).max(50),
31
+ email: z.string().email(),
32
+ age: z.number().int().min(18).optional()
33
+ });
34
+
35
+ @UseMiddleware(Auth, Sanitizer)
36
+ @Controller("/users")
37
+ class UserController {
38
+
39
+ @Get("/")
40
+ getAll(
41
+ @Query("page", ParseIntPipe) page: number = 1,
42
+ @Query("limit", ParseIntPipe) limit: number = 10
43
+ ) {
44
+ return new ServerResponse(200, "Users list", {
45
+ users: [],
46
+ pagination: { page, limit, total: 0 }
47
+ });
48
+ }
49
+
50
+ @Post("/")
51
+ create(
52
+ @Body(undefined, new ZodValidationPipe(CreateUserSchema)) data: z.infer<typeof CreateUserSchema>,
53
+ @User() user: UserModel
54
+ ) {
55
+ return new ServerResponse(201, "User created", { data, createdBy: user });
56
+ }
57
+ }
58
+ ```
59
+
60
+ ## Installation and Setup
61
+ Follow these steps to get your environment ready:
62
+
63
+ ### 1) Clone the Repository
64
+ Run the following command in your workspace to get the source code:
65
+
66
+ ```bash
67
+ git clone https://github.com/CristianRG/fastpress.git
68
+ ```
69
+
70
+ ### 2) Install Dependencies
71
+ Install the required packages using pnpm or npm:
72
+
73
+ ```bash
74
+ pnpm install
75
+ # or
76
+ npm install
77
+ ```
78
+
79
+ ### 3) Prisma Configuration
80
+ To start using the Prisma ORM, you need to make some configurations:
81
+
82
+ Configure Prisma. You can customize the `prisma.config.ts` file and an `.env` file with your database connection URL:
83
+
84
+ ```ts
85
+ // This is the default file provided by prisma
86
+ export default defineConfig({
87
+ schema: "prisma/schema.prisma",
88
+ migrations: {
89
+ path: "prisma/migrations",
90
+ },
91
+ datasource: {
92
+ url: process.env["DATABASE_URL"],
93
+ },
94
+ });
95
+ ```
96
+
97
+ Create your .env file. You must create a .env file with the following variable:
98
+
99
+ ```bash
100
+ DATABASE_URL=<your_database_url>
101
+ ```
102
+
103
+ **Note:** Before proceeding, define your database provider in `prisma/schema.prisma` (this file is included in the repository because it contains authentication models required by the system) under the datasource block:
104
+
105
+ ```javascript
106
+ datasource db {
107
+ provider = "sqlite" // Define your provider here
108
+ }
109
+ ```
110
+
111
+ Install the specific adapter for your chosen provider. For more details, refer to the official Prisma [documentation](https://www.prisma.io/docs/orm/overview/databases).
112
+
113
+ Configure the adapter in the [Fastpress Config File](fastpress.config.ts). By default, it will be using `PrismaBetterSqlite3`, but once you've installed your provider, you can change this instance to yours and uninstall the default provider.
114
+
115
+ ```typescript
116
+ server: {
117
+ // Some other configurations...
118
+ prismaAdapter: new PrismaBetterSqlite3({ url: process.env.DATABASE_URL || 'file:./dev.db' }),
119
+ },
120
+ ```
121
+
122
+ Once configured, define your models in `prisma/schema.prisma` and generate the types:
123
+
124
+ This creates a generated folder containing all the types Prisma needs to function and syncs your database with the models schema:
125
+
126
+ ```bash
127
+ pnpx prisma db push
128
+ ```
129
+
130
+ If the folder was not generated, you can run the following command:
131
+
132
+ ```bash
133
+ pnpx prisma generate
134
+ ```
135
+
136
+ ### 4) Run the Project
137
+ With everything configured, you are ready to start the server.
138
+
139
+ **Development mode:**
140
+
141
+ ```bash
142
+ pnpm run dev
143
+ ```
144
+
145
+ **Production mode:**
146
+
147
+ ```bash
148
+ pnpm run production
149
+ ```
150
+
151
+ ## Roadmap
152
+ Fastpress is in its early stages. Future milestones include:
153
+
154
+ - **Automated Prisma Setup**: A CLI-guided setup to install drivers automatically based on the selected database provider.
155
+
156
+ - **CLI Scaffolding**: Commands like `npx fastpress generate` to automatically create controllers in the modules folder based on your database models (including flags to overwrite or skip files).
157
+
158
+ - **Official CLI**: An `npx create fastpress` command to bootstrap new projects instantly.
159
+
160
+ - **Dependency Abstraction**: Decouple the core to allow alternatives to Prisma or custom logging libraries.
161
+
162
+ - **Testing Suite**: Built-in utilities to ensure application behavior remains consistent.
163
+
164
+ ## Motivation
165
+
166
+ Initially, it was just a simple server using Express syntax, as it was intended to be an embedded server within a desktop application using Electron. However, with the constant implementation of new modules, the application grew to a point where it seemed unsustainable.
167
+
168
+ Then Fastpress was born. Using modern syntax similar to frameworks like SpringBoot, it became possible to organize each new module in a simpler way, as they don't need to be manually imported into a file where the main instance is located along with other modules. Additionally, it features clean and easy-to-understand syntax.
169
+
170
+ Although it's a small project that I maintain alone, I hope it will be useful to anyone interested, and that both you and I can learn a lot by developing this project. Even better, I would love to see what new features you can implement.
171
+
172
+ ## Useful Resources
173
+ - [Prisma Documentation](https://www.prisma.io/docs)
174
+ - [Database Drivers Guide](https://www.prisma.io/docs/orm/overview/databases)
175
+
176
+ ## License
177
+ >This project is licensed under the MIT License.
178
+
179
+ ## Author
180
+ Created with 💙 by CristianRG
@@ -0,0 +1,20 @@
1
+ import "reflect-metadata";
2
+ import { PARAM_METADATA_KEY } from "./Param.js";
3
+ /**
4
+ * Body decorator to extract data from the request body and inject it into the route handler's parameters. You can also apply pipes to transform or validate the data before it is passed to the route handler.
5
+ * @param propertyKey propertyKey is optional. If provided, it will extract the specific property from the request body (e.g., if you want to extract req.body.username, you would use @Body("username")). If not provided, it will inject the entire request body object into the parameter.
6
+ * @param pipes An array of Pipe instances or Pipe classes to be applied to the extracted data. Pipes are functions that can transform or validate the data before it is passed to the route handler. The pipes will be executed in the order they are provided in the array.
7
+ * @returns
8
+ */
9
+ export function Body(propertyKey, ...pipes) {
10
+ return (target, methodName, parameterIndex) => {
11
+ const existingParams = Reflect.getMetadata(PARAM_METADATA_KEY, target, methodName) || [];
12
+ existingParams.push({
13
+ index: parameterIndex,
14
+ type: "body",
15
+ propertyKey,
16
+ pipes
17
+ });
18
+ Reflect.defineMetadata(PARAM_METADATA_KEY, existingParams, target, methodName);
19
+ };
20
+ }
@@ -0,0 +1,23 @@
1
+ import "reflect-metadata";
2
+ const PARAM_METADATA_KEY = "custom:params";
3
+ /**
4
+ * Param decorator to extract data from the route parameters and inject it into the route handler's parameters. You can also apply pipes to transform or validate the data before it is passed to the route handler.
5
+ * @param propertyKey propertyKey is optional. If provided, it will extract the specific property from the route parameters (e.g., if you want to extract req.params.id, you would use @Param("id")). If not provided, it will inject the entire route parameters object into the parameter.
6
+ * @param isOptional is optional. If set to true, it indicates that the parameter is optional. If the parameter is not present in the route parameters, it will be set to undefined instead of throwing an error.
7
+ * @param pipes An array of Pipe instances or Pipe classes to be applied to the extracted data. Pipes are functions that can transform or validate the data before it is passed to the route handler. The pipes will be executed in the order they are provided in the array.
8
+ * @returns
9
+ */
10
+ export function Param(propertyKey, isOptional = false, ...pipes) {
11
+ return (target, methodName, parameterIndex) => {
12
+ const existingParams = Reflect.getMetadata(PARAM_METADATA_KEY, target, methodName) || [];
13
+ existingParams.push({
14
+ index: parameterIndex,
15
+ type: "param",
16
+ propertyKey,
17
+ isOptional,
18
+ pipes
19
+ });
20
+ Reflect.defineMetadata(PARAM_METADATA_KEY, existingParams, target, methodName);
21
+ };
22
+ }
23
+ export { PARAM_METADATA_KEY };
@@ -0,0 +1,20 @@
1
+ import "reflect-metadata";
2
+ import { PARAM_METADATA_KEY } from "./Param.js";
3
+ /**
4
+ * Query decorator to extract data from the query parameters and inject it into the route handler's parameters. You can also apply pipes to transform or validate the data before it is passed to the route handler.
5
+ * @param propertyKey propertyKey is optional. If provided, it will extract the specific property from the query parameters (e.g., if you want to extract req.query.page, you would use @Query("page")). If not provided, it will inject the entire query parameters object into the parameter.
6
+ * @param pipes An array of Pipe instances or Pipe classes to be applied to the extracted data. Pipes are functions that can transform or validate the data before it is passed to the route handler. The pipes will be executed in the order they are provided in the array.
7
+ * @returns
8
+ */
9
+ export function Query(propertyKey, ...pipes) {
10
+ return (target, methodName, parameterIndex) => {
11
+ const existingParams = Reflect.getMetadata(PARAM_METADATA_KEY, target, methodName) || [];
12
+ existingParams.push({
13
+ index: parameterIndex,
14
+ type: "query",
15
+ propertyKey,
16
+ pipes
17
+ });
18
+ Reflect.defineMetadata(PARAM_METADATA_KEY, existingParams, target, methodName);
19
+ };
20
+ }
@@ -0,0 +1,32 @@
1
+ import "reflect-metadata";
2
+ import { PARAM_METADATA_KEY } from "./Param.js";
3
+ /**
4
+ * Req decorator to inject the entire request object into the route handler's parameters. This is useful when you need access to the full request object, including headers, body, query parameters, etc.
5
+ * @returns
6
+ */
7
+ export function Req() {
8
+ return (target, methodName, parameterIndex) => {
9
+ const existingParams = Reflect.getMetadata(PARAM_METADATA_KEY, target, methodName) || [];
10
+ existingParams.push({
11
+ index: parameterIndex,
12
+ type: "request",
13
+ pipes: []
14
+ });
15
+ Reflect.defineMetadata(PARAM_METADATA_KEY, existingParams, target, methodName);
16
+ };
17
+ }
18
+ /**
19
+ * Res decorator to inject the entire response object into the route handler's parameters. This is useful when you need access to the full response object to set headers, status codes, send responses, etc.
20
+ * @returns
21
+ */
22
+ export function Res() {
23
+ return (target, methodName, parameterIndex) => {
24
+ const existingParams = Reflect.getMetadata(PARAM_METADATA_KEY, target, methodName) || [];
25
+ existingParams.push({
26
+ index: parameterIndex,
27
+ type: "response",
28
+ pipes: []
29
+ });
30
+ Reflect.defineMetadata(PARAM_METADATA_KEY, existingParams, target, methodName);
31
+ };
32
+ }
@@ -0,0 +1,17 @@
1
+ import "reflect-metadata";
2
+ import { PARAM_METADATA_KEY } from "./Param.js";
3
+ /**
4
+ * User decorator to inject the authenticated user object into the route handler's parameters. This is useful when you have authentication middleware that attaches the user object to the request and you want to access it directly in your route handlers.
5
+ * @returns
6
+ */
7
+ export function User() {
8
+ return (target, methodName, parameterIndex) => {
9
+ const existingParams = Reflect.getMetadata(PARAM_METADATA_KEY, target, methodName) || [];
10
+ existingParams.push({
11
+ index: parameterIndex,
12
+ type: "user",
13
+ pipes: []
14
+ });
15
+ Reflect.defineMetadata(PARAM_METADATA_KEY, existingParams, target, methodName);
16
+ };
17
+ }
@@ -0,0 +1,37 @@
1
+ import conf from "../../conf/index.js";
2
+ import { Logger } from "../../shared/models/Logger.js";
3
+ import winston from "winston";
4
+ const defaultFormat = winston.format.combine(winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), winston.format.printf(({ timestamp, level, message }) => {
5
+ return `${timestamp} - [${level}]: ${message}`;
6
+ }));
7
+ const defaultTransports = [
8
+ conf.ENV === 'production' ? new winston.transports.File({ filename: "logs/combined.log" }) : new winston.transports.Console(),
9
+ new winston.transports.File({ filename: "logs/error.log", level: "error" }),
10
+ ];
11
+ class WinstonLogger extends Logger {
12
+ winstonLogger;
13
+ constructor(config) {
14
+ super();
15
+ this.winstonLogger = winston.createLogger({
16
+ level: conf.ENV === 'production' ? 'info' : 'debug',
17
+ format: config?.format ?? defaultFormat,
18
+ transports: config?.transports ?? defaultTransports,
19
+ });
20
+ }
21
+ log(message) {
22
+ this.winstonLogger.info(message);
23
+ }
24
+ info(message) {
25
+ this.winstonLogger.info(message);
26
+ }
27
+ error(message) {
28
+ this.winstonLogger.error(message);
29
+ }
30
+ warn(message) {
31
+ this.winstonLogger.warn(message);
32
+ }
33
+ debug(message) {
34
+ this.winstonLogger.debug(message);
35
+ }
36
+ }
37
+ export { WinstonLogger };
@@ -0,0 +1,11 @@
1
+ import { Pipe } from "./Pipe.js";
2
+ import { ServerResponse } from "../../shared/models/ServerResponse.js";
3
+ export class ParseIntPipe extends Pipe {
4
+ transform(value, ctx) {
5
+ const parsed = parseInt(value, 10);
6
+ if (isNaN(parsed)) {
7
+ throw new ServerResponse(400, `Validation failed: "${value}" is not an integer`);
8
+ }
9
+ return parsed;
10
+ }
11
+ }
@@ -0,0 +1,2 @@
1
+ export class Pipe {
2
+ }
@@ -0,0 +1,26 @@
1
+ import { Pipe } from "./Pipe.js";
2
+ import { ServerResponse } from "../../shared/models/ServerResponse.js";
3
+ import { z } from "zod";
4
+ export class ZodValidationPipe extends Pipe {
5
+ schema;
6
+ constructor(schema) {
7
+ super();
8
+ this.schema = schema;
9
+ }
10
+ transform(value, ctx) {
11
+ try {
12
+ return this.schema.parse(value);
13
+ }
14
+ catch (error) {
15
+ if (error instanceof z.ZodError) {
16
+ throw new ServerResponse(400, "Validation failed", {
17
+ errors: error.issues.map(err => ({
18
+ path: err.path.join('.'),
19
+ message: err.message
20
+ }))
21
+ });
22
+ }
23
+ throw error;
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ import conf from "../../conf/index.js";
2
+ import { PrismaClient } from "@prisma/client";
3
+ let prisma = null;
4
+ export function initializePrisma() {
5
+ if (!prisma) {
6
+ if (!conf.PRISMA_ADAPTER) {
7
+ throw new Error('Prisma adapter not configured. Make sure to call createServer() before using Prisma.');
8
+ }
9
+ prisma = new PrismaClient({ adapter: conf.PRISMA_ADAPTER });
10
+ }
11
+ return prisma;
12
+ }
13
+ export function getPrisma() {
14
+ if (!prisma) {
15
+ return initializePrisma();
16
+ }
17
+ return prisma;
18
+ }
19
+ const prismaProxy = new Proxy({}, {
20
+ get(target, prop) {
21
+ const instance = getPrisma();
22
+ const value = instance[prop];
23
+ return typeof value === 'function' ? value.bind(instance) : value;
24
+ }
25
+ });
26
+ export default prismaProxy;
@@ -0,0 +1,40 @@
1
+ import { createClient } from "redis";
2
+ import logger from "../../shared/repository/Logger.js";
3
+ let client = null;
4
+ let isConnected = false;
5
+ async function getRedisClient() {
6
+ if (isConnected && client) {
7
+ return client;
8
+ }
9
+ if (!client) {
10
+ try {
11
+ client = createClient({
12
+ socket: {
13
+ reconnectStrategy: false
14
+ }
15
+ });
16
+ client.on('error', (err) => {
17
+ logger.error(`Redis Client Error: ${err}`);
18
+ isConnected = false;
19
+ });
20
+ await client.connect();
21
+ isConnected = true;
22
+ return client;
23
+ }
24
+ catch (error) {
25
+ client = null;
26
+ isConnected = false;
27
+ return null;
28
+ }
29
+ }
30
+ return client;
31
+ }
32
+ async function disconnectRedis() {
33
+ if (client && isConnected) {
34
+ await client.quit();
35
+ client = null;
36
+ isConnected = false;
37
+ }
38
+ }
39
+ export { getRedisClient, disconnectRedis };
40
+ export default getRedisClient;
@@ -0,0 +1,13 @@
1
+ import bycrypt from 'bcrypt';
2
+ import z from 'zod';
3
+ const zodText = z.string().min(1, "Text cannot be empty").max(72, "Text exceeds maximum length of 72 bytes for bcrypt");
4
+ const encrypt = (text, options) => {
5
+ const parsedText = zodText.parse(text); // This will throw if the text is invalid, ensuring we only hash valid strings
6
+ const saltRounds = options?.saltRounds || 10;
7
+ const salt = bycrypt.genSaltSync(saltRounds);
8
+ return bycrypt.hashSync(parsedText, salt);
9
+ };
10
+ const compare = (text, hash) => {
11
+ return bycrypt.compareSync(text, hash);
12
+ };
13
+ export { encrypt, compare };
@@ -0,0 +1,4 @@
1
+ function defineFastPressConfig(config) {
2
+ return config;
3
+ }
4
+ export { defineFastPressConfig };
@@ -0,0 +1,60 @@
1
+ import { resolve } from 'path';
2
+ import { existsSync } from 'fs';
3
+ import { pathToFileURL } from 'url';
4
+ import createJiti from 'jiti';
5
+ const defaultConfig = {
6
+ PORT: parseInt(process.env.PORT || '3000'),
7
+ ALLOWED_ORIGINS: process.env.ALLOWED_ORIGINS ? process.env.ALLOWED_ORIGINS.split(',') : ['http://localhost:3000'],
8
+ JWT_SECRET: process.env.JWT_SECRET || Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15),
9
+ ENV: process.env.NODE_ENV || 'development',
10
+ ALGORITHM: 'HS256',
11
+ JWT_EXPIRATION: 15 * 60 * 1000, // 15 minutes
12
+ RJWT_EXPIRATION: 7 * 24 * 60 * 60 * 1000, // 7 days
13
+ LOGGER_INSTANCE: null,
14
+ PRISMA_ADAPTER: null,
15
+ };
16
+ const conf = { ...defaultConfig };
17
+ let isConfigLoaded = false;
18
+ function printConfigDetails() {
19
+ console.log(`\n================== FastPress Configuration =================`);
20
+ console.log(`PORT: ${conf.PORT}`);
21
+ console.log(`ENV: ${conf.ENV}`);
22
+ console.log(`============================================================`);
23
+ }
24
+ async function loadUserConfig() {
25
+ const configPath = resolve(process.cwd(), 'fastpress.config.ts');
26
+ const jiti = createJiti(import.meta.url);
27
+ if (existsSync(configPath)) {
28
+ try {
29
+ const configUrl = pathToFileURL(configPath).href;
30
+ const userConfigModule = await jiti.import(configUrl, { default: true });
31
+ return userConfigModule;
32
+ }
33
+ catch (error) {
34
+ console.warn('We cant load fastpress.config.ts:', error);
35
+ return undefined;
36
+ }
37
+ }
38
+ return undefined;
39
+ }
40
+ export async function initializeConfig() {
41
+ if (isConfigLoaded) {
42
+ return conf;
43
+ }
44
+ const userConfig = await loadUserConfig();
45
+ if (userConfig) {
46
+ conf.PORT = userConfig.server?.port ?? defaultConfig.PORT;
47
+ conf.ALLOWED_ORIGINS = userConfig.server?.allowedOrigins ?? defaultConfig.ALLOWED_ORIGINS;
48
+ conf.JWT_SECRET = userConfig.jwt?.secret ?? defaultConfig.JWT_SECRET;
49
+ conf.ENV = userConfig.server?.env ?? defaultConfig.ENV;
50
+ conf.ALGORITHM = userConfig.jwt?.algorithm ?? defaultConfig.ALGORITHM;
51
+ conf.JWT_EXPIRATION = userConfig.jwt?.jwt_exp ?? defaultConfig.JWT_EXPIRATION;
52
+ conf.RJWT_EXPIRATION = userConfig.jwt?.refresh_exp ?? defaultConfig.RJWT_EXPIRATION;
53
+ conf.LOGGER_INSTANCE = userConfig.server?.logger ?? defaultConfig.LOGGER_INSTANCE;
54
+ conf.PRISMA_ADAPTER = userConfig.server?.prismaAdapter ?? defaultConfig.PRISMA_ADAPTER;
55
+ printConfigDetails();
56
+ }
57
+ isConfigLoaded = true;
58
+ return conf;
59
+ }
60
+ export default conf;