-
Notifications
You must be signed in to change notification settings - Fork 0
/
restful.js
217 lines (186 loc) · 5.78 KB
/
restful.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
const { gotUserPost } = require('./user-post')
const debug = require('debug')(__filename.split('/').slice(-1).join())
// let streamCounter = 0
const attach = (appmgr, db) => {
const H = appmgr.H
const checked = H.safe(' checked="checked"')
const app = appmgr.app
app.get('/', async (req, res) => {
let text = ''
if (req.query.id) {
const obj = await db.get(req.query.id)
text = obj.text
}
res.send(H`<html>
<head>
<style>
/* from https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Your_first_HTML_form */
form {
/* Just to center the form on the page */
margin: 0 auto;
width: 95%;
/* To see the outline of the form */
padding: 1em;
border: 1px solid #CCC;
border-radius: 0.3em;
}
form div + div {
margin-top: 1em;
}
label {
/* To make sure that all labels have the same size and are properly aligned */
display: inline-block;
/* width: 90px; */
text-align: right;
}
input, textarea {
/* To make sure that all text fields have the same font settings
By default, textareas have a monospace font */
font: 1em sans-serif;
/* To give the same size to all text fields */
/* width: 300px; */
box-sizing: border-box;
/* To harmonize the look & feel of text field border */
border: 1px solid #999;
}
input:focus, textarea:focus {
/* To give a little highlight on active elements */
border-color: #000;
}
textarea {
/* To properly align multiline text fields with their labels */
vertical-align: top;
/* To give enough room to type some text */
height: 5em;
width: 95%;
}
.button {
/* To position the buttons to the same position of the text fields */
padding-left: 90px; /* same size as the label elements */
}
button {
/* This extra margin represent roughly the same space as the space
between the labels and their text fields */
margin-left: .5em;
}
</style>
</head>
<body>
<p>This is an installation of <a
href="https://github.com/sandhawke/snippet-server#readme">snippet-server</a>.</p>
<form action="/" method="post">
<p>Use this form for all site content modifications. To
append/replace/delete, you'll need the same bearer token as you provided
during create. There is currently no mechanism for token recovery.</p>
<div>
<label for="id">Page ID (URL path, lowercase hex):</label>
<input type="text" id="id" name="id" value="${req.query.id || ''}">
</div>
<div>Select Operation:
<input type="radio" id="opChoice0"
name="op" value="create"${req.query.op === 'create' ? checked : ''}>
<label for="opChoice0">Create</label>
<input type="radio" id="opChoice1"
name="op" value="append"${req.query.op === 'append' ? checked : ''}>
<label for="opChoice1">Append</label>
<input type="radio" id="opChoice2"
name="op" value="replace"${req.query.op === 'replace' ? checked : ''}>
<label for="opChoice2">Replace</label>
<input type="radio" id="opChoice3"
name="op" value="delete">
<label for="opChoice3">Delete</label>
</div>
<div>
<label for="text">Page text (for replace or append):</label>
<textarea id="text" name="text">${text}</textarea>
</div>
<div>
<label for="text">Bearer Token (20+ chars lowercase hex recommended):</label>
<input type="password" id="pw" name="pw"></input>
</div>
<p>
<button type="submit">Submit</button>
</p>
</form>
`)
})
app.post('/', async (req, res) => {
debug('POST %o', req.body)
const obj = await gotUserPost(req.body, db)
if (typeof obj === 'string') {
res.status(400).send(obj + '\n')
return
}
debug('created %o', obj)
res.redirect(303, '/' + obj.id)
})
app.get('/:id', async (req, res) => {
const id = req.params.id
const version = req.query.version
debug('looking for %j', id)
const post = await db.get(id, version)
if (!post) {
debug('no match')
res.status(404).send('Not found')
return
}
if (post.deleted) {
res.status(410).send('Gone (deleted)')
return
}
debug(post)
res.format({
'text/plain': () => {
res.send(post.text)
},
html: async () => {
res.send(H`<html>
<head></head><body>
<div style="border: 1px solid black; padding-left: 1em;">
<p>This page, below this box, is user-generated content. This site does not screen content, maintain accounts, or otherwise track user identity. We have no idea who posted this material and have probably not seen it. See <a href="/">site home page</a> for more details and policies.</p>
<p>If you know the bearer token used for creating this page, you can <a href="/?id=${id}&op=replace">edit it</a>.</p>
${H.safe(await ver(post, version))}
</div>
<pre>${post.text}</pre>
</body></html>`)
}
})
})
async function ver (post) {
const out = []
const latest = await db.getVersion(post.id)
const link = (num) => {
return `<a href="?version=${num}">${num}</a>`
}
const vers = () => {
const vout = []
if (post.version > 11) vout.push('...')
for (let x = post.version - 10; x < post.version + 10; x++) {
if (x < 1) continue
if (x > latest) continue
if (x === post.version) {
vout.push(`<<b>${x}</b>>`)
} else {
vout.push(link(x))
}
}
if (latest > post.version + 9) vout.push('...')
debug('vout is %o', vout)
return vout.join(' ')
}
out.push(`<p>Versions: ${vers()}</p>`)
debug('out is %o', out)
if (post.version !== latest) {
out.push(`
<p>Displaying version ${link(post.version)}. The latest is ${link(latest)}.`)
} else {
if (post.version === 1) {
out.push(`<p>This is version 1, the only version of this page.</p>`)
} else {
out.push(`<p>This is the latest version, ${post.version}.`)
}
}
return out.join('\n')
}
}
module.exports = { attach }