· 5 years ago · Aug 22, 2020, 10:20 AM
1const express = require('express');
2const bodyParser = require('body-parser');
3const utils = require('./utils');
4const Recaptcha = require('express-recaptcha').RecaptchaV3;
5const uuidv4 = require('uuid').v4;
6const Datastore = require('@google-cloud/datastore').Datastore;
7
8/* Just reCAPTCHA stuff. */
9const CAPTCHA_SITE_KEY = process.env.CAPTCHA_SITE_KEY || 'site-key';
10const CAPTCHA_SECRET_KEY = process.env.CAPTCHA_SECRET_KEY || 'secret-key';
11console.log("Captcha(%s, %s)", CAPTCHA_SECRET_KEY, CAPTCHA_SITE_KEY);
12const recaptcha = new Recaptcha(CAPTCHA_SITE_KEY, CAPTCHA_SECRET_KEY, {
13 'hl': 'en',
14 callback: 'captcha_cb'
15});
16
17/* Choo Choo! */
18const app = express();
19app.set('view engine', 'ejs');
20app.set('strict routing', true);
21app.use(utils.domains_mw);
22app.use('/static', express.static('static', {
23 etag: true,
24 maxAge: 300 * 1000,
25}));
26
27/* They say reCAPTCHA needs those. But does it? */
28app.use(bodyParser.urlencoded({
29 extended: true
30}));
31
32/* Just a datastore. I would be surprised if it's fragile. */
33class Database {
34 constructor() {
35 this._db = new Datastore({
36 namespace: 'littlethings'
37 });
38 }
39 add_note(note_id, content) {
40 const note = {
41 note_id: note_id,
42 owner: 'guest',
43 content: content,
44 public: 1,
45 created: Date.now()
46 }
47 return this._db.save({
48 key: this._db.key(['Note', note_id]),
49 data: note,
50 excludeFromIndexes: ['content']
51 });
52 }
53 async get_note(note_id) {
54 const key = this._db.key(['Note', note_id]);
55 let note;
56 try {
57 note = await this._db.get(key);
58 } catch (e) {
59 console.error(e);
60 return null;
61 }
62 if (!note || note.length < 1) {
63 return null;
64 }
65 note = note[0];
66 if (note === undefined || note.public !== 1) {
67 return null;
68 }
69 return note;
70 }
71}
72
73const DB = new Database();
74
75/* Who wants a slice? */
76const escape_string = unsafe => JSON.stringify(unsafe).slice(1, -1)
77 .replace(/</g, '\\x3C').replace(/>/g, '\\x3E');
78
79/* o/ */
80app.get('/', (req, res) => {
81 res.render('index');
82});
83
84/* \o/ [x] */
85app.post('/', async (req, res) => {
86 const note = req.body.content;
87 if (!note) {
88 return res.status(500).send("Nothing to add");
89 }
90 if (note.length > 2000) {
91 res.status(500);
92 return res.send("The note is too big");
93 }
94
95 const note_id = uuidv4();
96 try {
97 const result = await DB.add_note(note_id, note);
98 if (!result) {
99 res.status(500);
100 console.error(result);
101 return res.send("Something went wrong...");
102 }
103 } catch (err) {
104 res.status(500);
105 console.error(err);
106 return res.send("Something went wrong...");
107 }
108 await utils.sleep(500);
109 return res.redirect(`/${note_id}`);
110});
111
112/* Make sure to properly escape the note! */
113app.get('/:id([a-f0-9\-]{36})', recaptcha.middleware.render, utils.cache_mw, async (req, res) => {
114 const note_id = req.params.id;
115 const note = await DB.get_note(note_id);
116
117 if (note == null) {
118 return res.status(404).send("Paste not found or access has been denied.");
119 }
120
121 const unsafe_content = note.content;
122 const safe_content = escape_string(unsafe_content);
123
124 res.render('note_public', {
125 content: safe_content,
126 id: note_id,
127 captcha: res.recaptcha
128 });
129});
130
131/* Share your pastes with TJMike? */
132app.post('/report/:id([a-f0-9\-]{36})', recaptcha.middleware.verify, (req, res) => {
133 const id = req.params.id;
134
135 /* No robots please! */
136 if (req.recaptcha.error) {
137 console.error(req.recaptcha.error);
138 return res.redirect(`/${id}?msg=Something+wrong+with+Captcha+:(`);
139 }
140
141 /* Make TJMike visit the paste */
142 utils.visit(id, req);
143
144 res.redirect(`/${id}?msg=TJMike?+will+appreciate+your+paste+shortly.`);
145});
146
147/* This is my source I was telling you about! */
148app.get('/source', (req, res) => {
149 res.set("Content-type", "text/plain; charset=utf-8");
150 res.sendFile(__filename);
151});
152
153/* Let it begin! */
154const PORT = process.env.PORT || 8080;
155
156app.listen(PORT, () => {
157 console.log(`App listening on port ${PORT}`);
158 console.log('Press Ctrl+C to quit.');
159});
160
161module.exports = app;