This repository has been archived by the owner on Dec 1, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathserver.js
201 lines (189 loc) · 6.21 KB
/
server.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
require('dotenv').config()
const express = require('express')
const app = express()
const session = require('express-session')
const FileStore = require('session-file-store')(session)
const upload = require('express-fileupload')
const bodyParser = require('body-parser')
const request = require('request')
const cherrio = require('cheerio')
const site = 'https://micropub-client.herokuapp.com/'
const redirectURI = 'https://micropub-client.herokuapp.com/auth/callback'
const mustacheExpress = require('mustache-express')
const fs = require('fs')
const URL = require('url')
app.set('trust proxy', true)
app.use(session({
store: new FileStore({path: './.data/sessions'}),
secret: process.env.SECRET,
resave: true,
saveUninitialized: false,
cookie: { secure: true }
}))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: true}))
app.use(upload())
app.use(express.static('public'))
app.engine('html', mustacheExpress())
app.set('view engine', 'mustache')
app.get('/', function(req, res) {
console.log('get /')
if (hasSession(req)) return res.redirect('/new')
const parsed = URL.parse(req.query.url || '')
const url = parsed.hostname ? parsed.href : null
res.render('index.html', {url})
})
app.post('/logout', function(req, res) {
console.log('post /logout')
req.session.destroy((err) => {
console.log(err)
// LOL no error but [session-file-store] will retry, error on last attempt: Error: ENOENT: no such file or directory
// https://github.com/valery-barysok/session-file-store/issues/41#issuecomment-271166358
res.redirect('/')
})
})
app.post('/login', async function(req, res) {
console.log('post /login')
try {
const me = req.body.url
const params = new URLSearchParams()
const {authorization_endpoint, token_endpoint, micropub} = await(readEndpoints(me))
if (!(authorization_endpoint && token_endpoint && micropub)) throw Error()
req.session._mp = {authorization_endpoint, token_endpoint, endpoint: micropub, me}
params.append('response_type', 'code')
params.append('scope', 'create')
params.append('state', 'state')
params.append('me', me)
params.append('client_id', site)
params.append('redirect_uri', redirectURI)
req.session.save((err) => {
console.log(err)
res.redirect(authorization_endpoint + `?${params.toString()}`)
})
} catch(err) {
console.log(err)
handleError(res, 'Something went wrong when looking up the endpoints.')
}
})
app.get('/auth/callback', async function(req, res) {
console.log('get /auth/callback')
if (req.query.code) {
try {
const {me, token_endpoint, endpoint} = req.session._mp
request.post(token_endpoint, {
json: true,
form: {
grant_type: 'authorization_code',
me: me,
code: req.query.code,
client_id: site,
redirect_uri: redirectURI
}
}, async function(err, resres, body) {
const config = await getSettings('config', endpoint, body.access_token)
req.session._mp = {...req.session._mp, access_token: body.access_token, config}
res.redirect(`/new`)
})
} catch(err) {
console.log(err)
handleError(res, 'Couldn\'t find token_endpoint.')
}
} else {
handleError(res, `Missing [code] from the returned query: ${(new URLSearchParams(req.query)).toString()}`)
}
})
app.get('/new', async function(req, res) {
console.log('get /new')
if (!hasSession(req)) return res.redirect('/')
try {
const {me, endpoint, config} = req.session._mp
const locals = {me, endpoint, media_endpoint: config && config['media-endpoint']}
const action = req.query.action || 'create'
locals[action] = true
res.render('new.html', locals)
} catch(err) {
console.log(err)
handleError(res, 'No micropub endpoint.')
}
})
app.post('/new', async function(req, res) {
console.log('post /new')
let format
const {endpoint, access_token, config} = req.session._mp
const options = {
json: true,
url: req.query.action === 'upload' ? config['media-endpoint'] : endpoint,
method: 'post',
auth: {'bearer': access_token}
}
if (req.files && (req.files.photo || req.files.file)) {
const key = req.files.photo ? 'photo' : 'file'
req.body[key] = req.files[key].data
req.body[key].name = req.files[key].name
format = 'formData'
} else {
format = 'form'
}
if (req.body.as === 'json') {
options.body = req.body
if (req.query.action === 'create') {
options.body.type = ['h-entry']
options.body.properties = {content: [req.body.content], category: req.body.category, photo: [req.body.photo]}
}
} else {
options[format] = req.body
}
request(options, function(err, resres, body) {
let error, url
let result = 'Error'
if (err) {
console.log(err)
error = err
} else {
if ([200, 201, 204].indexOf(resres.statusCode) >= 0) {
result = 'Success'
url = resres.headers.location
} else {
result = resres.statusCode
error = body ? body.error_description : resres.statusCode
}
}
res.render('result.html', {
result, error, url
})
})
})
function readEndpoints(url) {
return new Promise(resolve => {
request(url, function(err, _, body) {
const obj = {}
const $ = cherrio.load(body)
for (const key of ['authorization_endpoint', 'token_endpoint', 'micropub']) {
obj[key] = $(`link[rel="${key}"]`).attr('href')
}
resolve(obj)
})
})
}
function handleError(res, error) {
res.render('result.html', {result: 'Error', error})
}
const listener = app.listen(process.env.PORT, function() {
console.log('Listening on port ' + listener.address().port)
})
function hasSession(req) {
const {me, authorization_endpoint, token_endpoint, endpoint, access_token} = req.session._mp || {}
return me && authorization_endpoint && token_endpoint && endpoint && access_token
}
async function getSettings(type, url, access_token) {
// type: config / syndicate-to
return new Promise(resolve => {
request({url: `${url}?q=${type}`, json: true, auth: {bearer: access_token}}, function(err, res, body) {
if (err || res.statusCode !== 200 || !body) {
resolve(null)
} else {
resolve(body)
}
})
})
}