diff --git a/README.md b/README.md index 5085a22..8f461eb 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,9 @@ files, and for your Ghost API interface. The parameters to set are: * `GHOST_URL` - URL of your Ghost installation * `GHOST_VERSION` - 'v2' or 'v3' depending on which version you're using * `GHOST_ADMIN_KEY` - 'Admi API key for Ghost' +* `BASE_PATH` - Subdomain to serve the website on (eg. `/seance`). + Default: `/`. If you're not planning to use the Seance server, then + you can safely ignore this option. In case you're wondering about the WebDAV server: that's the setup we use at Snipette. We'd like to eventually let you upload directly through @@ -103,7 +106,16 @@ file via the "Labs" section. This is required becaues Ghost doesn't let you directly add users; it only lets you import them. Seance also attempts to fetch the Medium user's profile image and upload -it via WebDAV. The JSON file will link to the WebDAV-uploaded image +it via WebDAV. The JSON file will link to the WebDAV-uploaded image. + +## Run the Seance Server + +The server is mainly needed when Seance'ing draft Medium posts, because +Medium doesn't let you access those anonymously so you need to resort +to bookmarklets instead. But it also comes in handy as a nicer, +friendlier interface for sending out posts for those who don't want to +use the command-line. The engine is more or less the same; it's just +the interface that's different! # credits diff --git a/cli.js b/cli.js index f3f53f9..b43867b 100755 --- a/cli.js +++ b/cli.js @@ -136,6 +136,26 @@ program.command('setup') config.ghost.admin_key = res.admin_key console.log(`Right. So that's Ghost ${config.ghost.version} running at ${config.ghost.url} with key ${config.ghost.admin_key}`) + console.log('\n\nNow, a bit about the server.') + console.log( + 'This server usually runs on the main path (eg. example.com/).' + + 'If you want to host seance on a different subpath (eg. ' + + 'example.com/seance) then please enter that last part of the' + + 'string now (eg. /seance).' + ) + res = await prompt.get([ + { name: 'base_path', default: config.basePath || '/' }, + ]) + if (res.base_path == '/') { + console.log('Okay. Serving on the root path then :)') + } else { + config.basePath = res.base_path + console.log( + 'Right, so we\'ll be serving at yoursite.com' + + `${config.basePath} then :)` + ) + } + console.log( '\n\nA final thing. Do you have a "scissors" or other image ' + 'used as a separator in your article? If so, enter the path ' + diff --git a/config.js b/config.js index 295fc31..86ebcbd 100644 --- a/config.js +++ b/config.js @@ -95,6 +95,13 @@ let config = convict({ env: 'SEPARATOR_IMAGE', default: null, }, + basePath: { + doc: 'Base subpath on which Seance is hosted (eg. /seance)', + format: 'String', // TODO: validate by checking path + env: 'BASE_PATH', + default: null, + nullable: true, + }, }) // Load configs from home directory, if present diff --git a/server.js b/server.js index c69a9b5..0e58347 100644 --- a/server.js +++ b/server.js @@ -5,6 +5,7 @@ const slugify = require('underscore.string/slugify') const fs = require('fs') const { Seance } = require ('./seance') +const config = require('./config') const app = express() var expressWs = require('express-ws')(app) @@ -18,8 +19,12 @@ app.use(bodyParser.urlencoded({ app.use(bodyParser('json')) // Enable static files -app.use(express.static('public')) -app.use(express.static('static')) +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) // Set up VueXpress let options = { @@ -29,12 +34,12 @@ let options = { metaInfo: { title: 'Seance', script: [ - { type: 'text/javascript', src: '/app.js' }, + { type: 'text/javascript', src: config.basePath + '/app.js' }, ], }, extractCSS: true, cssOutputPath: '/css/styles.css', - publicPath: 'public', + publicPath: 'public' + config.basePath, compilerConfig: { // custom webpack config }, @@ -46,19 +51,22 @@ let options = { } const renderer = vueRenderer(options) -app.use(renderer) +router.use(renderer) // Views -app.get('/', (req, res) => { +router.get('/', (req, res) => { res.render('index', { - baseUrl: req.hostname.startsWith('localhost') - ? req.protocol + '://' + req.headers.host - : '//' + req.hostname, // auto choose http or https + seanceUrl: req.hostname.startsWith('localhost') + ? req.headers.host + req.baseUrl + : req.hostname + req.baseUrl, + protocol: req.hostname.startsWith('localhost') + ? req.protocol + '://' + : '//', }) }) -app.post('/fetch', (req, res) => { +router.post('/fetch', (req, res) => { var json var post @@ -110,6 +118,9 @@ app.post('/fetch', (req, res) => { // 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, @@ -125,14 +136,14 @@ app.get('/fetch', (req, res) => { res.redirect(303, '/') }) -app.get('/api', (req, res) => { +router.get('/api', (req, res) => { res.json({ status: 'success', message: 'Welcome to the Seance API :)', }) }) -app.ws('/ws/fetch-medium', (ws, req) => { +router.ws('/ws/fetch-medium', (ws, req) => { ws.on('message', async(msg) => { command = msg.split(' ') @@ -182,7 +193,7 @@ app.ws('/ws/fetch-medium', (ws, req) => { }) }) -app.ws('/ws/push-ghost', (ws, req) => { +router.ws('/ws/push-ghost', (ws, req) => { ws.on('message', async(msg) => { // respond to keepalive diff --git a/static/app.js b/static/app.js index 089ad73..ae6a133 100644 --- a/static/app.js +++ b/static/app.js @@ -11,12 +11,13 @@ function addNotification(msg, className='is-warning') { function mediumFetch(e) { if(e) { e.preventDefault() } + const baseUrl = document.querySelector('[data-seance-url]').dataset.seanceUrl const postSlug = document.querySelector('[data-post-slug]').dataset.postSlug const mediumUrl = document.querySelector('[data-post-mediumurl]').dataset.postMediumurl var password = document.querySelector('input[name=password]').value console.log('Fetching ' + postSlug) const socketProtocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:') - const socketUrl = socketProtocol + '//' + window.location.host + '/ws/fetch-medium/' + const socketUrl = socketProtocol + '//' + baseUrl + '/ws/fetch-medium/' const socket = new WebSocket(socketUrl) // set up the div diff --git a/views/fetch-medium.vue b/views/fetch-medium.vue index 173418e..0dae3e0 100644 --- a/views/fetch-medium.vue +++ b/views/fetch-medium.vue @@ -2,7 +2,7 @@
@@ -62,6 +62,7 @@ title: 'Seance', user: null, host: 'http://localhost:4000', + seanceUrl: 'http://localhost:4000', post: { title: '(untitled)', subtitle: '', diff --git a/views/index.vue b/views/index.vue index c607922..13428fc 100644 --- a/views/index.vue +++ b/views/index.vue @@ -2,7 +2,7 @@