-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
159 lines (132 loc) · 3.81 KB
/
index.js
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
const crypto = require('crypto');
const fs = require('fs');
const md = require('markdown-it')();
const package = require('./package.json');
const { Deta } = require('deta');
const express = require('express')
const deta = Deta(process.env.DETATOKEN || "");
const articles = deta.Base('articles');
const users = deta.Base('users');
const app = express();
app.use(express.urlencoded({ extended: true }))
app.use(express.json());
// TODO: Add express centralized error handling
app.use((req, res, next) => {
res.error = (code, msg) => {
res.json({
error: true,
msg: msg
});
res.status(code).end();
};
res.success = (data, msg='') => {
res.json({
error: false,
data: data,
msg: msg
});
res.status(200).end();
};
next();
});
// TODO: Refactor these endpoints into their own file, use a factory i guess.
app.get('/', (req, res) => res.send(`GodotID Blog API ${package.version}`));
app.post('/register', async (req, res) => {
// TODO: Verify user email
// WARN: TODO: Validate req.body user input
let rd = new Author(req.body.username, req.body.password, req.body.email);
let user = null;
try {
user = await users.insert(rd, rd.hash);
} catch (e) {
// XXX: We should provide clear detail as to why this fails
return res.error(400, "User already exists");
}
return res.success({
name: rd.name,
hash: rd.hash
});
});
app.post('/login', async (req, res) => {
// WARN: TODO: Validate req.body user input
let hash = crypto.createHash('sha256')
.update(req.body.username + req.body.password)
.digest('hex');
let user = await users.get(hash);
if (!user) {
return res.error(403, 'Username or password mismatch.');
}
user.password = 'REDACTED';
return res.success(user);
});
app.get('/user/:key', async (req, res) => {
// WARN: TODO: Validate req.body user input
let user = await users.get(req.params.key);
if (user === null) {
return res.error(404, "User not found");
}
user.password = 'REDACTED';
return res.success(user);
});
app.post('/submit', async (req, res) => {
// WARN: TODO: Validate req.body user input
let user = await users.get(req.body.userhash);
if (!user) {
return res.error(403, 'Please login before submitting post.');
}
let article = new Article(req.body.title, req.body.content, user.name, user.key);
await articles.put(article);
res.location(`https://blog.godot.id/${article.key}`);
res.status(302).end();
});
app.get('/article/:article', async (req, res) => {
let article = await articles.get(req.params.article);
if (!article) {
return res.error(404, 'Article did not exists');
}
let { content, title } = article;
content = md.render(`# ${title}\n${content}`);
// Someday we will use `article`, i don't want to forgot this line at that time
article.authorHash = 'REDACTED';
return res.success({
author: article.author,
rendered: content
});
});
function normalizeTitle(title) {
return title.replace(/\s/g, '-').replace(/[^a-z0-9\-]/ig, '').toLowerCase();
}
class Author {
constructor(name, password, email, bio, profilePicture) {
this.name = name;
// TODO: Omit password, use hash instead.
this.password = password;
this.email = email;
this.hash = crypto.createHash('sha256')
.update(name + password)
.digest('hex');
this.bio = bio;
this.profilePicture = profilePicture;
}
}
class Article {
constructor(title, content, author, authorHash) {
const noncestr = String(Date.now());
let hash = crypto.createHash('sha256')
.update(title + content + author + noncestr)
.digest('hex').substring(0, 8);
this.key = `${normalizeTitle(title)}-${hash}`;
this.title = title;
this.content = content;
this.author = author;
this.authorHash = authorHash;
this.creationDate = noncestr;
}
}
if (!process.env.DETA_RUNTIME) {
app.listen(8080, async () => {
console.log("Ran at 8080");
});
} else {
module.exports = app;
}