1 | const fetch = require("node-fetch");
|
2 | const fs = require("fs");
|
3 | const os = require("os");
|
4 | const path = require("path");
|
5 | const execa = require("execa");
|
6 | const chalk = require("chalk");
|
7 | const { fetchLatest, updateAvailable } = require("gh-release-fetch");
|
8 | const {
|
9 | NETLIFYDEVLOG,
|
10 |
|
11 | NETLIFYDEVERR
|
12 | } = require("netlify-cli-logo");
|
13 |
|
14 | async function createTunnel(siteId, netlifyApiToken, log) {
|
15 | await installTunnelClient(log);
|
16 |
|
17 | if (!siteId) {
|
18 |
|
19 | console.error(
|
20 | `${NETLIFYDEVERR} Error: no siteId defined, did you forget to run ${chalk.yellow(
|
21 | "netlify init"
|
22 | )} or ${chalk.yellow("netlify link")}?`
|
23 | );
|
24 | process.exit(1);
|
25 | }
|
26 | log(`${NETLIFYDEVLOG} Creating Live Tunnel for ` + siteId);
|
27 | const url = `https://api.netlify.com/api/v1/live_sessions?site_id=${siteId}`;
|
28 |
|
29 | const response = await fetch(url, {
|
30 | method: "POST",
|
31 | headers: {
|
32 | "Content-Type": "application/json",
|
33 | Authorization: `Bearer ${netlifyApiToken}`
|
34 | },
|
35 | body: JSON.stringify({})
|
36 | });
|
37 |
|
38 | const data = await response.json();
|
39 |
|
40 | if (response.status !== 201) {
|
41 | throw new Error(data.message);
|
42 | }
|
43 |
|
44 | return data;
|
45 | }
|
46 |
|
47 | async function connectTunnel(session, netlifyApiToken, localPort, log) {
|
48 | const execPath = path.join(
|
49 | os.homedir(),
|
50 | ".netlify",
|
51 | "tunnel",
|
52 | "bin",
|
53 | "live-tunnel-client"
|
54 | );
|
55 | const args = [
|
56 | "connect",
|
57 | "-s",
|
58 | session.id,
|
59 | "-t",
|
60 | netlifyApiToken,
|
61 | "-l",
|
62 | localPort
|
63 | ];
|
64 | if (process.env.DEBUG) {
|
65 | args.push("-v");
|
66 | log(execPath, args);
|
67 | }
|
68 |
|
69 | const ps = execa(execPath, args, { stdio: "inherit" });
|
70 | ps.on("close", code => process.exit(code));
|
71 | ps.on("SIGINT", process.exit);
|
72 | ps.on("SIGTERM", process.exit);
|
73 | }
|
74 |
|
75 | async function installTunnelClient(log) {
|
76 | const binPath = path.join(os.homedir(), ".netlify", "tunnel", "bin");
|
77 | const execPath = path.join(binPath, "live-tunnel-client");
|
78 | const newVersion = await fetchTunnelClient(execPath);
|
79 | if (!newVersion) {
|
80 | return;
|
81 | }
|
82 |
|
83 | log(`${NETLIFYDEVLOG} Installing Live Tunnel Client`);
|
84 |
|
85 | const win = isWindows();
|
86 | const platform = win ? "windows" : process.platform;
|
87 | const extension = win ? "zip" : "tar.gz";
|
88 | const release = {
|
89 | repository: "netlify/live-tunnel-client",
|
90 | package: `live-tunnel-client-${platform}-amd64.${extension}`,
|
91 | destination: binPath,
|
92 | extract: true
|
93 | };
|
94 | await fetchLatest(release);
|
95 | }
|
96 |
|
97 | async function fetchTunnelClient(execPath) {
|
98 | if (!execExist(execPath)) {
|
99 | return true;
|
100 | }
|
101 |
|
102 | const { stdout } = await execa(execPath, ["version"]);
|
103 | if (!stdout) {
|
104 | return false;
|
105 | }
|
106 |
|
107 | const match = stdout.match(/^live-tunnel-client\/v?([^\s]+)/);
|
108 | if (!match) {
|
109 | return false;
|
110 | }
|
111 |
|
112 | return updateAvailable("netlify/live-tunnel-client", match[1]);
|
113 | }
|
114 |
|
115 | function execExist(binPath) {
|
116 | if (!fs.existsSync(binPath)) {
|
117 | return false;
|
118 | }
|
119 | const stat = fs.statSync(binPath);
|
120 | return stat && stat.isFile() && isExe(stat.mode, stat.gid, stat.uid);
|
121 | }
|
122 |
|
123 | function isExe(mode, gid, uid) {
|
124 | if (isWindows()) {
|
125 | return true;
|
126 | }
|
127 |
|
128 | const isGroup = gid ? process.getgid && gid === process.getgid() : true;
|
129 | const isUser = uid ? process.getuid && uid === process.getuid() : true;
|
130 |
|
131 | return Boolean(
|
132 | mode & 0o0001 || (mode & 0o0010 && isGroup) || (mode & 0o0100 && isUser)
|
133 | );
|
134 | }
|
135 |
|
136 | function isWindows() {
|
137 | return process.platform === "win32";
|
138 | }
|
139 |
|
140 | module.exports = {
|
141 | createTunnel: createTunnel,
|
142 | connectTunnel: connectTunnel
|
143 | };
|