-
Notifications
You must be signed in to change notification settings - Fork 9
/
index.js
116 lines (102 loc) · 3.01 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
const ngrok = require('ngrok')
const envFile = require('envfile')
const path = require('path')
const fs = require('fs')
const _ = require('lodash')
/**
* Creates public tunnels for provided ports on localhost. Also, writes tunnels url to .env file and deletes them after session is over.
*/
class ServerlessTunnel {
constructor (serverless, options) {
this.serverless = serverless
this.log = serverless.cli.log.bind(serverless.cli)
this.slsOptions = options
this.reconnectTried = false
this.noEnvFile = true
this.commands = {
tunnel: {
lifecycleEvents: ['init']
}
}
// Run tunnels after serverless-offline
this.hooks = {
'tunnel:init': this.runServer.bind(this, true),
'before:offline:start:init': this.runServer.bind(this)
}
}
async runTunnel ({port, envProp, ws, path, ngrokOptions}) {
try {
const url = await ngrok.connect({
addr: port,
proto: 'http',
region: 'eu',
...(ngrokOptions || {})
})
this.onConnect(url, envProp, ws, path)
} catch (e) {
this.errorHandler(e)
}
}
onConnect (url, envProp, ws, path) {
const tunnel = ws ? url.replace('http', 'ws') : url
if (envProp) {
this.envContent[envProp] = `${tunnel}${path || ''}`
this.log(`${envProp} available at: ${this.envContent[envProp]}`)
} else {
this.log(`Tunnel created at ${tunnel}${path || ''}`)
}
this.writeToEnv()
}
errorHandler (e) {
this.log(`Tunnels error: ${e.message}. Trying to reconnect in 5 seconds...`)
this.tryReconnect()
}
onTunnelClose () {
this.log('Closing tunnels...')
}
runServer (selfInit) {
this.options = _.get(this.serverless, 'service.custom.ngrokTunnel', {})
if (this.options.envPath) {
this.noEnvFile = false
this.envPath = path.resolve(process.cwd(), this.options.envPath)
try {
this.envContent = envFile.parseFileSync(this.envPath)
} catch (e) {
this.envContent = {}
this.noEnvFile = true
}
}
if (this.slsOptions.tunnel === 'true' || this.slsOptions.param?.includes('tunnel=true') || selfInit) {
if (this.options.tunnels && this.options.tunnels.length) {
this.log('Starting tunnels...')
this.options.tunnels.forEach((opt) => this.runTunnel(opt))
process.on('SIGINT', () => this.stopTunnel())
} else {
this.log('Tunnels are not configured. Skipping...')
}
}
}
stopTunnel () {
ngrok.kill()
if (!this.noEnvFile) {
(this.options.tunnels || []).forEach(({envProp}) => {
delete this.envContent[envProp]
})
this.writeToEnv()
}
}
tryReconnect () {
if (!this.reconnectTried) {
setTimeout(() => {
(this.options.tunnels || []).forEach((opt) => this.runTunnel(opt))
}, 5000)
this.reconnectTried = true
}
}
writeToEnv () {
if (!this.noEnvFile) {
fs.writeFileSync(this.envPath, envFile.stringifySync(this.envContent))
}
}
}
module.exports = ServerlessTunnel