./src/index.js annotated source

Back to index

        
1const fs = require('fs');
2
3const config = require('../config.js');
4
5const express = require('express');
6const bodyParser = require('body-parser');
7const app = express();
8app.use(bodyParser.text({
9    limit: '50kb',
10}));
11
12const api = require('./api.js');
13
14const CONTENT_TYPES = {
15    '.js': 'text/javascript',
16    '.html': 'text/html',
17    '.ico': 'image/x-icon',
18}
19
20// STATIC ASSETS
21const STATIC_PATHS = {
22    '/': 'index.html',
23
24    // Editor alias routes
25    '/new': 'editor.html',
26    '/welcome': 'editor.html',
27
28    '/favicon.ico': 'assets/favicon.ico',
29    '/h/:htmlFrameHash/j/:jsFrameHash/edit': 'editor.html',
30}
31const respondWith = (res, static_path) => {
32    fs.readFile(`static/${static_path}`, (err, data) => {
33        if (err) {
34            throw err;
35        }
36

We determine the content-type based on requested resource file ending, and fall back to HTML.

39        let contentType = 'text/html';
40        for (const [ending, type] of Object.entries(CONTENT_TYPES)) {
41            if (static_path.endsWith(ending)) {
42                contentType = type;
43                break;
44            }
45        }
46
47        res.set('Content-Type', contentType);
48        res.send(data);
49    });
50}
51for (const [uri, path] of Object.entries(STATIC_PATHS)) {
52    app.get(uri, (_req, res) => {
53        try {
54            respondWith(res, path);
55        } catch (e) {
56            console.error(e);
57            // For now, assume it's a not-found error
58            respondWith(res, '404.html');
59        }
60    });
61}
62app.use('/static', express.static('static'));
63

Easily redirect /f/*.html/edit to an editor view to edit statically rendered Codeframes by just appending /edit to the URL.

66app.get('/f/:htmlFrameHash/:jsFrameHash.html/edit', (req, res) => {
67    res.redirect(302, `/h/${req.params.htmlFrameHash}/j/${req.params.jsFrameHash}/edit`);
68});
69
70// API
71const API_PATHS = {
72    'GET /api/frame/:frameHash': api.frame.get,
73    'POST /api/frame/': api.frame.post,
74
75    'GET /f/:htmlFrameHash/:jsFrameHash.html': api.frame.getPage,
76}
77const METHODS = ['GET', 'POST', 'PUT', 'DELETE'];
78for (const [spec, handler] of Object.entries(API_PATHS)) {
79    const [method, route] = spec.split(' ');
80    let appMethod;
81    if (METHODS.includes(method)) {
82        appMethod = app[method.toLowerCase()].bind(app);
83    } else {
84        throw new Error(`Method ${method} for route ${route} is not valid`);
85    }
86

We determine the content-type based on requested resource file ending, and fall back to JSON.

89    let contentType = 'application/json';
90    for (const [ending, type] of Object.entries(CONTENT_TYPES)) {
91        if (route.endsWith(ending)) {
92            contentType = type;
93            break;
94        }
95    }
96
97    appMethod(route, async (req, res) => {
98        try {
99            res.set('Content-Type', contentType);
100            const result = await handler(req.params, req.query, req.body);
101            if (typeof result === 'string') {
102                res.send(result);
103            } else {
104                res.send(JSON.stringify(result));
105            }
106        } catch (e) {
107            console.error(e);
108            res.send('error');
109        }
110    });
111}
112
113// 404 last
114app.use((_req, res) => respondWith(res, '404.html'));
115
116app.listen(
117    config.PORT,
118    () => console.log(`Codeframe running on localhost:${config.PORT}`),
119);
120