1 | import { createSecureContext } from 'tls'
|
2 | import chalk from 'chalk'
|
3 | import createHandler from './serve-handler'
|
4 | import fs from 'fs'
|
5 | import http from 'http'
|
6 | import https from 'https'
|
7 |
|
8 | const HOST = '0.0.0.0'
|
9 |
|
10 | export default function serve(apps) {
|
11 | const appsByRoot = {}
|
12 | const roots = apps.map(app => {
|
13 | const root = `${app.domain}${app.root}`
|
14 | appsByRoot[root] = app
|
15 | if (app.secure) {
|
16 | app.secureContext = createSecureContext({
|
17 | cert: fs.readFileSync(app.secure.cert),
|
18 | key: fs.readFileSync(app.secure.key)
|
19 | })
|
20 | }
|
21 | return root
|
22 | })
|
23 |
|
24 | const findApp = req => {
|
25 | const fullUrl = `${req.headers.host}${req.url}`
|
26 | const root = roots.filter(r => fullUrl.indexOf(r.replace(/\/$/, '')) === 0).sort()[0]
|
27 | return appsByRoot[root] || apps[0]
|
28 | }
|
29 |
|
30 | let warnedAboutSecure = false
|
31 |
|
32 | const SNICallback = (domain, cb) => {
|
33 | try {
|
34 |
|
35 | const app = apps.find(a => a.domain === domain)
|
36 |
|
37 | if (!app.secure && !warnedAboutSecure) {
|
38 | warnedAboutSecure = true
|
39 | setTimeout(() => warnedAboutSecure = false, 100)
|
40 |
|
41 | console.log(chalk.red(`You tried to access ${domain} through https but we don't have its certificate and key.`),
|
42 | `Does the app's panels.config.js include a secure key like?:
|
43 |
|
44 | secure: {
|
45 | cert: '/path/to/file.cert',
|
46 | key: '/path/to/file.key'
|
47 | }
|
48 |
|
49 | If you don't care about https, you can always access http://${domain}`)
|
50 | }
|
51 |
|
52 | cb(null, app.secureContext)
|
53 | } catch(err) {
|
54 | console.log(chalk.red(domain), err)
|
55 | }
|
56 | }
|
57 |
|
58 | const handler = (req, res) => {
|
59 | const app = findApp(req)
|
60 | app.handler(req, res, () => createHandler(app)(req, res))
|
61 | }
|
62 |
|
63 | const s = https.createServer({ SNICallback }, handler)
|
64 | s.on('error', console.error.bind(console))
|
65 | s.on('listening', () => {
|
66 | const list = apps.filter(a => a.secure)
|
67 | if (list.length) {
|
68 | console.log(chalk.green('secure apps'))
|
69 | console.log(list.map(app => ` https://${app.domain}${app.root}`).join('\n'))
|
70 | }
|
71 | })
|
72 | s.listen(443, HOST)
|
73 |
|
74 | const s2 = http.createServer((req, res) => {
|
75 | const app = findApp(req)
|
76 |
|
77 | if (app.secure) {
|
78 | res.writeHead(302, { Location: `https://${req.headers.host}${app.root}` })
|
79 | res.end()
|
80 | } else {
|
81 | handler(req, res)
|
82 | }
|
83 | })
|
84 | s2.on('error', console.error.bind(console))
|
85 | s2.on('listening', () => {
|
86 | const list = apps.filter(a => !a.secure)
|
87 |
|
88 | if (list.length) {
|
89 | console.log(chalk.yellow('insecure apps'))
|
90 | console.log(list.map(app => ` http://${app.domain}${app.root}`).join('\n'))
|
91 | }
|
92 | })
|
93 | s2.listen(80, HOST)
|
94 | }
|