xynginc 1.0.95 → 1.0.96
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/mods/constant.js +1 -1
- package/dist/mods/downloadBinary.js +68 -10
- package/dist/mods/ensureBinary.js +18 -2
- package/dist/mods/requirements.js +15 -17
- package/dist/startPlugin.js +41 -20
- package/package.json +2 -2
- package/scripts/postinstall.js +49 -24
- package/xypriss.plugin.xsig +4 -4
package/dist/mods/constant.js
CHANGED
|
@@ -4,6 +4,7 @@ import { BINARY_NAME, BINARY_DIR, GITHUB_REPO } from "./constant";
|
|
|
4
4
|
import { __strl__ } from "strulink";
|
|
5
5
|
const fs = __sys__.fs;
|
|
6
6
|
const path = __sys__.path;
|
|
7
|
+
let activeDownloadPromise = null;
|
|
7
8
|
/**
|
|
8
9
|
* Downloads the xynginc binary from GitHub releases.
|
|
9
10
|
*
|
|
@@ -11,6 +12,18 @@ const path = __sys__.path;
|
|
|
11
12
|
* @returns The path to the downloaded binary.
|
|
12
13
|
*/
|
|
13
14
|
export async function downloadBinary(version) {
|
|
15
|
+
if (activeDownloadPromise) {
|
|
16
|
+
return activeDownloadPromise;
|
|
17
|
+
}
|
|
18
|
+
activeDownloadPromise = doDownload(version);
|
|
19
|
+
try {
|
|
20
|
+
return await activeDownloadPromise;
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
activeDownloadPromise = null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function doDownload(version) {
|
|
14
27
|
const platform = __sys__.os.platform();
|
|
15
28
|
const arch = __sys__.os.arch();
|
|
16
29
|
if (platform !== "linux") {
|
|
@@ -21,35 +34,80 @@ export async function downloadBinary(version) {
|
|
|
21
34
|
? `https://github.com/${GITHUB_REPO}/releases/latest/download/${binaryName}`
|
|
22
35
|
: `https://github.com/${GITHUB_REPO}/releases/download/${version}/${binaryName}`;
|
|
23
36
|
Logger.info(`[XyNginC] Downloading from: ${__strl__.createUrl(downloadUrl).hostname}`);
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
// Ensure bin directory exists (with fallback if root package directory is read-only)
|
|
38
|
+
let targetDir = BINARY_DIR;
|
|
39
|
+
try {
|
|
40
|
+
if (!fs.exists(targetDir)) {
|
|
41
|
+
fs.ensureDir(targetDir);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
const home = __sys__.os.homeDir
|
|
46
|
+
? __sys__.os.homeDir()
|
|
47
|
+
: (typeof process !== "undefined" && process.env.HOME) || "/tmp";
|
|
48
|
+
targetDir = path.join(home, ".xynginc", "bin");
|
|
49
|
+
fs.ensureDir(targetDir);
|
|
50
|
+
}
|
|
51
|
+
const localPath = path.join(targetDir, BINARY_NAME);
|
|
27
52
|
return new Promise((resolve, reject) => {
|
|
28
|
-
function download(url) {
|
|
53
|
+
function download(url, redirectCount = 0) {
|
|
54
|
+
if (redirectCount > 5) {
|
|
55
|
+
reject(new Error("[XyNginC] Too many redirects while downloading binary"));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
29
58
|
https
|
|
30
|
-
.get(url,
|
|
59
|
+
.get(url, {
|
|
60
|
+
headers: {
|
|
61
|
+
"User-Agent": "xynginc",
|
|
62
|
+
Accept: "*/*",
|
|
63
|
+
},
|
|
64
|
+
}, (response) => {
|
|
31
65
|
if (response.statusCode &&
|
|
32
66
|
response.statusCode >= 300 &&
|
|
33
67
|
response.statusCode < 400 &&
|
|
34
68
|
response.headers.location) {
|
|
35
|
-
|
|
69
|
+
response.resume();
|
|
70
|
+
download(response.headers.location, redirectCount + 1);
|
|
36
71
|
return;
|
|
37
72
|
}
|
|
38
73
|
if (response.statusCode !== 200) {
|
|
39
|
-
|
|
74
|
+
response.resume();
|
|
75
|
+
reject(new Error(`Failed to download binary from ${url}: HTTP ${response.statusCode}`));
|
|
40
76
|
return;
|
|
41
77
|
}
|
|
42
78
|
const file = fs.createWriteStream(localPath);
|
|
79
|
+
file.on("error", (err) => {
|
|
80
|
+
try {
|
|
81
|
+
fs.rmIfExists(localPath);
|
|
82
|
+
}
|
|
83
|
+
catch { }
|
|
84
|
+
reject(err);
|
|
85
|
+
});
|
|
86
|
+
response.on("error", (err) => {
|
|
87
|
+
try {
|
|
88
|
+
fs.rmIfExists(localPath);
|
|
89
|
+
}
|
|
90
|
+
catch { }
|
|
91
|
+
reject(err);
|
|
92
|
+
});
|
|
43
93
|
response.pipe(file);
|
|
44
94
|
file.on("finish", () => {
|
|
45
|
-
file.close();
|
|
46
|
-
|
|
95
|
+
file.close();
|
|
96
|
+
try {
|
|
97
|
+
fs.chmod(localPath, 0o755);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Ignore chmod error if any
|
|
101
|
+
}
|
|
47
102
|
Logger.success("[XyNginC] ✓ Binary downloaded successfully");
|
|
48
103
|
resolve(localPath);
|
|
49
104
|
});
|
|
50
105
|
})
|
|
51
106
|
.on("error", (err) => {
|
|
52
|
-
|
|
107
|
+
try {
|
|
108
|
+
fs.rmIfExists(localPath);
|
|
109
|
+
}
|
|
110
|
+
catch { }
|
|
53
111
|
reject(err);
|
|
54
112
|
});
|
|
55
113
|
}
|
|
@@ -30,12 +30,28 @@ export async function ensureBinary(customPath, autoDownload, version) {
|
|
|
30
30
|
catch {
|
|
31
31
|
// Not in PATH
|
|
32
32
|
}
|
|
33
|
-
// 3. Try local bin directory
|
|
33
|
+
// 3. Try local bin directory (<package_root>/bin/xynginc)
|
|
34
34
|
const localPath = path.join(BINARY_DIR, BINARY_NAME);
|
|
35
35
|
if (fs.exists(localPath)) {
|
|
36
|
+
try {
|
|
37
|
+
fs.chmod(localPath, 0o755);
|
|
38
|
+
}
|
|
39
|
+
catch { }
|
|
36
40
|
return localPath;
|
|
37
41
|
}
|
|
38
|
-
// 4.
|
|
42
|
+
// 4. Try user home cache (~/.xynginc/bin/xynginc)
|
|
43
|
+
const home = __sys__.os.homeDir
|
|
44
|
+
? __sys__.os.homeDir()
|
|
45
|
+
: (typeof process !== "undefined" && process.env.HOME) || "/tmp";
|
|
46
|
+
const userPath = path.join(home, ".xynginc", "bin", BINARY_NAME);
|
|
47
|
+
if (fs.exists(userPath)) {
|
|
48
|
+
try {
|
|
49
|
+
fs.chmod(userPath, 0o755);
|
|
50
|
+
}
|
|
51
|
+
catch { }
|
|
52
|
+
return userPath;
|
|
53
|
+
}
|
|
54
|
+
// 5. Auto-download if enabled
|
|
39
55
|
if (autoDownload) {
|
|
40
56
|
Logger.info("[XyNginC] Binary not found, downloading...");
|
|
41
57
|
return await downloadBinary(version);
|
|
@@ -35,6 +35,8 @@ export async function installRequirementsHandler(binaryPath, sudoCmd) {
|
|
|
35
35
|
});
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
+
const fs = __sys__.fs;
|
|
39
|
+
const path = __sys__.path;
|
|
38
40
|
/**
|
|
39
41
|
* Applies the configuration using the xynginc binary.
|
|
40
42
|
*
|
|
@@ -52,8 +54,8 @@ export async function applyConfig(binaryPath, config, sudoCmd) {
|
|
|
52
54
|
port: d.port,
|
|
53
55
|
ssl: d.ssl,
|
|
54
56
|
email: d.email,
|
|
55
|
-
host: d.host,
|
|
56
|
-
max_body_size: d.maxBodySize,
|
|
57
|
+
host: d.host || "127.0.0.1",
|
|
58
|
+
max_body_size: d.maxBodySize || "10M",
|
|
57
59
|
})),
|
|
58
60
|
};
|
|
59
61
|
const configJson = XStringify(mappedConfig, {
|
|
@@ -63,6 +65,9 @@ export async function applyConfig(binaryPath, config, sudoCmd) {
|
|
|
63
65
|
truncateStrings: 1000000, // 1MB limit per string (pour le HTML)
|
|
64
66
|
reportCircularPath: true,
|
|
65
67
|
});
|
|
68
|
+
const tmpDir = typeof fs.tempDir === "function" ? fs.tempDir() : "/tmp";
|
|
69
|
+
const tempConfigFile = path.join(tmpDir, `.xynginc-config-${Date.now()}-${Math.random().toString(36).substring(2, 8)}.json`);
|
|
70
|
+
fs.writeFileSync(tempConfigFile, configJson);
|
|
66
71
|
try {
|
|
67
72
|
// Test nginx BEFORE applying new config
|
|
68
73
|
Logger.info("[XyNginC] Testing current nginx config...");
|
|
@@ -70,21 +75,8 @@ export async function applyConfig(binaryPath, config, sudoCmd) {
|
|
|
70
75
|
if (!testResult) {
|
|
71
76
|
Logger.warn("[XyNginC] ⚠️ Current nginx config has errors. Attempting to fix...");
|
|
72
77
|
}
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
if (sudoCmd.includes("-S")) {
|
|
76
|
-
const pwdMatch = sudoCmd.match(/echo '(.*)' \| sudo -S/);
|
|
77
|
-
if (pwdMatch) {
|
|
78
|
-
const pwd = pwdMatch[1];
|
|
79
|
-
await execStream(`(echo '${pwd}'; echo '${configJson}') | sudo -S ${binaryPath} apply --config -`);
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
await execStream(`echo '${configJson}' | ${sudoCmd} ${binaryPath} apply --config -`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
await execStream(`echo '${configJson}' | ${sudoCmd} ${binaryPath} apply --config -`);
|
|
87
|
-
}
|
|
78
|
+
// Apply configuration using the temporary file to avoid stdin collision with sudo
|
|
79
|
+
await execStream(`${sudoCmd} ${binaryPath} apply --config ${tempConfigFile}`);
|
|
88
80
|
}
|
|
89
81
|
catch (error) {
|
|
90
82
|
// If it fails, show more helpful error
|
|
@@ -93,6 +85,12 @@ export async function applyConfig(binaryPath, config, sudoCmd) {
|
|
|
93
85
|
Logger.info("[XyNginC] Check: /etc/nginx/sites-enabled/");
|
|
94
86
|
throw new Error(`Failed to apply configuration: ${error.message}`);
|
|
95
87
|
}
|
|
88
|
+
finally {
|
|
89
|
+
try {
|
|
90
|
+
fs.rmIfExists(tempConfigFile);
|
|
91
|
+
}
|
|
92
|
+
catch { }
|
|
93
|
+
}
|
|
96
94
|
}
|
|
97
95
|
/**
|
|
98
96
|
* Adds a new domain configuration using the binary.
|
package/dist/startPlugin.js
CHANGED
|
@@ -26,6 +26,8 @@ const getSudo = (sudoPassword) => {
|
|
|
26
26
|
// Return non-interactive sudo to prevent infinite blocking/hanging
|
|
27
27
|
return "sudo -n";
|
|
28
28
|
};
|
|
29
|
+
let appliedConfigKey = null;
|
|
30
|
+
let activeInitPromise = null;
|
|
29
31
|
export async function startXNCPlugin(server, options) {
|
|
30
32
|
const { binaryPath, autoDownload, version, domains, autoReload, autoFixFirewall, installRequirements, sudoPassword, } = options;
|
|
31
33
|
Logger.info("[XyNginC] Initializing Nginx Controller...");
|
|
@@ -42,28 +44,47 @@ export async function startXNCPlugin(server, options) {
|
|
|
42
44
|
binary,
|
|
43
45
|
},
|
|
44
46
|
});
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
const configKey = JSON.stringify({
|
|
48
|
+
domains,
|
|
49
|
+
autoReload,
|
|
50
|
+
autoFixFirewall,
|
|
51
|
+
});
|
|
52
|
+
if (appliedConfigKey !== configKey) {
|
|
53
|
+
if (!activeInitPromise) {
|
|
54
|
+
activeInitPromise = (async () => {
|
|
55
|
+
// 2. Check system requirements
|
|
56
|
+
Logger.info("[XyNginC] Checking system requirements...");
|
|
57
|
+
const requirementsOk = await checkRequirements(binary, getSudo(sudoPassword));
|
|
58
|
+
if (!requirementsOk && installRequirements) {
|
|
59
|
+
Logger.info("[XyNginC] Requirements missing, installing automatically...");
|
|
60
|
+
await installRequirementsHandler(binary, getSudo(sudoPassword));
|
|
61
|
+
Logger.info("[XyNginC] Requirements installed, re-checking...");
|
|
62
|
+
await checkRequirements(binary, getSudo(sudoPassword));
|
|
63
|
+
}
|
|
64
|
+
else if (!requirementsOk) {
|
|
65
|
+
throw new Error("[XyNginC] System requirements not satisfied. Install with 'installRequirements: true' or run: sudo xynginc install");
|
|
66
|
+
}
|
|
67
|
+
// 3. Apply configuration
|
|
68
|
+
Logger.info("[XyNginC] Applying configuration...");
|
|
69
|
+
await applyConfig(binary, {
|
|
70
|
+
domains,
|
|
71
|
+
auto_reload: autoReload,
|
|
72
|
+
auto_fix_firewall: autoFixFirewall,
|
|
73
|
+
}, getSudo(sudoPassword));
|
|
74
|
+
appliedConfigKey = configKey;
|
|
75
|
+
Logger.success("[XyNginC] Configuration applied successfully!");
|
|
76
|
+
})();
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
await activeInitPromise;
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
activeInitPromise = null;
|
|
83
|
+
}
|
|
55
84
|
}
|
|
56
|
-
else
|
|
57
|
-
|
|
85
|
+
else {
|
|
86
|
+
Logger.info("[XyNginC] Configuration already applied for this multi-server group.");
|
|
58
87
|
}
|
|
59
|
-
// 3. Apply configuration
|
|
60
|
-
Logger.info("[XyNginC] Applying configuration...");
|
|
61
|
-
await applyConfig(binary, {
|
|
62
|
-
domains,
|
|
63
|
-
auto_reload: autoReload,
|
|
64
|
-
auto_fix_firewall: autoFixFirewall,
|
|
65
|
-
}, getSudo(sudoPassword));
|
|
66
|
-
Logger.success("[XyNginC] Configuration applied successfully!");
|
|
67
88
|
// Expose CLI helper methods on server
|
|
68
89
|
const sUtil = {
|
|
69
90
|
addDomain: (domain, port, ssl = false, email, maxBodySize) => addDomain(binary, domain, port, ssl, email, maxBodySize, getSudo(sudoPassword)),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xynginc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.96",
|
|
4
4
|
"description": "XyPriss Nginx Controller - Automatic Nginx & SSL management for XyPriss servers",
|
|
5
5
|
"author": "Seth Eleazar - iDevo",
|
|
6
6
|
"license": "NOSL",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"xypriss-security": "^2.1.16"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"xypriss": ">=9.
|
|
51
|
+
"xypriss": ">=9.12.69"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^20.19.37",
|
package/scripts/postinstall.js
CHANGED
|
@@ -27,32 +27,57 @@ if (!fs.existsSync(binDir)) {
|
|
|
27
27
|
fs.mkdirSync(binDir, { recursive: true });
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
function download(url, dest) {
|
|
30
|
+
function download(url, dest, redirectCount = 0) {
|
|
31
31
|
return new Promise((resolve, reject) => {
|
|
32
|
+
if (redirectCount > 5) {
|
|
33
|
+
reject(new Error("Too many redirects while downloading binary"));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
32
37
|
https
|
|
33
|
-
.get(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
38
|
+
.get(
|
|
39
|
+
url,
|
|
40
|
+
{
|
|
41
|
+
headers: {
|
|
42
|
+
"User-Agent": "xynginc",
|
|
43
|
+
Accept: "*/*",
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
(response) => {
|
|
47
|
+
if (
|
|
48
|
+
response.statusCode >= 300 &&
|
|
49
|
+
response.statusCode < 400 &&
|
|
50
|
+
response.headers.location
|
|
51
|
+
) {
|
|
52
|
+
response.resume();
|
|
53
|
+
download(response.headers.location, dest, redirectCount + 1)
|
|
54
|
+
.then(resolve)
|
|
55
|
+
.catch(reject);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (response.statusCode !== 200) {
|
|
60
|
+
response.resume();
|
|
61
|
+
reject(new Error(`Failed to download: HTTP ${response.statusCode}`));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const file = fs.createWriteStream(dest);
|
|
66
|
+
file.on("error", (err) => {
|
|
67
|
+
fs.unlink(dest, () => {});
|
|
68
|
+
reject(err);
|
|
69
|
+
});
|
|
70
|
+
response.on("error", (err) => {
|
|
71
|
+
fs.unlink(dest, () => {});
|
|
72
|
+
reject(err);
|
|
73
|
+
});
|
|
74
|
+
response.pipe(file);
|
|
75
|
+
file.on("finish", () => {
|
|
76
|
+
file.close();
|
|
77
|
+
resolve();
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
)
|
|
56
81
|
.on("error", (err) => {
|
|
57
82
|
fs.unlink(dest, () => {});
|
|
58
83
|
reject(err);
|
package/xypriss.plugin.xsig
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
--- XYPRISS SIGNATURE (G3) ---
|
|
2
|
-
Manifest: xynginc@1.0.
|
|
2
|
+
Manifest: xynginc@1.0.96
|
|
3
3
|
Min-Engine: 1.0.81
|
|
4
|
-
Fingerprint: sha256:
|
|
4
|
+
Fingerprint: sha256:e7e5a57bf5e0a262062baa0c78a5c59c91044c00da42aabc6af91edffc80abbe
|
|
5
5
|
Identity: ed25519:0e2e1accdbba45480979305256d50d368ae6cc2c0d7ee378c5f1999ae834cf58
|
|
6
6
|
Privileges: XHS.HOOK.LIFECYCLE.REGISTER,XHS.HOOK.LIFECYCLE.SERVER_START,XHS.HOOK.LIFECYCLE.SERVER_STOP,XHS.HOOK.LIFECYCLE.SERVER_READY
|
|
7
|
-
Expires: 2027-09-
|
|
7
|
+
Expires: 2027-09-13T22:07:58Z
|
|
8
8
|
Revision: sha256:none
|
|
9
9
|
--- BEGIN CRYPTOGRAPHIC PROOF ---
|
|
10
|
-
base64:
|
|
10
|
+
base64:VSQiC693lzyQXwMlaho4Hlequr97rUQCbWIx2KhaGMNBRiiEMjn1RBSFPGCskwaA3nUmmcBMd23LTAxDpERaDw==
|
|
11
11
|
--- END XYPRISS SIGNATURE ---
|