ztechno_core 0.0.98 → 0.0.100
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/lib/scripts/docker-update.d.ts +19 -13
- package/lib/scripts/docker-update.js +142 -29
- package/package.json +5 -1
|
@@ -1,21 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* @param opt.port - The port number
|
|
5
|
-
* @param opt.volumes - Optional array of volume mappings
|
|
6
|
-
* @param opt.c - Optional logger
|
|
7
|
-
* @returns Promise resolving to success status and message
|
|
8
|
-
*/
|
|
9
|
-
export declare function updateDocker(opt: {
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export interface DockerUpdateOptions {
|
|
3
|
+
/** The package/image name */
|
|
10
4
|
packagename: string;
|
|
5
|
+
/** The port number */
|
|
11
6
|
port: string | number;
|
|
7
|
+
/** Optional array of volume mappings */
|
|
12
8
|
volumes?: string[];
|
|
9
|
+
/** Optional logger (defaults to silent) */
|
|
13
10
|
c?: {
|
|
14
|
-
log: any;
|
|
15
|
-
error: any;
|
|
11
|
+
log: (...args: any[]) => void;
|
|
12
|
+
error: (...args: any[]) => void;
|
|
16
13
|
};
|
|
17
|
-
|
|
14
|
+
/** Request timeout in ms (default: 30000) */
|
|
15
|
+
timeout?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface DockerUpdateResult {
|
|
18
18
|
success: boolean;
|
|
19
19
|
message?: string;
|
|
20
20
|
err?: string;
|
|
21
|
-
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Update a remote Docker container via the V2 Secure API
|
|
24
|
+
* @param opt - Update options
|
|
25
|
+
* @returns Promise resolving to success status and message
|
|
26
|
+
*/
|
|
27
|
+
export declare function updateDocker(opt: DockerUpdateOptions): Promise<DockerUpdateResult>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
'use strict';
|
|
2
3
|
var __importDefault =
|
|
3
4
|
(this && this.__importDefault) ||
|
|
@@ -8,33 +9,40 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
8
9
|
exports.updateDocker = void 0;
|
|
9
10
|
const http_1 = __importDefault(require('http'));
|
|
10
11
|
const crypto_1 = __importDefault(require('crypto'));
|
|
12
|
+
const path_1 = __importDefault(require('path'));
|
|
13
|
+
const fs_1 = __importDefault(require('fs'));
|
|
14
|
+
const dotenv_1 = require('dotenv');
|
|
11
15
|
const crypto_service_1 = require('../crypto_service');
|
|
16
|
+
// ANSI color helpers
|
|
17
|
+
const red = (msg) => `\x1b[31m${msg}\x1b[0m`;
|
|
18
|
+
const green = (msg) => `\x1b[32m${msg}\x1b[0m`;
|
|
19
|
+
const cyan = (msg) => `\x1b[36m${msg}\x1b[0m`;
|
|
20
|
+
const blue = (msg) => `\x1b[34m${msg}\x1b[0m`;
|
|
21
|
+
/** Default request timeout in milliseconds (30 seconds) */
|
|
22
|
+
const REQUEST_TIMEOUT_MS = 30000;
|
|
12
23
|
/**
|
|
13
24
|
* Update a remote Docker container via the V2 Secure API
|
|
14
|
-
* @param opt
|
|
15
|
-
* @param opt.port - The port number
|
|
16
|
-
* @param opt.volumes - Optional array of volume mappings
|
|
17
|
-
* @param opt.c - Optional logger
|
|
25
|
+
* @param opt - Update options
|
|
18
26
|
* @returns Promise resolving to success status and message
|
|
19
27
|
*/
|
|
20
28
|
function updateDocker(opt) {
|
|
21
29
|
return new Promise((resolve, reject) => {
|
|
22
|
-
const { packagename, port, volumes, c } = opt;
|
|
30
|
+
const { packagename, port, volumes, c, timeout = REQUEST_TIMEOUT_MS } = opt;
|
|
23
31
|
const secretKey = process.env.ZTECHNO_API_SECRET;
|
|
24
32
|
if (!secretKey) {
|
|
25
|
-
c
|
|
33
|
+
c?.log(red('✗ Error: ZTECHNO_API_SECRET environment variable not set'));
|
|
26
34
|
return reject(new Error('ZTECHNO_API_SECRET environment variable not set'));
|
|
27
35
|
}
|
|
28
|
-
// Generate timestamp and signature
|
|
36
|
+
// Generate timestamp and HMAC signature
|
|
29
37
|
const timestamp = Date.now().toString();
|
|
30
38
|
const volumesString = volumes && volumes.length > 0 ? volumes.join(',') : '';
|
|
31
39
|
const payload = timestamp + packagename + port + volumesString;
|
|
32
40
|
const signature = crypto_1.default.createHmac('sha256', secretKey).update(payload).digest('hex');
|
|
33
|
-
c?.log(
|
|
34
|
-
// Build query parameters
|
|
35
|
-
|
|
41
|
+
c?.log(green(`[Updating Remote Docker via V2 Secure API]`));
|
|
42
|
+
// Build query parameters with proper encoding
|
|
43
|
+
const query = new URLSearchParams({ port: String(port) });
|
|
36
44
|
if (volumesString) {
|
|
37
|
-
|
|
45
|
+
query.set('volumes', volumesString);
|
|
38
46
|
}
|
|
39
47
|
const options = {
|
|
40
48
|
hostname: crypto_service_1.ZCryptoService.decrypt({
|
|
@@ -42,8 +50,9 @@ function updateDocker(opt) {
|
|
|
42
50
|
encryptedData: '05b9d826539fe2cbdf7d7ecccfe57635',
|
|
43
51
|
}),
|
|
44
52
|
port: 7998,
|
|
45
|
-
path: `/v2/images/${packagename}/update?${
|
|
53
|
+
path: `/v2/images/${encodeURIComponent(packagename)}/update?${query}`,
|
|
46
54
|
method: 'GET',
|
|
55
|
+
timeout,
|
|
47
56
|
headers: {
|
|
48
57
|
'x-timestamp': timestamp,
|
|
49
58
|
'x-signature': signature,
|
|
@@ -57,38 +66,142 @@ function updateDocker(opt) {
|
|
|
57
66
|
res.on('end', () => {
|
|
58
67
|
try {
|
|
59
68
|
const response = JSON.parse(data);
|
|
60
|
-
c?.log(
|
|
61
|
-
c?.log(
|
|
69
|
+
c?.log(cyan(`Status Code: ${res.statusCode}`));
|
|
70
|
+
c?.log(blue(`Response: ${JSON.stringify(response, null, 2)}`));
|
|
62
71
|
if (response.success) {
|
|
63
|
-
c?.log(
|
|
64
|
-
resolve(response);
|
|
72
|
+
c?.log(green(`✓ ${response.message}`));
|
|
65
73
|
} else {
|
|
66
|
-
c?.log(
|
|
67
|
-
resolve(response);
|
|
74
|
+
c?.log(red(`✗ Error: ${response.err}`));
|
|
68
75
|
}
|
|
76
|
+
resolve(response);
|
|
69
77
|
} catch (err) {
|
|
70
|
-
c?.log(
|
|
78
|
+
c?.log(red(`Failed to parse response: ${data}`));
|
|
71
79
|
c?.error(err);
|
|
72
80
|
reject(err);
|
|
73
81
|
}
|
|
74
82
|
});
|
|
75
83
|
});
|
|
84
|
+
req.on('timeout', () => {
|
|
85
|
+
req.destroy();
|
|
86
|
+
const err = new Error(`Request timed out after ${timeout}ms`);
|
|
87
|
+
c?.log(red(`✗ ${err.message}`));
|
|
88
|
+
reject(err);
|
|
89
|
+
});
|
|
76
90
|
req.on('error', (err) => {
|
|
77
|
-
c?.log(
|
|
91
|
+
c?.log(red(`✗ Request failed: ${err.message}`));
|
|
78
92
|
reject(err);
|
|
79
93
|
});
|
|
80
94
|
req.end();
|
|
81
95
|
});
|
|
82
96
|
}
|
|
83
97
|
exports.updateDocker = updateDocker;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Read the consumer's package.json from the current working directory.
|
|
100
|
+
* Returns null if not found or unreadable.
|
|
101
|
+
*/
|
|
102
|
+
function loadPackageJson() {
|
|
103
|
+
try {
|
|
104
|
+
const pkgPath = path_1.default.join(process.cwd(), 'package.json');
|
|
105
|
+
if (!fs_1.default.existsSync(pkgPath)) return null;
|
|
106
|
+
return JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf-8'));
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Auto-detect options from .env and/or package.json in the current working directory.
|
|
113
|
+
*
|
|
114
|
+
* Resolution order for each field:
|
|
115
|
+
* 1. .env / environment variable
|
|
116
|
+
* 2. package.json (name → packagename, config.port → port, config.volumes → volumes)
|
|
117
|
+
*
|
|
118
|
+
* Required: packagename, port, ZTECHNO_API_SECRET (env only)
|
|
119
|
+
* Optional: volumes (comma-separated)
|
|
120
|
+
*/
|
|
121
|
+
function loadOptionsFromEnv() {
|
|
122
|
+
// Load .env file
|
|
123
|
+
const envPath = path_1.default.join(process.cwd(), '.env');
|
|
124
|
+
const cfg = (0, dotenv_1.config)({ path: envPath });
|
|
125
|
+
if (cfg.error && cfg.error.message && !cfg.error.message.includes('ENOENT')) {
|
|
126
|
+
throw cfg.error;
|
|
127
|
+
}
|
|
128
|
+
// Load package.json as fallback
|
|
129
|
+
const pkg = loadPackageJson();
|
|
130
|
+
const packagename = cfg.parsed?.packagename || process.env.packagename || pkg?.name;
|
|
131
|
+
const port =
|
|
132
|
+
cfg.parsed?.port || process.env.port || (pkg?.config?.port != null ? String(pkg.config.port) : undefined);
|
|
133
|
+
const volumesRaw = cfg.parsed?.volumes || process.env.volumes || pkg?.config?.volumes;
|
|
134
|
+
if (pkg) {
|
|
135
|
+
console.log(`Detected package.json: ${pkg.name || '(unnamed)'}`);
|
|
136
|
+
}
|
|
137
|
+
if (!packagename) {
|
|
138
|
+
throw new Error('Missing packagename. Set it in .env, environment, or package.json "name".');
|
|
90
139
|
}
|
|
91
|
-
|
|
92
|
-
|
|
140
|
+
if (!port) {
|
|
141
|
+
throw new Error('Missing port. Set it in .env, environment, or package.json "config.port".');
|
|
142
|
+
}
|
|
143
|
+
// Normalize volumes: accept string[] from package.json or comma-separated string from .env
|
|
144
|
+
let volumes;
|
|
145
|
+
if (Array.isArray(volumesRaw)) {
|
|
146
|
+
volumes = volumesRaw.filter(Boolean);
|
|
147
|
+
} else if (typeof volumesRaw === 'string') {
|
|
148
|
+
volumes = volumesRaw.split(',').filter(Boolean);
|
|
149
|
+
}
|
|
150
|
+
return { packagename, port, volumes };
|
|
151
|
+
}
|
|
152
|
+
const USAGE = `
|
|
153
|
+
Usage:
|
|
154
|
+
ztechno-docker-update <packagename>:<port>
|
|
155
|
+
ztechno-docker-update (auto-detect from .env / package.json)
|
|
156
|
+
|
|
157
|
+
Auto-detection priority:
|
|
158
|
+
1. .env file → packagename, port, volumes, ZTECHNO_API_SECRET
|
|
159
|
+
2. package.json → "name" as packagename, "config.port", "config.volumes"
|
|
160
|
+
|
|
161
|
+
.env file format:
|
|
162
|
+
ZTECHNO_API_SECRET=your_secret
|
|
163
|
+
packagename=my-image
|
|
164
|
+
port=3000
|
|
165
|
+
volumes=/host/path:/container/path (optional, comma-separated)
|
|
166
|
+
|
|
167
|
+
package.json format:
|
|
168
|
+
{
|
|
169
|
+
"name": "my-image",
|
|
170
|
+
"config": {
|
|
171
|
+
"port": 3000,
|
|
172
|
+
"volumes": ["/host/path:/container/path", "/logs:/app/logs"]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
`.trim();
|
|
176
|
+
// Run if executed directly (node docker-update.js or npx ztechno-docker-update)
|
|
177
|
+
if (require.main === module) {
|
|
178
|
+
const main = async () => {
|
|
179
|
+
const arg = process.argv[2];
|
|
180
|
+
if (arg === '--help' || arg === '-h') {
|
|
181
|
+
console.log(USAGE);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
// No argument: load from .env
|
|
185
|
+
if (arg === undefined) {
|
|
186
|
+
const { packagename, port, volumes } = loadOptionsFromEnv();
|
|
187
|
+
await updateDocker({ packagename, port, volumes, c: console });
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
// Argument with colon: packagename:port
|
|
191
|
+
if (arg.includes(':')) {
|
|
192
|
+
const [packagename, port] = arg.split(':');
|
|
193
|
+
if (!packagename || !port) {
|
|
194
|
+
throw new Error(`Invalid argument "${arg}". Expected format: <packagename>:<port>`);
|
|
195
|
+
}
|
|
196
|
+
await updateDocker({ packagename, port, c: console });
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
console.error(red(`✗ Invalid argument: "${arg}"`));
|
|
200
|
+
console.log(USAGE);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
};
|
|
203
|
+
main().catch((err) => {
|
|
204
|
+
console.error(red(`✗ Error: ${err.message}`));
|
|
205
|
+
process.exit(1);
|
|
206
|
+
});
|
|
93
207
|
}
|
|
94
|
-
module.exports = { updateDocker };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ztechno_core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.100",
|
|
4
4
|
"description": "Core files for ztechno framework",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
"postversion": "git push && git push --tags",
|
|
21
21
|
"update": "npm run build && npm version patch && npm publish"
|
|
22
22
|
},
|
|
23
|
+
"bin": {
|
|
24
|
+
"ztechno-docker-update": "lib/scripts/docker-update.js"
|
|
25
|
+
},
|
|
23
26
|
"keywords": [
|
|
24
27
|
"ztechno",
|
|
25
28
|
"core",
|
|
@@ -40,6 +43,7 @@
|
|
|
40
43
|
"dependencies": {
|
|
41
44
|
"@types/express": "^5.0.0",
|
|
42
45
|
"dom-parser": "^1.1.5",
|
|
46
|
+
"dotenv": "^17.3.1",
|
|
43
47
|
"mysql": "^2.18.1",
|
|
44
48
|
"nodemailer": "^6.8.0",
|
|
45
49
|
"translate": "^1.4.1"
|