mirror of
https://github.com/PretendoNetwork/BOSS.git
synced 2026-10-25 08:43:36 -05:00
Convert to TypeScript
This commit is contained in:
49
src/config-manager.ts
Normal file
49
src/config-manager.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import dotenv from 'dotenv';
|
||||
import { md5 } from '@/util';
|
||||
import { LOG_INFO, LOG_ERROR } from '@/logger';
|
||||
import { Config } from '@/types/common/config';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const BOSS_WIIU_AES_KEY_HASH = Buffer.from('5202ce5099232c3d365e28379790a919', 'hex');
|
||||
const BOSS_WIIU_HMAC_KEY_HASH = Buffer.from('b4482fef177b0100090ce0dbeb8ce977', 'hex');
|
||||
const BOSS_3DS_AES_KEY_HASH = Buffer.from('86fbc2bb4cb703b2a4c6cc9961319926', 'hex');
|
||||
|
||||
LOG_INFO('Loading config');
|
||||
|
||||
export const config: Config = {
|
||||
http: {
|
||||
port: Number(process.env.PN_BOSS_CONFIG_HTTP_PORT || '')
|
||||
},
|
||||
crypto: {
|
||||
wup: {
|
||||
aes_key: Buffer.from(process.env.PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY || ''),
|
||||
hmac_key: Buffer.from(process.env.PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY || ''),
|
||||
},
|
||||
ctr: {
|
||||
aes_key: Buffer.from(process.env.PN_BOSS_CONFIG_BOSS_3DS_AES_KEY || '', 'hex')
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
LOG_INFO('Config loaded, checking integrity');
|
||||
|
||||
if (!config.http.port) {
|
||||
LOG_ERROR('Failed to find HTTP port. Set the PN_BOSS_CONFIG_HTTP_PORT environment variable');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!BOSS_WIIU_AES_KEY_HASH.equals(md5(config.crypto.wup.aes_key))) {
|
||||
LOG_ERROR('Invalid BOSS WiiU AES key. Set or correct the PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY environment variable');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!BOSS_WIIU_HMAC_KEY_HASH.equals(md5(config.crypto.wup.hmac_key))) {
|
||||
LOG_ERROR('Invalid BOSS WiiU HMAC key. Set or correct the PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY environment variable');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!BOSS_3DS_AES_KEY_HASH.equals(md5(config.crypto.ctr.aes_key))) {
|
||||
LOG_ERROR('Invalid BOSS 3DS AES key. Set or correct the PN_BOSS_CONFIG_BOSS_3DS_AES_KEY environment variable');
|
||||
process.exit(0);
|
||||
}
|
||||
47
src/logger.ts
Normal file
47
src/logger.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import fs from 'fs-extra';
|
||||
import colors from 'colors';
|
||||
|
||||
colors.enable();
|
||||
|
||||
const root = process.env.PN_BOSS_CONFIG_LOGGER_PATH ? process.env.PN_BOSS_CONFIG_LOGGER_PATH : `${__dirname}/..`;
|
||||
fs.ensureDirSync(`${root}/logs`);
|
||||
|
||||
const streams = {
|
||||
latest: fs.createWriteStream(`${root}/logs/latest.log`),
|
||||
success: fs.createWriteStream(`${root}/logs/success.log`),
|
||||
error: fs.createWriteStream(`${root}/logs/error.log`),
|
||||
warn: fs.createWriteStream(`${root}/logs/warn.log`),
|
||||
info: fs.createWriteStream(`${root}/logs/info.log`)
|
||||
} as const;
|
||||
|
||||
export function LOG_SUCCESS(input: string): void {
|
||||
const time = new Date();
|
||||
input = `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}] [SUCCESS]: ${input}`;
|
||||
streams.success.write(`${input}\n`);
|
||||
|
||||
console.log(`${input}`.green.bold);
|
||||
}
|
||||
|
||||
export function LOG_ERROR(input: string): void {
|
||||
const time = new Date();
|
||||
input = `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}] [ERROR]: ${input}`;
|
||||
streams.error.write(`${input}\n`);
|
||||
|
||||
console.log(`${input}`.red.bold);
|
||||
}
|
||||
|
||||
export function LOG_WARN(input: string): void {
|
||||
const time = new Date();
|
||||
input = `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}] [WARN]: ${input}`;
|
||||
streams.warn.write(`${input}\n`);
|
||||
|
||||
console.log(`${input}`.yellow.bold);
|
||||
}
|
||||
|
||||
export function LOG_INFO(input: string): void {
|
||||
const time = new Date();
|
||||
input = `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}] [INFO]: ${input}`;
|
||||
streams.info.write(`${input}\n`);
|
||||
|
||||
console.log(`${input}`.cyan.bold);
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
process.title = 'Pretendo - BOSS';
|
||||
|
||||
const express = require('express');
|
||||
const morgan = require('morgan');
|
||||
const logger = require('../logger');
|
||||
const config = require('../config.json');
|
||||
|
||||
const { http: { port } } = config;
|
||||
const app = express();
|
||||
|
||||
const nptsService = require('./services/npts');
|
||||
const npplService = require('./services/nppl');
|
||||
const npdiService = require('./services/npdi');
|
||||
|
||||
// START APPLICATION
|
||||
app.set('etag', false);
|
||||
app.disable('x-powered-by');
|
||||
|
||||
// Create router
|
||||
logger.info('Setting up Middleware');
|
||||
app.use(morgan('dev'));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({
|
||||
extended: true
|
||||
}));
|
||||
|
||||
app.use(nptsService);
|
||||
app.use(npplService);
|
||||
app.use(npdiService);
|
||||
|
||||
// 404 handler
|
||||
logger.info('Creating 404 status handler');
|
||||
app.use((request, response) => {
|
||||
response.status(404);
|
||||
response.json({
|
||||
app: 'api',
|
||||
status: 404,
|
||||
error: 'Route not found'
|
||||
});
|
||||
});
|
||||
|
||||
// non-404 error handler
|
||||
logger.info('Creating non-404 status handler');
|
||||
app.use((error, request, response) => {
|
||||
const status = error.status || 500;
|
||||
|
||||
response.status(status);
|
||||
response.json({
|
||||
app: 'api',
|
||||
status,
|
||||
error: error.message
|
||||
});
|
||||
});
|
||||
|
||||
// Starts the server
|
||||
logger.info('Starting server');
|
||||
|
||||
app.listen(port, () => {
|
||||
logger.success(`Server started on port ${port}`);
|
||||
});
|
||||
53
src/server.ts
Normal file
53
src/server.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
process.title = 'Pretendo - BOSS';
|
||||
|
||||
import express from 'express';
|
||||
import morgan from 'morgan';
|
||||
import { LOG_INFO, LOG_SUCCESS } from '@/logger';
|
||||
import { config } from '@/config-manager';
|
||||
|
||||
import nppl from '@/services/nppl';
|
||||
import npts from '@/services/npts';
|
||||
import npdi from '@/services/npdi';
|
||||
|
||||
const app = express();
|
||||
|
||||
app.set('etag', false);
|
||||
app.disable('x-powered-by');
|
||||
|
||||
LOG_INFO('Setting up Middleware');
|
||||
app.use(morgan('dev'));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({
|
||||
extended: true
|
||||
}));
|
||||
|
||||
app.use(nppl);
|
||||
app.use(npts);
|
||||
app.use(npdi);
|
||||
|
||||
LOG_INFO('Creating 404 status handler');
|
||||
app.use((_request, response) => {
|
||||
response.status(404);
|
||||
response.json({
|
||||
app: 'api',
|
||||
status: 404,
|
||||
error: 'Route not found'
|
||||
});
|
||||
});
|
||||
|
||||
LOG_INFO('Creating non-404 status handler');
|
||||
app.use((error: any, _request: express.Request, response: express.Response, _next: express.NextFunction) => {
|
||||
const status: number = error.status || 500;
|
||||
|
||||
response.status(status);
|
||||
response.json({
|
||||
app: 'boss',
|
||||
status: status,
|
||||
error: error
|
||||
});
|
||||
});
|
||||
|
||||
LOG_INFO('Starting server');
|
||||
app.listen(config.http.port, () => {
|
||||
LOG_SUCCESS(`Server started on port ${config.http.port}`);
|
||||
});
|
||||
@@ -1,12 +1,10 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const express = require('express');
|
||||
const subdomain = require('express-subdomain');
|
||||
import path from 'node:path';
|
||||
import fs from 'fs-extra';
|
||||
import express from 'express';
|
||||
import subdomain from 'express-subdomain';
|
||||
|
||||
// Router to handle the subdomain restriction
|
||||
const npdi = express.Router();
|
||||
|
||||
// Setup routes
|
||||
npdi.get('/p01/data/1/:titleHash/:dataID/:fileHash', (request, response) => {
|
||||
const { titleHash, fileHash } = request.params;
|
||||
const contentPath = path.normalize(`${__dirname}/../../cdn/content/encrypted/${titleHash}/${fileHash}`);
|
||||
@@ -22,10 +20,8 @@ npdi.get('/p01/data/1/:titleHash/:dataID/:fileHash', (request, response) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Main router for endpoints
|
||||
const router = express.Router();
|
||||
|
||||
// Create subdomain
|
||||
router.use(subdomain('npdi.cdn', npdi));
|
||||
|
||||
module.exports = router;
|
||||
export default router;
|
||||
@@ -1,28 +0,0 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const express = require('express');
|
||||
const subdomain = require('express-subdomain');
|
||||
|
||||
// Router to handle the subdomain restriction
|
||||
const npts = express.Router();
|
||||
|
||||
// Setup routes
|
||||
npts.get('/p01/policylist/1/1/:region', (request, response) => {
|
||||
//const { region } = request.params;
|
||||
const policylistPath = path.normalize(`${__dirname}/../../cdn/policylist/policylist.xml`);
|
||||
|
||||
if (fs.existsSync(policylistPath)) {
|
||||
response.set('Content-Type', 'application/xml; charset=utf-8');
|
||||
response.sendFile(policylistPath);
|
||||
} else {
|
||||
response.sendStatus(404);
|
||||
}
|
||||
});
|
||||
|
||||
// Main router for endpoints
|
||||
const router = express.Router();
|
||||
|
||||
// Create subdomain
|
||||
router.use(subdomain('nppl.app', npts));
|
||||
|
||||
module.exports = router;
|
||||
23
src/services/nppl.ts
Normal file
23
src/services/nppl.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import path from 'node:path';
|
||||
import fs from 'fs-extra';
|
||||
import express from 'express';
|
||||
import subdomain from 'express-subdomain';
|
||||
|
||||
const nppl = express.Router();
|
||||
|
||||
nppl.get('/p01/policylist/1/1/:region', (_request, response) => {
|
||||
const policylistPath = path.normalize(`${__dirname}/../../cdn/policylist/policylist.xml`);
|
||||
|
||||
if (fs.existsSync(policylistPath)) {
|
||||
response.set('Content-Type', 'application/xml; charset=utf-8');
|
||||
response.sendFile(policylistPath);
|
||||
} else {
|
||||
response.sendStatus(404);
|
||||
}
|
||||
});
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.use(subdomain('nppl.app', nppl));
|
||||
|
||||
export default router;
|
||||
@@ -1,12 +1,10 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const express = require('express');
|
||||
const subdomain = require('express-subdomain');
|
||||
import path from 'node:path';
|
||||
import fs from 'fs-extra';
|
||||
import express from 'express';
|
||||
import subdomain from 'express-subdomain';
|
||||
|
||||
// Router to handle the subdomain restriction
|
||||
const npts = express.Router();
|
||||
|
||||
// Setup routes
|
||||
npts.get('/p01/tasksheet/:id/:hash/:fileName', (request, response) => {
|
||||
const { id, hash, fileName } = request.params;
|
||||
const tasksheetPath = path.normalize(`${__dirname}/../../cdn/tasksheet/${id}/${hash}/${fileName}`);
|
||||
@@ -31,10 +29,8 @@ npts.get('/p01/tasksheet/:id/:hash/:subfolder/:fileName', (request, response) =>
|
||||
}
|
||||
});
|
||||
|
||||
// Main router for endpoints
|
||||
const router = express.Router();
|
||||
|
||||
// Create subdomain
|
||||
router.use(subdomain('npts.app', npts));
|
||||
|
||||
module.exports = router;
|
||||
export default router;
|
||||
14
src/types/common/config.ts
Normal file
14
src/types/common/config.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export interface Config {
|
||||
http: {
|
||||
port: number;
|
||||
};
|
||||
crypto: {
|
||||
wup: {
|
||||
aes_key: Buffer;
|
||||
hmac_key: Buffer;
|
||||
};
|
||||
ctr: {
|
||||
aes_key: Buffer;
|
||||
};
|
||||
};
|
||||
}
|
||||
16
src/types/express-subdomain.d.ts
vendored
Normal file
16
src/types/express-subdomain.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// * Credit to https://github.com/bmullan91/express-subdomain/pull/61 for the types!
|
||||
|
||||
declare module 'express-subdomain'{
|
||||
import type { Request, Response, Router } from 'express';
|
||||
|
||||
/**
|
||||
* @description The subdomain function.
|
||||
* @param subdomain The subdomain to listen on.
|
||||
* @param fn The listener function, takes a response and request.
|
||||
* @returns A function call to the value passed as FN, or void (the next function).
|
||||
*/
|
||||
export default function subdomain(
|
||||
subdomain: string,
|
||||
fn: Router | ((req: Request, res: Response) => void | any)
|
||||
): (req: Request, res: Response, next: () => void) => void | typeof fn;
|
||||
}
|
||||
5
src/util.ts
Normal file
5
src/util.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import crypto from 'node:crypto';
|
||||
|
||||
export function md5(input: crypto.BinaryLike): Buffer {
|
||||
return crypto.createHash('md5').update(input).digest();
|
||||
}
|
||||
Reference in New Issue
Block a user