seance/server.js
Hippo 1edee70807 Ignore duplicate titles and subtitles
The long-standing "make title proper" bug has finally been fixed.
Yayy! So now, basically, the title and subtitle gets set properly
even if Medium messed it up by showing some of them twice.
2021-03-29 21:52:12 +05:30

243 lines
5.2 KiB
JavaScript

const express = require('express')
const bodyParser = require('body-parser')
const { vueRenderer } = require('@doweb/vuexpress')
const slugify = require('underscore.string/slugify')
const fs = require('fs')
const { Seance } = require ('./seance')
const app = express()
var expressWs = require('express-ws')(app)
// ALlow URLencoded API
app.use(bodyParser.urlencoded({
extended: false
}))
// Allow JSON API
app.use(bodyParser('json'))
// Enable static files
app.use(express.static('public'))
app.use(express.static('static'))
// Set up VueXpress
let options = {
views: './views',
cache: true,
watch: true,
metaInfo: {
title: 'Seance',
script: [
{ type: 'text/javascript', src: '/app.js' },
],
},
extractCSS: true,
cssOutputPath: '/css/styles.css',
publicPath: 'public',
compilerConfig: {
// custom webpack config
},
compilerConfigCallback: function(webpackConfig) {
// change the merged webpackconfig if you like
return webpackConfig;
},
}
const renderer = vueRenderer(options)
app.use(renderer)
// Views
app.get('/', (req, res) => {
res.render('index', {
baseUrl: req.hostname.startsWith('localhost')
? req.protocol + '://' + req.headers.host
: '//' + req.hostname, // auto choose http or https
})
})
app.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', {
post: {
title: post.title,
subtitle: post.content.subtitle,
author: post.author,
featuredImage: post.featuredImage,
mediumUrl: post.mediumUrl,
slug: post.slug,
}
})
})
app.get('/fetch', (req, res) => {
res.redirect(303, '/')
})
app.get('/api', (req, res) => {
res.json({
status: 'success',
message: 'Welcome to the Seance API :)',
})
})
app.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')
})
})
app.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') {
const 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()
// 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.pushToGhost(postSlug)
.then(() => {
console.info(`"${postSlug}" pushed successfully.`)
// send 'done' message
ws.send(`done: ${postSlug}`)
})
} else {
ws.send('error: Malformed message')
return
}
})
ws.on('close', () => {
console.log('socket closed')
})
})
const port = process.env.PORT || 4000
app.listen(port, () => {
console.log(`Listening on ${port}`)
})