seance/server.js

266 lines
6 KiB
JavaScript
Raw Normal View History

2020-05-05 09:10:41 -04:00
const express = require('express')
const bodyParser = require('body-parser')
const { vueRenderer } = require('@doweb/vuexpress')
const slugify = require('underscore.string/slugify')
const fs = require('fs')
2020-05-05 09:10:41 -04:00
const { Seance } = require ('./seance')
const config = require('./config')
2020-05-05 09:10:41 -04:00
const app = express()
var expressWs = require('express-ws')(app)
2020-05-05 09:10:41 -04:00
// ALlow URLencoded API
app.use(bodyParser.urlencoded({
extended: false
}))
// Allow JSON API
2020-05-05 09:10:41 -04:00
app.use(bodyParser('json'))
// Enable static files
app.use('/', express.static('public')) // basePath is prefixed later
app.use(config.basePath || '/', express.static('static'))
// Router
var router = express.Router()
app.use(config.basePath || '/', router)
2020-05-05 09:10:41 -04:00
// Set up VueXpress
let options = {
views: './views',
cache: true,
watch: true,
2020-05-05 09:10:41 -04:00
metaInfo: {
title: 'Seance',
script: [
{
type: 'text/javascript',
src: (config.basePath || '') + '/app.js'
},
],
2020-05-05 09:10:41 -04:00
},
extractCSS: true,
cssOutputPath: (config.basePath || '') + '/css/styles.css',
publicPath: 'public' + (config.basePath || ''),
compilerConfig: {
// custom webpack config
},
compilerConfigCallback: function(webpackConfig) {
// change the merged webpackconfig if you like
return webpackConfig;
},
2020-05-05 09:10:41 -04:00
}
const renderer = vueRenderer(options)
router.use(renderer)
2020-05-05 09:10:41 -04:00
// Views
router.get('/', (req, res) => {
res.render('index', {
seanceUrl: req.hostname.startsWith('localhost')
? req.headers.host + req.baseUrl
: req.hostname + req.baseUrl,
protocol: req.hostname.startsWith('localhost')
? req.protocol + '://'
: '//',
})
})
router.post('/fetch', (req, res) => {
var json
var post
try {
json = JSON.parse(req.body.data)
} catch (err) {
console.log(err)
}
if (json) {
post = json.payload.value
console.log(post)
// set author
post.author = post.displayAuthor
// If the author's not available, get it from somewhere else
// function courtesy mediumexporter
let authors = []
if (json.payload.references && json.payload.references.User) {
Object.keys(json.payload.references.User).forEach(k => {
let u = json.payload.references.User[k]
authors.push({
name: u.name,
username: u.username,
userId: u.userId
})
})
post.authors = authors
if (!post.author) {
post.author = authors[0].name
}
}
// set featured image
if (post.virtuals.previewImage) {
post.featuredImage = 'https://cdn-images-1.medium.com/max/800/' + post.virtuals.previewImage.imageId
}
// set ID
if (!post.slug) {
post.slug = slugify(post.title)
}
// save to disk
fs.writeFileSync(`${post.slug}.json`, JSON.stringify(json), 'utf-8')
}
// render the final post
res.render('fetch-medium', {
seanceUrl: req.hostname.startsWith('localhost')
? req.headers.host + req.baseUrl
: req.hostname + req.baseUrl,
post: {
title: post.title,
subtitle: post.content.subtitle,
author: post.author,
featuredImage: post.featuredImage,
mediumUrl: post.mediumUrl,
slug: post.slug,
}
})
2020-05-05 09:10:41 -04:00
})
2020-05-12 06:46:33 -04:00
app.get('/fetch', (req, res) => {
res.redirect(303, '/')
})
router.get('/api', (req, res) => {
2020-05-05 09:10:41 -04:00
res.json({
status: 'success',
message: 'Welcome to the Seance API :)',
})
})
router.ws('/ws/fetch-medium', (ws, req) => {
ws.on('message', async(msg) => {
command = msg.split(' ')
if (command.length == 3 && command[0] == 'fetch') {
const postSlug = command[1]
const password = command[2]
if (password != process.env.SEANCE_PW) {
ws.send('error: Something went wrong. Please try again :(')
return
}
const postMetaFile = `${postSlug}.json`
if (!fs.existsSync(postMetaFile)) {
ws.send(`error: post ${postSlug} not yet loaded`)
return
}
// Start the Seance session
seance = new Seance()
// set up handlers
seance.on('update', (e) => {
ws.send(`update: ${e.message}`)
})
seance.on('notification', (e) => {
ws.send(`notification: ${e.message}`)
})
seance.on('error', (e) => {
ws.send(`error: ${e.message}`)
})
seance.fetchFromMedium(postMetaFile)
.then((post) => {
console.info(`"${post.title}" fetched successfully.`)
ws.send(`done: ${post.slug}`)
})
}
})
ws.on('close', () => {
console.log('socket closed')
})
})
router.ws('/ws/push-ghost', (ws, req) => {
ws.on('message', async(msg) => {
// respond to keepalive
if (msg == '__ping__') {
ws.send('__pong__')
return
}
command = msg.split(' ')
if (command.length == 3 && command[0] == 'push') {
let postSlug = command[1]
// check password
if (command[2] != process.env.SEANCE_PW) {
ws.send('error: Something went wrong. Please try again :(')
return
}
// Start the Seance session
seance = new Seance()
// catch data
let postId
// set up handlers
seance.on('update', (e) => {
ws.send(`update: ${e.message}`)
})
seance.on('notification', (e) => {
ws.send(`notification: ${e.message}`)
})
seance.on('error', (e) => {
ws.send(`error: ${e.message}`)
})
// run seance and wait till it finishes
let res = await seance.pushToGhost(postSlug)
console.info(`"${postSlug}" pushed successfully.`)
// send 'done' message
let ghostSite = await seance.ghostAdmin.site.read()
let postEditUrl
if (!!res.id) postEditUrl = `${ghostSite.url}ghost/#/editor/post/${res.id}`
if (!!res.slug) postSlug = res.slug
ws.send(`done: ${postSlug} ${postEditUrl}`)
} else {
ws.send('error: Malformed message')
return
}
})
ws.on('close', () => {
console.log('socket closed')
})
})
2020-05-05 09:10:41 -04:00
const port = process.env.PORT || 4000
app.listen(port, () => {
console.log(`Listening on ${port}`)
})