@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.

Potentially problematic release.


This version of @cristianrg/fastpress might be problematic. Click here for more details.

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
@@ -0,0 +1,45 @@
1
+ import { Middleware } from "../models/Middleware.js";
2
+ class Sanitizer extends Middleware {
3
+ handle(ctx) {
4
+ const url = ctx.req.url;
5
+ ctx.req.url = this.sanitizeUrl(url);
6
+ ctx.query = this.santitizeQuery(ctx.query);
7
+ ctx.next();
8
+ }
9
+ sanitizeUrl(url) {
10
+ if (!url)
11
+ return url;
12
+ let sanitizedUrl = url.replace(/\0/g, '');
13
+ sanitizedUrl = decodeURIComponent(sanitizedUrl);
14
+ while (this.detectTraversal(sanitizedUrl)) {
15
+ sanitizedUrl = sanitizedUrl.replace(/(\.\.\/|%2e%2e%2f|\.{2}\\|%2e%2e%5c)/gi, '');
16
+ }
17
+ return sanitizedUrl;
18
+ }
19
+ santitizeQuery(query) {
20
+ if (!query)
21
+ return query;
22
+ const formatedQuery = Object.entries(query).map(([key, value]) => ({ key, value }));
23
+ let sanitizedQuery = new Map();
24
+ for (const { key, value } of formatedQuery) {
25
+ const sanitizedKey = key.replace(/\0/g, '');
26
+ const sanitizedValue = typeof value === 'string' ? value.replace(/\0/g, '') : value;
27
+ sanitizedQuery.set(sanitizedKey, sanitizedValue);
28
+ const decodedKey = decodeURIComponent(sanitizedKey);
29
+ const decodedValue = typeof sanitizedValue === 'string' ? decodeURIComponent(sanitizedValue) : sanitizedValue;
30
+ sanitizedQuery.set(decodedKey, decodedValue);
31
+ if (this.detectTraversal(decodedKey)) {
32
+ sanitizedQuery.delete(decodedKey);
33
+ }
34
+ if (this.detectTraversal(decodedValue)) {
35
+ sanitizedQuery.delete(decodedKey);
36
+ }
37
+ }
38
+ return sanitizedQuery;
39
+ }
40
+ detectTraversal(url) {
41
+ const traversalPatterns = [/(\.\.\/)/, /(%2e%2e%2f)/i, /(\.\.\\)/, /(%2e%2e%5c)/i];
42
+ return traversalPatterns.some(pattern => pattern.test(url));
43
+ }
44
+ }
45
+ export { Sanitizer };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * A class representing the context of an Express request, encapsulating the request, response, and next function.
3
+ * It also provides typed access to query parameters, route parameters, and the request body.
4
+ */
5
+ export class Context {
6
+ req;
7
+ res;
8
+ next;
9
+ query;
10
+ params;
11
+ body;
12
+ user;
13
+ constructor(req, res, next) {
14
+ this.req = req;
15
+ this.res = res;
16
+ this.next = next;
17
+ this.query = req.query;
18
+ this.params = req.params;
19
+ this.body = req.body;
20
+ this.user = req.user; // Assuming authentication middleware attaches user to req
21
+ }
22
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Guard is an abstract class that defines the structure for creating guards in the application. Guards are used to protect routes by determining whether a request is allowed to proceed based on custom logic. The canActivate method must be implemented by any class that extends Guard, and it should return either a boolean or a GuardResponse object indicating whether the request is allowed or not.
3
+ */
4
+ export class Guard {
5
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Base class for hooks that can be used with the `@UseHooks` decorator. Hooks allow you to run custom logic before and after a route handler is executed.
3
+ * They can be used for tasks like logging, modifying the request/response, or implementing custom behavior that should run around the execution of a route handler.
4
+ */
5
+ class Hook {
6
+ }
7
+ export { Hook };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Logger is an abstract class that defines the structure for creating loggers in the application. It implements the ILogger interface, which specifies methods for logging messages at different levels (log, info, error, warn, debug). Any class that extends Logger must implement these methods to provide the actual logging functionality, such as writing to the console, a file, or an external logging service.
3
+ */
4
+ class Logger {
5
+ }
6
+ export { Logger };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Abstract class representing a middleware component.
3
+ */
4
+ export class Middleware {
5
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Class representing a server response.
3
+ * It includes a status code, an optional message, and optional data.
4
+ */
5
+ export class ServerResponse {
6
+ statusCode;
7
+ message;
8
+ data;
9
+ constructor(statusCode, message, data) {
10
+ this.statusCode = statusCode;
11
+ this.message = message;
12
+ this.data = data;
13
+ }
14
+ }
@@ -0,0 +1,107 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { Delete, Get, Post, Put } from "../../core/decorators/Methods.js";
14
+ import { Service } from "./Service.js";
15
+ import { Body } from "../../common/decorators/Body.js";
16
+ import { Query } from "../../common/decorators/Query.js";
17
+ import { ServerResponse } from "../../shared/models/ServerResponse.js";
18
+ import { ParseIntPipe } from "../../common/pipes/ParseIntPipe.js";
19
+ import { UseMiddleware } from "../../core/decorators/UseMiddleware.js";
20
+ import { Auth } from "../../shared/middlewares/Auth.js";
21
+ let BaseController = class BaseController {
22
+ service;
23
+ modelName;
24
+ constructor(modelName, service) {
25
+ this.modelName = modelName;
26
+ this.service = service || new Service(modelName);
27
+ }
28
+ async get(skip, take) {
29
+ const items = await this.service.findAll({
30
+ skip,
31
+ take
32
+ });
33
+ return new ServerResponse(200, `Found ${items.length} items in ${String(this.service.getModelName())} table`, items);
34
+ }
35
+ async getById(id) {
36
+ const item = await this.service.findById(id);
37
+ if (!item) {
38
+ return new ServerResponse(404, "Item not found");
39
+ }
40
+ return new ServerResponse(200, `Item retrieved successfully from ${String(this.service.getModelName())} table`, item);
41
+ }
42
+ async create(data) {
43
+ if (!data)
44
+ return new ServerResponse(400, `Item data is required to create a new ${String(this.service.getModelName())}`);
45
+ const createdItem = await this.service.create(data);
46
+ return new ServerResponse(201, `Item created successfully in ${String(this.service.getModelName())} table`, createdItem);
47
+ }
48
+ async update(id, data) {
49
+ if (!data)
50
+ return new ServerResponse(400, `Item data is required to update a ${String(this.service.getModelName())}`);
51
+ const updatedItem = await this.service.update({ id }, data);
52
+ return new ServerResponse(200, `Item updated successfully in ${String(this.service.getModelName())} table`, updatedItem);
53
+ }
54
+ async delete(id) {
55
+ const item = await this.service.findById(id);
56
+ if (!item)
57
+ return new ServerResponse(404, "Item not found");
58
+ await this.service.delete({ id });
59
+ return new ServerResponse(200, `Item deleted successfully from ${String(this.service.getModelName())} table`);
60
+ }
61
+ };
62
+ __decorate([
63
+ Get("/"),
64
+ __param(0, Query("skip", ParseIntPipe)),
65
+ __param(1, Query("take", ParseIntPipe)),
66
+ __metadata("design:type", Function),
67
+ __metadata("design:paramtypes", [Number, Number]),
68
+ __metadata("design:returntype", Promise)
69
+ ], BaseController.prototype, "get", null);
70
+ __decorate([
71
+ Get("/:id"),
72
+ __param(0, Query("id", ParseIntPipe)),
73
+ __metadata("design:type", Function),
74
+ __metadata("design:paramtypes", [Number]),
75
+ __metadata("design:returntype", Promise)
76
+ ], BaseController.prototype, "getById", null);
77
+ __decorate([
78
+ Post("/"),
79
+ __param(0, Body(undefined)),
80
+ __metadata("design:type", Function),
81
+ __metadata("design:paramtypes", [Object]),
82
+ __metadata("design:returntype", Promise)
83
+ ], BaseController.prototype, "create", null);
84
+ __decorate([
85
+ Put("/:id"),
86
+ __param(0, Query("id", ParseIntPipe)),
87
+ __param(1, Body(undefined)),
88
+ __metadata("design:type", Function),
89
+ __metadata("design:paramtypes", [Number, Object]),
90
+ __metadata("design:returntype", Promise)
91
+ ], BaseController.prototype, "update", null);
92
+ __decorate([
93
+ Delete("/:id"),
94
+ __param(0, Query("id", ParseIntPipe)),
95
+ __metadata("design:type", Function),
96
+ __metadata("design:paramtypes", [Number]),
97
+ __metadata("design:returntype", Promise)
98
+ ], BaseController.prototype, "delete", null);
99
+ BaseController = __decorate([
100
+ UseMiddleware(Auth)
101
+ /**
102
+ * BaseController is a generic controller class that provides basic CRUD operations for any Prisma model. It uses the Service class to interact with the database and defines route handlers for getting all items, getting an item by ID, creating a new item, updating an existing item, and deleting an item. The routes are decorated with the appropriate HTTP method decorators (Get, Post, Put, Delete) and use parameter decorators (Query, Body) to extract data from the request. The controller also returns standardized ServerResponse objects for consistent API responses.
103
+ */
104
+ ,
105
+ __metadata("design:paramtypes", [Object, Object])
106
+ ], BaseController);
107
+ export { BaseController };
@@ -0,0 +1,65 @@
1
+ import conf from "../../conf/index.js";
2
+ import { Logger as LoggerModel } from "../models/Logger.js";
3
+ class DefaultLogger extends LoggerModel {
4
+ log(message) {
5
+ console.log(this.formatMessage("log", message));
6
+ }
7
+ info(message) {
8
+ console.info(this.formatMessage("info", message));
9
+ }
10
+ error(message) {
11
+ console.error(this.formatMessage("error", message));
12
+ }
13
+ warn(message) {
14
+ console.warn(this.formatMessage("warn", message));
15
+ }
16
+ debug(message) {
17
+ console.debug(this.formatMessage("debug", message));
18
+ }
19
+ formatMessage(level, message) {
20
+ const timestamp = new Date().toISOString();
21
+ return `${timestamp} - [${level.toUpperCase()}]: ${message}`;
22
+ }
23
+ }
24
+ /**
25
+ * A simple Logger class that can be extended or replaced with different logging implementations.
26
+ * It uses the abstract Logger class as a base and delegates logging to an instance of ILogger, which can be customized.
27
+ * By default, it uses the DefaultLogger implementation that logs to the console with timestamps and log levels.
28
+ */
29
+ class Logger extends LoggerModel {
30
+ logger = new DefaultLogger();
31
+ static instance;
32
+ constructor() {
33
+ super();
34
+ }
35
+ static getInstance() {
36
+ if (!Logger.instance) {
37
+ Logger.instance = new Logger();
38
+ }
39
+ return Logger.instance;
40
+ }
41
+ setLogger(logger) {
42
+ this.logger = logger;
43
+ }
44
+ getLogger() {
45
+ return this.logger;
46
+ }
47
+ log(message) {
48
+ this.logger.log(message);
49
+ }
50
+ info(message) {
51
+ this.logger.info(message);
52
+ }
53
+ error(message) {
54
+ this.logger.error(message);
55
+ }
56
+ warn(message) {
57
+ this.logger.warn(message);
58
+ }
59
+ debug(message) {
60
+ this.logger.debug(message);
61
+ }
62
+ }
63
+ const logger = Logger.getInstance();
64
+ logger.setLogger(conf.LOGGER_INSTANCE ? new DefaultLogger() : conf.LOGGER_INSTANCE);
65
+ export default logger;
@@ -0,0 +1,67 @@
1
+ import conf from "../../conf/index.js";
2
+ import { PrismaClient } from "@prisma/client";
3
+ /**
4
+ * BaseService is an abstract class that provides common database operations for any Prisma model. It defines methods for finding all records, finding a single record by criteria, finding a record by ID, creating a new record, updating an existing record, deleting a record, paginating results, counting records, and checking for the existence of records. The Service class extends BaseService and provides a concrete implementation that requires specifying the model name. This structure allows for easy creation of services for different models by simply extending the Service class and providing the appropriate model name.
5
+ */
6
+ export class BaseService {
7
+ prisma;
8
+ constructor(prisma) {
9
+ this.prisma = prisma || new PrismaClient({ adapter: conf.PRISMA_ADAPTER });
10
+ }
11
+ get model() {
12
+ return this.prisma[this.modelName];
13
+ }
14
+ async findAll(options) {
15
+ return this.model.findMany({
16
+ skip: options?.skip,
17
+ take: options?.take,
18
+ orderBy: options?.orderBy,
19
+ });
20
+ }
21
+ async findOne(where) {
22
+ return this.model.findFirst({ where });
23
+ }
24
+ async findById(id) {
25
+ return this.model.findUnique({ where: { id } });
26
+ }
27
+ async create(data) {
28
+ return this.model.create({ data });
29
+ }
30
+ async update(where, data) {
31
+ return this.model.update({ where, data });
32
+ }
33
+ async delete(where) {
34
+ return this.model.delete({ where });
35
+ }
36
+ async paginate(where = {}, page = 1, pageSize = 10, orderBy) {
37
+ const skip = (page - 1) * pageSize;
38
+ const [data, total] = await Promise.all([
39
+ this.model.findMany({ where, skip, take: pageSize, orderBy }),
40
+ this.model.count({ where })
41
+ ]);
42
+ return {
43
+ data,
44
+ total,
45
+ page,
46
+ pageSize,
47
+ hasNext: skip + pageSize < total
48
+ };
49
+ }
50
+ async count(where = {}) {
51
+ return this.model.count({ where });
52
+ }
53
+ async exists(where) {
54
+ const count = await this.model.count({ where });
55
+ return count > 0;
56
+ }
57
+ }
58
+ export class Service extends BaseService {
59
+ modelName;
60
+ constructor(modelName, prisma) {
61
+ super(prisma);
62
+ this.modelName = modelName;
63
+ }
64
+ getModelName() {
65
+ return this.modelName;
66
+ }
67
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@cristianrg/fastpress",
3
+ "version": "1.0.1",
4
+ "main": "dist/index.js",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/CristianRG/fastpress.git"
8
+ },
9
+ "files": [
10
+ "dist/**/*",
11
+ "prisma/schema.prisma"
12
+ ],
13
+ "author": "Cristian Alexander Rizo Gómez",
14
+ "description": "A fast and minimalistic web framework for Node.js, inspired by Express and built with TypeScript.",
15
+ "license": "MIT",
16
+ "type": "module",
17
+ "devDependencies": {
18
+ "@types/bcrypt": "6.0.0",
19
+ "@types/cors": "2.8.19",
20
+ "@types/express": "5.0.6",
21
+ "@types/jsonwebtoken": "9.0.10",
22
+ "nodemon": "3.1.11",
23
+ "reflect-metadata": "0.2.2",
24
+ "tsc-alias": "1.8.16",
25
+ "tsx": "4.19.2",
26
+ "typescript": "5.9.3"
27
+ },
28
+ "dependencies": {
29
+ "@prisma/adapter-better-sqlite3": "^7.4.0",
30
+ "@prisma/client": "7.4.0",
31
+ "bcrypt": "6.0.0",
32
+ "cors": "2.8.6",
33
+ "dotenv": "17.2.4",
34
+ "express": "5.2.1",
35
+ "glob": "13.0.1",
36
+ "install": "0.13.0",
37
+ "jiti": "2.6.1",
38
+ "jsonwebtoken": "9.0.3",
39
+ "prisma": "7.4.0",
40
+ "redis": "5.11.0",
41
+ "winston": "3.19.0",
42
+ "zod": "4.3.6"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc && tsc-alias",
46
+ "postinstall": "prisma generate"
47
+ }
48
+ }
@@ -0,0 +1,41 @@
1
+ // This is your Prisma schema file,
2
+ // learn more about it in the docs: https://pris.ly/d/prisma-schema
3
+
4
+ // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
5
+ // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
6
+
7
+ generator client {
8
+ provider = "prisma-client-js"
9
+ }
10
+
11
+ datasource db {
12
+ provider = "sqlite" // Set your database provider (e.g., "postgresql", "mysql", "sqlite", etc.)
13
+ }
14
+
15
+
16
+ // ============================= WARNING =====================================
17
+ // This is the base schema for the authentication system.
18
+ // You can customize it as needed, but make sure to keep the relationships and
19
+ // fields that are essential for user authentication and session management.
20
+ // ============================================================================
21
+
22
+ // Model generated by system
23
+ model User {
24
+ id String @id @default(uuid())
25
+ name String
26
+ email String @unique
27
+ password String
28
+
29
+ sessions Session[]
30
+ }
31
+
32
+ // Model generated by system
33
+ model Session {
34
+ id String @id @default(uuid())
35
+ userId String
36
+ token String @unique
37
+ createdAt DateTime @default(now())
38
+ expiresAt DateTime
39
+
40
+ user User @relation(fields: [userId], references: [id])
41
+ }