-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.ts
101 lines (79 loc) · 3.01 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import express from 'express';
import bodyParser from 'body-parser';
import wrap from 'express-async-wrap';
import request from 'request-promise';
import dotenv from 'dotenv';
import path from 'path';
import shajs from 'sha.js';
import Keyv from 'keyv';
dotenv.config();
const asciidoctor = require('@asciidoctor/core')();
import asciidoctorRevealjs from '@asciidoctor/reveal.js';
asciidoctorRevealjs.register();
const PORT = process.env.PORT || 5000;
const SAVED_SLIDES_CACHE_TTL_MS = parseInt(process.env.SAVED_SLIDES_CACHE_TTL_MS ?? "10000");
express()
.use(bodyParser.json())
// Serve frontend static files
.use(express.static(path.join(__dirname, '../client/build')))
.get('/render', wrap(handle_render_slides))
.post('/save', wrap(handle_save_slides))
// Render built react application in production
.get('*', (_, res) => res.sendFile(path.join(__dirname, '../client/build/index.html')))
.listen(PORT, () => console.log(`Listening on ${ PORT }`));
function extract_base_address(url: string): string | undefined {
const last_slash = url.lastIndexOf('/');
return last_slash != -1 ? url.substring(0, last_slash) : undefined;
}
type RenderRequest = {
readonly text?: string;
readonly url?: string;
readonly slides_id?: SlidesId;
}
async function handle_render_slides(req: express.Request, res: express.Response) {
const query = req.query as RenderRequest;
res.send(await render_slides(query));
}
async function render_slides(query: RenderRequest): Promise<string> {
if (query.text != undefined) {
return conver_ascii_doc_to_slides(query.text);
}
if (query.url != undefined) {
const slides_downloaded_source_code = await request(query.url);
const base_address = extract_base_address(query.url);
return conver_ascii_doc_to_slides(slides_downloaded_source_code, base_address);
}
const saved_slides_source_code = await saved_slides_cache.get(query.slides_id!!) || "";
return conver_ascii_doc_to_slides(saved_slides_source_code);
}
function conver_ascii_doc_to_slides(adoc: string, imagesdir?: string): string {
const options = {
safe: 'safe',
backend: 'revealjs',
header_footer: true,
attributes: {
revealjsdir: `https://cdnjs.cloudflare.com/ajax/libs/reveal.js/${process.env.REVEALJS_VERSION}/`,
revealjs_history: "true", // enables slide anchors in the url
imagesdir
}
};
return asciidoctor.convert(adoc, options);
}
type SaveSlidesRequest = {
readonly text: string;
}
type SlidesId = string;
// TODO: replace with RLU cache
const saved_slides_cache = new Keyv<string>();
async function handle_save_slides(req: express.Request, res: express.Response) {
const slides_id = await save_slides_text(req.body);
res.end(slides_id);
}
async function save_slides_text(query: SaveSlidesRequest): Promise<SlidesId> {
const slides_id = hash(query.text);
await saved_slides_cache.set(slides_id, query.text, SAVED_SLIDES_CACHE_TTL_MS);
return slides_id;
}
function hash(text: string): string {
return shajs('sha256').update(text).digest('hex');
}