Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 29 additions & 26 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ const express = require('express');
const fs = require('node:fs');
const { join } = require('node:path');
const morgan = require('morgan');
require("./utils/instrument");
require('./utils/instrument');

const { statusCodeHandler } = require(join(__dirname, 'utils', 'status-code-handler'));
const errorHandler = require(join(__dirname, 'utils', 'api', 'error-handler'));
const { createReverseProxy } = require(join(__dirname, 'utils', 'reverse-proxy'));

const app = express();

app.use(express.json()); // <--- ¡Esto es obligatorio para JSON!

const port = process.env.PORT || 3000;

global.__basedir = __dirname;
Expand All @@ -21,9 +20,9 @@ app.use(morgan('dev', {
skip: (req, res) => res.statusCode < 400,
}));

app.use(errorHandler)
app.use(errorHandler);

//Replace the X-Powered-By header with our own
// Replace the X-Powered-By header with our own
app.use((req, res, next) => {
res.setHeader('X-Powered-By', 'Any Dev Code');
res.setHeader('X-Developer', 'MDCDEV');
Expand All @@ -34,7 +33,7 @@ app.use((req, res, next) => {
next();
});

//Allow CORS
// Allow CORS
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
Expand All @@ -43,54 +42,58 @@ app.use((req, res, next) => {
next();
});

app.use('/v2/peruserver', createReverseProxy({
targetOrigin: 'https://api-data.peruserver.de',
}));

app.use(express.json());

// Rutas deshabilitadas
const DISABLED_ROUTES = process.env.DISABLED_ROUTES
const DISABLED_ROUTES = process.env.DISABLED_ROUTES
? process.env.DISABLED_ROUTES.split(',').map(r => r.trim())
: [];

//We have a folder called "routes", and inside that folder we have a folders called "v1", "v2", "v3" and more, inside those folders we have a folder called "users", "guilds" and more, inside those folders we have a file called "@me.js", "index.js" and more
//This is how we require all the files in the "routes" folder
//The route is the path to the file, and the file is the file that we require
function requireRoutes(path, fullpath = "") {
function requireRoutes(path, fullpath = '') {
fs.readdirSync(join(__dirname, path)).forEach(file => {
if (file.endsWith(".js")) {
let routePath = "";
if (file === "index.js") {
routePath = `${fullpath.replace("~", ":")}`;
if (file.endsWith('.js')) {
let routePath = '';
if (file === 'index.js') {
routePath = `${fullpath.replace('~', ':')}`;
} else {
routePath = `${fullpath.replace(".js", ).replace("~", ":")}/${file.replace(".js", "")}`;
routePath = `${fullpath.replace('.js', '').replace('~', ':')}/${file.replace('.js', '')}`;
}
// Verificar si la ruta está deshabilitada

// Verificar si la ruta esta deshabilitada
const isDisabled = DISABLED_ROUTES.some(disabled => routePath.includes(disabled));

if (isDisabled) {
console.log(`Ruta deshabilitada (saltada): ${routePath}`);
console.log(`Ruta deshabilitada (saltada): ${routePath}`);
} else {
if (file === "index.js") {
app.use(`${fullpath.replace("~", ":")}`, require(join(__dirname, path, file)));
console.log(`Ruta cargada: ${fullpath.replace("~", ":")}`);
if (file === 'index.js') {
app.use(`${fullpath.replace('~', ':')}`, require(join(__dirname, path, file)));
console.log(`Ruta cargada: ${fullpath.replace('~', ':')}`);
} else {
app.use(`${fullpath.replace(".js", ).replace("~", ":")}/${file.replace(".js", "")}`, require(join(__dirname, path, file)));
console.log(`Ruta cargada: ${fullpath.replace(".js", ).replace("~", ":")}/${file.replace(".js", "")}`);
app.use(`${fullpath.replace('.js', '').replace('~', ':')}/${file.replace('.js', '')}`, require(join(__dirname, path, file)));
console.log(`Ruta cargada: ${fullpath.replace('.js', '').replace('~', ':')}/${file.replace('.js', '')}`);
}
}
} else {
requireRoutes(join(path, file), `${fullpath}/${file}`);
}

});
}

requireRoutes("routes");
requireRoutes('routes');

app.get('/', (req, res) => res.json({ message: 'Welcome to the MDCDEV API', documentation: 'https://docs.api.mdcdev.me' }));

app.all('*', (req, res) => {
statusCodeHandler({ statusCode: 10005 }, res);
});


app.listen(port, () => console.log(`API listening on port ${port}!`));

process.on('unhandledRejection', (reason, promise) => {
Expand All @@ -99,4 +102,4 @@ process.on('unhandledRejection', (reason, promise) => {
});
process.on('uncaughtException', (err, origin) => {
console.error(`Caught exception: ${err}\nException origin: ${origin}`);
});
});
85 changes: 85 additions & 0 deletions src/utils/reverse-proxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
const http = require('node:http');
const https = require('node:https');
const { URL } = require('node:url');

function appendForwardedFor(existingForwardedFor, clientIp) {
if (!clientIp) {
return existingForwardedFor;
}

return existingForwardedFor
? `${existingForwardedFor}, ${clientIp}`
: clientIp;
}

function rewriteLocationHeader(location, targetOrigin, requestOrigin) {
if (!location || !location.startsWith(targetOrigin)) {
return location;
}

return `${requestOrigin}${location.slice(targetOrigin.length)}`;
}

function createReverseProxy({ targetOrigin, timeoutMs = 30000 }) {
const normalizedTargetOrigin = targetOrigin.replace(/\/+$/, '');
const transport = normalizedTargetOrigin.startsWith('https://') ? https : http;

return (req, res) => {
const upstreamUrl = new URL(req.originalUrl, `${normalizedTargetOrigin}/`);
const requestOrigin = `${req.protocol}://${req.get('host')}`;
const headers = { ...req.headers };

delete headers.host;
delete headers.connection;

headers['x-forwarded-host'] = req.get('host');
headers['x-forwarded-proto'] = req.protocol;
headers['x-forwarded-for'] = appendForwardedFor(req.headers['x-forwarded-for'], req.ip);

const proxyReq = transport.request(
upstreamUrl,
{
method: req.method,
headers,
timeout: timeoutMs,
},
(proxyRes) => {
const responseHeaders = { ...proxyRes.headers };

if (responseHeaders.location) {
responseHeaders.location = rewriteLocationHeader(
responseHeaders.location,
normalizedTargetOrigin,
requestOrigin
);
}

res.writeHead(proxyRes.statusCode || 502, responseHeaders);
proxyRes.pipe(res);
}
);

proxyReq.on('timeout', () => {
proxyReq.destroy(new Error('Upstream timeout'));
});

proxyReq.on('error', (error) => {
if (res.headersSent) {
res.end();
return;
}

res.status(502).json({
error: 'bad_gateway',
message: 'No se pudo contactar al upstream de PeruServer.',
details: error.message,
});
});

req.pipe(proxyReq);
};
}

module.exports = {
createReverseProxy,
};
Loading