UNPKG

3.28 kBPlain TextView Raw
1import fastify from 'fastify'
2import auth from 'basic-auth'
3import fastifyStatic from 'fastify-static'
4import path from 'path'
5
6import connectionsHandler from './connections'
7import configHandler from './config'
8import controlHandler from './control'
9import discordHandler from './discord'
10import messengerHandler from './messenger'
11import { Reply } from '../types/fastify'
12
13const log = logger.withScope('api')
14
15export default function runServer () {
16 const app = fastify()
17
18 app.register(fastifyStatic, {
19 root: path.join(__dirname, '..', '..', 'node_modules'),
20 prefix: '/node_modules/',
21 decorateReply: false
22 })
23
24 app.register(fastifyStatic, {
25 root: path.join(__dirname, '..', '..', 'static'),
26 prefix: '/static/'
27 })
28
29 app.decorateReply('sendError', function (this: Reply, code: number, message: string) {
30 this.code(code).send(new Error(message))
31 })
32
33 if (config.api.key) {
34 app.addHook('preHandler', (request, reply, next) => {
35 const { authorization } = request.headers
36
37 if (!authorization) {
38 reply.sendError(403, 'API key missing')
39 return
40 }
41 if (!authorization.startsWith('Bearer ')) {
42 reply.sendError(403, 'API key incorrect')
43 return
44 }
45
46 const key = authorization.split(' ')[1]
47 if (key !== config.api.key) {
48 reply.sendError(403, 'API key incorrect')
49 return
50 }
51
52 next()
53 })
54 } else {
55 app.addHook('preHandler', (request, reply, next) => {
56 const send401 = () => reply
57 .code(401)
58 .header('WWW-Authenticate', 'Basic realm="Miscord API"')
59 .header('Content-Type', 'text/html')
60 .send('<h1>Forbidden</h1>')
61
62 if (!request.headers.authorization) {
63 send401()
64 return
65 }
66
67 const creds = auth.parse(request.headers.authorization)
68 if (!creds) {
69 send401()
70 return
71 }
72
73 const { name, pass } = creds
74 log.debug('login', { name, pass })
75 if (name === config.api.username && pass === config.api.password) return next()
76
77 send401()
78 })
79 }
80
81 app.get('/', (request, reply) => {
82 reply.sendFile('dashboard/index.html')
83 })
84 app.get('/config-discord/', (request, reply) => {
85 reply.sendFile('dashboard/config-discord.html')
86 })
87 app.get('/config-messenger/', (request, reply) => {
88 reply.sendFile('dashboard/config-messenger.html')
89 })
90 app.get('/config-miscellaneous/', (request, reply) => {
91 reply.sendFile('dashboard/config-miscellaneous.html')
92 })
93 app.get('/connections/', (request, reply) => {
94 reply.sendFile('dashboard/connections.html')
95 })
96 app.register(connectionsHandler, { prefix: '/connections' })
97 app.register(configHandler, { prefix: '/config' })
98 app.register(controlHandler, { prefix: '/control' })
99 app.register(discordHandler, { prefix: '/discord' })
100 app.register(messengerHandler, { prefix: '/messenger' })
101
102 const port = Number(config.api.port || 9448)
103
104 app.listen(port, '0.0.0.0')
105 .then(() => log.success(`API is listening on port ${port}`))
106 .catch(err => {
107 if (err.code === 'EADDRINUSE') {
108 app.listen(port + 1, '0.0.0.0')
109 .then(() => log.success(`API is listening on port ${port + 1}`))
110 } else {
111 throw err
112 }
113 })
114 return app
115}