2020-05-05 09:10:41 -04:00
|
|
|
const express = require('express')
|
|
|
|
const bodyParser = require('body-parser')
|
|
|
|
const { vueRenderer } = require('@doweb/vuexpress')
|
2020-05-08 06:13:34 -04:00
|
|
|
const slugify = require('underscore.string/slugify')
|
|
|
|
const fs = require('fs')
|
2020-05-05 09:10:41 -04:00
|
|
|
|
2020-05-14 12:35:29 -04:00
|
|
|
const { Seance } = require ('./seance')
|
2021-07-25 12:09:08 -04:00
|
|
|
const config = require('./config')
|
2020-05-14 12:35:29 -04:00
|
|
|
|
2020-05-05 09:10:41 -04:00
|
|
|
const app = express()
|
2020-05-14 12:35:29 -04:00
|
|
|
var expressWs = require('express-ws')(app)
|
2020-05-05 09:10:41 -04:00
|
|
|
|
2020-05-07 13:27:34 -04:00
|
|
|
// ALlow URLencoded API
|
|
|
|
app.use(bodyParser.urlencoded({
|
|
|
|
extended: false
|
|
|
|
}))
|
|
|
|
|
2020-05-05 09:52:46 -04:00
|
|
|
// Allow JSON API
|
2020-05-05 09:10:41 -04:00
|
|
|
app.use(bodyParser('json'))
|
|
|
|
|
2020-05-05 09:52:46 -04:00
|
|
|
// Enable static files
|
2021-07-25 12:09:08 -04:00
|
|
|
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:52:46 -04:00
|
|
|
|
2020-05-05 09:10:41 -04:00
|
|
|
// Set up VueXpress
|
|
|
|
let options = {
|
|
|
|
views: './views',
|
2020-05-12 06:45:18 -04:00
|
|
|
cache: true,
|
|
|
|
watch: true,
|
2020-05-05 09:10:41 -04:00
|
|
|
metaInfo: {
|
|
|
|
title: 'Seance',
|
2020-05-14 12:35:29 -04:00
|
|
|
script: [
|
2021-07-26 13:13:25 -04:00
|
|
|
{
|
|
|
|
type: 'text/javascript',
|
|
|
|
src: (config.basePath || '') + '/app.js'
|
|
|
|
},
|
2020-05-14 12:35:29 -04:00
|
|
|
],
|
2020-05-05 09:10:41 -04:00
|
|
|
},
|
|
|
|
extractCSS: true,
|
2021-07-26 13:29:04 -04:00
|
|
|
cssOutputPath: 'css/styles.css',
|
2021-07-26 13:13:25 -04:00
|
|
|
publicPath: 'public' + (config.basePath || ''),
|
2020-05-12 06:45:18 -04:00
|
|
|
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)
|
2021-07-25 12:09:08 -04:00
|
|
|
router.use(renderer)
|
2020-05-05 09:10:41 -04:00
|
|
|
|
|
|
|
// Views
|
|
|
|
|
2021-07-25 12:09:08 -04:00
|
|
|
router.get('/', (req, res) => {
|
2020-05-10 13:14:47 -04:00
|
|
|
res.render('index', {
|
2021-07-25 12:09:08 -04:00
|
|
|
seanceUrl: req.hostname.startsWith('localhost')
|
|
|
|
? req.headers.host + req.baseUrl
|
|
|
|
: req.hostname + req.baseUrl,
|
|
|
|
protocol: req.hostname.startsWith('localhost')
|
|
|
|
? req.protocol + '://'
|
|
|
|
: '//',
|
2020-05-10 13:14:47 -04:00
|
|
|
})
|
2020-05-07 13:27:34 -04:00
|
|
|
})
|
|
|
|
|
2021-07-25 12:09:08 -04:00
|
|
|
router.post('/fetch', (req, res) => {
|
2020-05-07 13:27:34 -04:00
|
|
|
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
|
|
|
|
}
|
2020-05-08 06:13:34 -04:00
|
|
|
|
|
|
|
// set ID
|
|
|
|
if (!post.slug) {
|
|
|
|
post.slug = slugify(post.title)
|
|
|
|
}
|
|
|
|
|
|
|
|
// save to disk
|
|
|
|
fs.writeFileSync(`${post.slug}.json`, JSON.stringify(json), 'utf-8')
|
2020-05-07 13:27:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// render the final post
|
|
|
|
res.render('fetch-medium', {
|
2021-07-25 12:09:08 -04:00
|
|
|
seanceUrl: req.hostname.startsWith('localhost')
|
|
|
|
? req.headers.host + req.baseUrl
|
|
|
|
: req.hostname + req.baseUrl,
|
2020-05-07 13:27:34 -04:00
|
|
|
post: {
|
|
|
|
title: post.title,
|
|
|
|
subtitle: post.content.subtitle,
|
|
|
|
author: post.author,
|
|
|
|
featuredImage: post.featuredImage,
|
|
|
|
mediumUrl: post.mediumUrl,
|
2020-05-14 12:35:29 -04:00
|
|
|
slug: post.slug,
|
2020-05-07 13:27:34 -04:00
|
|
|
}
|
|
|
|
})
|
2020-05-05 09:10:41 -04:00
|
|
|
})
|
|
|
|
|
2020-05-12 06:46:33 -04:00
|
|
|
app.get('/fetch', (req, res) => {
|
|
|
|
res.redirect(303, '/')
|
|
|
|
})
|
|
|
|
|
2021-07-25 12:09:08 -04:00
|
|
|
router.get('/api', (req, res) => {
|
2020-05-05 09:10:41 -04:00
|
|
|
res.json({
|
|
|
|
status: 'success',
|
|
|
|
message: 'Welcome to the Seance API :)',
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2021-07-25 12:09:08 -04:00
|
|
|
router.ws('/ws/fetch-medium', (ws, req) => {
|
2020-05-14 12:35:29 -04:00
|
|
|
ws.on('message', async(msg) => {
|
|
|
|
|
|
|
|
command = msg.split(' ')
|
|
|
|
if (command.length == 3 && command[0] == 'fetch') {
|
|
|
|
const postSlug = command[1]
|
2020-05-14 13:04:27 -04:00
|
|
|
const password = command[2]
|
|
|
|
|
|
|
|
if (password != process.env.SEANCE_PW) {
|
|
|
|
ws.send('error: Something went wrong. Please try again :(')
|
|
|
|
return
|
|
|
|
}
|
2020-05-14 12:35:29 -04:00
|
|
|
|
|
|
|
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')
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2021-07-25 12:09:08 -04:00
|
|
|
router.ws('/ws/push-ghost', (ws, req) => {
|
2020-05-14 12:35:29 -04:00
|
|
|
ws.on('message', async(msg) => {
|
|
|
|
|
2020-06-04 09:40:37 -04:00
|
|
|
// respond to keepalive
|
|
|
|
if (msg == '__ping__') {
|
|
|
|
ws.send('__pong__')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-14 12:35:29 -04:00
|
|
|
command = msg.split(' ')
|
2020-05-14 13:04:27 -04:00
|
|
|
if (command.length == 3 && command[0] == 'push') {
|
2021-03-29 12:42:06 -04:00
|
|
|
let postSlug = command[1]
|
2020-05-14 12:35:29 -04:00
|
|
|
|
2020-05-14 13:04:27 -04:00
|
|
|
// check password
|
|
|
|
if (command[2] != process.env.SEANCE_PW) {
|
|
|
|
ws.send('error: Something went wrong. Please try again :(')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-14 12:35:29 -04:00
|
|
|
// Start the Seance session
|
|
|
|
seance = new Seance()
|
|
|
|
|
2021-03-29 12:42:06 -04:00
|
|
|
// catch data
|
|
|
|
let postId
|
|
|
|
|
2020-05-14 12:35:29 -04:00
|
|
|
// 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}`)
|
|
|
|
})
|
|
|
|
|
2021-03-29 12:42:06 -04:00
|
|
|
// 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}`)
|
2020-07-30 08:43:56 -04:00
|
|
|
|
2020-05-14 13:04:27 -04:00
|
|
|
} else {
|
|
|
|
ws.send('error: Malformed message')
|
|
|
|
return
|
2020-05-14 12:35:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
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}`)
|
|
|
|
})
|