locadot 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +10 -28
- package/dist/server.js +120 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,46 +7,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
exports.isValidLocalhostDomain = isValidLocalhostDomain;
|
|
8
8
|
exports.isDomainRunning = isDomainRunning;
|
|
9
9
|
const https_1 = __importDefault(require("https"));
|
|
10
|
-
const http_proxy_1 = __importDefault(require("http-proxy"));
|
|
11
10
|
const yargs_1 = __importDefault(require("yargs"));
|
|
12
11
|
const helpers_1 = require("yargs/helpers");
|
|
13
|
-
const
|
|
14
|
-
const utils_1 = require("./utils");
|
|
12
|
+
const server_1 = require("./server");
|
|
15
13
|
function isValidLocalhostDomain(domain) {
|
|
16
14
|
const localhostPattern = /^(?:[a-zA-Z0-9-]+\.)*localhost$/;
|
|
17
15
|
return localhostPattern.test(domain);
|
|
18
16
|
}
|
|
19
|
-
async function createReverseProxy(targetPort, domain) {
|
|
20
|
-
const proxy = http_proxy_1.default.createProxyServer({});
|
|
21
|
-
const { key, cert } = await (0, certs_1.createSSL)(domain);
|
|
22
|
-
const options = { key, cert };
|
|
23
|
-
const server = https_1.default.createServer(options, (req, res) => {
|
|
24
|
-
const host = req.headers.host;
|
|
25
|
-
if (host !== domain && host !== `${domain}:443`) {
|
|
26
|
-
res.writeHead(502, { "Content-Type": "text/html" });
|
|
27
|
-
res.end((0, utils_1.proxyNotFound)(host));
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
proxy.web(req, res, { target: `http://localhost:${targetPort}` }, (err) => {
|
|
31
|
-
console.error("Proxy error:", err);
|
|
32
|
-
res.writeHead(502);
|
|
33
|
-
res.end("Connection failed. Host not found.");
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
server.listen(443, () => {
|
|
37
|
-
console.log(`✅ HTTPS available at https://${domain}`);
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
17
|
function isDomainRunning(domain, port = 443) {
|
|
41
18
|
return new Promise((resolve) => {
|
|
42
19
|
const req = https_1.default.request({
|
|
43
|
-
hostname:
|
|
20
|
+
hostname: "localhost", // we're always connecting to localhost
|
|
44
21
|
port,
|
|
45
22
|
method: "HEAD",
|
|
46
23
|
rejectUnauthorized: false, // ignore self-signed certs
|
|
47
24
|
timeout: 1000,
|
|
25
|
+
headers: {
|
|
26
|
+
Host: domain, // 👈 critical: ask the proxy "Do you know this host?"
|
|
27
|
+
},
|
|
48
28
|
}, (res) => {
|
|
49
|
-
|
|
29
|
+
// Only return true if it returns a valid 2xx or 3xx status
|
|
30
|
+
resolve(res.statusCode >= 200 && res.statusCode < 400);
|
|
50
31
|
res.destroy();
|
|
51
32
|
});
|
|
52
33
|
req.on("error", () => resolve(false));
|
|
@@ -71,7 +52,7 @@ async function run() {
|
|
|
71
52
|
demandOption: true,
|
|
72
53
|
type: "number",
|
|
73
54
|
})
|
|
74
|
-
.usage("Usage: npx
|
|
55
|
+
.usage("Usage: npx locadot --host local.dev --port 3350")
|
|
75
56
|
.help().argv;
|
|
76
57
|
if (!isValidLocalhostDomain(argv.host)) {
|
|
77
58
|
console.error("❌ Invalid domain. Please use a valid localhost domain like dev.localhost, localhost, test.localhost, etc.");
|
|
@@ -84,6 +65,7 @@ async function run() {
|
|
|
84
65
|
}
|
|
85
66
|
const domain = argv.host;
|
|
86
67
|
const port = argv.port;
|
|
87
|
-
|
|
68
|
+
await (0, server_1.startCentralProxy)();
|
|
69
|
+
await (0, server_1.registerDomain)(domain, port);
|
|
88
70
|
}
|
|
89
71
|
run();
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.cleanUp = exports.REGISTRY_FILE = exports.LOCK_FILE = void 0;
|
|
7
|
+
exports.startCentralProxy = startCentralProxy;
|
|
8
|
+
exports.registerDomain = registerDomain;
|
|
9
|
+
exports.isProxyAlreadyRunning = isProxyAlreadyRunning;
|
|
10
|
+
const http_proxy_1 = __importDefault(require("http-proxy"));
|
|
11
|
+
const certs_1 = require("./certs");
|
|
12
|
+
const https_1 = __importDefault(require("https"));
|
|
13
|
+
const http_1 = __importDefault(require("http"));
|
|
14
|
+
const fs_1 = __importDefault(require("fs"));
|
|
15
|
+
const utils_1 = require("./utils");
|
|
16
|
+
const path_1 = __importDefault(require("path"));
|
|
17
|
+
exports.LOCK_FILE = path_1.default.join(require("os").homedir(), ".locadot.lock");
|
|
18
|
+
exports.REGISTRY_FILE = path_1.default.join(require("os").homedir(), ".locadot-registry.json");
|
|
19
|
+
async function startCentralProxy() {
|
|
20
|
+
if (await isProxyAlreadyRunning()) {
|
|
21
|
+
console.log("🔌 Proxy is already running");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
let domainMap = {};
|
|
25
|
+
// Create lock file
|
|
26
|
+
fs_1.default.writeFileSync(exports.LOCK_FILE, process.pid.toString());
|
|
27
|
+
if (!fs_1.default.existsSync(exports.REGISTRY_FILE)) {
|
|
28
|
+
fs_1.default.writeFileSync(exports.REGISTRY_FILE, "{}");
|
|
29
|
+
}
|
|
30
|
+
if (fs_1.default.existsSync(exports.REGISTRY_FILE)) {
|
|
31
|
+
domainMap = JSON.parse(fs_1.default.readFileSync(exports.REGISTRY_FILE, "utf-8"));
|
|
32
|
+
}
|
|
33
|
+
const proxy = http_proxy_1.default.createProxyServer({});
|
|
34
|
+
const defaultCert = await (0, certs_1.createSSL)("localhost");
|
|
35
|
+
const watcher = fs_1.default.watch(exports.REGISTRY_FILE, (eventType) => {
|
|
36
|
+
if (eventType === "change") {
|
|
37
|
+
domainMap = JSON.parse(fs_1.default.readFileSync(exports.REGISTRY_FILE, "utf-8"));
|
|
38
|
+
console.log("🔄 Updated domain mappings:", domainMap);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
process.on("SIGINT", cleanupFn);
|
|
42
|
+
process.on("SIGTERM", cleanupFn);
|
|
43
|
+
function cleanupFn() {
|
|
44
|
+
watcher.close();
|
|
45
|
+
(0, exports.cleanUp)();
|
|
46
|
+
process.exit();
|
|
47
|
+
}
|
|
48
|
+
const requestHandler = (req, res) => {
|
|
49
|
+
const host = req.headers.host?.split(":")[0];
|
|
50
|
+
const targetPort = domainMap[host];
|
|
51
|
+
if (!targetPort) {
|
|
52
|
+
res.writeHead(502, { "Content-Type": "text/html" });
|
|
53
|
+
res.end((0, utils_1.proxyNotFound)(host));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
proxy.web(req, res, { target: `http://localhost:${targetPort}` }, (err) => {
|
|
57
|
+
console.error("Proxy error:", err);
|
|
58
|
+
res.writeHead(502);
|
|
59
|
+
res.end("Connection failed. Host not found.");
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
const httpsServer = https_1.default.createServer(defaultCert, requestHandler);
|
|
63
|
+
const httpServer = http_1.default.createServer(requestHandler);
|
|
64
|
+
httpsServer.listen(443, () => {
|
|
65
|
+
console.log("🌐 HTTPS proxy running on port 443");
|
|
66
|
+
});
|
|
67
|
+
httpServer.listen(80, () => {
|
|
68
|
+
console.log("🌐 HTTP proxy running on port 80");
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async function registerDomain(domain, port) {
|
|
72
|
+
if (!(await isProxyAlreadyRunning())) {
|
|
73
|
+
console.error('❌ Proxy is not running. Start it first with "locadot start"');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
let registry = {};
|
|
77
|
+
if (!fs_1.default.existsSync(exports.REGISTRY_FILE)) {
|
|
78
|
+
fs_1.default.writeFileSync(exports.REGISTRY_FILE, "{}");
|
|
79
|
+
}
|
|
80
|
+
if (fs_1.default.existsSync(exports.REGISTRY_FILE)) {
|
|
81
|
+
registry = JSON.parse(fs_1.default.readFileSync(exports.REGISTRY_FILE, "utf-8"));
|
|
82
|
+
}
|
|
83
|
+
if (registry[domain]) {
|
|
84
|
+
console.error(`❌ ${domain} already mapped to port ${registry[domain]}`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
registry[domain] = port;
|
|
88
|
+
fs_1.default.writeFileSync(exports.REGISTRY_FILE, JSON.stringify(registry, null, 2));
|
|
89
|
+
console.log(`✅ ${domain} => http://localhost:${port}`);
|
|
90
|
+
}
|
|
91
|
+
async function isProxyAlreadyRunning() {
|
|
92
|
+
try {
|
|
93
|
+
if (fs_1.default.existsSync(exports.LOCK_FILE)) {
|
|
94
|
+
const pid = parseInt(fs_1.default.readFileSync(exports.LOCK_FILE, "utf-8"));
|
|
95
|
+
try {
|
|
96
|
+
process.kill(pid, 0); // Check if process exists
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// PID doesn't exist, remove stale lock file
|
|
101
|
+
(0, exports.cleanUp)();
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
(0, exports.cleanUp)();
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
(0, exports.cleanUp)();
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const cleanUp = () => {
|
|
114
|
+
try {
|
|
115
|
+
fs_1.default.unlinkSync(exports.LOCK_FILE);
|
|
116
|
+
fs_1.default.rmSync(exports.REGISTRY_FILE);
|
|
117
|
+
}
|
|
118
|
+
catch (error) { }
|
|
119
|
+
};
|
|
120
|
+
exports.cleanUp = cleanUp;
|
package/package.json
CHANGED