Files

309 lines
8.8 KiB
JavaScript

import express from 'express';
import cors from 'cors';
const app = express();
app.use(cors());
app.use(express.json());
// Log all requests to terminal
app.use((req, res, next) => {
console.log(`[Mock] ${req.method} ${req.url}`);
next();
});
const PORT = 8081;
let logs = [
"[PLDMGR] Mock Server initialized",
"[PLDMGR] System ready",
"[PLDMGR] Found 3 payloads on storage"
];
const remoteRepository = [
{
name: "ftpsrv",
filename: "ftpsrv_v0.19.elf",
url: "https://itsplk.github.io/ps5_payloads/payloads/ftpsrv_v0.19.elf",
description: "A simple FTP server that accepts connections on port 2121",
version: "v0.19",
checksum: "e6c1babbfd5e1b766d12b659853b514b9faedf6333cbe8cb514b1a3e79b7ce39"
},
{
name: "kstuff-lite",
filename: "kstuff-lite_v1.03.elf",
url: "https://itsplk.github.io/ps5_payloads/payloads/kstuff-lite_v1.03.elf",
description: "Lite version of kstuff",
version: "v1.03",
checksum: "54df47a48d9c5ee4338ef70ba66093908a4f2845e53468bdd7c080b65d7488c1"
}
];
const communityRepository = [
{
name: "PS5 Hen Test",
filename: "ps5_hen_test_v1.0.elf",
url: "https://example.com/payloads/ps5_hen_test_v1.0.elf",
description: "Community hen test payload — no checksum",
version: "v1.0"
},
{
name: "Debug Tool",
filename: "debug_tool_v2.1.elf",
url: "https://example.com/payloads/debug_tool_v2.1.elf",
description: "Debug utility from community repo",
version: "v2.1"
}
];
let lastRepositoryUpdate = Math.floor(Date.now() / 1000);
let mockSources = [
{ id: 'default', name: 'Official Repository', url: 'https://itsplk.github.io/ps5-payloads-mirror/payloads.json', removable: false },
{ id: 'source_community', name: 'Community Payloads', url: 'https://example.com/community-payloads.json', removable: true }
];
let autoloadStatus = {
remaining: 10,
total: 2,
done: 0,
current: "IDLE",
list: "goldhen_v2.4b17.elf,etaHEN_1.8.elf"
};
const autoloadList = autoloadStatus.list.split(',');
let simulationTicks = 0;
setInterval(() => {
if (autoloadStatus.remaining > 0) {
autoloadStatus.remaining--;
if (autoloadStatus.remaining === 0) {
autoloadStatus.current = autoloadList[0];
logs.push(`[PLDMGR] Autoload sequence started: ${autoloadList[0]}`);
}
} else if (autoloadStatus.remaining === 0 && autoloadStatus.current !== "DONE" && autoloadStatus.current !== "IDLE") {
simulationTicks++;
if (simulationTicks >= 3) { // 3 seconds per payload simulation
simulationTicks = 0;
autoloadStatus.done++;
if (autoloadStatus.done < autoloadStatus.total) {
autoloadStatus.current = autoloadList[autoloadStatus.done];
logs.push(`[PLDMGR] Autoloading: ${autoloadStatus.current}`);
} else {
autoloadStatus.current = "DONE";
logs.push(`[PLDMGR] Autoload sequence complete`);
}
}
}
}, 1000);
// --- API Routes ---
app.get('/getip', (req, res) => {
res.send('127.0.0.1');
});
app.get('/version', (req, res) => {
res.send('1.0.2-mock');
});
app.get('/list_payloads', (req, res) => {
res.json({
payloads: [
"/data/pldmgr/goldhen_v2.4b17.elf",
"/data/pldmgr/etaHEN_1.8.elf",
"/data/pldmgr/kstuff.elf",
"/mnt/usb0/pldmgr/linux_loader.elf"
],
meta: {
// payloads with no entry here = local/direct upload (no badge shown)
"kstuff.elf": {
source_name: "Community Payloads",
install_source: "repository",
install_source_detail: "https://example.com/community-payloads.json"
}
}
});
});
app.get('/autoload_status', (req, res) => {
res.json(autoloadStatus);
});
app.get('/get_config', (req, res) => {
res.json({
AUTOLOAD_ENABLED: true,
AUTOLOAD_LIST: "goldhen_v2.4b17.elf,etaHEN_1.8.elf",
LAST_REPOSITORY_UPDATE: lastRepositoryUpdate,
AUTO_INSTALL_APP: true,
MULTI_SOURCES_ENABLED: true
});
});
app.get('/repository_payloads', (req, res) => {
// Return source-grouped format (multi-source mode always on in mock)
res.json({
sources: [
{
id: 'default',
name: 'Official Repository',
last_update: lastRepositoryUpdate,
error: false,
payloads: remoteRepository.map(p => ({ ...p, source_id: 'default', source_name: 'Official Repository' }))
},
{
id: 'source_community',
name: 'Community Payloads',
last_update: lastRepositoryUpdate,
error: false,
payloads: communityRepository.map(p => ({ ...p, source_id: 'source_community', source_name: 'Community Payloads' }))
}
]
});
});
app.get('/repository_refresh', (req, res) => {
lastRepositoryUpdate = Math.floor(Date.now() / 1000);
logs.push(`[PLDMGR] Repository manually refreshed`);
res.json({
sources: [
{
id: 'default',
name: 'Official Repository',
last_update: lastRepositoryUpdate,
error: false,
payloads: remoteRepository.map(p => ({ ...p, source_id: 'default', source_name: 'Official Repository' }))
},
{
id: 'source_community',
name: 'Community Payloads',
last_update: lastRepositoryUpdate,
error: false,
payloads: communityRepository.map(p => ({ ...p, source_id: 'source_community', source_name: 'Community Payloads' }))
}
]
});
});
app.get('/sources_list', (req, res) => {
res.json({ sources: mockSources });
});
app.post('/sources_set', (req, res) => {
const { sources } = req.body;
if (Array.isArray(sources)) {
mockSources = sources;
logs.push(`[PLDMGR] Sources updated: ${sources.length} sources`);
}
res.send('OK');
});
app.get('/sources_add', (req, res) => {
const { url } = req.query;
if (!url) return res.status(400).json({ ok: false, message: 'Missing url' });
// Mock: always succeed with a fake source name derived from the URL
const fakeName = `Community Source (${new URL(url).hostname})`;
const newSource = { id: `source_${Date.now()}`, name: fakeName, url, removable: true };
mockSources.push(newSource);
logs.push(`[PLDMGR] Source added: ${fakeName}`);
res.json({ ok: true, name: fakeName });
});
app.get('/sources_remove', (req, res) => {
const idx = parseInt(req.query.index);
if (isNaN(idx) || idx <= 0 || idx >= mockSources.length) {
return res.status(400).json({ ok: false, message: 'Invalid index or cannot remove default source' });
}
const removed = mockSources.splice(idx, 1);
logs.push(`[PLDMGR] Source removed: ${removed[0]?.name}`);
res.json({ ok: true, message: 'OK' });
});
app.get('/repository_install', (req, res) => {
const { filename } = req.query;
if (!filename) {
return res.status(400).json({ ok: false, message: 'Missing filename' });
}
logs.push(`[PLDMGR] Repository install requested: ${filename}`);
res.json({ ok: true, message: `Installed ${filename}` });
});
app.post('/set_config', (req, res) => {
console.log('Received Config:', req.body);
logs.push(`[PLDMGR] Config updated: ${JSON.stringify(req.body)}`);
res.send('OK');
});
app.get(/^\/loadpayload:(.*)/, (req, res) => {
const path = req.params[0];
logs.push(`[PLDMGR] Executing payload: ${path}`);
res.send('OK');
});
app.get('/manage\\:delete', (req, res) => {
const filename = req.query.filename;
logs.push(`[PLDMGR] Deleted payload: ${filename}`);
res.send('OK');
});
app.post('/manage\\:upload', (req, res) => {
const filename = req.query.filename;
logs.push(`[PLDMGR] Uploaded payload: ${filename}`);
res.send('OK');
});
app.get('/abort', (req, res) => {
logs.push(`[PLDMGR] Autoload sequence aborted by user`);
autoloadStatus.remaining = -1;
autoloadStatus.current = "IDLE";
res.send('OK');
});
app.get('/autoload_clear', (req, res) => {
logs.push(`[PLDMGR] Autoload status cleared`);
autoloadStatus.done = 0;
autoloadStatus.current = "IDLE";
autoloadStatus.remaining = -1;
res.send('OK');
});
app.get('/shutdown', (req, res) => {
logs.push(`[PLDMGR] System shutdown requested`);
res.send('Shutting down...');
process.exit(0);
});
// SSE for logs
app.get('/events', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const sendLog = (data) => {
res.write(`data: ${data}\n\n`);
};
// Send history
logs.forEach(sendLog);
// Poll for new logs
let lastCount = logs.length;
const interval = setInterval(() => {
if (logs.length > lastCount) {
for (let i = lastCount; i < logs.length; i++) {
sendLog(logs[i]);
}
lastCount = logs.length;
}
}, 500);
req.on('close', () => {
clearInterval(interval);
});
});
app.listen(PORT, '127.0.0.1', () => {
console.log(`\x1b[36m%s\x1b[0m`, `--- Payload Manager Mock Backend ---`);
console.log(`Running at http://localhost:${PORT}`);
console.log(`Proxy your Vite requests to this port to test the frontend locally.`);
});