ztechno_core 0.0.98 → 0.0.99

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.
@@ -1,21 +1,27 @@
1
- /**
2
- * Update a remote Docker container via the V2 Secure API
3
- * @param opt.packagename - The package/image name
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
- }): Promise<{
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,38 @@ 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 dotenv_1 = require('dotenv');
11
13
  const crypto_service_1 = require('../crypto_service');
14
+ // ANSI color helpers
15
+ const red = (msg) => `\x1b[31m${msg}\x1b[0m`;
16
+ const green = (msg) => `\x1b[32m${msg}\x1b[0m`;
17
+ const cyan = (msg) => `\x1b[36m${msg}\x1b[0m`;
18
+ const blue = (msg) => `\x1b[34m${msg}\x1b[0m`;
19
+ /** Default request timeout in milliseconds (30 seconds) */
20
+ const REQUEST_TIMEOUT_MS = 30000;
12
21
  /**
13
22
  * Update a remote Docker container via the V2 Secure API
14
- * @param opt.packagename - The package/image name
15
- * @param opt.port - The port number
16
- * @param opt.volumes - Optional array of volume mappings
17
- * @param opt.c - Optional logger
23
+ * @param opt - Update options
18
24
  * @returns Promise resolving to success status and message
19
25
  */
20
26
  function updateDocker(opt) {
21
27
  return new Promise((resolve, reject) => {
22
- const { packagename, port, volumes, c } = opt;
28
+ const { packagename, port, volumes, c, timeout = REQUEST_TIMEOUT_MS } = opt;
23
29
  const secretKey = process.env.ZTECHNO_API_SECRET;
24
30
  if (!secretKey) {
25
- c.log('\x1b[31m✗ Error: ZTECHNO_API_SECRET environment variable not set\x1b[0m');
31
+ c?.log(red('✗ Error: ZTECHNO_API_SECRET environment variable not set'));
26
32
  return reject(new Error('ZTECHNO_API_SECRET environment variable not set'));
27
33
  }
28
- // Generate timestamp and signature
34
+ // Generate timestamp and HMAC signature
29
35
  const timestamp = Date.now().toString();
30
36
  const volumesString = volumes && volumes.length > 0 ? volumes.join(',') : '';
31
37
  const payload = timestamp + packagename + port + volumesString;
32
38
  const signature = crypto_1.default.createHmac('sha256', secretKey).update(payload).digest('hex');
33
- c?.log('\x1b[32m' + `[Updating Remote Docker via V2 Secure API]`);
34
- // Build query parameters
35
- let queryParams = `port=${port}`;
39
+ c?.log(green(`[Updating Remote Docker via V2 Secure API]`));
40
+ // Build query parameters with proper encoding
41
+ const query = new URLSearchParams({ port: String(port) });
36
42
  if (volumesString) {
37
- queryParams += `&volumes=${volumesString}`;
43
+ query.set('volumes', volumesString);
38
44
  }
39
45
  const options = {
40
46
  hostname: crypto_service_1.ZCryptoService.decrypt({
@@ -42,8 +48,9 @@ function updateDocker(opt) {
42
48
  encryptedData: '05b9d826539fe2cbdf7d7ecccfe57635',
43
49
  }),
44
50
  port: 7998,
45
- path: `/v2/images/${packagename}/update?${queryParams}`,
51
+ path: `/v2/images/${encodeURIComponent(packagename)}/update?${query}`,
46
52
  method: 'GET',
53
+ timeout,
47
54
  headers: {
48
55
  'x-timestamp': timestamp,
49
56
  'x-signature': signature,
@@ -57,38 +64,95 @@ function updateDocker(opt) {
57
64
  res.on('end', () => {
58
65
  try {
59
66
  const response = JSON.parse(data);
60
- c?.log('\x1b[36mStatus Code:', res.statusCode, '\x1b[0m');
61
- c?.log('\x1b[34mResponse:', JSON.stringify(response, null, 2), '\x1b[0m');
67
+ c?.log(cyan(`Status Code: ${res.statusCode}`));
68
+ c?.log(blue(`Response: ${JSON.stringify(response, null, 2)}`));
62
69
  if (response.success) {
63
- c?.log('\x1b[32m✓', response.message, '\x1b[0m');
64
- resolve(response);
70
+ c?.log(green(`✓ ${response.message}`));
65
71
  } else {
66
- c?.log('\x1b[31m✗ Error:', response.err, '\x1b[0m');
67
- resolve(response);
72
+ c?.log(red(`✗ Error: ${response.err}`));
68
73
  }
74
+ resolve(response);
69
75
  } catch (err) {
70
- c?.log('\x1b[31mFailed to parse response:', data, '\x1b[0m');
76
+ c?.log(red(`Failed to parse response: ${data}`));
71
77
  c?.error(err);
72
78
  reject(err);
73
79
  }
74
80
  });
75
81
  });
82
+ req.on('timeout', () => {
83
+ req.destroy();
84
+ const err = new Error(`Request timed out after ${timeout}ms`);
85
+ c?.log(red(`✗ ${err.message}`));
86
+ reject(err);
87
+ });
76
88
  req.on('error', (err) => {
77
- c?.log('\x1b[31m✗ Request failed:', err.message, '\x1b[0m');
89
+ c?.log(red(`✗ Request failed: ${err.message}`));
78
90
  reject(err);
79
91
  });
80
92
  req.end();
81
93
  });
82
94
  }
83
95
  exports.updateDocker = updateDocker;
84
- // Run if executed directly (node docker-update.js or npm run docker-update)
85
- if (require.main === module) {
86
- const arg = process.argv.slice(2)[0];
87
- if (!arg || !arg.includes(':')) {
88
- throw new Error('\x1b[31m✗ Usage: node docker-update.js <packagename>:<port>\x1b[0m');
89
- // process.exit(1)
96
+ /**
97
+ * Parse options from a .env file in the current working directory.
98
+ * Required keys: packagename, port, ZTECHNO_API_SECRET
99
+ * Optional key: volumes (comma-separated)
100
+ */
101
+ function loadOptionsFromEnv() {
102
+ const envPath = process.cwd() + '/.env';
103
+ console.log(`Loading env from: ${envPath}`);
104
+ const cfg = (0, dotenv_1.config)({ path: envPath });
105
+ if (cfg.error) {
106
+ throw cfg.error;
107
+ }
108
+ const packagename = cfg.parsed?.packagename || process.env.packagename;
109
+ const port = cfg.parsed?.port || process.env.port;
110
+ const volumes = cfg.parsed?.volumes || process.env.volumes;
111
+ if (!packagename || !port) {
112
+ throw new Error('Missing required .env variables: packagename and port');
90
113
  }
91
- const [packagename, port] = arg.split(':');
92
- updateDocker({ packagename, port, c: console }).catch(() => process.exit(1));
114
+ return { packagename, port, volumes: volumes?.split(',').filter(Boolean) };
115
+ }
116
+ const USAGE = `
117
+ Usage:
118
+ ztechno-docker-update <packagename>:<port>
119
+ ztechno-docker-update (reads from .env file)
120
+
121
+ .env file format:
122
+ ZTECHNO_API_SECRET=your_secret
123
+ packagename=my-image
124
+ port=3000
125
+ volumes=/host/path:/container/path (optional, comma-separated)
126
+ `.trim();
127
+ // Run if executed directly (node docker-update.js or npx ztechno-docker-update)
128
+ if (require.main === module) {
129
+ const main = async () => {
130
+ const arg = process.argv[2];
131
+ if (arg === '--help' || arg === '-h') {
132
+ console.log(USAGE);
133
+ return;
134
+ }
135
+ // No argument: load from .env
136
+ if (arg === undefined) {
137
+ const { packagename, port, volumes } = loadOptionsFromEnv();
138
+ await updateDocker({ packagename, port, volumes, c: console });
139
+ return;
140
+ }
141
+ // Argument with colon: packagename:port
142
+ if (arg.includes(':')) {
143
+ const [packagename, port] = arg.split(':');
144
+ if (!packagename || !port) {
145
+ throw new Error(`Invalid argument "${arg}". Expected format: <packagename>:<port>`);
146
+ }
147
+ await updateDocker({ packagename, port, c: console });
148
+ return;
149
+ }
150
+ console.error(red(`✗ Invalid argument: "${arg}"`));
151
+ console.log(USAGE);
152
+ process.exit(1);
153
+ };
154
+ main().catch((err) => {
155
+ console.error(red(`✗ Error: ${err.message}`));
156
+ process.exit(1);
157
+ });
93
158
  }
94
- module.exports = { updateDocker };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ztechno_core",
3
- "version": "0.0.98",
3
+ "version": "0.0.99",
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"