Version 1.3.0

- Implemented authentication and billing routes for Jpn region.
- Refactored and changed the project structure from CommonJS to ES Modules
This commit is contained in:
Junior 2025-04-29 16:20:09 -03:00
parent 9584e58143
commit c3d9e7afb5
76 changed files with 3847 additions and 1109 deletions

18
src/servers/jpnApp.js Normal file
View file

@ -0,0 +1,18 @@
import express, { urlencoded } from 'express';
import config from '../config.js';
const { ports, ips, logger } = config;
import authJpnRouter from '../routes/authJpn.js';
import billingJpnRouter from '../routes/billingJpn.js';
const app = express();
app.use(urlencoded({ extended: false, type: 'application/x-www-form-urlencoded' }));
app.use('/Auth', authJpnRouter);
app.use('/Billing', billingJpnRouter);
const startServer = () => {
return app.listen(ports.jpnApp, ips.local, () => {
logger.info(`API (JPN) listening on ${ips.local}:${ports.jpnApp}`);
});
};
export { app, startServer };

85
src/servers/mainApp.js Normal file
View file

@ -0,0 +1,85 @@
import express from 'express';
import config from '../config.js';
const { ports, ips, apiConfig, middleware, staticPaths, logger } = config;
import rateLimiter from '../lib/rateLimiter.js';
import { closeConnection } from '../lib/closeConnection.js';
import path from 'path';
// Routers
import gatewayRouter from '../routes/gateway.js';
import loginRouter from '../routes/launcher/login.js';
import registerRouter from '../routes/launcher/registerAccount.js';
import codeVerificationRouter from '../routes/launcher/codeVerification.js';
import passwordResetEmailRouter from '../routes/launcher/passwordResetEmail.js';
import passwordChangeRouter from '../routes/launcher/changePassword.js';
import verificationEmailRouter from '../routes/launcher/verificationEmail.js';
import launcherUpdaterRouter from '../routes/launcher/launcherUpdater.js';
import onlineCountRouter from '../routes/onlineCount.js';
const app = express();
if (apiConfig.trustProxyEnabled) {
const trustProxyHosts = apiConfig.trustProxyHosts || [];
if (trustProxyHosts.length > 0) {
app.set("trust proxy", trustProxyHosts);
} else {
app.set("trust proxy", true);
}
}
app.disable("x-powered-by");
app.disable("etag");
// Middleware
app.use(...middleware.getMiddleware());
// Routes
app.use('/launcher/GetGatewayAction', closeConnection, rateLimiter, gatewayRouter);
app.use('/launcher/SignupAction', closeConnection, rateLimiter, registerRouter);
app.use('/launcher/LoginAction', closeConnection, rateLimiter, loginRouter);
app.use('/launcher/VerifyCodeAction', closeConnection, rateLimiter, codeVerificationRouter);
app.use('/launcher/ResetPasswordAction', closeConnection, rateLimiter, passwordChangeRouter);
app.use('/launcher/SendPasswordResetEmailAction', closeConnection, rateLimiter, passwordResetEmailRouter);
app.use('/launcher/SendVerificationEmailAction', closeConnection, rateLimiter, verificationEmailRouter);
app.use('/launcherAction', closeConnection, rateLimiter, launcherUpdaterRouter);
app.use('/launcher/GetOnlineCountAction', closeConnection, rateLimiter, onlineCountRouter);
// Static files
app.use(express.static(staticPaths.public));
app.use('/launcher/news', express.static(staticPaths.launcherNews));
app.use('/site', express.static(staticPaths.site));
app.use('/launcher/patch', express.static(staticPaths.launcherPatch));
app.use('/launcher/client', express.static(staticPaths.launcherClient));
// HTML routes
app.get('/launcher/news', (req, res) => {
res.sendFile(path.join(staticPaths.launcherNews, 'news-panel.html'));
});
app.get('/launcher/agreement', (req, res) => {
res.sendFile(path.join(staticPaths.launcherNews, 'agreement.html'));
});
app.get('/favicon.ico', (req, res) => {
res.sendFile(path.join(staticPaths.launcherNews, 'favicon.ico'));
});
app.get('/Register', (req, res) => {
res.sendFile(path.join(staticPaths.site, 'Signup.html'));
});
// Error handler
app.use((err, req, res, next) => {
logger.error(err.stack);
return res.status(500).json({
result: 'ServerError',
message: 'A server error occurred. Please try again later.'
});
});
const startServer = () => {
return app.listen(ports.main, ips.public, () => {
logger.info(`API listening on ${ips.public}:${ports.main}`);
});
};
export { app, startServer };

151
src/servers/proxyServer.js Normal file
View file

@ -0,0 +1,151 @@
import { createServer } from 'net';
import { request } from 'http';
import config from '../config.js';
const { ports, ips, logger, BACKENDS } = config;
const parseRequest = (data) => {
const lines = data.split('\r\n');
let realPath = '/';
let isMalformed = false;
if (lines.some(line => line.startsWith('POST /') && line.includes('HTTP/1.1Content-Type:'))) {
isMalformed = true;
for (const line of lines) {
if (line.startsWith('POST /') && line.includes('HTTP/1.1Content-Type:')) {
const match = line.match(/POST (\/[^ ]*) HTTP\/1\.1/);
if (match) realPath = match[1];
break;
}
}
} else {
const requestLine = lines[0];
const match = requestLine.match(/^(POST|GET) (\/(?:[^ ]*)) HTTP\/1\.1$/i);
if (match) realPath = match[2];
}
if (realPath.startsWith('/cgi-bin/')) {
realPath = '/Auth' + realPath;
} else if (realPath.startsWith('/S1/')) {
realPath = '/Billing' + realPath;
}
return { realPath, isMalformed, lines };
};
const findBackend = (realPath) => {
if (BACKENDS.AUTH.paths.includes(realPath)) {
return { ...BACKENDS.AUTH, port: ports.jpnApp };
} else if (BACKENDS.BILLING.paths.some(path => realPath.startsWith(path))) {
return { ...BACKENDS.BILLING, port: ports.jpnApp };
} else {
return null;
}
};
const buildHeaders = (lines, body, backend, isMalformed) => {
const headers = {
'Host': `${ips.local}:${backend.port}`,
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(body),
'User-Agent': 'Mozilla/4.0 (ISAO/1.00;Auth)',
};
if (!isMalformed) {
const headerLines = lines.slice(1, lines.indexOf(''));
for (const line of headerLines) {
const colonIndex = line.indexOf(':');
if (colonIndex > 0) {
const key = line.substring(0, colonIndex).trim().toLowerCase();
const value = line.substring(colonIndex + 1).trim();
if (['content-type', 'user-agent', 'content-length'].includes(key)) {
headers[key] = value;
}
}
}
}
return headers;
};
const handleProxyRequest = (socket, data) => {
try {
const { realPath, isMalformed, lines } = parseRequest(data);
const backend = findBackend(realPath);
if (!backend) {
logger.error('[Proxy] Unknown path:', realPath);
socket.end('HTTP/1.1 404 Not Found\r\n\r\n');
return;
}
const body = data.split('\r\n\r\n')[1] || '';
const headers = buildHeaders(lines, body, backend, isMalformed);
const options = {
hostname: ips.local,
port: backend.port,
path: realPath,
method: 'POST',
headers,
};
const proxyReq = request(options, (proxyRes) => {
socket.write(`HTTP/1.1 ${proxyRes.statusCode} ${proxyRes.statusMessage}\r\n`);
Object.entries(proxyRes.headers).forEach(([key, value]) => {
socket.write(`${key}: ${value}\r\n`);
});
socket.write('\r\n');
proxyRes.pipe(socket);
});
proxyReq.on('error', (err) => {
logger.error('[Proxy] Proxy error:', err);
socket.end('HTTP/1.1 500 Internal Server Error\r\n\r\n');
});
proxyReq.write(body);
proxyReq.end();
} catch (err) {
logger.error('[Proxy] Error parsing request:', err);
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
}
};
const createProxyServer = () => {
const server = createServer((socket) => {
let data = '';
socket.on('data', (chunk) => {
data += chunk.toString('binary');
if (data.includes('\r\n\r\n')) {
handleProxyRequest(socket, data);
}
});
socket.on('error', (err) => {
logger.error('[Proxy] Socket error:', err);
});
socket.on('timeout', () => {
logger.warn('[Proxy] Socket timeout.');
socket.end();
});
});
return server;
};
const startServer = () => {
const server = createProxyServer();
return server.listen(ports.proxy, ips.local, () => {
logger.info(`Proxy (JPN) listening on ${ips.local}:${ports.proxy}`);
console.log('Configured backends:');
console.log(`- AUTH (${ports.jpnApp}):`, BACKENDS.AUTH.paths);
console.log(`- BILLING (${ports.jpnApp}):`, BACKENDS.BILLING.paths);
});
};
export { createProxyServer, startServer };

17
src/servers/usaApp.js Normal file
View file

@ -0,0 +1,17 @@
import express from 'express';
import config from '../config.js';
const { ports, ips, logger } = config;
import authUsaRouter from '../routes/authUsa.js';
import billingRouter from '../routes/billingUsa.js';
const app = express();
app.use('/Auth', authUsaRouter);
app.use('/Billing', billingRouter);
const startServer = () => {
return app.listen(ports.usaApp, ips.local, () => {
logger.info(`API (USA) listening on ${ips.local}:${ports.usaApp}`);
});
};
export { app, startServer };