mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-05 18:24:34 +08:00
⛅ Sync 2026-09-29 21:45:00
This commit is contained in:
1 parent
a4d6e3e4b9
commit
da77daf88e
191 files changed
+8077
-6247
No files matched your search
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*
|
||||
* The per-scheme parsers in
|
||||
* parser/protocols.uc produce a FLAT UCI-key dict; that is their output
|
||||
* shape. The Adapter, the Loader, the policy step and the Repository's
|
||||
* write path all think in the canonical Node shape instead: nested tls /
|
||||
* transport / multiplex / credentials / protocol_options sub-objects.
|
||||
*
|
||||
* normalize() is the crossing point: every caller that needs the
|
||||
* canonical Node - the subscription orchestrator, the RPC that imports a
|
||||
* share link, tests - runs the parsed config through it. parser/flatten.uc
|
||||
* is the inverse and is used only where UCI has to be written, which is
|
||||
* the Repository's apply_nodes().
|
||||
*
|
||||
* The mapping is sourced from parser/mapping.uc (canonical -> UCI)
|
||||
* so the Loader, the Adapter and the parser cannot drift apart.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { PROTOCOL_TO_UCI } from './mapping.uc';
|
||||
|
||||
/* Pick every field that PROTOCOL_TO_UCI maps for this protocol off
|
||||
* the flat config and rename it from UCI to its canonical name. The
|
||||
* result is the protocol_options sub-object. Fields the parser wrote
|
||||
* that are not mapped (e.g. a stale `vless_udp_over_tcp` left over
|
||||
* from a shadowsocks -> vless type switch) are dropped here, which is
|
||||
* the same "stale UCI option becomes a config error" failure the
|
||||
* Loader has been guarding against via PROTOCOL_OPTIONS. */
|
||||
function build_protocol_options(protocol, flat) {
|
||||
const map = PROTOCOL_TO_UCI[protocol] || {};
|
||||
const out = {};
|
||||
|
||||
for (let canonical, uci in map) {
|
||||
if (uci in flat) {
|
||||
const value = flat[uci];
|
||||
/* Empty-string or null means "absent": do not propagate
|
||||
* to the canonical side either. */
|
||||
if (value !== '' && value !== null)
|
||||
out[canonical] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/* TLS fields are shared across protocols, not per-protocol, so they
|
||||
* live outside PROTOCOL_TO_UCI. The Loader uses the same shape
|
||||
* (load_tls() in config/loader.uc), so we mirror it. */
|
||||
function build_tls(flat) {
|
||||
const tls = {
|
||||
enabled: flat.tls,
|
||||
server_name: flat.tls_sni,
|
||||
insecure: flat.tls_insecure,
|
||||
alpn: flat.tls_alpn,
|
||||
min_version: flat.tls_min_version,
|
||||
max_version: flat.tls_max_version,
|
||||
handshake_timeout: flat.tls_handshake_timeout,
|
||||
cipher_suites: flat.tls_cipher_suites,
|
||||
cert_path: flat.tls_cert_path,
|
||||
ech: {
|
||||
enabled: flat.tls_ech,
|
||||
config: flat.tls_ech_config,
|
||||
config_path: flat.tls_ech_config_path
|
||||
},
|
||||
utls: {
|
||||
fingerprint: flat.tls_utls
|
||||
},
|
||||
reality: {
|
||||
enabled: flat.tls_reality,
|
||||
public_key: flat.tls_reality_public_key,
|
||||
short_id: flat.tls_reality_short_id
|
||||
}
|
||||
};
|
||||
|
||||
return tls;
|
||||
}
|
||||
|
||||
/* Transport fields are also shared; mirror the shape the Loader's
|
||||
* load_transport() produces. Returns `{ type: null }` when the parser
|
||||
* did not write a transport. */
|
||||
function build_transport(flat) {
|
||||
const t = flat.transport;
|
||||
if (t == null || t === '')
|
||||
return { type: null };
|
||||
|
||||
return {
|
||||
type: t,
|
||||
host: flat.http_host || flat.httpupgrade_host,
|
||||
path: flat.http_path || flat.ws_path,
|
||||
headers: flat.ws_host ? { Host: flat.ws_host } : null,
|
||||
method: flat.http_method,
|
||||
max_early_data: flat.websocket_early_data,
|
||||
early_data_header_name: flat.websocket_early_data_header,
|
||||
service_name: flat.grpc_servicename,
|
||||
idle_timeout: flat.http_idle_timeout,
|
||||
ping_timeout: flat.http_ping_timeout,
|
||||
permit_without_stream: flat.grpc_permit_without_stream
|
||||
};
|
||||
}
|
||||
|
||||
/* Common fields that every outbound protocol carries. Mirrors the
|
||||
* Loader's load_common(). */
|
||||
function build_common(flat) {
|
||||
return {
|
||||
proxy_protocol: flat.proxy_protocol,
|
||||
tcp_fast_open: flat.tcp_fast_open,
|
||||
tcp_multi_path: flat.tcp_multi_path,
|
||||
udp_fragment: flat.udp_fragment
|
||||
};
|
||||
}
|
||||
|
||||
/* Multiplex fields - mirrors the Loader's load_multiplex(). */
|
||||
function build_multiplex(flat) {
|
||||
return {
|
||||
enabled: flat.multiplex,
|
||||
protocol: flat.multiplex_protocol,
|
||||
max_connections: flat.multiplex_max_connections,
|
||||
min_streams: flat.multiplex_min_streams,
|
||||
max_streams: flat.multiplex_max_streams,
|
||||
padding: flat.multiplex_padding,
|
||||
brutal: {
|
||||
enabled: flat.multiplex_brutal,
|
||||
up_mbps: flat.multiplex_brutal_up,
|
||||
down_mbps: flat.multiplex_brutal_down
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* Credentials: pull every UCI key the protocol's CREDENTIALS row lists
|
||||
* (canonical -> UCI) and put them under the canonical name. Empty /
|
||||
* null values stay null so the Adapter's null-strip pass keeps the
|
||||
* field out of the emitted sing-box JSON. */
|
||||
const CREDENTIALS = {
|
||||
vless: { uuid: 'uuid' },
|
||||
vmess: { uuid: 'uuid' },
|
||||
trojan: { password: 'password' },
|
||||
hysteria: { auth_str: 'auth', auth_base64: 'auth' },
|
||||
hysteria2: { password: 'password' },
|
||||
tuic: { uuid: 'uuid', password: 'password' },
|
||||
shadowsocks: { password: 'password', method: 'shadowsocks_encrypt_method' },
|
||||
socks: { username: 'username', password: 'password' },
|
||||
http: { username: 'username', password: 'password' },
|
||||
snell: { psk: 'password', userkey: 'snell_userkey' },
|
||||
ssh: { user: 'username', password: 'password',
|
||||
private_key: 'ssh_priv_key', private_key_passphrase: 'ssh_priv_key_pp' },
|
||||
anytls: { password: 'password' },
|
||||
shadowtls: { password: 'password' },
|
||||
direct: {}
|
||||
};
|
||||
|
||||
function build_credentials(protocol, flat) {
|
||||
const map = CREDENTIALS[protocol] || {};
|
||||
const out = {};
|
||||
|
||||
for (let canonical, uci in map)
|
||||
out[canonical] = flat[uci];
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Top-level: produce a Node-shaped object from the parser's flat
|
||||
* output. Returns null when the input is null / empty so callers can
|
||||
* do `normalize(parse_uri(uri))` and propagate parse failures
|
||||
* unchanged. */
|
||||
export function normalize(flat) {
|
||||
if (!flat)
|
||||
return null;
|
||||
|
||||
const protocol = flat.type;
|
||||
|
||||
return {
|
||||
/* identity */
|
||||
id: null,
|
||||
name: flat.label,
|
||||
type: protocol,
|
||||
|
||||
/* endpoint */
|
||||
address: flat.address,
|
||||
port: flat.port,
|
||||
|
||||
/* sub-objects */
|
||||
common: build_common(flat),
|
||||
credentials: build_credentials(protocol, flat),
|
||||
tls: build_tls(flat),
|
||||
transport: build_transport(flat),
|
||||
multiplex: build_multiplex(flat),
|
||||
protocol_options: build_protocol_options(protocol, flat)
|
||||
};
|
||||
};
|
||||
Reference in new issue
Block a user