Skip to content

Commit

Permalink
fix web service error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
a-sync committed Mar 23, 2024
1 parent 4e44586 commit 7269213
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 94 deletions.
4 changes: 2 additions & 2 deletions public/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ let configEditor;
$(async () => {
const gswFeatures = await fetchApi('GET', 'features');
if (gswFeatures) {
$('#versions').html('gsw v' + gswFeatures.features.version + '<br>gamedig v' + gswFeatures.features.gamedig);
$('#versions').html('gsw v' + gswFeatures.versions.gsw + '<br>gamedig v' + gswFeatures.versions.gamedig);

await setRandomBg().catch(() => { });
setInterval(setRandomBg, 60000);
Expand Down Expand Up @@ -46,7 +46,7 @@ $(async () => {
"ajax": true,
"ajaxCredentials": false,
"ajax_cache_responses": !Boolean(gswFeatures.debug),
"ajax_cache_buster": 'gsw-v' + gswFeatures.features.version + '+gamedig-v' + gswFeatures.features.gamedig,
"ajax_cache_buster": 'gsw-v' + gswFeatures.versions.gsw + '+gamedig-v' + gswFeatures.versions.gamedig,
"disable_edit_json": false,
"disable_collapse": true,
"disable_properties": false,
Expand Down
203 changes: 111 additions & 92 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ interface ApiResponse extends FeaturesResponse, ConfigResponse {
}

interface FeaturesResponse{
features?: {
version: string;
versions?: {
gsw: string;
gamedig: string;
}
services?: {
steam: boolean;
discord: boolean;
telegram: boolean;
Expand Down Expand Up @@ -66,110 +68,127 @@ watcher.start();
createServer(async (req, res) => {
if (DBG) console.log('DBG: %j %j', (new Date()), req.url);

const reqUrl = new URL(req.url || '', 'http://localhost');
const p = reqUrl.pathname === '/' ? 'index.html' : reqUrl.pathname.slice(1);
const ext = path.extname(p).slice(1);
try {
const reqUrl = new URL(req.url || '', 'http://localhost');
const p = req.url === '/' ? 'index.html' : reqUrl.pathname.slice(1);
const ext = path.extname(p).slice(1);

if (ext in EXT_MIME && !p.includes('/') && !p.includes('\\')) {
if (SECRET !== '') {
const filePath = path.resolve('./public/', p);
if (fs.existsSync(filePath)) {
res.writeHead(200, {
'Content-Type': EXT_MIME[ext] || 'plain/text'
});
fs.createReadStream(filePath).on('error', (err: any) => {
console.error(err?.message || err);
res.end();
}).pipe(res);
} else {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<html><head></head><body>404 &#x1F4A2</body></html>');
}
} else {
res.end('Configure the `SECRET` env var to enable the web UI!');
}
} else if (p === 'ping') {
if (DBG) console.log('ping');
res.end('pong');
} else if (p === 'gamedig-games') {
const gdProtocols = Object.keys(protocols).map(p => `protocol-${p}`);
const gdGameTypes = [];
const gdGamesNames = [];

for (const [type, g] of Object.entries(games)) {
gdGameTypes.push(type);
gdGamesNames.push(g.name + ' (' + g.release_year + ')');
}

if (ext in EXT_MIME && !p.includes('/') && !p.includes('\\')) {
if (SECRET !== '') {
res.writeHead(200, {
'Content-Type': EXT_MIME[ext] || 'plain/text'
'Content-Type': 'application/json',
'Cache-Control': 'max-age=0'
});
fs.createReadStream(path.resolve('./public/', p)).pipe(res);
} else {
res.end('Configure the `SECRET` env var to enable the web UI!');
}
} else if (p === 'ping') {
if (DBG) console.log('ping');
res.end('pong');
} else if (p === 'gamedig-games') {
const gdProtocols = Object.keys(protocols).map(p => `protocol-${p}`);
const gdGameTypes = [];
const gdGamesNames = [];

for (const [type, g] of Object.entries(games)) {
gdGameTypes.push(type);
gdGamesNames.push(g.name + ' (' + g.release_year + ')');
}

res.writeHead(200, {
'Content-Type': 'application/json',
'Cache-Control': 'max-age=0'
});

res.end(JSON.stringify({
enum: [...gdGameTypes, ...gdProtocols],
options: {
enum_titles: [...gdGamesNames, ...gdProtocols]
}
} as SelectOptionsResponse, null, DBG ? 2 : 0));
} else if (SECRET !== '' && req.headers['x-btoken']) {
let status = 200;
let re: ApiResponse = {};

if (DBG) re.debug = true;

if (validateBearerToken(String(req.headers['x-btoken']))) {
const reqPath = p.split('/');
try {
if (reqPath[0] === 'features') {
re.features = {
version: String(gswVersion),
gamedig: String(gamedigVersion),
steam: Boolean(process.env.STEAM_WEB_API_KEY),
discord: Boolean(process.env.DISCORD_BOT_TOKEN),
telegram: Boolean(process.env.TELEGRAM_BOT_TOKEN),
slack: Boolean(process.env.SLACK_BOT_TOKEN && process.env.SLACK_APP_TOKEN)
};
} else if (reqPath[0] === 'config') {
if (req.method === 'GET') {
re.config = await readConfig();
} else if (req.method === 'POST') {
const body = await new Promise(resolve => {
let body = '';
req.on('data', (chunk: string) => {
body += chunk;
});
req.on('end', () => {
resolve(body);
res.end(JSON.stringify({
enum: [...gdGameTypes, ...gdProtocols],
options: {
enum_titles: [...gdGamesNames, ...gdProtocols]
}
} as SelectOptionsResponse, null, DBG ? 2 : 0));
} else if (SECRET !== '' && req.headers['x-btoken']) {
let status = 200;
let re: ApiResponse = {};

if (validateBearerToken(String(req.headers['x-btoken']))) {
const reqPath = p.split('/');
try {
if (reqPath[0] === 'features') {
if (DBG) re.debug = true;
re.versions = {
gsw: String(gswVersion),
gamedig: String(gamedigVersion)
};
re.services = {
steam: Boolean(process.env.STEAM_WEB_API_KEY),
discord: Boolean(process.env.DISCORD_BOT_TOKEN),
telegram: Boolean(process.env.TELEGRAM_BOT_TOKEN),
slack: Boolean(process.env.SLACK_BOT_TOKEN && process.env.SLACK_APP_TOKEN)
};
} else if (reqPath[0] === 'config') {
if (req.method === 'GET') {
re.config = await readConfig();
} else if (req.method === 'POST') {
const body = await new Promise(resolve => {
let body = '';
req.on('data', (chunk: string) => {
body += chunk;
});
req.on('end', () => {
resolve(body);
});
});
});

// TODO: validate (ajv)
await updateConfig(JSON.parse(String(body)) || [] as GameServerConfig[]);
await watcher.restart();

re.message = 'Configuration updated. Watcher restarted.';
// TODO: validate (ajv)
await updateConfig(JSON.parse(String(body)) || [] as GameServerConfig[]);
await watcher.restart();

re.message = 'Configuration updated. Watcher restarted.';
} else {
status = 400;
re.error = 'Invalid Request';
}
} else if (reqPath[0] === 'flush' && ['servers', 'discord', 'telegram', 'slack'].includes(reqPath[1])) {
//TODO: check for and append host:port if available
await watcher.restart(reqPath[1]);
re.message = '🗑️ ' + reqPath[1].slice(0, 1).toUpperCase() + reqPath[1].slice(1) + ' data flushed.';
} else {
status = 400;
re.error = 'Invalid Request';
}
} else if (reqPath[0] === 'flush' && ['servers', 'discord', 'telegram', 'slack'].includes(reqPath[1])) {
//TODO: check for and append host:port if available
await watcher.restart(reqPath[1]);
re.message = '🗑️ ' + reqPath[1].slice(0, 1).toUpperCase() + reqPath[1].slice(1) + ' data flushed.';
} else {
status = 400;
re.error = 'Invalid Request';
} catch (err: any) {
status = 500;
re.error = err.message || String(err);
}
} catch (err: any) {
status = 500;
re.error = err.message || String(err);
} else {
status = 401;
re.error = 'Unauthorized';
}
} else {
status = 401;
re.error = 'Unauthorized';
}

res.writeHead(status, {
'Content-Type': 'application/json',
'Cache-Control': 'max-age=0'
});
res.writeHead(status, {
'Content-Type': 'application/json',
'Cache-Control': 'max-age=0'
});

res.end(JSON.stringify(re, null, DBG ? 2 : 0));
} else {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<html><head></head><body>404 &#x1F4A2</body></html>');
res.end(JSON.stringify(re, null, DBG ? 2 : 0));
} else {
res.writeHead(400, { 'Content-Type': 'text/html' });
res.end('<html><head></head><body>400 &#x1F4A2</body></html>');
}
} catch (err: any) {
if (DBG) console.error(err?.message || err);
const code = err.message === 'Invalid URL' ? 400 : 500;
res.writeHead(code, { 'Content-Type': 'text/html' });
res.end(`<html><head></head><body>${code} &#x1F4A2</body></html>`);
}
}).listen(PORT, HOST, () => {
console.log('GSW Control Panel service started %s:%s', HOST, PORT);
Expand Down

0 comments on commit 7269213

Please sign in to comment.