use hafas-rest-api
This commit is contained in:
parent
99f15760a8
commit
fcc996837d
5 changed files with 30 additions and 222 deletions
61
api.js
61
api.js
|
@ -1,61 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const express = require('express')
|
|
||||||
const hsts = require('hsts')
|
|
||||||
const morgan = require('morgan')
|
|
||||||
const shorthash = require('shorthash').unique
|
|
||||||
const corser = require('corser')
|
|
||||||
const compression = require('compression')
|
|
||||||
const nocache = require('nocache')
|
|
||||||
|
|
||||||
const pkg = require('./package.json')
|
|
||||||
const departures = require('./lib/departures')
|
|
||||||
const journeys = require('./lib/journeys')
|
|
||||||
const stations = require('./lib/stations')
|
|
||||||
const allStations = require('./lib/all-stations')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const api = express()
|
|
||||||
module.exports = api
|
|
||||||
|
|
||||||
api.use(hsts({maxAge: 24 * 60 * 60 * 1000}))
|
|
||||||
|
|
||||||
morgan.token('id', (req, res) => req.headers['x-identifier'] || shorthash(req.ip))
|
|
||||||
api.use(morgan(':date[iso] :id :method :url :status :response-time ms'))
|
|
||||||
|
|
||||||
const allowed = corser.simpleRequestHeaders.concat(['User-Agent', 'X-Identifier'])
|
|
||||||
api.use(corser.create({requestHeaders: allowed})) // CORS
|
|
||||||
|
|
||||||
api.use(compression())
|
|
||||||
|
|
||||||
api.use((req, res, next) => {
|
|
||||||
if (!res.headersSent)
|
|
||||||
res.setHeader('X-Powered-By', pkg.name + ' ' + pkg.homepage)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const noCache = nocache()
|
|
||||||
|
|
||||||
api.get('/stations/:id/departures', noCache, departures)
|
|
||||||
api.get('/journeys', noCache, journeys)
|
|
||||||
api.get('/stations', stations)
|
|
||||||
api.get('/stations/all', allStations)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
api.use((err, req, res, next) => {
|
|
||||||
if (process.env.NODE_ENV === 'dev') console.error(err)
|
|
||||||
if (res.headersSent) return next(err)
|
|
||||||
|
|
||||||
let msg = err.message
|
|
||||||
let code = err.statusCode
|
|
||||||
if (err.isHafasError) {
|
|
||||||
msg = 'DB error: ' + msg
|
|
||||||
code = 502
|
|
||||||
}
|
|
||||||
res.status(code).json({error: true, msg})
|
|
||||||
next()
|
|
||||||
})
|
|
35
index.js
35
index.js
|
@ -1,18 +1,37 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const http = require('http')
|
const hafas = require('hafas-client')
|
||||||
|
const dbProfile = require('hafas-client/p/db')
|
||||||
|
const createApi = require('hafas-rest-api')
|
||||||
|
const createLogging = require('hafas-rest-api/logging')
|
||||||
|
const hsts = require('hsts')
|
||||||
|
|
||||||
const api = require('./api')
|
const pkg = require('./package.json')
|
||||||
const server = http.createServer(api)
|
const stations = require('./lib/stations')
|
||||||
|
const allStations = require('./lib/all-stations')
|
||||||
|
|
||||||
const port = process.env.PORT || 3000
|
const config = {
|
||||||
const hostname = process.env.HOSTNAME || ''
|
hostname: process.env.HOSTNAME || '1.db.transport.rest',
|
||||||
|
port: process.env.PORT || 3000,
|
||||||
|
name: pkg.name,
|
||||||
|
homepage: pkg.homepage
|
||||||
|
}
|
||||||
|
|
||||||
server.listen(port, (err) => {
|
const client = hafas(dbProfile)
|
||||||
|
const api = createApi(client, config)
|
||||||
|
|
||||||
|
api.use(createLogging())
|
||||||
|
|
||||||
|
api.get('/stations', stations)
|
||||||
|
api.get('/stations/all', allStations)
|
||||||
|
|
||||||
|
module.exports = api
|
||||||
|
|
||||||
|
api.listen(config.port, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
process.exit(1)
|
process.exitCode = 1
|
||||||
} else {
|
} else {
|
||||||
console.info(`Listening on ${hostname}:${port}.`)
|
console.info(`Listening on ${config.hostname}:${config.port}.`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const parse = require('parse-messy-time')
|
|
||||||
const hafas = require('db-hafas')
|
|
||||||
const createDepsInDirection = require('hafas-departures-in-direction')
|
|
||||||
|
|
||||||
const depsInDirection = createDepsInDirection(hafas.departures, hafas.journeyPart)
|
|
||||||
|
|
||||||
const err400 = (msg) => {
|
|
||||||
const err = new Error(msg)
|
|
||||||
err.statusCode = 400
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
const isNumber = /^\d+$/
|
|
||||||
|
|
||||||
const departures = (req, res, next) => {
|
|
||||||
const id = req.params.id.trim()
|
|
||||||
if (!isNumber.test(id)) return next(err400('Invalid station id.'))
|
|
||||||
|
|
||||||
const opt = {}
|
|
||||||
if ('when' in req.query) {
|
|
||||||
opt.when = isNumber.test(req.query.when)
|
|
||||||
? new Date(req.query.when * 1000)
|
|
||||||
: parse(req.query.when)
|
|
||||||
}
|
|
||||||
|
|
||||||
let task
|
|
||||||
if ('nextStation' in req.query) {
|
|
||||||
const nS = req.query.nextStation
|
|
||||||
if (!isNumber.test(nS)) return next(err400('Invalid nextStation parameter.'))
|
|
||||||
|
|
||||||
if ('results' in req.query) {
|
|
||||||
const r = +req.query.results
|
|
||||||
if (Number.isNaN(r)) return next(err400('Invalid results parameter.'))
|
|
||||||
opt.results = Math.max(0, Math.min(r, 20))
|
|
||||||
}
|
|
||||||
if ('maxQueries' in req.query) {
|
|
||||||
const mQ = +req.query.maxQueries
|
|
||||||
if (Number.isNaN(mQ)) return next(err400('Invalid maxQueries parameter.'))
|
|
||||||
opt.maxQueries = Math.max(0, Math.min(mQ, 30))
|
|
||||||
}
|
|
||||||
|
|
||||||
task = depsInDirection(id, nS, opt)
|
|
||||||
} else {
|
|
||||||
if ('direction' in req.query) opt.direction = req.query.direction
|
|
||||||
if ('duration' in req.query) opt.duration = +req.query.duration
|
|
||||||
task = hafas.departures(id, opt)
|
|
||||||
}
|
|
||||||
|
|
||||||
task
|
|
||||||
.then((deps) => {
|
|
||||||
res.json(deps)
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
.catch(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = departures
|
|
|
@ -1,84 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const time = require('parse-messy-time')
|
|
||||||
const parse = require('cli-native').to
|
|
||||||
const hafas = require('db-hafas')
|
|
||||||
|
|
||||||
const err400 = (msg) => {
|
|
||||||
const err = new Error(msg)
|
|
||||||
err.statusCode = 400
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
const location = (q, t) => {
|
|
||||||
if (q[t]) return q[t] // station id
|
|
||||||
else if (q[t + '.latitude'] && q[t + '.longitude']) {
|
|
||||||
const l = {
|
|
||||||
type: 'address',
|
|
||||||
name: 'foo bar', // todo
|
|
||||||
latitude: +q[t + `.latitude`],
|
|
||||||
longitude: +q[t + `.longitude`]
|
|
||||||
}
|
|
||||||
if (q[t + '.name']) l.name = q[t + '.name']
|
|
||||||
if (q[t + '.id']) {
|
|
||||||
l.type = 'poi'
|
|
||||||
l.id = q[t + '.id']
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
else return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const isNumber = /^\d+$/
|
|
||||||
|
|
||||||
const journeys = (req, res, next) => {
|
|
||||||
const from = location(req.query, 'from')
|
|
||||||
if (!from) return next(err400('Missing origin.'))
|
|
||||||
const to = location(req.query, 'to')
|
|
||||||
if (!to) return next(err400('Missing destination.'))
|
|
||||||
|
|
||||||
const opt = {}
|
|
||||||
if ('when' in req.query) {
|
|
||||||
opt.when = isNumber.test(req.query.when)
|
|
||||||
? new Date(req.query.when * 1000)
|
|
||||||
: time(req.query.when)
|
|
||||||
}
|
|
||||||
if ('results' in req.query) {
|
|
||||||
opt.results = +req.query.results
|
|
||||||
}
|
|
||||||
if ('via' in req.query) {
|
|
||||||
opt.via = req.query.via
|
|
||||||
}
|
|
||||||
if ('passedStations' in req.query) {
|
|
||||||
opt.passedStations = parse(req.query.passedStations)
|
|
||||||
}
|
|
||||||
if ('transfers' in req.query) {
|
|
||||||
opt.transfers = +req.query.transfers
|
|
||||||
}
|
|
||||||
if ('transferTime' in req.query) {
|
|
||||||
opt.transferTime = +req.query.transferTime
|
|
||||||
}
|
|
||||||
if ('accessibility' in req.query) {
|
|
||||||
opt.accessibility = req.query.accessibility
|
|
||||||
}
|
|
||||||
if ('bike' in req.query) {
|
|
||||||
opt.bike = parse(req.query.bike)
|
|
||||||
}
|
|
||||||
// todo: loyaltyCard
|
|
||||||
|
|
||||||
const products = [
|
|
||||||
'suburban', 'subway', 'tram', 'bus', 'ferry', 'express', 'regional'
|
|
||||||
].reduce((acc, type) => {
|
|
||||||
if (type in req.query) acc[type] = parse(req.query[type])
|
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
if (Object.keys(products)) opt.products = products
|
|
||||||
|
|
||||||
hafas.journeys(from, to, opt)
|
|
||||||
.then((journeys) => {
|
|
||||||
res.json(journeys)
|
|
||||||
next()
|
|
||||||
}, (err) => next(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = journeys
|
|
13
package.json
13
package.json
|
@ -28,19 +28,12 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cli-native": "^1.0.0",
|
"cli-native": "^1.0.0",
|
||||||
"compression": "^1.7.1",
|
|
||||||
"corser": "^2.0.1",
|
|
||||||
"db-hafas": "^1.1.0",
|
|
||||||
"db-stations": "^1.24.0",
|
"db-stations": "^1.24.0",
|
||||||
"db-stations-autocomplete": "^1.0.0",
|
"db-stations-autocomplete": "^1.0.0",
|
||||||
"express": "^4.16.2",
|
"hafas-client": "^2.0.0-alpha.3",
|
||||||
"hafas-departures-in-direction": "^0.1.0",
|
"hafas-rest-api": "0.1.0-alpha.2",
|
||||||
"hsts": "^2.1.0",
|
"hsts": "^2.1.0",
|
||||||
"morgan": "^1.9.0",
|
"ndjson": "^1.5.0"
|
||||||
"ndjson": "^1.5.0",
|
|
||||||
"nocache": "^2.0.0",
|
|
||||||
"parse-messy-time": "^2.1.0",
|
|
||||||
"shorthash": "^0.0.2"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node index.js"
|
"start": "node index.js"
|
||||||
|
|
Loading…
Reference in a new issue