-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
142 lines (123 loc) · 4.08 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// This has a lot of code in common with flextag-relay, stuff that
// should perhaps be factored out into a flextag-server or something
const WebSocket = require('ws')
const bodyParser = require('body-parser')
const cors = require('cors')
const AppMgr = require('appmgr')
const EventEmitter = require('eventemitter3')
const psdad = require('psdad')
const restful = require('./restful')
const squareQuotes = require('square-quotes')
const dbmodule = require('./db')
const debug = require('debug')(__filename.split('/').slice(-1).join())
const talkAPI = require('./talkAPI')
const apis = [ talkAPI ]
const run = async (config = {}) => {
const db = await dbmodule.start(config.dbname || 'pages')
const appmgr = new AppMgr(config)
appmgr.app.use(cors())
await appmgr.start()
const wss = new WebSocket.Server({ server: appmgr.server })
console.log(`Try:
firefox ${appmgr.siteurl}
wscat -c ${appmgr.siteurl.replace('http', 'ws')}
`)
appmgr.app.use(bodyParser.json())
appmgr.app.use(bodyParser.urlencoded({ extended: true }))
restful.attach(appmgr, db)
let streamCounter = 0
const conns = []
wss.on('connection', ws => {
const n = streamCounter++
const handlers = {}
debug('stream %d open, ws', n)
const conn = new EventEmitter()
conns.push(conn)
conn._raw = ws
conn.siteurl = appmgr.siteurl
conn.send = (first, ...rest) => {
if (rest.length) throw Error() // best not allow this, I thinkg
debug(`stream ${n} snd> %o`, first)
ws.send(first)
}
conn.addHandlers = additions => {
Object.assign(handlers, additions)
}
conn.log = (first, ...rest) => {
debug(`stream ${n} log: ${first}`, ...rest)
}
const mapper = psdad.mapper()
const lines = []
conn.onMatch = (texts, func) => {
for (const text of asArray(texts)) {
lines.push(text)
mapper.addPair({ input: func }, text)
}
}
const add = (key, value) => {
lines.push(value)
mapper.addPair({
input: (...args) => {
// not quite sure why I wanted to allow handlers to be changed
// during processing... maybe just addpair the handler,
// processin the .schema after the .talk?
const handler = handlers[key]
if (handler) {
handler(...args)
} else {
console.error('ERROR: no handler for schema entry tagged %j', key)
}
},
output: x => x.hasOwnProperty(key)
}, value)
}
add('help', 'help')
handlers.help = () => {
conn.send('This service implements the following flextag statements:')
for (const line of lines) conn.send(' * ' + line)
}
conn.send('Connected. Send the message "help" to see implemented statements.')
// make a new mapping from flextags to their handlers for this
// connection, since the handlers may be different functions (with
// internal state) for each connection.
for (const api of apis) {
// use some normalizeSchema function?
for (let [key, value] of Object.entries(api.schema || {})) {
for (let v of asArray(value)) {
add(key, v)
}
}
}
for (const api of apis) api.talk(conn, db)
ws.on('message', async (message) => {
debug(`stream ${n} <rcv %o`, message)
if (conn.hijack) {
// allow a handler to take over the connection and be send the
// next message
conn.hijack = null
conn.hijack(message)
} else {
// it would be nice to know if SOMETHING happened, to give a more
// helpful response conversationally, instead of silence. Maybe mapper
// can include a counter or something?
mapper.parse(squareQuotes.convert(message))
}
})
ws.on('close', () => {
conn.emit('close')
debug('stream %d closed', n)
})
})
const realStop = appmgr.stop.bind(appmgr)
appmgr.stop = async () => {
await Promise.all(conns.map(x => x._raw.close()))
await db.close()
await realStop()
}
return appmgr
}
function asArray (x) {
if (Array.isArray(x)) return x
return [x]
}
module.exports = { run }