@vnptnan/gate 1.0.1 → 1.0.4
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/VERSION +1 -1
- package/bin/cli.js +38 -7
- package/package.json +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.4
|
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,12 +28,12 @@ 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
|
|
|
34
35
|
function say(s) { if (!opt.quiet) console.log(s); log(s); }
|
|
35
|
-
function log(s) { try { fs.mkdirSync(path.dirname(LOG), { recursive: true }); fs.appendFileSync(LOG, `${new Date().toISOString()}\t${s}\n`); } catch (_) { /* chưa có quyền ghi */ } }
|
|
36
|
+
function log(s) { try { fs.mkdirSync(path.dirname(LOG), { recursive: true }); if (!fs.existsSync(LOG)) fs.writeFileSync(LOG, '\ufeff'); fs.appendFileSync(LOG, `${new Date().toISOString()}\t${s}\n`); } catch (_) { /* chưa có quyền ghi */ } }
|
|
36
37
|
function die(s, code = 1) { console.error(`[VNPTNAN_GATE] ${s}`); log(`LOI\t${s}`); process.exit(code); }
|
|
37
38
|
function readJson(p) { try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch (_) { return null; } }
|
|
38
39
|
const cmpVer = (a, b) => { const x = a.split(/[.-]/).map(Number), y = b.split(/[.-]/).map(Number); for (let i = 0; i < 3; i++) if ((x[i] || 0) !== (y[i] || 0)) return (x[i] || 0) - (y[i] || 0); return 0; };
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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`;
|
|
@@ -167,7 +195,9 @@ async function setup() {
|
|
|
167
195
|
if (!isAdmin()) return elevate(passArgs(s));
|
|
168
196
|
const stage = path.join(HOME, 'staging', PKG.version);
|
|
169
197
|
fs.rmSync(stage, { recursive: true, force: true }); fs.mkdirSync(stage, { recursive: true });
|
|
170
|
-
|
|
198
|
+
// Lọc theo đường dẫn TƯƠNG ĐỐI: chạy qua npx thì chính ROOT nằm trong ...\npm-cache\_npx\...\node_modules\@vnptnan\gate
|
|
199
|
+
fs.cpSync(ROOT, stage, { recursive: true, filter: (p) => !path.relative(ROOT, p).split(/[\\/]/).includes('node_modules') });
|
|
200
|
+
if (!fs.existsSync(path.join(stage, 'installer', 'install.ps1'))) throw new Error(`Chép bộ cài vào ${stage} không đủ file`);
|
|
171
201
|
const mode = modeFor(s, stage);
|
|
172
202
|
installFrom(stage, mode);
|
|
173
203
|
fs.rmSync(stage, { recursive: true, force: true });
|
|
@@ -206,6 +236,7 @@ async function status() {
|
|
|
206
236
|
const s = settings(), saved = readJson(CFG) || {};
|
|
207
237
|
console.log(`Đang cài : ${installedVersion() || '(chưa cài)'}${IS_WIN ? ` tại ${path.join(HOME, 'current')}` : ''}`);
|
|
208
238
|
console.log(`Kho gói : ${s.registry}${s.ca ? ` (CA: ${s.ca})` : ''}`);
|
|
239
|
+
console.log(`Proxy : ${s.proxy ? s.proxy.replace(/\/\/[^@/]*@/, '//***@') : '(đi thẳng)'}`);
|
|
209
240
|
console.log(`Chế độ : ${saved.mode ? `${saved.mode} (cố định trên máy này)` : 'theo policy.json của gói'}`);
|
|
210
241
|
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
242
|
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.
|
|
3
|
+
"version": "1.0.4",
|
|
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",
|