require('./lib/Extensions').install(); import * as express from 'express'; import * as bodyParser from 'body-parser'; import __rootDir from './lib/RootDirFinder'; import { c } from './lib/Log'; import db from './models/Database'; import Doc, { ALL_MODELS, ModelId } from './models/Doc'; import { config } from './lib/config'; import { hbs } from './lib/Hbs'; const app = express(); const PORT = process.env.PORT || config.appPort; const __frontDir = __rootDir+`/front`; // Express setup app.set('trust proxy', 'loopback'); app.disable('x-powered-by'); app.use(bodyParser.json()); app.set('views', `${__frontDir}/views`); app.set('view engine', 'hbs'); app.engine('hbs', hbs.__express); const staticHandler = express.static(__frontDir); const staticHandlerNoHtml: express.RequestHandler = function(req, res, next) { if (req.path === '/' || req.path.endsWith('.html') || req.path.endsWith('.hbs')) { return next(); } else { return staticHandler(req, res, next); } }; app.get('/favicon.ico', (req, res) => res.sendFile(`${__frontDir}/favicon.ico`)); app.get('/robots.txt', (req, res) => res.sendFile(`${__frontDir}/robots.txt`)); app.use('/front', staticHandlerNoHtml); /// ^^ in production, those aren't used b/c we serve static files from nginx. /** * Routes(html) */ app.get('/', async function(req, res) { const docs = await db.docs .find({ featured: true }) .sort({ _id: -1 }) .limit(10) .project({ shortId: true, title: true }) .toArray() ; /// ^^ cache this query at some point for performance. res.render('landing', { body_classes: `landing`, docs }); }); app.get('/model/:model', function(req, res) { const model = req.params.model; if (! ['arxiv-nlp', 'distil-gpt2', 'ctrl', 'pplm'].includes(model)) { return res.sendStatus(404); } res.render('model', { body_classes: `model`, model, meta: { /// ^^ do NOT call this key `layout`. title: (model === 'distil-gpt2') ? `🐎 DistilGPT-2 model checkpoint` : (model === 'ctrl') ? `☁️ Salesforce Research CTRL` : (model === 'pplm') ? `🚕 Uber AI Plug and Play Language Model` : `🤓 ArXiv NLP model checkpoint`, thumbnail: (model === 'distil-gpt2') ? 'thumbnail-large-distilgpt2' : (model === 'pplm') ? `thumbnail-large-pplm` : undefined, path: req.path, }, }); }); app.get('/doc/:model', function(req, res) { const model = req.params.model; if (! ALL_MODELS.includes(model)) { return res.sendStatus(404); } const doc = Doc.seed(model as ModelId); /// ^^ new document. It doesn't exist in the db yet, /// will only be stored if user presses save. res.render('index', { body_classes: `app`, editable: true, doc, }); }); app.get('/doc/:model/:id/edit', async function(req, res) { const model = req.params.model; if (! ALL_MODELS.includes(model)) { return res.sendStatus(404); } const doc = await Doc.findOne({ longId: req.params.id, }); if (!doc) { return res.sendStatus(404); } /// Existing document, accessed through its private edit url. res.render('index', { body_classes: `app`, editable: true, doc, }); }); app.get('/share/:shortId', async function(req, res) { const doc = await Doc.findOne(req.params.shortId); if (!doc) { return res.sendStatus(404); } /// Existing document, accessed through its public share url. /// CAUTION: Make sure we don't expose the edit url! delete doc.longId; res.render('index', { body_classes: `app`, editable: false, doc, }); }); /** * Routes(ajax) */ app.post('/edit/:model/:longId/:shortId', async function(req, res) { const query = { shortId: req.params.shortId, longId: req.params.longId, model: req.params.model, }; c.debug(`––`); c.log(`Attempting to save doc`, query); const result = await db.docs.updateOne( query, { $set: req.body }, { upsert: true } ); c.log(result.result); res.sendStatus(200); }); app.post('/duplicate/:shortId', async function(req, res) { const doc = await Doc.findOne(req.params.shortId); if (!doc) { return res.sendStatus(404); } c.debug(`––`); c.log(`Duplicating doc`, doc.shortId); const newdoc = await doc.duplicate(); res.send(newdoc.editUrl); }); // Start engine. (async () => { await db.connect(); app.listen(PORT, () => { c.debug(`Running on http://localhost:${PORT}`); }); })();