@nano-app/backend 0.6.4 → 0.7.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/LICENSE +21 -0
- package/README.md +233 -0
- package/build/index.d.ts +149 -0
- package/build/index.js +544 -0
- package/build/nano-app.js +41 -0
- package/package.json +70 -26
- package/dist/app.d.ts +0 -9
- package/dist/app.js +0 -74
- package/dist/auth/ApiKeys.d.ts +0 -25
- package/dist/auth/ApiKeys.js +0 -146
- package/dist/auth/ApiKeysEndpoint.d.ts +0 -19
- package/dist/auth/ApiKeysEndpoint.js +0 -36
- package/dist/auth/ClientCredentials.d.ts +0 -15
- package/dist/auth/ClientCredentials.js +0 -132
- package/dist/auth/TokenEndpoint.d.ts +0 -5
- package/dist/auth/TokenEndpoint.js +0 -33
- package/dist/http/Endpoint.d.ts +0 -17
- package/dist/http/Endpoint.js +0 -59
- package/dist/http/Router.d.ts +0 -6
- package/dist/http/Router.js +0 -20
- package/dist/index.d.ts +0 -12
- package/dist/index.js +0 -28
- package/dist/odata/Entities.d.ts +0 -10
- package/dist/odata/Entities.js +0 -52
- package/dist/odata/QueryBuilder.d.ts +0 -10
- package/dist/odata/QueryBuilder.js +0 -60
- package/dist/prisma/Client.d.ts +0 -12
- package/dist/prisma/Client.js +0 -34
- package/dist/types/api.d.ts +0 -6
- package/dist/types/api.js +0 -2
- package/dist/types/app.d.ts +0 -29
- package/dist/types/app.js +0 -2
- package/dist/types/odata.d.ts +0 -8
- package/dist/types/odata.js +0 -2
- package/dist/utils/Formatter.d.ts +0 -6
- package/dist/utils/Formatter.js +0 -31
package/dist/http/Endpoint.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Endpoint = void 0;
|
|
4
|
-
const Client_1 = require("../prisma/Client");
|
|
5
|
-
const Formatter_1 = require("../utils/Formatter");
|
|
6
|
-
class Endpoint {
|
|
7
|
-
prismaClient = new Client_1.PrismaClient();
|
|
8
|
-
path = '';
|
|
9
|
-
methodMap;
|
|
10
|
-
constructor(path) {
|
|
11
|
-
this.path = path ?? 'default';
|
|
12
|
-
this.methodMap = {
|
|
13
|
-
GET: this.GET.bind(this),
|
|
14
|
-
POST: this.POST.bind(this),
|
|
15
|
-
PUT: this.PUT.bind(this),
|
|
16
|
-
DELETE: this.DELETE.bind(this),
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
VALIDATE(_req) {
|
|
20
|
-
return Promise.resolve();
|
|
21
|
-
}
|
|
22
|
-
BEFORE(_req) {
|
|
23
|
-
return Promise.resolve({});
|
|
24
|
-
}
|
|
25
|
-
AFTER(_req, result) {
|
|
26
|
-
return Promise.resolve(result);
|
|
27
|
-
}
|
|
28
|
-
async GET(_req, _res, _before) {
|
|
29
|
-
throw { message: `GET no implementado para ${this.path}` };
|
|
30
|
-
}
|
|
31
|
-
async POST(_req, _res, _before) {
|
|
32
|
-
throw { message: `POST no implementado para ${this.path}` };
|
|
33
|
-
}
|
|
34
|
-
async PUT(_req, _res, _before) {
|
|
35
|
-
throw { message: `PUT no implementado para ${this.path}` };
|
|
36
|
-
}
|
|
37
|
-
async DELETE(_req, _res, _before) {
|
|
38
|
-
throw { message: `DELETE no implementado para ${this.path}` };
|
|
39
|
-
}
|
|
40
|
-
execute(req, res) {
|
|
41
|
-
const handler = this.methodMap[req.method];
|
|
42
|
-
if (!handler) {
|
|
43
|
-
res.status(405).json({ success: false, message: `Método ${req.method} no soportado` });
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
this.VALIDATE(req)
|
|
47
|
-
.then(() => this.BEFORE(req))
|
|
48
|
-
.then(before => handler(req, res, before))
|
|
49
|
-
.then(result => this.AFTER(req, Formatter_1.Formatter.sanitizeAndParse(result)))
|
|
50
|
-
.then(result => res.json({ success: true, ...result }))
|
|
51
|
-
.catch((error) => {
|
|
52
|
-
res.status(error.status || 500).json({
|
|
53
|
-
success: false,
|
|
54
|
-
message: error.message || 'Error interno del servidor',
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
exports.Endpoint = Endpoint;
|
package/dist/http/Router.d.ts
DELETED
package/dist/http/Router.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Router = void 0;
|
|
7
|
-
const express_1 = __importDefault(require("express"));
|
|
8
|
-
class Router {
|
|
9
|
-
router = express_1.default.Router();
|
|
10
|
-
constructor(endpoints) {
|
|
11
|
-
endpoints.forEach(endpoint => {
|
|
12
|
-
console.log(` - ${endpoint.path}`);
|
|
13
|
-
this.router.all(endpoint.path, endpoint.execute.bind(endpoint));
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
getRoutes() {
|
|
17
|
-
return this.router;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
exports.Router = Router;
|
package/dist/index.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export * from "./http/Endpoint";
|
|
2
|
-
export * from "./http/Router";
|
|
3
|
-
export * from "./odata/Entities";
|
|
4
|
-
export * from "./odata/QueryBuilder";
|
|
5
|
-
export * from "./prisma/Client";
|
|
6
|
-
export * from "./types/api";
|
|
7
|
-
export * from "./types/app";
|
|
8
|
-
export * from "./types/odata";
|
|
9
|
-
export * from "./utils/Formatter";
|
|
10
|
-
export * from "./auth/ApiKeys";
|
|
11
|
-
export * from "./auth/ApiKeysEndpoint";
|
|
12
|
-
export * from "./app";
|
package/dist/index.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./http/Endpoint"), exports);
|
|
18
|
-
__exportStar(require("./http/Router"), exports);
|
|
19
|
-
__exportStar(require("./odata/Entities"), exports);
|
|
20
|
-
__exportStar(require("./odata/QueryBuilder"), exports);
|
|
21
|
-
__exportStar(require("./prisma/Client"), exports);
|
|
22
|
-
__exportStar(require("./types/api"), exports);
|
|
23
|
-
__exportStar(require("./types/app"), exports);
|
|
24
|
-
__exportStar(require("./types/odata"), exports);
|
|
25
|
-
__exportStar(require("./utils/Formatter"), exports);
|
|
26
|
-
__exportStar(require("./auth/ApiKeys"), exports);
|
|
27
|
-
__exportStar(require("./auth/ApiKeysEndpoint"), exports);
|
|
28
|
-
__exportStar(require("./app"), exports);
|
package/dist/odata/Entities.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Endpoint, type ApiResponse, type AppModels } from "../index";
|
|
2
|
-
import type { Request, Response } from 'express';
|
|
3
|
-
export declare class Entities<BeforeResult = any> extends Endpoint<BeforeResult> {
|
|
4
|
-
protected entity: AppModels[keyof AppModels];
|
|
5
|
-
protected VALIDATE({ params: { endpoint }, path }: Request): Promise<void>;
|
|
6
|
-
protected GET(req: Request, _res: Response): Promise<ApiResponse>;
|
|
7
|
-
protected POST({ body }: Request, _res: Response): Promise<ApiResponse>;
|
|
8
|
-
protected PUT({ body, params: { id } }: Request, _res: Response): Promise<ApiResponse>;
|
|
9
|
-
protected DELETE(req: Request, _res: Response): Promise<ApiResponse>;
|
|
10
|
-
}
|
package/dist/odata/Entities.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Entities = void 0;
|
|
4
|
-
const index_1 = require("../index");
|
|
5
|
-
class Entities extends index_1.Endpoint {
|
|
6
|
-
entity;
|
|
7
|
-
VALIDATE({ params: { endpoint }, path }) {
|
|
8
|
-
// Valida que la entidad exista
|
|
9
|
-
const entity = this.prismaClient.getModel(endpoint);
|
|
10
|
-
if (!entity) {
|
|
11
|
-
throw { message: `Endpoint ${path} no encontrado` };
|
|
12
|
-
}
|
|
13
|
-
this.entity = entity;
|
|
14
|
-
return Promise.resolve();
|
|
15
|
-
}
|
|
16
|
-
async GET(req, _res) {
|
|
17
|
-
const query = req.query;
|
|
18
|
-
let result;
|
|
19
|
-
// Si se proporciona un ID, se busca un registro único
|
|
20
|
-
if (req.params.id) {
|
|
21
|
-
query.id = req.params.id.toString();
|
|
22
|
-
result = await this.entity.findUnique(index_1.QueryBuilder.build(query));
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
result = await this.entity.findMany(index_1.QueryBuilder.build(query));
|
|
26
|
-
}
|
|
27
|
-
return { result };
|
|
28
|
-
}
|
|
29
|
-
async POST({ body }, _res) {
|
|
30
|
-
const result = await this.entity.create({ data: body });
|
|
31
|
-
return { result, message: `Registro creado correctamente` };
|
|
32
|
-
}
|
|
33
|
-
async PUT({ body, params: { id } }, _res) {
|
|
34
|
-
if (!id) {
|
|
35
|
-
throw { message: "ID es requerido para actualizar" };
|
|
36
|
-
}
|
|
37
|
-
const result = await this.entity.update({
|
|
38
|
-
where: { id: Number(id) },
|
|
39
|
-
data: body
|
|
40
|
-
});
|
|
41
|
-
return { result, message: `Registro actualizado correctamente` };
|
|
42
|
-
}
|
|
43
|
-
async DELETE(req, _res) {
|
|
44
|
-
let result;
|
|
45
|
-
if (!req.params.id) {
|
|
46
|
-
throw { message: "ID es requerido para eliminar" };
|
|
47
|
-
}
|
|
48
|
-
result = await this.entity.delete({ where: { id: Number(req.params.id) } });
|
|
49
|
-
return { result, message: `Registro eliminado correctamente` };
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
exports.Entities = Entities;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { QueryParams } from "../types/odata";
|
|
2
|
-
export declare class QueryBuilder {
|
|
3
|
-
static build(query: QueryParams): {
|
|
4
|
-
where: Record<string, any>;
|
|
5
|
-
include: Record<string, boolean> | undefined;
|
|
6
|
-
take: number | undefined;
|
|
7
|
-
skip: number | undefined;
|
|
8
|
-
orderBy: Record<string, string> | undefined;
|
|
9
|
-
};
|
|
10
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.QueryBuilder = void 0;
|
|
4
|
-
const OPERATORS = {
|
|
5
|
-
eq: (f, v) => ({ [f]: v }),
|
|
6
|
-
ne: (f, v) => ({ [f]: { not: v } }),
|
|
7
|
-
ge: (f, v) => ({ [f]: { gte: v } }),
|
|
8
|
-
le: (f, v) => ({ [f]: { lte: v } }),
|
|
9
|
-
contains: (f, v) => ({ [f]: { contains: v, mode: 'insensitive' } }),
|
|
10
|
-
startswith: (f, v) => ({ [f]: { startsWith: v, mode: 'insensitive' } }),
|
|
11
|
-
endswith: (f, v) => ({ [f]: { endsWith: v, mode: 'insensitive' } }),
|
|
12
|
-
};
|
|
13
|
-
function parseValue(value) {
|
|
14
|
-
if (!isNaN(Number(value)))
|
|
15
|
-
return Number(value);
|
|
16
|
-
if (value === 'true')
|
|
17
|
-
return true;
|
|
18
|
-
if (value === 'false')
|
|
19
|
-
return false;
|
|
20
|
-
return value;
|
|
21
|
-
}
|
|
22
|
-
function parseExpression(expr) {
|
|
23
|
-
const [field, op, ...rest] = expr.trim().split(' ');
|
|
24
|
-
if (!OPERATORS[op])
|
|
25
|
-
throw { message: `Operador '${op}' no soportado` };
|
|
26
|
-
return OPERATORS[op](field, parseValue(rest.join(' ')));
|
|
27
|
-
}
|
|
28
|
-
function buildFilter(filter) {
|
|
29
|
-
if (!filter)
|
|
30
|
-
return {};
|
|
31
|
-
if (filter.includes(' or '))
|
|
32
|
-
return { OR: filter.split(' or ').map(parseExpression) };
|
|
33
|
-
if (filter.includes(' and '))
|
|
34
|
-
return { AND: filter.split(' and ').map(parseExpression) };
|
|
35
|
-
return parseExpression(filter);
|
|
36
|
-
}
|
|
37
|
-
function buildExpand(expand) {
|
|
38
|
-
return expand?.split(',').reduce((acc, key) => ({ ...acc, [key]: true }), {});
|
|
39
|
-
}
|
|
40
|
-
function buildOrderBy(orderby) {
|
|
41
|
-
if (!orderby)
|
|
42
|
-
return undefined;
|
|
43
|
-
const [field, direction] = orderby.split(' ');
|
|
44
|
-
if (direction !== 'asc' && direction !== 'desc')
|
|
45
|
-
return undefined;
|
|
46
|
-
// Soporta campos anidados: "subscription.price desc" → { subscription: { price: 'desc' } }
|
|
47
|
-
return field.split('.').reduceRight((acc, part) => ({ [part]: acc }), direction);
|
|
48
|
-
}
|
|
49
|
-
class QueryBuilder {
|
|
50
|
-
static build(query) {
|
|
51
|
-
return {
|
|
52
|
-
where: buildFilter(query.filter),
|
|
53
|
-
include: buildExpand(query.expand),
|
|
54
|
-
take: query.top ? Number(query.top) : undefined,
|
|
55
|
-
skip: query.skip ? Number(query.skip) : undefined,
|
|
56
|
-
orderBy: buildOrderBy(query.orderby),
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
exports.QueryBuilder = QueryBuilder;
|
package/dist/prisma/Client.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import "dotenv/config";
|
|
2
|
-
declare const Prisma: any;
|
|
3
|
-
type GeneratedPrismaClient = typeof Prisma;
|
|
4
|
-
export type AppModels = Omit<GeneratedPrismaClient, `$${string}` | symbol>;
|
|
5
|
-
/** Cliente Prisma. getModel retorna el tipo correcto según el nombre del modelo. */
|
|
6
|
-
export declare class PrismaClient {
|
|
7
|
-
private prisma;
|
|
8
|
-
getModel<K extends keyof AppModels>(entityName: K): AppModels[K];
|
|
9
|
-
/** Para consultas que comparan columnas u otras condiciones no soportadas en findMany */
|
|
10
|
-
queryRaw<T = unknown>(strings: TemplateStringsArray, ...values: any[]): Promise<T>;
|
|
11
|
-
}
|
|
12
|
-
export {};
|
package/dist/prisma/Client.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.PrismaClient = void 0;
|
|
7
|
-
require("dotenv/config");
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const adapter_pg_1 = require("@prisma/adapter-pg");
|
|
10
|
-
// Crea el adaptador a la base de datos
|
|
11
|
-
if (!process.env.DATABASE_URL) {
|
|
12
|
-
throw { message: 'DATABASE_URL no está configurada' };
|
|
13
|
-
}
|
|
14
|
-
const adapter = new adapter_pg_1.PrismaPg({
|
|
15
|
-
connectionString: process.env.DATABASE_URL
|
|
16
|
-
});
|
|
17
|
-
// Carga el cliente generado por el binario
|
|
18
|
-
const clientPath = path_1.default.join(process.cwd(), "node_modules/.prisma/prisma-client/client");
|
|
19
|
-
const client = require(clientPath);
|
|
20
|
-
const Prisma = client.PrismaClient ?? client.default?.PrismaClient ?? client;
|
|
21
|
-
// Crea una instancia de Prisma
|
|
22
|
-
const prismaInstance = new Prisma({ adapter });
|
|
23
|
-
/** Cliente Prisma. getModel retorna el tipo correcto según el nombre del modelo. */
|
|
24
|
-
class PrismaClient {
|
|
25
|
-
prisma = prismaInstance;
|
|
26
|
-
getModel(entityName) {
|
|
27
|
-
return this.prisma[entityName];
|
|
28
|
-
}
|
|
29
|
-
/** Para consultas que comparan columnas u otras condiciones no soportadas en findMany */
|
|
30
|
-
async queryRaw(strings, ...values) {
|
|
31
|
-
return this.prisma.$queryRaw(strings, ...values);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
exports.PrismaClient = PrismaClient;
|
package/dist/types/api.d.ts
DELETED
package/dist/types/api.js
DELETED
package/dist/types/app.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { Endpoint } from "../index";
|
|
2
|
-
import type { ErrorRequestHandler, RequestHandler } from "express";
|
|
3
|
-
import type { Options } from "express-rate-limit";
|
|
4
|
-
export type NanoAppConfig = {
|
|
5
|
-
endpoints: Endpoint[];
|
|
6
|
-
enableEntities?: boolean;
|
|
7
|
-
errorMiddleware?: ErrorRequestHandler;
|
|
8
|
-
middlewares?: RequestHandler[];
|
|
9
|
-
port?: number;
|
|
10
|
-
rateLimit?: Partial<Options>;
|
|
11
|
-
expressConfig?: Partial<Express.Application>;
|
|
12
|
-
setters?: Record<string, any>;
|
|
13
|
-
morgan?: {
|
|
14
|
-
tokens?: Record<string, any>;
|
|
15
|
-
format?: string;
|
|
16
|
-
};
|
|
17
|
-
apiKeys?: {
|
|
18
|
-
/** Habilita los endpoints y middleware de API keys. Por defecto: false */
|
|
19
|
-
enabled: boolean;
|
|
20
|
-
/** Ruta base para los endpoints de gestión. Por defecto: /api-keys */
|
|
21
|
-
path?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Middleware de autenticación custom (ej: JWT de usuario).
|
|
24
|
-
* Si se provee, se ejecuta primero. Si falla, se intenta validar con API key.
|
|
25
|
-
* Si ambos fallan, retorna 401.
|
|
26
|
-
*/
|
|
27
|
-
middleware?: RequestHandler;
|
|
28
|
-
};
|
|
29
|
-
};
|
package/dist/types/app.js
DELETED
package/dist/types/odata.d.ts
DELETED
package/dist/types/odata.js
DELETED
package/dist/utils/Formatter.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Formatter = void 0;
|
|
4
|
-
class Formatter {
|
|
5
|
-
static price(value) {
|
|
6
|
-
return `$${new Intl.NumberFormat("de-DE", { maximumFractionDigits: 0 }).format(value || 0)}`;
|
|
7
|
-
}
|
|
8
|
-
static logDate(date = new Date()) {
|
|
9
|
-
return new Intl.DateTimeFormat("es-ES", {
|
|
10
|
-
year: "2-digit",
|
|
11
|
-
month: "2-digit",
|
|
12
|
-
day: "2-digit",
|
|
13
|
-
hour: "2-digit",
|
|
14
|
-
minute: "2-digit",
|
|
15
|
-
second: "2-digit",
|
|
16
|
-
}).format(date);
|
|
17
|
-
}
|
|
18
|
-
static sanitizeAndParse(result) {
|
|
19
|
-
const jsonString = JSON.stringify(result);
|
|
20
|
-
// La expresión regular busca exactamente la clave "password": "cualquier_valor"
|
|
21
|
-
// donde:
|
|
22
|
-
// - "password" se encuentra entre comillas.
|
|
23
|
-
// - Puede haber espacios opcionales alrededor de los dos puntos.
|
|
24
|
-
// - El valor se asume estar entre comillas.
|
|
25
|
-
const regex = /("password"\s*:\s*)"[^"]*"/gi;
|
|
26
|
-
// Reemplazamos el valor por el literal JSON null (sin comillas)
|
|
27
|
-
const sanitizedString = jsonString.replace(regex, '$1null');
|
|
28
|
-
return JSON.parse(sanitizedString);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
exports.Formatter = Formatter;
|