mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-20 23:37:41 +00:00
209 lines
6.9 KiB
TypeScript
209 lines
6.9 KiB
TypeScript
import express = require('express');
|
|
import multer = require('multer');
|
|
import ws = require('ws');
|
|
import handleIncKeyboardsJs from './handlers/inc/keyboards-js';
|
|
import { data, DebugFont, DebugKeyboard, DebugModel, DebugObject, DebugPackage, isValidId } from './data';
|
|
import apiGet from './handlers/api/debugobject/get';
|
|
import apiRegister, { apiRegisterFile } from './handlers/api/debugobject/register';
|
|
import apiKeyboardRegister from './handlers/api/keyboard/register';
|
|
import apiFontRegister from './handlers/api/font/register';
|
|
import apiUnregister from './handlers/api/debugobject/unregister';
|
|
import handleIncPackagesJson from './handlers/inc/packages-json';
|
|
import apiPackageRegister from './handlers/api/package/register';
|
|
import handleIncKeyboardsCss from './handlers/inc/keyboards-css';
|
|
import { Environment } from './version-data';
|
|
import { configuration } from './config';
|
|
import chalk = require('chalk');
|
|
|
|
export default function setupRoutes(app: express.Express, upload: multer.Multer, wsServer: ws.Server, environment: Environment ) {
|
|
|
|
/* Middleware - JSON and logging */
|
|
|
|
app.use(express.json()); // for parsing application/json
|
|
|
|
app.use(function (req, _res, next) {
|
|
// if(environment.environment == 'local') {
|
|
console.log(req.method + ' ' + req.path);
|
|
// }
|
|
next();
|
|
});
|
|
|
|
function isLocalhost(req: express.Request) {
|
|
return (
|
|
req.socket.remoteAddress == '127.0.0.1' ||
|
|
req.socket.remoteAddress == '::1' ||
|
|
req.socket.remoteAddress == '::ffff:127.0.0.1' // ipv4 localhost over ipv6
|
|
);
|
|
}
|
|
function localhostOnly(req: express.Request, res: express.Response, next: express.NextFunction) {
|
|
if(!isLocalhost(req)) {
|
|
res.sendStatus(401);
|
|
} else {
|
|
next();
|
|
}
|
|
}
|
|
|
|
// Only allow connections from localhost to /api/
|
|
|
|
app.use('/api/', localhostOnly);
|
|
|
|
/* All routes */
|
|
|
|
app.use('/', express.static('dist/site'));
|
|
|
|
app.post('/upload', localhostOnly, upload.single('file'), (req, res, next) => {
|
|
const name = req.file.originalname;
|
|
if(!isValidId(name)) {
|
|
res.sendStatus(400);
|
|
return;
|
|
}
|
|
let fileType = '';
|
|
const rModel = /^([a-zA-Z0-9_\.-]+)\.model\.js$/.exec(name);
|
|
const rKeyboard = /^([a-zA-Z0-9_]+)\.js$/.exec(name);
|
|
const rPackage = /^([a-zA-Z0-9_]+)\.kmp$/.exec(name);
|
|
const rFont = /^(.+\.(ttf|otf))$/.exec(name);
|
|
if(rModel) {
|
|
apiRegisterFile(DebugModel, data.models, rModel[1], req.file.buffer);
|
|
fileType = 'model';
|
|
} else if(rKeyboard) {
|
|
apiRegisterFile(DebugKeyboard, data.keyboards, rKeyboard[1], req.file.buffer);
|
|
fileType = 'keyboard';
|
|
} else if(rPackage) {
|
|
apiRegisterFile(DebugPackage, data.packages, rPackage[1], req.file.buffer);
|
|
fileType = 'package';
|
|
} else if(rFont) {
|
|
apiRegisterFile(DebugFont, data.fonts, rFont[1], req.file.buffer);
|
|
fileType = 'font';
|
|
} else {
|
|
res.status(400).json({message: 'unrecognised file type'});
|
|
return;
|
|
}
|
|
|
|
res.json({message: 'success', type: fileType});
|
|
next();
|
|
},
|
|
saveState,
|
|
(req,res,next)=>notifyClients(wsServer,req,res,next)
|
|
);
|
|
|
|
app.get('/inc/keyboards.js', handleIncKeyboardsJs);
|
|
app.get('/inc/keyboards.css', handleIncKeyboardsCss);
|
|
app.get('/inc/packages.json', handleIncPackagesJson);
|
|
|
|
app.get('/api-public/version', (req,res,next)=>{
|
|
res.json({version: environment.versionWithTag, isApiAvailable: isLocalhost(req)});
|
|
next();
|
|
});
|
|
|
|
/* Localhost only routes -- todo /api/internal/ vs /api/... */
|
|
|
|
app.post('/api/shutdown', (_req,res) => { setTimeout(() => process.exit(0), 100); res.send('ok'); });
|
|
|
|
appGetData(app, /\/data\/keyboard\/(.+)\.js$/, data.keyboards);
|
|
appGetData(app, /\/data\/model\/(.+)\.model\.js$/, data.models);
|
|
appGetData(app, /\/data\/package\/(.+)\.kmp$/, data.packages);
|
|
appGetData(app, /\/data\/font\/(.+)\.ttf$/, data.fonts);
|
|
|
|
app.get('/api/font', (req,res,next)=>apiGet(data.fonts,req,res,next));
|
|
app.post('/api/font/register',
|
|
upload.single('file'),
|
|
(req,res,next)=>apiRegister(DebugFont, data.fonts, req, res, next),
|
|
apiFontRegister,
|
|
saveState,
|
|
(req,res,next)=>notifyClients(wsServer,req,res,next)
|
|
);
|
|
app.post('/api/font/unregister',
|
|
(req,res,next)=>apiUnregister(data.fonts,req,res,next),
|
|
saveState,
|
|
(req,res,next)=>notifyClients(wsServer,req,res,next)
|
|
);
|
|
|
|
app.get('/api/keyboard', (req,res,next)=>apiGet(data.keyboards,req,res,next));
|
|
app.post('/api/keyboard/register',
|
|
upload.single('file'),
|
|
(req,res,next)=>apiRegister(DebugKeyboard, data.keyboards, req, res, next),
|
|
apiKeyboardRegister,
|
|
saveState,
|
|
(req,res,next)=>notifyClients(wsServer,req,res,next)
|
|
);
|
|
app.post('/api/keyboard/unregister',
|
|
(req,res,next)=>apiUnregister(data.keyboards,req,res,next),
|
|
saveState,
|
|
(req,res,next)=>notifyClients(wsServer,req,res,next)
|
|
);
|
|
|
|
app.get('/api/model', (req,res,next)=>apiGet(data.models,req,res,next));
|
|
app.post('/api/model/register',
|
|
upload.single('file'),
|
|
(req,res,next)=>apiRegister(DebugModel, data.models, req, res, next),
|
|
saveState,
|
|
(req,res,next)=>notifyClients(wsServer,req,res,next)
|
|
);
|
|
app.post('/api/model/unregister',
|
|
(req,res,next)=>apiUnregister(data.models,req,res,next),
|
|
saveState,
|
|
(req,res,next)=>notifyClients(wsServer,req,res,next)
|
|
);
|
|
|
|
app.get('/api/package', (req,res,next)=>apiGet(data.packages,req,res,next));
|
|
app.post('/api/package/register',
|
|
upload.single('file'),
|
|
(req,res,next)=>apiRegister(DebugPackage, data.packages, req, res, next),
|
|
apiPackageRegister,
|
|
saveState,
|
|
(req,res,next)=>notifyClients(wsServer,req,res,next)
|
|
);
|
|
app.post('/api/package/unregister',
|
|
(req,res,next)=>apiUnregister(data.packages,req,res,next),
|
|
saveState,
|
|
(req,res,next)=>notifyClients(wsServer,req,res,next)
|
|
);
|
|
|
|
/* ngrok data */
|
|
|
|
app.get('/api/status', (_req,res,next) => {
|
|
const response = { ngrokEnabled: configuration.useNgrok, ngrokEndpoint: configuration.ngrokEndpoint };
|
|
res.send(response);
|
|
next();
|
|
});
|
|
}
|
|
|
|
/* Utility functions */
|
|
|
|
function notifyClients(wsServer: ws.Server, res: express.Request, req: express.Response, next: express.NextFunction) {
|
|
wsServer.clients.forEach(c => {
|
|
c.send('refresh', (err) => {
|
|
if(err) console.error(chalk.red('Websocket send error '+err.message));
|
|
});
|
|
});
|
|
next();
|
|
}
|
|
|
|
function saveState (res: express.Request, req: express.Response, next: express.NextFunction) {
|
|
data.saveState();
|
|
if(next) next();
|
|
}
|
|
|
|
function appGetData(app: express.Express, pathregex: RegExp, root: { [id: string]: DebugObject }) {
|
|
app.get(pathregex, (req,res,next)=>{
|
|
|
|
const r = pathregex.exec(req.path);
|
|
if(!r) {
|
|
res.status(404).send('invalid path');
|
|
return;
|
|
}
|
|
if(!isValidId(r[1])) {
|
|
res.sendStatus(400);
|
|
return;
|
|
}
|
|
const o = root[r[1]];
|
|
if(!o) {
|
|
res.status(404).send('not found');
|
|
} else {
|
|
o.lastUse = new Date();
|
|
saveState(null, null, null);
|
|
res.sendFile(o.filename, (err)=>{if(err) console.error(chalk.red(err))});
|
|
}
|
|
});
|
|
}
|
|
|