@vnptnan/gate 1.0.1 → 1.0.2

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.
Files changed (3) hide show
  1. package/VERSION +1 -1
  2. package/bin/cli.js +34 -5
  3. package/package.json +1 -1
package/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.0.2
package/bin/cli.js CHANGED
@@ -5,7 +5,8 @@
5
5
  // npx @vnptnan/gate status phiên bản đang cài, bản mới nhất trên kho, chế độ, lịch cập nhật
6
6
  // npx @vnptnan/gate update [--check] cập nhật ngay (lịch tự chạy lệnh này bằng tài khoản SYSTEM)
7
7
  // npx @vnptnan/gate uninstall gỡ cổng và lịch cập nhật
8
- // Tuỳ chọn: --registry URL, --ca FILE (CA khi dùng kho nội bộ tự ký), --token T (khi kho cần đăng nhập).
8
+ // Tuỳ chọn: --registry URL, --ca FILE (CA khi dùng kho nội bộ tự ký), --token T (khi kho cần đăng nhập),
9
+ // --proxy URL (mặc định lấy HTTPS_PROXY hoặc npm config https-proxy lúc cài, lưu lại cho lịch cập nhật chạy bằng SYSTEM).
9
10
  // Không phụ thuộc gói ngoài. Cổng chạy bằng bash như trước; Node chỉ dùng để tải, kiểm tra và cài.
10
11
  'use strict';
11
12
  const fs = require('fs'), path = require('path'), cp = require('child_process'), zlib = require('zlib');
@@ -27,7 +28,7 @@ for (let i = 0; i < argv.length; i++) {
27
28
  const a = argv[i];
28
29
  if (a === '--audit' || a === '--enforce') opt.mode = a.slice(2);
29
30
  else if (a === '--check' || a === '--quiet' || a === '--elevated') opt[a.slice(2)] = true;
30
- else if (['--registry', '--ca', '--token', '--stage-only'].includes(a)) opt[a.slice(2).replace('-o', 'O')] = argv[++i];
31
+ else if (['--registry', '--ca', '--token', '--proxy', '--stage-only'].includes(a)) opt[a.slice(2).replace('-o', 'O')] = argv[++i];
31
32
  else die(`Tham số không hiểu: ${a}`);
32
33
  }
33
34
 
@@ -46,17 +47,43 @@ function settings() {
46
47
  if (!registry || registry === 'undefined') registry = PKG.publishConfig && PKG.publishConfig.registry;
47
48
  if (!registry) die('Chưa biết địa chỉ kho gói. Chạy lại với --registry <URL Package Registry của GitLab>.');
48
49
  const ca = opt.ca || saved.ca || process.env.NODE_EXTRA_CA_CERTS || '';
49
- return { name: PKG.name, registry: registry.replace(/\/?$/, '/'), ca, token: opt.token || saved.token || '', mode: opt.mode || saved.mode || '' };
50
+ // Proxy: tham số > đã lưu lúc cài (lịch SYSTEM không có biến môi trường của dev) > biến môi trường > cấu hình npm
51
+ const env = process.env;
52
+ const proxy = [opt.proxy, saved.proxy, env.HTTPS_PROXY, env.https_proxy, env.HTTP_PROXY, env.http_proxy, npmConfig('https-proxy'), npmConfig('proxy')]
53
+ .find((x) => x && !['null', 'undefined', 'false'].includes(x)) || '';
54
+ return { name: PKG.name, registry: registry.replace(/\/?$/, '/'), ca, token: opt.token || saved.token || '', mode: opt.mode || saved.mode || '', proxy };
50
55
  }
51
56
 
52
57
  // ------------------------------------------------------------------ HTTP (có CA nội bộ, theo redirect)
58
+ function noProxy(host) {
59
+ const list = (process.env.NO_PROXY || process.env.no_proxy || '').split(',').map((x) => x.trim().replace(/^\*?\./, '')).filter(Boolean);
60
+ return list.some((d) => d === '*' || host === d || host.endsWith(`.${d}`));
61
+ }
62
+ // Đi qua proxy HTTP: đích https dùng CONNECT rồi bắt tay TLS với máy đích (chứng chỉ vẫn được kiểm như đi thẳng)
63
+ function proxied(u, s, o) {
64
+ const p = new URL(/^[a-z]+:\/\//i.test(s.proxy) ? s.proxy : `http://${s.proxy}`);
65
+ const plib = p.protocol === 'https:' ? https : http, pport = p.port || (p.protocol === 'https:' ? 443 : 80);
66
+ const auth = p.username ? { 'Proxy-Authorization': `Basic ${Buffer.from(`${decodeURIComponent(p.username)}:${decodeURIComponent(p.password)}`).toString('base64')}` } : {};
67
+ if (u.protocol === 'http:') return [http, { ...o, host: p.hostname, port: pport, path: u.href, headers: { ...o.headers, ...auth, Host: u.host } }];
68
+ const target = `${u.hostname}:${u.port || 443}`;
69
+ return [https, { ...o, host: u.hostname, port: u.port || 443, path: u.pathname + u.search, createConnection: (_, cb) => {
70
+ const r = plib.request({ host: p.hostname, port: pport, method: 'CONNECT', path: target, headers: { Host: target, ...auth } });
71
+ r.on('connect', (res, sock) => {
72
+ if (res.statusCode !== 200) { sock.destroy(); return cb(new Error(`Proxy ${p.host} từ chối kết nối tới ${target} (HTTP ${res.statusCode})`)); }
73
+ cb(null, tls.connect({ socket: sock, servername: u.hostname, ca: o.ca }));
74
+ });
75
+ r.on('error', (e) => cb(new Error(`Không kết nối được proxy ${p.host}: ${e.message}`))); r.end();
76
+ } }];
77
+ }
53
78
  function get(url, s, asJson, hops = 0) {
54
79
  return new Promise((resolve, reject) => {
55
80
  const u = new URL(url), lib = u.protocol === 'http:' ? http : https;
56
81
  const o = { headers: { 'User-Agent': `vnptnan-gate/${PKG.version}`, Accept: asJson ? 'application/json' : '*/*' }, timeout: 30000 };
57
82
  if (s.ca && fs.existsSync(s.ca)) o.ca = [...tls.rootCertificates, fs.readFileSync(s.ca, 'utf8')];
58
83
  if (s.token && u.host === new URL(s.registry).host) o.headers.Authorization = `Bearer ${s.token}`;
59
- const req = lib.get(u, o, (res) => {
84
+ let call = (cbk) => lib.get(u, o, cbk);
85
+ if (s.proxy && !noProxy(u.hostname)) { const [plib, po] = proxied(u, s, o); call = (cbk) => plib.get(po, cbk); }
86
+ const req = call((res) => {
60
87
  if ([301, 302, 303, 307, 308].includes(res.statusCode) && res.headers.location && hops < 5) {
61
88
  res.resume(); return resolve(get(new URL(res.headers.location, u).href, s, asJson, hops + 1));
62
89
  }
@@ -132,6 +159,7 @@ function elevate(extra) {
132
159
  }
133
160
  function passArgs(s) {
134
161
  const a = ['--registry', s.registry];
162
+ if (s.proxy) a.push('--proxy', s.proxy);
135
163
  if (s.ca) a.push('--ca', path.resolve(s.ca));
136
164
  if (s.token) a.push('--token', s.token);
137
165
  if (opt.mode) a.push(`--${opt.mode}`);
@@ -149,7 +177,7 @@ function modeFor(s, dir) {
149
177
  function saveConfig(s) {
150
178
  let ca = '';
151
179
  if (s.ca) { ca = path.join(HOME, 'gitlab-ca.crt'); if (path.resolve(s.ca) !== ca) fs.copyFileSync(s.ca, ca); }
152
- fs.writeFileSync(CFG, JSON.stringify({ registry: s.registry, ca, token: s.token, mode: s.mode }, null, 2));
180
+ fs.writeFileSync(CFG, JSON.stringify({ registry: s.registry, ca, token: s.token, mode: s.mode, proxy: s.proxy }, null, 2));
153
181
  }
154
182
  function schedule() {
155
183
  const tr = `"${process.execPath}" "${path.join(HOME, 'current', 'bin', 'cli.js')}" update --quiet`;
@@ -206,6 +234,7 @@ async function status() {
206
234
  const s = settings(), saved = readJson(CFG) || {};
207
235
  console.log(`Đang cài : ${installedVersion() || '(chưa cài)'}${IS_WIN ? ` tại ${path.join(HOME, 'current')}` : ''}`);
208
236
  console.log(`Kho gói : ${s.registry}${s.ca ? ` (CA: ${s.ca})` : ''}`);
237
+ console.log(`Proxy : ${s.proxy ? s.proxy.replace(/\/\/[^@/]*@/, '//***@') : '(đi thẳng)'}`);
209
238
  console.log(`Chế độ : ${saved.mode ? `${saved.mode} (cố định trên máy này)` : 'theo policy.json của gói'}`);
210
239
  try { const { ver } = await latest(s); console.log(`Bản mới nhất: ${ver}`); } catch (e) { console.log(`Bản mới nhất: không đọc được (${e.message})`); }
211
240
  if (IS_WIN) console.log(`Lịch cập nhật: ${cp.spawnSync('schtasks.exe', ['/Query', '/TN', TASK], { stdio: 'ignore' }).status === 0 ? 'có' : 'CHƯA CÓ'}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vnptnan/gate",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Cổng kiểm soát an toàn bảo mật thông tin khi dùng AI trong phát triển phần mềm: chặn secret, dữ liệu cá nhân, package bịa ở prompt, commit, push và máy chủ GitLab. Cài: npx @vnptnan/gate",
5
5
  "keywords": [
6
6
  "git-hooks",