Commit Graph
167 Commits
Author SHA1 Message Date
Z User 4dde61e8db fix(frontend): align listener config form with backend schema (hy2 fields + grpc-opts/mekya inputs)
HIGH-1: remove max-idle-time from hysteria2 form + set()
HIGH-2: remove handshake-timeout from hysteria2 form + set() (kept for sudoku)
MEDIUM-1: narrow UDP_PROTOCOLS to ['shadowsocks', 'snell'] — vmess/vless/trojan udp toggle was dead (backend compile_modules.go strips it)
MEDIUM-2: add 5 R-M4 grpc-opts client-metadata Form.Items (grpc-user-agent, ping-interval, max-connections, min-streams, max-streams) on vmess/vless/trojan, with formValuesToConfig set() and configToFormValues reads
MEDIUM-3: add 2 R-M3 mekya-config client-metadata Form.Items (polling-interval-initial, h2-pool-size) on vmess, with mekya-config builder/reader fields
i18n: add 7 new keys + 6 hints to en.ts and zh.ts listeners section
Lint PASS (0 errors, 33 pre-existing fast-refresh warnings)
Build PASS (tsc -b + vite build OK)
fix(frontend): align listener config form with backend schema (hy2 fields + grpc-opts/mekya inputs)
HIGH-1: remove max-idle-time from hysteria2 form + set()
HIGH-2: remove handshake-timeout from hysteria2 form + set() (kept for sudoku)
MEDIUM-1: narrow UDP_PROTOCOLS to ['shadowsocks', 'snell'] — vmess/vless/trojan udp toggle was dead (backend compile_modules.go strips it)
MEDIUM-2: add 5 R-M4 grpc-opts client-metadata Form.Items (grpc-user-agent, ping-interval, max-connections, min-streams, max-streams) on vmess/vless/trojan, with formValuesToConfig set() and configToFormValues reads
MEDIUM-3: add 2 R-M3 mekya-config client-metadata Form.Items (polling-interval-initial, h2-pool-size) on vmess, with mekya-config builder/reader fields
i18n: add 7 new keys + 6 hints to en.ts and zh.ts listeners section
Lint PASS (0 errors, 33 pre-existing fast-refresh warnings)
Build PASS (tsc -b + vite build OK)
fix(frontend): align listener config form with backend schema (hy2 fields + grpc-opts/mekya inputs)
HIGH-1: remove max-idle-time from hysteria2 form + set()
HIGH-2: remove handshake-timeout from hysteria2 form + set() (kept for sudoku)
MEDIUM-1: narrow UDP_PROTOCOLS to ['shadowsocks', 'snell'] — vmess/vless/trojan udp toggle was dead (backend compile_modules.go strips it)
MEDIUM-2: add 5 R-M4 grpc-opts client-metadata Form.Items (grpc-user-agent, ping-interval, max-connections, min-streams, max-streams) on vmess/vless/trojan, with formValuesToConfig set() and configToFormValues reads
MEDIUM-3: add 2 R-M3 mekya-config client-metadata Form.Items (polling-interval-initial, h2-pool-size) on vmess, with mekya-config builder/reader fields
i18n: add 7 new keys + 6 hints to en.ts and zh.ts listeners section
Lint PASS (0 errors, 33 pre-existing fast-refresh warnings)
Build PASS (tsc -b + vite build OK)
fix(frontend): align listener config form with backend schema (hy2 fields + grpc-opts/mekya inputs)
HIGH-1: remove max-idle-time from hysteria2 form + set()
HIGH-2: remove handshake-timeout from hysteria2 form + set() (kept for sudoku)
MEDIUM-1: narrow UDP_PROTOCOLS to ['shadowsocks', 'snell'] — vmess/vless/trojan udp toggle was dead (backend compile_modules.go strips it)
MEDIUM-2: add 5 R-M4 grpc-opts client-metadata Form.Items (grpc-user-agent, ping-interval, max-connections, min-streams, max-streams) on vmess/vless/trojan, with formValuesToConfig set() and configToFormValues reads
MEDIUM-3: add 2 R-M3 mekya-config client-metadata Form.Items (polling-interval-initial, h2-pool-size) on vmess, with mekya-config builder/reader fields
i18n: add 7 new keys + 6 hints to en.ts and zh.ts listeners section
Lint PASS (0 errors, 33 pre-existing fast-refresh warnings)
Build PASS (tsc -b + vite build OK)
fix(frontend): align listener config form with backend schema (hy2 fields + grpc-opts/mekya inputs)
HIGH-1: remove max-idle-time from hysteria2 form + set()
HIGH-2: remove handshake-timeout from hysteria2 form + set() (kept for sudoku)
MEDIUM-1: narrow UDP_PROTOCOLS to ['shadowsocks', 'snell'] — vmess/vless/trojan udp toggle was dead (backend compile_modules.go strips it)
MEDIUM-2: add 5 R-M4 grpc-opts client-metadata Form.Items (grpc-user-agent, ping-interval, max-connections, min-streams, max-streams) on vmess/vless/trojan, with formValuesToConfig set() and configToFormValues reads
MEDIUM-3: add 2 R-M3 mekya-config client-metadata Form.Items (polling-interval-initial, h2-pool-size) on vmess, with mekya-config builder/reader fields
i18n: add 7 new keys + 6 hints to en.ts and zh.ts listeners section
Lint PASS (0 errors, 33 pre-existing fast-refresh warnings)
Build PASS (tsc -b + vite build OK)
fix(frontend): align listener config form with backend schema (hy2 fields + grpc-opts/mekya inputs)
HIGH-1: remove max-idle-time from hysteria2 form + set()
HIGH-2: remove handshake-timeout from hysteria2 form + set() (kept for sudoku)
MEDIUM-1: narrow UDP_PROTOCOLS to ['shadowsocks', 'snell'] — vmess/vless/trojan udp toggle was dead (backend compile_modules.go strips it)
MEDIUM-2: add 5 R-M4 grpc-opts client-metadata Form.Items (grpc-user-agent, ping-interval, max-connections, min-streams, max-streams) on vmess/vless/trojan, with formValuesToConfig set() and configToFormValues reads
MEDIUM-3: add 2 R-M3 mekya-config client-metadata Form.Items (polling-interval-initial, h2-pool-size) on vmess, with mekya-config builder/reader fields
i18n: add 7 new keys + 6 hints to en.ts and zh.ts listeners section
Lint PASS (0 errors, 33 pre-existing fast-refresh warnings)
Build PASS (tsc -b + vite build OK)
fix(frontend): align listener config form with backend schema (hy2 fields + grpc-opts/mekya inputs)
HIGH-1: remove max-idle-time from hysteria2 form + set()
HIGH-2: remove handshake-timeout from hysteria2 form + set() (kept for sudoku)
MEDIUM-1: narrow UDP_PROTOCOLS to ['shadowsocks', 'snell'] — vmess/vless/trojan udp toggle was dead (backend compile_modules.go strips it)
MEDIUM-2: add 5 R-M4 grpc-opts client-metadata Form.Items (grpc-user-agent, ping-interval, max-connections, min-streams, max-streams) on vmess/vless/trojan, with formValuesToConfig set() and configToFormValues reads
MEDIUM-3: add 2 R-M3 mekya-config client-metadata Form.Items (polling-interval-initial, h2-pool-size) on vmess, with mekya-config builder/reader fields
i18n: add 7 new keys + 6 hints to en.ts and zh.ts listeners section
Lint PASS (0 errors, 33 pre-existing fast-refresh warnings)
Build PASS (tsc -b + vite build OK)
fix(frontend): align listener config form with backend schema (hy2 fields + grpc-opts/mekya inputs)
HIGH-1: remove max-idle-time from hysteria2 form + set()
HIGH-2: remove handshake-timeout from hysteria2 form + set() (kept for sudoku)
MEDIUM-1: narrow UDP_PROTOCOLS to ['shadowsocks', 'snell'] — vmess/vless/trojan udp toggle was dead (backend compile_modules.go strips it)
MEDIUM-2: add 5 R-M4 grpc-opts client-metadata Form.Items (grpc-user-agent, ping-interval, max-connections, min-streams, max-streams) on vmess/vless/trojan, with formValuesToConfig set() and configToFormValues reads
MEDIUM-3: add 2 R-M3 mekya-config client-metadata Form.Items (polling-interval-initial, h2-pool-size) on vmess, with mekya-config builder/reader fields
i18n: add 7 new keys + 6 hints to en.ts and zh.ts listeners section
Lint PASS (0 errors, 33 pre-existing fast-refresh warnings)
Build PASS (tsc -b + vite build OK)
fix(frontend): align listener config form with backend schema (hy2 fields + grpc-opts/mekya inputs)
HIGH-1: remove max-idle-time from hysteria2 form + set()
HIGH-2: remove handshake-timeout from hysteria2 form + set() (kept for sudoku)
MEDIUM-1: narrow UDP_PROTOCOLS to ['shadowsocks', 'snell'] — vmess/vless/trojan udp toggle was dead (backend compile_modules.go strips it)
MEDIUM-2: add 5 R-M4 grpc-opts client-metadata Form.Items (grpc-user-agent, ping-interval, max-connections, min-streams, max-streams) on vmess/vless/trojan, with formValuesToConfig set() and configToFormValues reads
MEDIUM-3: add 2 R-M3 mekya-config client-metadata Form.Items (polling-interval-initial, h2-pool-size) on vmess, with mekya-config builder/reader fields
i18n: add 7 new keys + 6 hints to en.ts and zh.ts listeners section
Lint PASS (0 errors, 33 pre-existing fast-refresh warnings)
Build PASS (tsc -b + vite build OK)
fix(frontend): align listener config form with backend schema (hy2 fields + grpc-opts/mekya inputs)
HIGH-1: remove max-idle-time from hysteria2 form + set()
HIGH-2: remove handshake-timeout from hysteria2 form + set() (kept for sudoku)
MEDIUM-1: narrow UDP_PROTOCOLS to ['shadowsocks', 'snell'] — vmess/vless/trojan udp toggle was dead (backend compile_modules.go strips it)
MEDIUM-2: add 5 R-M4 grpc-opts client-metadata Form.Items (grpc-user-agent, ping-interval, max-connections, min-streams, max-streams) on vmess/vless/trojan, with formValuesToConfig set() and configToFormValues reads
MEDIUM-3: add 2 R-M3 mekya-config client-metadata Form.Items (polling-interval-initial, h2-pool-size) on vmess, with mekya-config builder/reader fields
i18n: add 7 new keys + 6 hints to en.ts and zh.ts listeners section
Lint PASS (0 errors, 33 pre-existing fast-refresh warnings)
Build PASS (tsc -b + vite build OK)
fix(frontend): align listener config form with backend schema (hy2 fields + grpc-opts/mekya inputs)
HIGH-1: remove max-idle-time from hysteria2 form + set()
HIGH-2: remove handshake-timeout from hysteria2 form + set() (kept for sudoku)
MEDIUM-1: narrow UDP_PROTOCOLS to ['shadowsocks', 'snell'] — vmess/vless/trojan udp toggle was dead (backend compile_modules.go strips it)
MEDIUM-2: add 5 R-M4 grpc-opts client-metadata Form.Items (grpc-user-agent, ping-interval, max-connections, min-streams, max-streams) on vmess/vless/trojan, with formValuesToConfig set() and configToFormValues reads
MEDIUM-3: add 2 R-M3 mekya-config client-metadata Form.Items (polling-interval-initial, h2-pool-size) on vmess, with mekya-config builder/reader fields
i18n: add 7 new keys + 6 hints to en.ts and zh.ts listeners section
Lint PASS (0 errors, 33 pre-existing fast-refresh warnings)
Build PASS (tsc -b + vite build OK)
fix(frontend): align listener config form with backend schema (hy2 fields + grpc-opts/mekya inputs)
HIGH-1: remove max-idle-time from hysteria2 form + set()
HIGH-2: remove handshake-timeout from hysteria2 form + set() (kept for sudoku)
MEDIUM-1: narrow UDP_PROTOCOLS to ['shadowsocks', 'snell'] — vmess/vless/trojan udp toggle was dead (backend compile_modules.go strips it)
MEDIUM-2: add 5 R-M4 grpc-opts client-metadata Form.Items (grpc-user-agent, ping-interval, max-connections, min-streams, max-streams) on vmess/vless/trojan, with formValuesToConfig set() and configToFormValues reads
MEDIUM-3: add 2 R-M3 mekya-config client-metadata Form.Items (polling-interval-initial, h2-pool-size) on vmess, with mekya-config builder/reader fields
i18n: add 7 new keys + 6 hints to en.ts and zh.ts listeners section
Lint PASS (0 errors, 33 pre-existing fast-refresh warnings)
Build PASS (tsc -b + vite build OK)
2026-09-03 16:05:49 +00:00
Z User 6b11dc793e fix(ui): require Snell obfs host when mode is set 2026-09-01 16:18:18 +00:00
Z User 9724185d05 fix(snell): drop incomplete obfs-opts missing host/mode
Mihomo rejects listeners with obfs-opts that omit Host. Only emit the
block when both mode and host are set, and sanitize partial blocks on
save/config generation.
fix(snell): drop incomplete obfs-opts missing host/mode
Mihomo rejects listeners with obfs-opts that omit Host. Only emit the
block when both mode and host are set, and sanitize partial blocks on
save/config generation.
fix(snell): drop incomplete obfs-opts missing host/mode
Mihomo rejects listeners with obfs-opts that omit Host. Only emit the
block when both mode and host are set, and sanitize partial blocks on
save/config generation.
fix(snell): drop incomplete obfs-opts missing host/mode
Mihomo rejects listeners with obfs-opts that omit Host. Only emit the
block when both mode and host are set, and sanitize partial blocks on
save/config generation.
fix(snell): drop incomplete obfs-opts missing host/mode
Mihomo rejects listeners with obfs-opts that omit Host. Only emit the
block when both mode and host are set, and sanitize partial blocks on
save/config generation.
fix(snell): drop incomplete obfs-opts missing host/mode
Mihomo rejects listeners with obfs-opts that omit Host. Only emit the
block when both mode and host are set, and sanitize partial blocks on
save/config generation.
fix(snell): drop incomplete obfs-opts missing host/mode
Mihomo rejects listeners with obfs-opts that omit Host. Only emit the
block when both mode and host are set, and sanitize partial blocks on
save/config generation.
fix(snell): drop incomplete obfs-opts missing host/mode
Mihomo rejects listeners with obfs-opts that omit Host. Only emit the
block when both mode and host are set, and sanitize partial blocks on
save/config generation.
fix(snell): drop incomplete obfs-opts missing host/mode
Mihomo rejects listeners with obfs-opts that omit Host. Only emit the
block when both mode and host are set, and sanitize partial blocks on
save/config generation.
fix(snell): drop incomplete obfs-opts missing host/mode
Mihomo rejects listeners with obfs-opts that omit Host. Only emit the
block when both mode and host are set, and sanitize partial blocks on
save/config generation.
fix(snell): drop incomplete obfs-opts missing host/mode
Mihomo rejects listeners with obfs-opts that omit Host. Only emit the
block when both mode and host are set, and sanitize partial blocks on
save/config generation.
fix(snell): drop incomplete obfs-opts missing host/mode
Mihomo rejects listeners with obfs-opts that omit Host. Only emit the
block when both mode and host are set, and sanitize partial blocks on
save/config generation.
2026-09-01 16:11:26 +00:00
Z User 19916828e2 fix(ui): remove stray '(' text node before TUIC protocol section 2026-09-01 15:12:02 +00:00
Z User 32a3d56061 fix(sudoku): remove required validation from key field
The sudoku key field had rules={[{ required: true }]} which blocked
form submission when the operator left it empty. But the backend
autofill (autofill.go:74-80) automatically generates a 32-byte
base64 key when empty — the same pattern as snell PSK, SS password,
and TUIC token.
Fix: removed required rule, added tooltip 'Leave empty to auto-generate'.
This was the root cause of 'sudoku cannot create' — the form refused
to submit without a key, but the operator didn't know to fill one in
(and shouldn't need to).
fix(sudoku): remove required validation from key field
The sudoku key field had rules={[{ required: true }]} which blocked
form submission when the operator left it empty. But the backend
autofill (autofill.go:74-80) automatically generates a 32-byte
base64 key when empty — the same pattern as snell PSK, SS password,
and TUIC token.
Fix: removed required rule, added tooltip 'Leave empty to auto-generate'.
This was the root cause of 'sudoku cannot create' — the form refused
to submit without a key, but the operator didn't know to fill one in
(and shouldn't need to).
fix(sudoku): remove required validation from key field
The sudoku key field had rules={[{ required: true }]} which blocked
form submission when the operator left it empty. But the backend
autofill (autofill.go:74-80) automatically generates a 32-byte
base64 key when empty — the same pattern as snell PSK, SS password,
and TUIC token.
Fix: removed required rule, added tooltip 'Leave empty to auto-generate'.
This was the root cause of 'sudoku cannot create' — the form refused
to submit without a key, but the operator didn't know to fill one in
(and shouldn't need to).
fix(sudoku): remove required validation from key field
The sudoku key field had rules={[{ required: true }]} which blocked
form submission when the operator left it empty. But the backend
autofill (autofill.go:74-80) automatically generates a 32-byte
base64 key when empty — the same pattern as snell PSK, SS password,
and TUIC token.
Fix: removed required rule, added tooltip 'Leave empty to auto-generate'.
This was the root cause of 'sudoku cannot create' — the form refused
to submit without a key, but the operator didn't know to fill one in
(and shouldn't need to).
fix(sudoku): remove required validation from key field
The sudoku key field had rules={[{ required: true }]} which blocked
form submission when the operator left it empty. But the backend
autofill (autofill.go:74-80) automatically generates a 32-byte
base64 key when empty — the same pattern as snell PSK, SS password,
and TUIC token.
Fix: removed required rule, added tooltip 'Leave empty to auto-generate'.
This was the root cause of 'sudoku cannot create' — the form refused
to submit without a key, but the operator didn't know to fill one in
(and shouldn't need to).
fix(sudoku): remove required validation from key field
The sudoku key field had rules={[{ required: true }]} which blocked
form submission when the operator left it empty. But the backend
autofill (autofill.go:74-80) automatically generates a 32-byte
base64 key when empty — the same pattern as snell PSK, SS password,
and TUIC token.
Fix: removed required rule, added tooltip 'Leave empty to auto-generate'.
This was the root cause of 'sudoku cannot create' — the form refused
to submit without a key, but the operator didn't know to fill one in
(and shouldn't need to).
fix(sudoku): remove required validation from key field
The sudoku key field had rules={[{ required: true }]} which blocked
form submission when the operator left it empty. But the backend
autofill (autofill.go:74-80) automatically generates a 32-byte
base64 key when empty — the same pattern as snell PSK, SS password,
and TUIC token.
Fix: removed required rule, added tooltip 'Leave empty to auto-generate'.
This was the root cause of 'sudoku cannot create' — the form refused
to submit without a key, but the operator didn't know to fill one in
(and shouldn't need to).
fix(sudoku): remove required validation from key field
The sudoku key field had rules={[{ required: true }]} which blocked
form submission when the operator left it empty. But the backend
autofill (autofill.go:74-80) automatically generates a 32-byte
base64 key when empty — the same pattern as snell PSK, SS password,
and TUIC token.
Fix: removed required rule, added tooltip 'Leave empty to auto-generate'.
This was the root cause of 'sudoku cannot create' — the form refused
to submit without a key, but the operator didn't know to fill one in
(and shouldn't need to).
fix(sudoku): remove required validation from key field
The sudoku key field had rules={[{ required: true }]} which blocked
form submission when the operator left it empty. But the backend
autofill (autofill.go:74-80) automatically generates a 32-byte
base64 key when empty — the same pattern as snell PSK, SS password,
and TUIC token.
Fix: removed required rule, added tooltip 'Leave empty to auto-generate'.
This was the root cause of 'sudoku cannot create' — the form refused
to submit without a key, but the operator didn't know to fill one in
(and shouldn't need to).
fix(sudoku): remove required validation from key field
The sudoku key field had rules={[{ required: true }]} which blocked
form submission when the operator left it empty. But the backend
autofill (autofill.go:74-80) automatically generates a 32-byte
base64 key when empty — the same pattern as snell PSK, SS password,
and TUIC token.
Fix: removed required rule, added tooltip 'Leave empty to auto-generate'.
This was the root cause of 'sudoku cannot create' — the form refused
to submit without a key, but the operator didn't know to fill one in
(and shouldn't need to).
fix(sudoku): remove required validation from key field
The sudoku key field had rules={[{ required: true }]} which blocked
form submission when the operator left it empty. But the backend
autofill (autofill.go:74-80) automatically generates a 32-byte
base64 key when empty — the same pattern as snell PSK, SS password,
and TUIC token.
Fix: removed required rule, added tooltip 'Leave empty to auto-generate'.
This was the root cause of 'sudoku cannot create' — the form refused
to submit without a key, but the operator didn't know to fill one in
(and shouldn't need to).
fix(sudoku): remove required validation from key field
The sudoku key field had rules={[{ required: true }]} which blocked
form submission when the operator left it empty. But the backend
autofill (autofill.go:74-80) automatically generates a 32-byte
base64 key when empty — the same pattern as snell PSK, SS password,
and TUIC token.
Fix: removed required rule, added tooltip 'Leave empty to auto-generate'.
This was the root cause of 'sudoku cannot create' — the form refused
to submit without a key, but the operator didn't know to fill one in
(and shouldn't need to).
2026-08-31 16:12:49 +00:00
Z User 02f1b6ef24 fix(frontend): actually apply TUIC v4/v5 split to frontend
Previous commit 20d47e8 only modified backend files — frontend
Listeners.tsx and ListenerConfigFields.tsx were not included due to
a file copy error. This commit applies the frontend changes:
- Listeners.tsx: PROTOCOLS array splits 'tuic' into 'tuic-v4'/'tuic-v5'
- ListenerConfigFields.tsx:
- ALWAYS_TLS_PROTOCOLS includes tuic-v4/tuic-v5
- Form condition: protocol === 'tuic' || 'tuic-v4' || 'tuic-v5'
- case 'tuic': includes tuic-v4 and tuic-v5 fallthrough
- token field: only shows for tuic-v4 and tuic (backward compat)
- tuic-v5: no token field (uses panel-managed users)
fix(frontend): actually apply TUIC v4/v5 split to frontend
Previous commit 20d47e8 only modified backend files — frontend
Listeners.tsx and ListenerConfigFields.tsx were not included due to
a file copy error. This commit applies the frontend changes:
- Listeners.tsx: PROTOCOLS array splits 'tuic' into 'tuic-v4'/'tuic-v5'
- ListenerConfigFields.tsx:
- ALWAYS_TLS_PROTOCOLS includes tuic-v4/tuic-v5
- Form condition: protocol === 'tuic' || 'tuic-v4' || 'tuic-v5'
- case 'tuic': includes tuic-v4 and tuic-v5 fallthrough
- token field: only shows for tuic-v4 and tuic (backward compat)
- tuic-v5: no token field (uses panel-managed users)
fix(frontend): actually apply TUIC v4/v5 split to frontend
Previous commit 20d47e8 only modified backend files — frontend
Listeners.tsx and ListenerConfigFields.tsx were not included due to
a file copy error. This commit applies the frontend changes:
- Listeners.tsx: PROTOCOLS array splits 'tuic' into 'tuic-v4'/'tuic-v5'
- ListenerConfigFields.tsx:
- ALWAYS_TLS_PROTOCOLS includes tuic-v4/tuic-v5
- Form condition: protocol === 'tuic' || 'tuic-v4' || 'tuic-v5'
- case 'tuic': includes tuic-v4 and tuic-v5 fallthrough
- token field: only shows for tuic-v4 and tuic (backward compat)
- tuic-v5: no token field (uses panel-managed users)
fix(frontend): actually apply TUIC v4/v5 split to frontend
Previous commit 20d47e8 only modified backend files — frontend
Listeners.tsx and ListenerConfigFields.tsx were not included due to
a file copy error. This commit applies the frontend changes:
- Listeners.tsx: PROTOCOLS array splits 'tuic' into 'tuic-v4'/'tuic-v5'
- ListenerConfigFields.tsx:
- ALWAYS_TLS_PROTOCOLS includes tuic-v4/tuic-v5
- Form condition: protocol === 'tuic' || 'tuic-v4' || 'tuic-v5'
- case 'tuic': includes tuic-v4 and tuic-v5 fallthrough
- token field: only shows for tuic-v4 and tuic (backward compat)
- tuic-v5: no token field (uses panel-managed users)
fix(frontend): actually apply TUIC v4/v5 split to frontend
Previous commit 20d47e8 only modified backend files — frontend
Listeners.tsx and ListenerConfigFields.tsx were not included due to
a file copy error. This commit applies the frontend changes:
- Listeners.tsx: PROTOCOLS array splits 'tuic' into 'tuic-v4'/'tuic-v5'
- ListenerConfigFields.tsx:
- ALWAYS_TLS_PROTOCOLS includes tuic-v4/tuic-v5
- Form condition: protocol === 'tuic' || 'tuic-v4' || 'tuic-v5'
- case 'tuic': includes tuic-v4 and tuic-v5 fallthrough
- token field: only shows for tuic-v4 and tuic (backward compat)
- tuic-v5: no token field (uses panel-managed users)
fix(frontend): actually apply TUIC v4/v5 split to frontend
Previous commit 20d47e8 only modified backend files — frontend
Listeners.tsx and ListenerConfigFields.tsx were not included due to
a file copy error. This commit applies the frontend changes:
- Listeners.tsx: PROTOCOLS array splits 'tuic' into 'tuic-v4'/'tuic-v5'
- ListenerConfigFields.tsx:
- ALWAYS_TLS_PROTOCOLS includes tuic-v4/tuic-v5
- Form condition: protocol === 'tuic' || 'tuic-v4' || 'tuic-v5'
- case 'tuic': includes tuic-v4 and tuic-v5 fallthrough
- token field: only shows for tuic-v4 and tuic (backward compat)
- tuic-v5: no token field (uses panel-managed users)
fix(frontend): actually apply TUIC v4/v5 split to frontend
Previous commit 20d47e8 only modified backend files — frontend
Listeners.tsx and ListenerConfigFields.tsx were not included due to
a file copy error. This commit applies the frontend changes:
- Listeners.tsx: PROTOCOLS array splits 'tuic' into 'tuic-v4'/'tuic-v5'
- ListenerConfigFields.tsx:
- ALWAYS_TLS_PROTOCOLS includes tuic-v4/tuic-v5
- Form condition: protocol === 'tuic' || 'tuic-v4' || 'tuic-v5'
- case 'tuic': includes tuic-v4 and tuic-v5 fallthrough
- token field: only shows for tuic-v4 and tuic (backward compat)
- tuic-v5: no token field (uses panel-managed users)
fix(frontend): actually apply TUIC v4/v5 split to frontend
Previous commit 20d47e8 only modified backend files — frontend
Listeners.tsx and ListenerConfigFields.tsx were not included due to
a file copy error. This commit applies the frontend changes:
- Listeners.tsx: PROTOCOLS array splits 'tuic' into 'tuic-v4'/'tuic-v5'
- ListenerConfigFields.tsx:
- ALWAYS_TLS_PROTOCOLS includes tuic-v4/tuic-v5
- Form condition: protocol === 'tuic' || 'tuic-v4' || 'tuic-v5'
- case 'tuic': includes tuic-v4 and tuic-v5 fallthrough
- token field: only shows for tuic-v4 and tuic (backward compat)
- tuic-v5: no token field (uses panel-managed users)
fix(frontend): actually apply TUIC v4/v5 split to frontend
Previous commit 20d47e8 only modified backend files — frontend
Listeners.tsx and ListenerConfigFields.tsx were not included due to
a file copy error. This commit applies the frontend changes:
- Listeners.tsx: PROTOCOLS array splits 'tuic' into 'tuic-v4'/'tuic-v5'
- ListenerConfigFields.tsx:
- ALWAYS_TLS_PROTOCOLS includes tuic-v4/tuic-v5
- Form condition: protocol === 'tuic' || 'tuic-v4' || 'tuic-v5'
- case 'tuic': includes tuic-v4 and tuic-v5 fallthrough
- token field: only shows for tuic-v4 and tuic (backward compat)
- tuic-v5: no token field (uses panel-managed users)
fix(frontend): actually apply TUIC v4/v5 split to frontend
Previous commit 20d47e8 only modified backend files — frontend
Listeners.tsx and ListenerConfigFields.tsx were not included due to
a file copy error. This commit applies the frontend changes:
- Listeners.tsx: PROTOCOLS array splits 'tuic' into 'tuic-v4'/'tuic-v5'
- ListenerConfigFields.tsx:
- ALWAYS_TLS_PROTOCOLS includes tuic-v4/tuic-v5
- Form condition: protocol === 'tuic' || 'tuic-v4' || 'tuic-v5'
- case 'tuic': includes tuic-v4 and tuic-v5 fallthrough
- token field: only shows for tuic-v4 and tuic (backward compat)
- tuic-v5: no token field (uses panel-managed users)
fix(frontend): actually apply TUIC v4/v5 split to frontend
Previous commit 20d47e8 only modified backend files — frontend
Listeners.tsx and ListenerConfigFields.tsx were not included due to
a file copy error. This commit applies the frontend changes:
- Listeners.tsx: PROTOCOLS array splits 'tuic' into 'tuic-v4'/'tuic-v5'
- ListenerConfigFields.tsx:
- ALWAYS_TLS_PROTOCOLS includes tuic-v4/tuic-v5
- Form condition: protocol === 'tuic' || 'tuic-v4' || 'tuic-v5'
- case 'tuic': includes tuic-v4 and tuic-v5 fallthrough
- token field: only shows for tuic-v4 and tuic (backward compat)
- tuic-v5: no token field (uses panel-managed users)
fix(frontend): actually apply TUIC v4/v5 split to frontend
Previous commit 20d47e8 only modified backend files — frontend
Listeners.tsx and ListenerConfigFields.tsx were not included due to
a file copy error. This commit applies the frontend changes:
- Listeners.tsx: PROTOCOLS array splits 'tuic' into 'tuic-v4'/'tuic-v5'
- ListenerConfigFields.tsx:
- ALWAYS_TLS_PROTOCOLS includes tuic-v4/tuic-v5
- Form condition: protocol === 'tuic' || 'tuic-v4' || 'tuic-v5'
- case 'tuic': includes tuic-v4 and tuic-v5 fallthrough
- token field: only shows for tuic-v4 and tuic (backward compat)
- tuic-v5: no token field (uses panel-managed users)
2026-08-31 15:35:18 +00:00
Z User 4190024234 fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
fix(tuic): token field must be slice not string
Same class of bug as trusttunnel network: Mihomo expects TUIC v4 token
as an array (token: ["TOKEN"]), but the frontend stored a single
token as a string. Mihomo rejected with 'token' is not a slice.
Fix:
- Frontend: token always stored as array (even when single token)
- Backend: TUICCompiler converts string token to []string{token}
for legacy configs
Also audited all other array-type fields:
- alpn: Select mode=tags → array ✅
- quic-versions: Select mode=tags → array ✅
- short-id: Select mode=tags → array ✅
- server-names: Select mode=tags → array ✅
- network (trusttunnel): mode=multiple → array ✅ (fixed in prev commit)
- token (tuic v4): now always array ✅ (this fix)
No other type mismatches found.
2026-08-31 14:28:21 +00:00
Z User 156b21fcc1 fix(trusttunnel): network field must be slice not string
Mihomo expects network: ["tcp", "udp"] (array) for TrustTunnel
listeners. The frontend Select stored a single string ("tcp" or
"udp"), which copyConfigPassthrough emitted as network: "tcp"
(string). Mihomo rejected with: 'network' is not a slice.
Fix:
- Frontend: network Select changed to mode="multiple" (multi-select)
Storing as array, reading as array (backward compatible with string)
- Backend: TrustTunnelCompiler.Compile converts string network to
[]string{network} for legacy configs that still have a string
Official docs: network: ["tcp", "udp"] # http2+http3
fix(trusttunnel): network field must be slice not string
Mihomo expects network: ["tcp", "udp"] (array) for TrustTunnel
listeners. The frontend Select stored a single string ("tcp" or
"udp"), which copyConfigPassthrough emitted as network: "tcp"
(string). Mihomo rejected with: 'network' is not a slice.
Fix:
- Frontend: network Select changed to mode="multiple" (multi-select)
Storing as array, reading as array (backward compatible with string)
- Backend: TrustTunnelCompiler.Compile converts string network to
[]string{network} for legacy configs that still have a string
Official docs: network: ["tcp", "udp"] # http2+http3
fix(trusttunnel): network field must be slice not string
Mihomo expects network: ["tcp", "udp"] (array) for TrustTunnel
listeners. The frontend Select stored a single string ("tcp" or
"udp"), which copyConfigPassthrough emitted as network: "tcp"
(string). Mihomo rejected with: 'network' is not a slice.
Fix:
- Frontend: network Select changed to mode="multiple" (multi-select)
Storing as array, reading as array (backward compatible with string)
- Backend: TrustTunnelCompiler.Compile converts string network to
[]string{network} for legacy configs that still have a string
Official docs: network: ["tcp", "udp"] # http2+http3
fix(trusttunnel): network field must be slice not string
Mihomo expects network: ["tcp", "udp"] (array) for TrustTunnel
listeners. The frontend Select stored a single string ("tcp" or
"udp"), which copyConfigPassthrough emitted as network: "tcp"
(string). Mihomo rejected with: 'network' is not a slice.
Fix:
- Frontend: network Select changed to mode="multiple" (multi-select)
Storing as array, reading as array (backward compatible with string)
- Backend: TrustTunnelCompiler.Compile converts string network to
[]string{network} for legacy configs that still have a string
Official docs: network: ["tcp", "udp"] # http2+http3
fix(trusttunnel): network field must be slice not string
Mihomo expects network: ["tcp", "udp"] (array) for TrustTunnel
listeners. The frontend Select stored a single string ("tcp" or
"udp"), which copyConfigPassthrough emitted as network: "tcp"
(string). Mihomo rejected with: 'network' is not a slice.
Fix:
- Frontend: network Select changed to mode="multiple" (multi-select)
Storing as array, reading as array (backward compatible with string)
- Backend: TrustTunnelCompiler.Compile converts string network to
[]string{network} for legacy configs that still have a string
Official docs: network: ["tcp", "udp"] # http2+http3
fix(trusttunnel): network field must be slice not string
Mihomo expects network: ["tcp", "udp"] (array) for TrustTunnel
listeners. The frontend Select stored a single string ("tcp" or
"udp"), which copyConfigPassthrough emitted as network: "tcp"
(string). Mihomo rejected with: 'network' is not a slice.
Fix:
- Frontend: network Select changed to mode="multiple" (multi-select)
Storing as array, reading as array (backward compatible with string)
- Backend: TrustTunnelCompiler.Compile converts string network to
[]string{network} for legacy configs that still have a string
Official docs: network: ["tcp", "udp"] # http2+http3
fix(trusttunnel): network field must be slice not string
Mihomo expects network: ["tcp", "udp"] (array) for TrustTunnel
listeners. The frontend Select stored a single string ("tcp" or
"udp"), which copyConfigPassthrough emitted as network: "tcp"
(string). Mihomo rejected with: 'network' is not a slice.
Fix:
- Frontend: network Select changed to mode="multiple" (multi-select)
Storing as array, reading as array (backward compatible with string)
- Backend: TrustTunnelCompiler.Compile converts string network to
[]string{network} for legacy configs that still have a string
Official docs: network: ["tcp", "udp"] # http2+http3
fix(trusttunnel): network field must be slice not string
Mihomo expects network: ["tcp", "udp"] (array) for TrustTunnel
listeners. The frontend Select stored a single string ("tcp" or
"udp"), which copyConfigPassthrough emitted as network: "tcp"
(string). Mihomo rejected with: 'network' is not a slice.
Fix:
- Frontend: network Select changed to mode="multiple" (multi-select)
Storing as array, reading as array (backward compatible with string)
- Backend: TrustTunnelCompiler.Compile converts string network to
[]string{network} for legacy configs that still have a string
Official docs: network: ["tcp", "udp"] # http2+http3
fix(trusttunnel): network field must be slice not string
Mihomo expects network: ["tcp", "udp"] (array) for TrustTunnel
listeners. The frontend Select stored a single string ("tcp" or
"udp"), which copyConfigPassthrough emitted as network: "tcp"
(string). Mihomo rejected with: 'network' is not a slice.
Fix:
- Frontend: network Select changed to mode="multiple" (multi-select)
Storing as array, reading as array (backward compatible with string)
- Backend: TrustTunnelCompiler.Compile converts string network to
[]string{network} for legacy configs that still have a string
Official docs: network: ["tcp", "udp"] # http2+http3
fix(trusttunnel): network field must be slice not string
Mihomo expects network: ["tcp", "udp"] (array) for TrustTunnel
listeners. The frontend Select stored a single string ("tcp" or
"udp"), which copyConfigPassthrough emitted as network: "tcp"
(string). Mihomo rejected with: 'network' is not a slice.
Fix:
- Frontend: network Select changed to mode="multiple" (multi-select)
Storing as array, reading as array (backward compatible with string)
- Backend: TrustTunnelCompiler.Compile converts string network to
[]string{network} for legacy configs that still have a string
Official docs: network: ["tcp", "udp"] # http2+http3
fix(trusttunnel): network field must be slice not string
Mihomo expects network: ["tcp", "udp"] (array) for TrustTunnel
listeners. The frontend Select stored a single string ("tcp" or
"udp"), which copyConfigPassthrough emitted as network: "tcp"
(string). Mihomo rejected with: 'network' is not a slice.
Fix:
- Frontend: network Select changed to mode="multiple" (multi-select)
Storing as array, reading as array (backward compatible with string)
- Backend: TrustTunnelCompiler.Compile converts string network to
[]string{network} for legacy configs that still have a string
Official docs: network: ["tcp", "udp"] # http2+http3
fix(trusttunnel): network field must be slice not string
Mihomo expects network: ["tcp", "udp"] (array) for TrustTunnel
listeners. The frontend Select stored a single string ("tcp" or
"udp"), which copyConfigPassthrough emitted as network: "tcp"
(string). Mihomo rejected with: 'network' is not a slice.
Fix:
- Frontend: network Select changed to mode="multiple" (multi-select)
Storing as array, reading as array (backward compatible with string)
- Backend: TrustTunnelCompiler.Compile converts string network to
[]string{network} for legacy configs that still have a string
Official docs: network: ["tcp", "udp"] # http2+http3
2026-08-31 14:22:14 +00:00
Z User 3c8969812c fix(ui): filter XHTTP out of capability transport for non-VLESS 2026-08-31 07:29:07 +00:00
Z User 0e2d47d778 fix(protocol): XHTTP transport is VLESS-only in capabilities and UI
VMess/Trojan shared transportComponents() which listed XHTTP. Split a
VLESS-only transportComponentsWithXHTTP(); hide XHTTP radio/hint for
other protocols and refuse to emit xhttp-config unless protocol is vless.
fix(protocol): XHTTP transport is VLESS-only in capabilities and UI
VMess/Trojan shared transportComponents() which listed XHTTP. Split a
VLESS-only transportComponentsWithXHTTP(); hide XHTTP radio/hint for
other protocols and refuse to emit xhttp-config unless protocol is vless.
fix(protocol): XHTTP transport is VLESS-only in capabilities and UI
VMess/Trojan shared transportComponents() which listed XHTTP. Split a
VLESS-only transportComponentsWithXHTTP(); hide XHTTP radio/hint for
other protocols and refuse to emit xhttp-config unless protocol is vless.
fix(protocol): XHTTP transport is VLESS-only in capabilities and UI
VMess/Trojan shared transportComponents() which listed XHTTP. Split a
VLESS-only transportComponentsWithXHTTP(); hide XHTTP radio/hint for
other protocols and refuse to emit xhttp-config unless protocol is vless.
fix(protocol): XHTTP transport is VLESS-only in capabilities and UI
VMess/Trojan shared transportComponents() which listed XHTTP. Split a
VLESS-only transportComponentsWithXHTTP(); hide XHTTP radio/hint for
other protocols and refuse to emit xhttp-config unless protocol is vless.
fix(protocol): XHTTP transport is VLESS-only in capabilities and UI
VMess/Trojan shared transportComponents() which listed XHTTP. Split a
VLESS-only transportComponentsWithXHTTP(); hide XHTTP radio/hint for
other protocols and refuse to emit xhttp-config unless protocol is vless.
fix(protocol): XHTTP transport is VLESS-only in capabilities and UI
VMess/Trojan shared transportComponents() which listed XHTTP. Split a
VLESS-only transportComponentsWithXHTTP(); hide XHTTP radio/hint for
other protocols and refuse to emit xhttp-config unless protocol is vless.
fix(protocol): XHTTP transport is VLESS-only in capabilities and UI
VMess/Trojan shared transportComponents() which listed XHTTP. Split a
VLESS-only transportComponentsWithXHTTP(); hide XHTTP radio/hint for
other protocols and refuse to emit xhttp-config unless protocol is vless.
fix(protocol): XHTTP transport is VLESS-only in capabilities and UI
VMess/Trojan shared transportComponents() which listed XHTTP. Split a
VLESS-only transportComponentsWithXHTTP(); hide XHTTP radio/hint for
other protocols and refuse to emit xhttp-config unless protocol is vless.
fix(protocol): XHTTP transport is VLESS-only in capabilities and UI
VMess/Trojan shared transportComponents() which listed XHTTP. Split a
VLESS-only transportComponentsWithXHTTP(); hide XHTTP radio/hint for
other protocols and refuse to emit xhttp-config unless protocol is vless.
fix(protocol): XHTTP transport is VLESS-only in capabilities and UI
VMess/Trojan shared transportComponents() which listed XHTTP. Split a
VLESS-only transportComponentsWithXHTTP(); hide XHTTP radio/hint for
other protocols and refuse to emit xhttp-config unless protocol is vless.
fix(protocol): XHTTP transport is VLESS-only in capabilities and UI
VMess/Trojan shared transportComponents() which listed XHTTP. Split a
VLESS-only transportComponentsWithXHTTP(); hide XHTTP radio/hint for
other protocols and refuse to emit xhttp-config unless protocol is vless.
2026-08-31 07:28:58 +00:00
Z User 214605d9c8 fix(ui/protocol): P1 always-TLS UX, P2 wrapper sanitize, P3 ShadowQUIC/tlsmirror
- Hy2/TUIC/AnyTLS/TrustTunnel: no Security=None; always show cert with auto hint
- Optional protocols: cert only when security_layer=tls
- Do not emit incomplete shadow-tls/res-tls/jls/jls-upstream/tlsmirror
- Sanitize same blocks on save and config generation
- ShadowQUIC default congestion-controller=cubic
- VMess minimal tlsmirror-config (dest + primary-key)
fix(ui/protocol): P1 always-TLS UX, P2 wrapper sanitize, P3 ShadowQUIC/tlsmirror
- Hy2/TUIC/AnyTLS/TrustTunnel: no Security=None; always show cert with auto hint
- Optional protocols: cert only when security_layer=tls
- Do not emit incomplete shadow-tls/res-tls/jls/jls-upstream/tlsmirror
- Sanitize same blocks on save and config generation
- ShadowQUIC default congestion-controller=cubic
- VMess minimal tlsmirror-config (dest + primary-key)
fix(ui/protocol): P1 always-TLS UX, P2 wrapper sanitize, P3 ShadowQUIC/tlsmirror
- Hy2/TUIC/AnyTLS/TrustTunnel: no Security=None; always show cert with auto hint
- Optional protocols: cert only when security_layer=tls
- Do not emit incomplete shadow-tls/res-tls/jls/jls-upstream/tlsmirror
- Sanitize same blocks on save and config generation
- ShadowQUIC default congestion-controller=cubic
- VMess minimal tlsmirror-config (dest + primary-key)
fix(ui/protocol): P1 always-TLS UX, P2 wrapper sanitize, P3 ShadowQUIC/tlsmirror
- Hy2/TUIC/AnyTLS/TrustTunnel: no Security=None; always show cert with auto hint
- Optional protocols: cert only when security_layer=tls
- Do not emit incomplete shadow-tls/res-tls/jls/jls-upstream/tlsmirror
- Sanitize same blocks on save and config generation
- ShadowQUIC default congestion-controller=cubic
- VMess minimal tlsmirror-config (dest + primary-key)
fix(ui/protocol): P1 always-TLS UX, P2 wrapper sanitize, P3 ShadowQUIC/tlsmirror
- Hy2/TUIC/AnyTLS/TrustTunnel: no Security=None; always show cert with auto hint
- Optional protocols: cert only when security_layer=tls
- Do not emit incomplete shadow-tls/res-tls/jls/jls-upstream/tlsmirror
- Sanitize same blocks on save and config generation
- ShadowQUIC default congestion-controller=cubic
- VMess minimal tlsmirror-config (dest + primary-key)
fix(ui/protocol): P1 always-TLS UX, P2 wrapper sanitize, P3 ShadowQUIC/tlsmirror
- Hy2/TUIC/AnyTLS/TrustTunnel: no Security=None; always show cert with auto hint
- Optional protocols: cert only when security_layer=tls
- Do not emit incomplete shadow-tls/res-tls/jls/jls-upstream/tlsmirror
- Sanitize same blocks on save and config generation
- ShadowQUIC default congestion-controller=cubic
- VMess minimal tlsmirror-config (dest + primary-key)
fix(ui/protocol): P1 always-TLS UX, P2 wrapper sanitize, P3 ShadowQUIC/tlsmirror
- Hy2/TUIC/AnyTLS/TrustTunnel: no Security=None; always show cert with auto hint
- Optional protocols: cert only when security_layer=tls
- Do not emit incomplete shadow-tls/res-tls/jls/jls-upstream/tlsmirror
- Sanitize same blocks on save and config generation
- ShadowQUIC default congestion-controller=cubic
- VMess minimal tlsmirror-config (dest + primary-key)
fix(ui/protocol): P1 always-TLS UX, P2 wrapper sanitize, P3 ShadowQUIC/tlsmirror
- Hy2/TUIC/AnyTLS/TrustTunnel: no Security=None; always show cert with auto hint
- Optional protocols: cert only when security_layer=tls
- Do not emit incomplete shadow-tls/res-tls/jls/jls-upstream/tlsmirror
- Sanitize same blocks on save and config generation
- ShadowQUIC default congestion-controller=cubic
- VMess minimal tlsmirror-config (dest + primary-key)
fix(ui/protocol): P1 always-TLS UX, P2 wrapper sanitize, P3 ShadowQUIC/tlsmirror
- Hy2/TUIC/AnyTLS/TrustTunnel: no Security=None; always show cert with auto hint
- Optional protocols: cert only when security_layer=tls
- Do not emit incomplete shadow-tls/res-tls/jls/jls-upstream/tlsmirror
- Sanitize same blocks on save and config generation
- ShadowQUIC default congestion-controller=cubic
- VMess minimal tlsmirror-config (dest + primary-key)
fix(ui/protocol): P1 always-TLS UX, P2 wrapper sanitize, P3 ShadowQUIC/tlsmirror
- Hy2/TUIC/AnyTLS/TrustTunnel: no Security=None; always show cert with auto hint
- Optional protocols: cert only when security_layer=tls
- Do not emit incomplete shadow-tls/res-tls/jls/jls-upstream/tlsmirror
- Sanitize same blocks on save and config generation
- ShadowQUIC default congestion-controller=cubic
- VMess minimal tlsmirror-config (dest + primary-key)
fix(ui/protocol): P1 always-TLS UX, P2 wrapper sanitize, P3 ShadowQUIC/tlsmirror
- Hy2/TUIC/AnyTLS/TrustTunnel: no Security=None; always show cert with auto hint
- Optional protocols: cert only when security_layer=tls
- Do not emit incomplete shadow-tls/res-tls/jls/jls-upstream/tlsmirror
- Sanitize same blocks on save and config generation
- ShadowQUIC default congestion-controller=cubic
- VMess minimal tlsmirror-config (dest + primary-key)
fix(ui/protocol): P1 always-TLS UX, P2 wrapper sanitize, P3 ShadowQUIC/tlsmirror
- Hy2/TUIC/AnyTLS/TrustTunnel: no Security=None; always show cert with auto hint
- Optional protocols: cert only when security_layer=tls
- Do not emit incomplete shadow-tls/res-tls/jls/jls-upstream/tlsmirror
- Sanitize same blocks on save and config generation
- ShadowQUIC default congestion-controller=cubic
- VMess minimal tlsmirror-config (dest + primary-key)
2026-08-31 07:20:24 +00:00
Z User 313164131d fix(ui): use full ListenerConfigFields instead of reduced capability form
useCapabilityForm was hardcoded true, so create/edit node only showed the
thin schema-driven CapabilityFormFields and hid protocol sections
(ShadowQUIC jls-upstream, Reality details, etc.).
fix(ui): use full ListenerConfigFields instead of reduced capability form
useCapabilityForm was hardcoded true, so create/edit node only showed the
thin schema-driven CapabilityFormFields and hid protocol sections
(ShadowQUIC jls-upstream, Reality details, etc.).
fix(ui): use full ListenerConfigFields instead of reduced capability form
useCapabilityForm was hardcoded true, so create/edit node only showed the
thin schema-driven CapabilityFormFields and hid protocol sections
(ShadowQUIC jls-upstream, Reality details, etc.).
fix(ui): use full ListenerConfigFields instead of reduced capability form
useCapabilityForm was hardcoded true, so create/edit node only showed the
thin schema-driven CapabilityFormFields and hid protocol sections
(ShadowQUIC jls-upstream, Reality details, etc.).
fix(ui): use full ListenerConfigFields instead of reduced capability form
useCapabilityForm was hardcoded true, so create/edit node only showed the
thin schema-driven CapabilityFormFields and hid protocol sections
(ShadowQUIC jls-upstream, Reality details, etc.).
fix(ui): use full ListenerConfigFields instead of reduced capability form
useCapabilityForm was hardcoded true, so create/edit node only showed the
thin schema-driven CapabilityFormFields and hid protocol sections
(ShadowQUIC jls-upstream, Reality details, etc.).
fix(ui): use full ListenerConfigFields instead of reduced capability form
useCapabilityForm was hardcoded true, so create/edit node only showed the
thin schema-driven CapabilityFormFields and hid protocol sections
(ShadowQUIC jls-upstream, Reality details, etc.).
fix(ui): use full ListenerConfigFields instead of reduced capability form
useCapabilityForm was hardcoded true, so create/edit node only showed the
thin schema-driven CapabilityFormFields and hid protocol sections
(ShadowQUIC jls-upstream, Reality details, etc.).
fix(ui): use full ListenerConfigFields instead of reduced capability form
useCapabilityForm was hardcoded true, so create/edit node only showed the
thin schema-driven CapabilityFormFields and hid protocol sections
(ShadowQUIC jls-upstream, Reality details, etc.).
fix(ui): use full ListenerConfigFields instead of reduced capability form
useCapabilityForm was hardcoded true, so create/edit node only showed the
thin schema-driven CapabilityFormFields and hid protocol sections
(ShadowQUIC jls-upstream, Reality details, etc.).
fix(ui): use full ListenerConfigFields instead of reduced capability form
useCapabilityForm was hardcoded true, so create/edit node only showed the
thin schema-driven CapabilityFormFields and hid protocol sections
(ShadowQUIC jls-upstream, Reality details, etc.).
fix(ui): use full ListenerConfigFields instead of reduced capability form
useCapabilityForm was hardcoded true, so create/edit node only showed the
thin schema-driven CapabilityFormFields and hid protocol sections
(ShadowQUIC jls-upstream, Reality details, etc.).
2026-08-31 06:59:53 +00:00
Z User d46ad2a94e fix(listener): drop incomplete jls-upstream before Mihomo validate
Empty jls-upstream (missing addr) fails mihomo -t on ShadowQUIC.
Require addr on the form and sanitize at save/generation.
fix(listener): drop incomplete jls-upstream before Mihomo validate
Empty jls-upstream (missing addr) fails mihomo -t on ShadowQUIC.
Require addr on the form and sanitize at save/generation.
fix(listener): drop incomplete jls-upstream before Mihomo validate
Empty jls-upstream (missing addr) fails mihomo -t on ShadowQUIC.
Require addr on the form and sanitize at save/generation.
fix(listener): drop incomplete jls-upstream before Mihomo validate
Empty jls-upstream (missing addr) fails mihomo -t on ShadowQUIC.
Require addr on the form and sanitize at save/generation.
fix(listener): drop incomplete jls-upstream before Mihomo validate
Empty jls-upstream (missing addr) fails mihomo -t on ShadowQUIC.
Require addr on the form and sanitize at save/generation.
fix(listener): drop incomplete jls-upstream before Mihomo validate
Empty jls-upstream (missing addr) fails mihomo -t on ShadowQUIC.
Require addr on the form and sanitize at save/generation.
fix(listener): drop incomplete jls-upstream before Mihomo validate
Empty jls-upstream (missing addr) fails mihomo -t on ShadowQUIC.
Require addr on the form and sanitize at save/generation.
fix(listener): drop incomplete jls-upstream before Mihomo validate
Empty jls-upstream (missing addr) fails mihomo -t on ShadowQUIC.
Require addr on the form and sanitize at save/generation.
fix(listener): drop incomplete jls-upstream before Mihomo validate
Empty jls-upstream (missing addr) fails mihomo -t on ShadowQUIC.
Require addr on the form and sanitize at save/generation.
fix(listener): drop incomplete jls-upstream before Mihomo validate
Empty jls-upstream (missing addr) fails mihomo -t on ShadowQUIC.
Require addr on the form and sanitize at save/generation.
fix(listener): drop incomplete jls-upstream before Mihomo validate
Empty jls-upstream (missing addr) fails mihomo -t on ShadowQUIC.
Require addr on the form and sanitize at save/generation.
fix(listener): drop incomplete jls-upstream before Mihomo validate
Empty jls-upstream (missing addr) fails mihomo -t on ShadowQUIC.
Require addr on the form and sanitize at save/generation.
2026-08-31 06:52:38 +00:00
Z User 9a7370cc96 chore(i18n): remove unused auto-generate field guide strings
Drop autoGenerateHint, field guide titles, per-protocol guide blocks,
and password/reality auto tooltips from zh/en after UI simplification.
chore(i18n): remove unused auto-generate field guide strings
Drop autoGenerateHint, field guide titles, per-protocol guide blocks,
and password/reality auto tooltips from zh/en after UI simplification.
chore(i18n): remove unused auto-generate field guide strings
Drop autoGenerateHint, field guide titles, per-protocol guide blocks,
and password/reality auto tooltips from zh/en after UI simplification.
chore(i18n): remove unused auto-generate field guide strings
Drop autoGenerateHint, field guide titles, per-protocol guide blocks,
and password/reality auto tooltips from zh/en after UI simplification.
chore(i18n): remove unused auto-generate field guide strings
Drop autoGenerateHint, field guide titles, per-protocol guide blocks,
and password/reality auto tooltips from zh/en after UI simplification.
chore(i18n): remove unused auto-generate field guide strings
Drop autoGenerateHint, field guide titles, per-protocol guide blocks,
and password/reality auto tooltips from zh/en after UI simplification.
chore(i18n): remove unused auto-generate field guide strings
Drop autoGenerateHint, field guide titles, per-protocol guide blocks,
and password/reality auto tooltips from zh/en after UI simplification.
chore(i18n): remove unused auto-generate field guide strings
Drop autoGenerateHint, field guide titles, per-protocol guide blocks,
and password/reality auto tooltips from zh/en after UI simplification.
chore(i18n): remove unused auto-generate field guide strings
Drop autoGenerateHint, field guide titles, per-protocol guide blocks,
and password/reality auto tooltips from zh/en after UI simplification.
chore(i18n): remove unused auto-generate field guide strings
Drop autoGenerateHint, field guide titles, per-protocol guide blocks,
and password/reality auto tooltips from zh/en after UI simplification.
chore(i18n): remove unused auto-generate field guide strings
Drop autoGenerateHint, field guide titles, per-protocol guide blocks,
and password/reality auto tooltips from zh/en after UI simplification.
chore(i18n): remove unused auto-generate field guide strings
Drop autoGenerateHint, field guide titles, per-protocol guide blocks,
and password/reality auto tooltips from zh/en after UI simplification.
2026-08-31 06:37:05 +00:00
Z User 76da8746e1 ui(listeners): drop auto-generate prose; use placeholder auto
Remove field-guide alerts and tooltips about auto-generation.
SS password, Snell PSK, REALITY key/short-id keep placeholder "auto".
ui(listeners): drop auto-generate prose; use placeholder auto
Remove field-guide alerts and tooltips about auto-generation.
SS password, Snell PSK, REALITY key/short-id keep placeholder "auto".
ui(listeners): drop auto-generate prose; use placeholder auto
Remove field-guide alerts and tooltips about auto-generation.
SS password, Snell PSK, REALITY key/short-id keep placeholder "auto".
ui(listeners): drop auto-generate prose; use placeholder auto
Remove field-guide alerts and tooltips about auto-generation.
SS password, Snell PSK, REALITY key/short-id keep placeholder "auto".
ui(listeners): drop auto-generate prose; use placeholder auto
Remove field-guide alerts and tooltips about auto-generation.
SS password, Snell PSK, REALITY key/short-id keep placeholder "auto".
ui(listeners): drop auto-generate prose; use placeholder auto
Remove field-guide alerts and tooltips about auto-generation.
SS password, Snell PSK, REALITY key/short-id keep placeholder "auto".
ui(listeners): drop auto-generate prose; use placeholder auto
Remove field-guide alerts and tooltips about auto-generation.
SS password, Snell PSK, REALITY key/short-id keep placeholder "auto".
ui(listeners): drop auto-generate prose; use placeholder auto
Remove field-guide alerts and tooltips about auto-generation.
SS password, Snell PSK, REALITY key/short-id keep placeholder "auto".
ui(listeners): drop auto-generate prose; use placeholder auto
Remove field-guide alerts and tooltips about auto-generation.
SS password, Snell PSK, REALITY key/short-id keep placeholder "auto".
ui(listeners): drop auto-generate prose; use placeholder auto
Remove field-guide alerts and tooltips about auto-generation.
SS password, Snell PSK, REALITY key/short-id keep placeholder "auto".
ui(listeners): drop auto-generate prose; use placeholder auto
Remove field-guide alerts and tooltips about auto-generation.
SS password, Snell PSK, REALITY key/short-id keep placeholder "auto".
ui(listeners): drop auto-generate prose; use placeholder auto
Remove field-guide alerts and tooltips about auto-generation.
SS password, Snell PSK, REALITY key/short-id keep placeholder "auto".
2026-08-31 06:34:15 +00:00
Z User d6e64ab313 chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
2026-08-31 06:21:47 +00:00
Z User 8f9ffb1a08 fix(listener): strip incomplete jls-config before Mihomo validate
Empty or half-filled jls-config (missing dest/users) causes
mihomo -t to fail. Require complete JLS on the form; sanitize on
save and at config generation.
fix(listener): strip incomplete jls-config before Mihomo validate
Empty or half-filled jls-config (missing dest/users) causes
mihomo -t to fail. Require complete JLS on the form; sanitize on
save and at config generation.
fix(listener): strip incomplete jls-config before Mihomo validate
Empty or half-filled jls-config (missing dest/users) causes
mihomo -t to fail. Require complete JLS on the form; sanitize on
save and at config generation.
fix(listener): strip incomplete jls-config before Mihomo validate
Empty or half-filled jls-config (missing dest/users) causes
mihomo -t to fail. Require complete JLS on the form; sanitize on
save and at config generation.
fix(listener): strip incomplete jls-config before Mihomo validate
Empty or half-filled jls-config (missing dest/users) causes
mihomo -t to fail. Require complete JLS on the form; sanitize on
save and at config generation.
fix(listener): strip incomplete jls-config before Mihomo validate
Empty or half-filled jls-config (missing dest/users) causes
mihomo -t to fail. Require complete JLS on the form; sanitize on
save and at config generation.
fix(listener): strip incomplete jls-config before Mihomo validate
Empty or half-filled jls-config (missing dest/users) causes
mihomo -t to fail. Require complete JLS on the form; sanitize on
save and at config generation.
fix(listener): strip incomplete jls-config before Mihomo validate
Empty or half-filled jls-config (missing dest/users) causes
mihomo -t to fail. Require complete JLS on the form; sanitize on
save and at config generation.
fix(listener): strip incomplete jls-config before Mihomo validate
Empty or half-filled jls-config (missing dest/users) causes
mihomo -t to fail. Require complete JLS on the form; sanitize on
save and at config generation.
fix(listener): strip incomplete jls-config before Mihomo validate
Empty or half-filled jls-config (missing dest/users) causes
mihomo -t to fail. Require complete JLS on the form; sanitize on
save and at config generation.
fix(listener): strip incomplete jls-config before Mihomo validate
Empty or half-filled jls-config (missing dest/users) causes
mihomo -t to fail. Require complete JLS on the form; sanitize on
save and at config generation.
fix(listener): strip incomplete jls-config before Mihomo validate
Empty or half-filled jls-config (missing dest/users) causes
mihomo -t to fail. Require complete JLS on the form; sanitize on
save and at config generation.
2026-08-31 05:57:21 +00:00
Z User 5b8e42dfc1 feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only
Show a unified field guide alert for each protocol. Keep XHTTP out of
VMess/Trojan transport options and document that on the transport control.
feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only
Show a unified field guide alert for each protocol. Keep XHTTP out of
VMess/Trojan transport options and document that on the transport control.
feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only
Show a unified field guide alert for each protocol. Keep XHTTP out of
VMess/Trojan transport options and document that on the transport control.
feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only
Show a unified field guide alert for each protocol. Keep XHTTP out of
VMess/Trojan transport options and document that on the transport control.
feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only
Show a unified field guide alert for each protocol. Keep XHTTP out of
VMess/Trojan transport options and document that on the transport control.
feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only
Show a unified field guide alert for each protocol. Keep XHTTP out of
VMess/Trojan transport options and document that on the transport control.
feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only
Show a unified field guide alert for each protocol. Keep XHTTP out of
VMess/Trojan transport options and document that on the transport control.
feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only
Show a unified field guide alert for each protocol. Keep XHTTP out of
VMess/Trojan transport options and document that on the transport control.
feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only
Show a unified field guide alert for each protocol. Keep XHTTP out of
VMess/Trojan transport options and document that on the transport control.
feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only
Show a unified field guide alert for each protocol. Keep XHTTP out of
VMess/Trojan transport options and document that on the transport control.
feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only
Show a unified field guide alert for each protocol. Keep XHTTP out of
VMess/Trojan transport options and document that on the transport control.
feat(ui): per-protocol required/optional/auto field guide; XHTTP VLESS-only
Show a unified field guide alert for each protocol. Keep XHTTP out of
VMess/Trojan transport options and document that on the transport control.
2026-08-31 05:31:24 +00:00
Z User f14e95b684 fix(all-remaining): 24 audit findings — Medium + Low severity
All remaining unfixed issues from 3-round exhaustive audit are now
resolved. 8 Medium + 16 Low = 24 items.
== Medium (8) ==
M1: GetLogs timestamp always time.Now()
mihomo/service.go GetLogs now parses RFC3339 prefix from stored log
lines, infers level (ERROR/WARN/INFO/DEBUG) from payload prefix.
M2: Cron parser silently ignores */N and range
telegram/scheduler.go cronFieldMatches now supports */N (step),
A-B (range), A-B/N (stepped range) in addition to * and comma.
M3: findExistingProcesses holds pm.mu during /proc scan
mihomo/process.go start() now calls findExistingProcesses() BEFORE
acquiring pm.mu. Logs/Status/Stop no longer blocked during scan.
M4: Cluster HealthCheckAll sequential (N*12s)
cluster/service.go now fans out goroutines with WaitGroup. Worst
case drops from N*12s to ~12s.
M5: Panel settings PUT not transactional
router/panel_settings.go PUT handler now wraps create/update loop
in db.Transaction(). Partial failure rolls back.
M6: Cluster api_token no validation at save time
cluster/service.go Create/Update now validate JWT shape (starts
with 'ey', 3 dot-separated parts). Clear error message guides
operator to POST /api/v1/auth/login.
M7: Disk full — stale config warning not prominent
app/app.go soft-fail warnings changed to [WARNING] prefix + hint
to check listener configs.
M8: Telegram bot retries forever on 401
telegram/bot.go loop now detects 401/Unauthorized, switches to
5-minute retry interval (was 5s). Clears on success.
== Low (16) ==
L1: Scheduler/Bot Start() idempotent via sync.Once
L2: Container.Shutdown() method added
L3: Enforcer lastBlocked prunes deleted users
L4: traffic.Service first-call rate skipped (was bogus spike)
L5: strings.HasPrefix replaces path[:4]=="/api"
L6: ActiveCredentialsByListener batch-load (N+1→1 query)
L7: Telegram bot commands check DB errors
L8: Cluster health save error logged
L9: Collector continues on AddSample failure (was return)
L10: user.Create logs warning instead of returning error after
successful DB write
L11-L14: Documentation comments for known limitations (HTTP-01
goroutine, GlobalConfig thread-safety, credentialsChanged
thread-safety, SubToken TOCTOU)
L15: useAuth.ts calls fetchMe() to refresh mustChangePassword
L16: Verified access-tokens/client-access endpoints are NOT dead
Verification: go build/vet/test PASS, npm run build PASS, gofmt clean.
fix(all-remaining): 24 audit findings — Medium + Low severity
All remaining unfixed issues from 3-round exhaustive audit are now
resolved. 8 Medium + 16 Low = 24 items.
== Medium (8) ==
M1: GetLogs timestamp always time.Now()
mihomo/service.go GetLogs now parses RFC3339 prefix from stored log
lines, infers level (ERROR/WARN/INFO/DEBUG) from payload prefix.
M2: Cron parser silently ignores */N and range
telegram/scheduler.go cronFieldMatches now supports */N (step),
A-B (range), A-B/N (stepped range) in addition to * and comma.
M3: findExistingProcesses holds pm.mu during /proc scan
mihomo/process.go start() now calls findExistingProcesses() BEFORE
acquiring pm.mu. Logs/Status/Stop no longer blocked during scan.
M4: Cluster HealthCheckAll sequential (N*12s)
cluster/service.go now fans out goroutines with WaitGroup. Worst
case drops from N*12s to ~12s.
M5: Panel settings PUT not transactional
router/panel_settings.go PUT handler now wraps create/update loop
in db.Transaction(). Partial failure rolls back.
M6: Cluster api_token no validation at save time
cluster/service.go Create/Update now validate JWT shape (starts
with 'ey', 3 dot-separated parts). Clear error message guides
operator to POST /api/v1/auth/login.
M7: Disk full — stale config warning not prominent
app/app.go soft-fail warnings changed to [WARNING] prefix + hint
to check listener configs.
M8: Telegram bot retries forever on 401
telegram/bot.go loop now detects 401/Unauthorized, switches to
5-minute retry interval (was 5s). Clears on success.
== Low (16) ==
L1: Scheduler/Bot Start() idempotent via sync.Once
L2: Container.Shutdown() method added
L3: Enforcer lastBlocked prunes deleted users
L4: traffic.Service first-call rate skipped (was bogus spike)
L5: strings.HasPrefix replaces path[:4]=="/api"
L6: ActiveCredentialsByListener batch-load (N+1→1 query)
L7: Telegram bot commands check DB errors
L8: Cluster health save error logged
L9: Collector continues on AddSample failure (was return)
L10: user.Create logs warning instead of returning error after
successful DB write
L11-L14: Documentation comments for known limitations (HTTP-01
goroutine, GlobalConfig thread-safety, credentialsChanged
thread-safety, SubToken TOCTOU)
L15: useAuth.ts calls fetchMe() to refresh mustChangePassword
L16: Verified access-tokens/client-access endpoints are NOT dead
Verification: go build/vet/test PASS, npm run build PASS, gofmt clean.
fix(all-remaining): 24 audit findings — Medium + Low severity
All remaining unfixed issues from 3-round exhaustive audit are now
resolved. 8 Medium + 16 Low = 24 items.
== Medium (8) ==
M1: GetLogs timestamp always time.Now()
mihomo/service.go GetLogs now parses RFC3339 prefix from stored log
lines, infers level (ERROR/WARN/INFO/DEBUG) from payload prefix.
M2: Cron parser silently ignores */N and range
telegram/scheduler.go cronFieldMatches now supports */N (step),
A-B (range), A-B/N (stepped range) in addition to * and comma.
M3: findExistingProcesses holds pm.mu during /proc scan
mihomo/process.go start() now calls findExistingProcesses() BEFORE
acquiring pm.mu. Logs/Status/Stop no longer blocked during scan.
M4: Cluster HealthCheckAll sequential (N*12s)
cluster/service.go now fans out goroutines with WaitGroup. Worst
case drops from N*12s to ~12s.
M5: Panel settings PUT not transactional
router/panel_settings.go PUT handler now wraps create/update loop
in db.Transaction(). Partial failure rolls back.
M6: Cluster api_token no validation at save time
cluster/service.go Create/Update now validate JWT shape (starts
with 'ey', 3 dot-separated parts). Clear error message guides
operator to POST /api/v1/auth/login.
M7: Disk full — stale config warning not prominent
app/app.go soft-fail warnings changed to [WARNING] prefix + hint
to check listener configs.
M8: Telegram bot retries forever on 401
telegram/bot.go loop now detects 401/Unauthorized, switches to
5-minute retry interval (was 5s). Clears on success.
== Low (16) ==
L1: Scheduler/Bot Start() idempotent via sync.Once
L2: Container.Shutdown() method added
L3: Enforcer lastBlocked prunes deleted users
L4: traffic.Service first-call rate skipped (was bogus spike)
L5: strings.HasPrefix replaces path[:4]=="/api"
L6: ActiveCredentialsByListener batch-load (N+1→1 query)
L7: Telegram bot commands check DB errors
L8: Cluster health save error logged
L9: Collector continues on AddSample failure (was return)
L10: user.Create logs warning instead of returning error after
successful DB write
L11-L14: Documentation comments for known limitations (HTTP-01
goroutine, GlobalConfig thread-safety, credentialsChanged
thread-safety, SubToken TOCTOU)
L15: useAuth.ts calls fetchMe() to refresh mustChangePassword
L16: Verified access-tokens/client-access endpoints are NOT dead
Verification: go build/vet/test PASS, npm run build PASS, gofmt clean.
fix(all-remaining): 24 audit findings — Medium + Low severity
All remaining unfixed issues from 3-round exhaustive audit are now
resolved. 8 Medium + 16 Low = 24 items.
== Medium (8) ==
M1: GetLogs timestamp always time.Now()
mihomo/service.go GetLogs now parses RFC3339 prefix from stored log
lines, infers level (ERROR/WARN/INFO/DEBUG) from payload prefix.
M2: Cron parser silently ignores */N and range
telegram/scheduler.go cronFieldMatches now supports */N (step),
A-B (range), A-B/N (stepped range) in addition to * and comma.
M3: findExistingProcesses holds pm.mu during /proc scan
mihomo/process.go start() now calls findExistingProcesses() BEFORE
acquiring pm.mu. Logs/Status/Stop no longer blocked during scan.
M4: Cluster HealthCheckAll sequential (N*12s)
cluster/service.go now fans out goroutines with WaitGroup. Worst
case drops from N*12s to ~12s.
M5: Panel settings PUT not transactional
router/panel_settings.go PUT handler now wraps create/update loop
in db.Transaction(). Partial failure rolls back.
M6: Cluster api_token no validation at save time
cluster/service.go Create/Update now validate JWT shape (starts
with 'ey', 3 dot-separated parts). Clear error message guides
operator to POST /api/v1/auth/login.
M7: Disk full — stale config warning not prominent
app/app.go soft-fail warnings changed to [WARNING] prefix + hint
to check listener configs.
M8: Telegram bot retries forever on 401
telegram/bot.go loop now detects 401/Unauthorized, switches to
5-minute retry interval (was 5s). Clears on success.
== Low (16) ==
L1: Scheduler/Bot Start() idempotent via sync.Once
L2: Container.Shutdown() method added
L3: Enforcer lastBlocked prunes deleted users
L4: traffic.Service first-call rate skipped (was bogus spike)
L5: strings.HasPrefix replaces path[:4]=="/api"
L6: ActiveCredentialsByListener batch-load (N+1→1 query)
L7: Telegram bot commands check DB errors
L8: Cluster health save error logged
L9: Collector continues on AddSample failure (was return)
L10: user.Create logs warning instead of returning error after
successful DB write
L11-L14: Documentation comments for known limitations (HTTP-01
goroutine, GlobalConfig thread-safety, credentialsChanged
thread-safety, SubToken TOCTOU)
L15: useAuth.ts calls fetchMe() to refresh mustChangePassword
L16: Verified access-tokens/client-access endpoints are NOT dead
Verification: go build/vet/test PASS, npm run build PASS, gofmt clean.
fix(all-remaining): 24 audit findings — Medium + Low severity
All remaining unfixed issues from 3-round exhaustive audit are now
resolved. 8 Medium + 16 Low = 24 items.
== Medium (8) ==
M1: GetLogs timestamp always time.Now()
mihomo/service.go GetLogs now parses RFC3339 prefix from stored log
lines, infers level (ERROR/WARN/INFO/DEBUG) from payload prefix.
M2: Cron parser silently ignores */N and range
telegram/scheduler.go cronFieldMatches now supports */N (step),
A-B (range), A-B/N (stepped range) in addition to * and comma.
M3: findExistingProcesses holds pm.mu during /proc scan
mihomo/process.go start() now calls findExistingProcesses() BEFORE
acquiring pm.mu. Logs/Status/Stop no longer blocked during scan.
M4: Cluster HealthCheckAll sequential (N*12s)
cluster/service.go now fans out goroutines with WaitGroup. Worst
case drops from N*12s to ~12s.
M5: Panel settings PUT not transactional
router/panel_settings.go PUT handler now wraps create/update loop
in db.Transaction(). Partial failure rolls back.
M6: Cluster api_token no validation at save time
cluster/service.go Create/Update now validate JWT shape (starts
with 'ey', 3 dot-separated parts). Clear error message guides
operator to POST /api/v1/auth/login.
M7: Disk full — stale config warning not prominent
app/app.go soft-fail warnings changed to [WARNING] prefix + hint
to check listener configs.
M8: Telegram bot retries forever on 401
telegram/bot.go loop now detects 401/Unauthorized, switches to
5-minute retry interval (was 5s). Clears on success.
== Low (16) ==
L1: Scheduler/Bot Start() idempotent via sync.Once
L2: Container.Shutdown() method added
L3: Enforcer lastBlocked prunes deleted users
L4: traffic.Service first-call rate skipped (was bogus spike)
L5: strings.HasPrefix replaces path[:4]=="/api"
L6: ActiveCredentialsByListener batch-load (N+1→1 query)
L7: Telegram bot commands check DB errors
L8: Cluster health save error logged
L9: Collector continues on AddSample failure (was return)
L10: user.Create logs warning instead of returning error after
successful DB write
L11-L14: Documentation comments for known limitations (HTTP-01
goroutine, GlobalConfig thread-safety, credentialsChanged
thread-safety, SubToken TOCTOU)
L15: useAuth.ts calls fetchMe() to refresh mustChangePassword
L16: Verified access-tokens/client-access endpoints are NOT dead
Verification: go build/vet/test PASS, npm run build PASS, gofmt clean.
fix(all-remaining): 24 audit findings — Medium + Low severity
All remaining unfixed issues from 3-round exhaustive audit are now
resolved. 8 Medium + 16 Low = 24 items.
== Medium (8) ==
M1: GetLogs timestamp always time.Now()
mihomo/service.go GetLogs now parses RFC3339 prefix from stored log
lines, infers level (ERROR/WARN/INFO/DEBUG) from payload prefix.
M2: Cron parser silently ignores */N and range
telegram/scheduler.go cronFieldMatches now supports */N (step),
A-B (range), A-B/N (stepped range) in addition to * and comma.
M3: findExistingProcesses holds pm.mu during /proc scan
mihomo/process.go start() now calls findExistingProcesses() BEFORE
acquiring pm.mu. Logs/Status/Stop no longer blocked during scan.
M4: Cluster HealthCheckAll sequential (N*12s)
cluster/service.go now fans out goroutines with WaitGroup. Worst
case drops from N*12s to ~12s.
M5: Panel settings PUT not transactional
router/panel_settings.go PUT handler now wraps create/update loop
in db.Transaction(). Partial failure rolls back.
M6: Cluster api_token no validation at save time
cluster/service.go Create/Update now validate JWT shape (starts
with 'ey', 3 dot-separated parts). Clear error message guides
operator to POST /api/v1/auth/login.
M7: Disk full — stale config warning not prominent
app/app.go soft-fail warnings changed to [WARNING] prefix + hint
to check listener configs.
M8: Telegram bot retries forever on 401
telegram/bot.go loop now detects 401/Unauthorized, switches to
5-minute retry interval (was 5s). Clears on success.
== Low (16) ==
L1: Scheduler/Bot Start() idempotent via sync.Once
L2: Container.Shutdown() method added
L3: Enforcer lastBlocked prunes deleted users
L4: traffic.Service first-call rate skipped (was bogus spike)
L5: strings.HasPrefix replaces path[:4]=="/api"
L6: ActiveCredentialsByListener batch-load (N+1→1 query)
L7: Telegram bot commands check DB errors
L8: Cluster health save error logged
L9: Collector continues on AddSample failure (was return)
L10: user.Create logs warning instead of returning error after
successful DB write
L11-L14: Documentation comments for known limitations (HTTP-01
goroutine, GlobalConfig thread-safety, credentialsChanged
thread-safety, SubToken TOCTOU)
L15: useAuth.ts calls fetchMe() to refresh mustChangePassword
L16: Verified access-tokens/client-access endpoints are NOT dead
Verification: go build/vet/test PASS, npm run build PASS, gofmt clean.
fix(all-remaining): 24 audit findings — Medium + Low severity
All remaining unfixed issues from 3-round exhaustive audit are now
resolved. 8 Medium + 16 Low = 24 items.
== Medium (8) ==
M1: GetLogs timestamp always time.Now()
mihomo/service.go GetLogs now parses RFC3339 prefix from stored log
lines, infers level (ERROR/WARN/INFO/DEBUG) from payload prefix.
M2: Cron parser silently ignores */N and range
telegram/scheduler.go cronFieldMatches now supports */N (step),
A-B (range), A-B/N (stepped range) in addition to * and comma.
M3: findExistingProcesses holds pm.mu during /proc scan
mihomo/process.go start() now calls findExistingProcesses() BEFORE
acquiring pm.mu. Logs/Status/Stop no longer blocked during scan.
M4: Cluster HealthCheckAll sequential (N*12s)
cluster/service.go now fans out goroutines with WaitGroup. Worst
case drops from N*12s to ~12s.
M5: Panel settings PUT not transactional
router/panel_settings.go PUT handler now wraps create/update loop
in db.Transaction(). Partial failure rolls back.
M6: Cluster api_token no validation at save time
cluster/service.go Create/Update now validate JWT shape (starts
with 'ey', 3 dot-separated parts). Clear error message guides
operator to POST /api/v1/auth/login.
M7: Disk full — stale config warning not prominent
app/app.go soft-fail warnings changed to [WARNING] prefix + hint
to check listener configs.
M8: Telegram bot retries forever on 401
telegram/bot.go loop now detects 401/Unauthorized, switches to
5-minute retry interval (was 5s). Clears on success.
== Low (16) ==
L1: Scheduler/Bot Start() idempotent via sync.Once
L2: Container.Shutdown() method added
L3: Enforcer lastBlocked prunes deleted users
L4: traffic.Service first-call rate skipped (was bogus spike)
L5: strings.HasPrefix replaces path[:4]=="/api"
L6: ActiveCredentialsByListener batch-load (N+1→1 query)
L7: Telegram bot commands check DB errors
L8: Cluster health save error logged
L9: Collector continues on AddSample failure (was return)
L10: user.Create logs warning instead of returning error after
successful DB write
L11-L14: Documentation comments for known limitations (HTTP-01
goroutine, GlobalConfig thread-safety, credentialsChanged
thread-safety, SubToken TOCTOU)
L15: useAuth.ts calls fetchMe() to refresh mustChangePassword
L16: Verified access-tokens/client-access endpoints are NOT dead
Verification: go build/vet/test PASS, npm run build PASS, gofmt clean.
fix(all-remaining): 24 audit findings — Medium + Low severity
All remaining unfixed issues from 3-round exhaustive audit are now
resolved. 8 Medium + 16 Low = 24 items.
== Medium (8) ==
M1: GetLogs timestamp always time.Now()
mihomo/service.go GetLogs now parses RFC3339 prefix from stored log
lines, infers level (ERROR/WARN/INFO/DEBUG) from payload prefix.
M2: Cron parser silently ignores */N and range
telegram/scheduler.go cronFieldMatches now supports */N (step),
A-B (range), A-B/N (stepped range) in addition to * and comma.
M3: findExistingProcesses holds pm.mu during /proc scan
mihomo/process.go start() now calls findExistingProcesses() BEFORE
acquiring pm.mu. Logs/Status/Stop no longer blocked during scan.
M4: Cluster HealthCheckAll sequential (N*12s)
cluster/service.go now fans out goroutines with WaitGroup. Worst
case drops from N*12s to ~12s.
M5: Panel settings PUT not transactional
router/panel_settings.go PUT handler now wraps create/update loop
in db.Transaction(). Partial failure rolls back.
M6: Cluster api_token no validation at save time
cluster/service.go Create/Update now validate JWT shape (starts
with 'ey', 3 dot-separated parts). Clear error message guides
operator to POST /api/v1/auth/login.
M7: Disk full — stale config warning not prominent
app/app.go soft-fail warnings changed to [WARNING] prefix + hint
to check listener configs.
M8: Telegram bot retries forever on 401
telegram/bot.go loop now detects 401/Unauthorized, switches to
5-minute retry interval (was 5s). Clears on success.
== Low (16) ==
L1: Scheduler/Bot Start() idempotent via sync.Once
L2: Container.Shutdown() method added
L3: Enforcer lastBlocked prunes deleted users
L4: traffic.Service first-call rate skipped (was bogus spike)
L5: strings.HasPrefix replaces path[:4]=="/api"
L6: ActiveCredentialsByListener batch-load (N+1→1 query)
L7: Telegram bot commands check DB errors
L8: Cluster health save error logged
L9: Collector continues on AddSample failure (was return)
L10: user.Create logs warning instead of returning error after
successful DB write
L11-L14: Documentation comments for known limitations (HTTP-01
goroutine, GlobalConfig thread-safety, credentialsChanged
thread-safety, SubToken TOCTOU)
L15: useAuth.ts calls fetchMe() to refresh mustChangePassword
L16: Verified access-tokens/client-access endpoints are NOT dead
Verification: go build/vet/test PASS, npm run build PASS, gofmt clean.
fix(all-remaining): 24 audit findings — Medium + Low severity
All remaining unfixed issues from 3-round exhaustive audit are now
resolved. 8 Medium + 16 Low = 24 items.
== Medium (8) ==
M1: GetLogs timestamp always time.Now()
mihomo/service.go GetLogs now parses RFC3339 prefix from stored log
lines, infers level (ERROR/WARN/INFO/DEBUG) from payload prefix.
M2: Cron parser silently ignores */N and range
telegram/scheduler.go cronFieldMatches now supports */N (step),
A-B (range), A-B/N (stepped range) in addition to * and comma.
M3: findExistingProcesses holds pm.mu during /proc scan
mihomo/process.go start() now calls findExistingProcesses() BEFORE
acquiring pm.mu. Logs/Status/Stop no longer blocked during scan.
M4: Cluster HealthCheckAll sequential (N*12s)
cluster/service.go now fans out goroutines with WaitGroup. Worst
case drops from N*12s to ~12s.
M5: Panel settings PUT not transactional
router/panel_settings.go PUT handler now wraps create/update loop
in db.Transaction(). Partial failure rolls back.
M6: Cluster api_token no validation at save time
cluster/service.go Create/Update now validate JWT shape (starts
with 'ey', 3 dot-separated parts). Clear error message guides
operator to POST /api/v1/auth/login.
M7: Disk full — stale config warning not prominent
app/app.go soft-fail warnings changed to [WARNING] prefix + hint
to check listener configs.
M8: Telegram bot retries forever on 401
telegram/bot.go loop now detects 401/Unauthorized, switches to
5-minute retry interval (was 5s). Clears on success.
== Low (16) ==
L1: Scheduler/Bot Start() idempotent via sync.Once
L2: Container.Shutdown() method added
L3: Enforcer lastBlocked prunes deleted users
L4: traffic.Service first-call rate skipped (was bogus spike)
L5: strings.HasPrefix replaces path[:4]=="/api"
L6: ActiveCredentialsByListener batch-load (N+1→1 query)
L7: Telegram bot commands check DB errors
L8: Cluster health save error logged
L9: Collector continues on AddSample failure (was return)
L10: user.Create logs warning instead of returning error after
successful DB write
L11-L14: Documentation comments for known limitations (HTTP-01
goroutine, GlobalConfig thread-safety, credentialsChanged
thread-safety, SubToken TOCTOU)
L15: useAuth.ts calls fetchMe() to refresh mustChangePassword
L16: Verified access-tokens/client-access endpoints are NOT dead
Verification: go build/vet/test PASS, npm run build PASS, gofmt clean.
fix(all-remaining): 24 audit findings — Medium + Low severity
All remaining unfixed issues from 3-round exhaustive audit are now
resolved. 8 Medium + 16 Low = 24 items.
== Medium (8) ==
M1: GetLogs timestamp always time.Now()
mihomo/service.go GetLogs now parses RFC3339 prefix from stored log
lines, infers level (ERROR/WARN/INFO/DEBUG) from payload prefix.
M2: Cron parser silently ignores */N and range
telegram/scheduler.go cronFieldMatches now supports */N (step),
A-B (range), A-B/N (stepped range) in addition to * and comma.
M3: findExistingProcesses holds pm.mu during /proc scan
mihomo/process.go start() now calls findExistingProcesses() BEFORE
acquiring pm.mu. Logs/Status/Stop no longer blocked during scan.
M4: Cluster HealthCheckAll sequential (N*12s)
cluster/service.go now fans out goroutines with WaitGroup. Worst
case drops from N*12s to ~12s.
M5: Panel settings PUT not transactional
router/panel_settings.go PUT handler now wraps create/update loop
in db.Transaction(). Partial failure rolls back.
M6: Cluster api_token no validation at save time
cluster/service.go Create/Update now validate JWT shape (starts
with 'ey', 3 dot-separated parts). Clear error message guides
operator to POST /api/v1/auth/login.
M7: Disk full — stale config warning not prominent
app/app.go soft-fail warnings changed to [WARNING] prefix + hint
to check listener configs.
M8: Telegram bot retries forever on 401
telegram/bot.go loop now detects 401/Unauthorized, switches to
5-minute retry interval (was 5s). Clears on success.
== Low (16) ==
L1: Scheduler/Bot Start() idempotent via sync.Once
L2: Container.Shutdown() method added
L3: Enforcer lastBlocked prunes deleted users
L4: traffic.Service first-call rate skipped (was bogus spike)
L5: strings.HasPrefix replaces path[:4]=="/api"
L6: ActiveCredentialsByListener batch-load (N+1→1 query)
L7: Telegram bot commands check DB errors
L8: Cluster health save error logged
L9: Collector continues on AddSample failure (was return)
L10: user.Create logs warning instead of returning error after
successful DB write
L11-L14: Documentation comments for known limitations (HTTP-01
goroutine, GlobalConfig thread-safety, credentialsChanged
thread-safety, SubToken TOCTOU)
L15: useAuth.ts calls fetchMe() to refresh mustChangePassword
L16: Verified access-tokens/client-access endpoints are NOT dead
Verification: go build/vet/test PASS, npm run build PASS, gofmt clean.
fix(all-remaining): 24 audit findings — Medium + Low severity
All remaining unfixed issues from 3-round exhaustive audit are now
resolved. 8 Medium + 16 Low = 24 items.
== Medium (8) ==
M1: GetLogs timestamp always time.Now()
mihomo/service.go GetLogs now parses RFC3339 prefix from stored log
lines, infers level (ERROR/WARN/INFO/DEBUG) from payload prefix.
M2: Cron parser silently ignores */N and range
telegram/scheduler.go cronFieldMatches now supports */N (step),
A-B (range), A-B/N (stepped range) in addition to * and comma.
M3: findExistingProcesses holds pm.mu during /proc scan
mihomo/process.go start() now calls findExistingProcesses() BEFORE
acquiring pm.mu. Logs/Status/Stop no longer blocked during scan.
M4: Cluster HealthCheckAll sequential (N*12s)
cluster/service.go now fans out goroutines with WaitGroup. Worst
case drops from N*12s to ~12s.
M5: Panel settings PUT not transactional
router/panel_settings.go PUT handler now wraps create/update loop
in db.Transaction(). Partial failure rolls back.
M6: Cluster api_token no validation at save time
cluster/service.go Create/Update now validate JWT shape (starts
with 'ey', 3 dot-separated parts). Clear error message guides
operator to POST /api/v1/auth/login.
M7: Disk full — stale config warning not prominent
app/app.go soft-fail warnings changed to [WARNING] prefix + hint
to check listener configs.
M8: Telegram bot retries forever on 401
telegram/bot.go loop now detects 401/Unauthorized, switches to
5-minute retry interval (was 5s). Clears on success.
== Low (16) ==
L1: Scheduler/Bot Start() idempotent via sync.Once
L2: Container.Shutdown() method added
L3: Enforcer lastBlocked prunes deleted users
L4: traffic.Service first-call rate skipped (was bogus spike)
L5: strings.HasPrefix replaces path[:4]=="/api"
L6: ActiveCredentialsByListener batch-load (N+1→1 query)
L7: Telegram bot commands check DB errors
L8: Cluster health save error logged
L9: Collector continues on AddSample failure (was return)
L10: user.Create logs warning instead of returning error after
successful DB write
L11-L14: Documentation comments for known limitations (HTTP-01
goroutine, GlobalConfig thread-safety, credentialsChanged
thread-safety, SubToken TOCTOU)
L15: useAuth.ts calls fetchMe() to refresh mustChangePassword
L16: Verified access-tokens/client-access endpoints are NOT dead
Verification: go build/vet/test PASS, npm run build PASS, gofmt clean.
fix(all-remaining): 24 audit findings — Medium + Low severity
All remaining unfixed issues from 3-round exhaustive audit are now
resolved. 8 Medium + 16 Low = 24 items.
== Medium (8) ==
M1: GetLogs timestamp always time.Now()
mihomo/service.go GetLogs now parses RFC3339 prefix from stored log
lines, infers level (ERROR/WARN/INFO/DEBUG) from payload prefix.
M2: Cron parser silently ignores */N and range
telegram/scheduler.go cronFieldMatches now supports */N (step),
A-B (range), A-B/N (stepped range) in addition to * and comma.
M3: findExistingProcesses holds pm.mu during /proc scan
mihomo/process.go start() now calls findExistingProcesses() BEFORE
acquiring pm.mu. Logs/Status/Stop no longer blocked during scan.
M4: Cluster HealthCheckAll sequential (N*12s)
cluster/service.go now fans out goroutines with WaitGroup. Worst
case drops from N*12s to ~12s.
M5: Panel settings PUT not transactional
router/panel_settings.go PUT handler now wraps create/update loop
in db.Transaction(). Partial failure rolls back.
M6: Cluster api_token no validation at save time
cluster/service.go Create/Update now validate JWT shape (starts
with 'ey', 3 dot-separated parts). Clear error message guides
operator to POST /api/v1/auth/login.
M7: Disk full — stale config warning not prominent
app/app.go soft-fail warnings changed to [WARNING] prefix + hint
to check listener configs.
M8: Telegram bot retries forever on 401
telegram/bot.go loop now detects 401/Unauthorized, switches to
5-minute retry interval (was 5s). Clears on success.
== Low (16) ==
L1: Scheduler/Bot Start() idempotent via sync.Once
L2: Container.Shutdown() method added
L3: Enforcer lastBlocked prunes deleted users
L4: traffic.Service first-call rate skipped (was bogus spike)
L5: strings.HasPrefix replaces path[:4]=="/api"
L6: ActiveCredentialsByListener batch-load (N+1→1 query)
L7: Telegram bot commands check DB errors
L8: Cluster health save error logged
L9: Collector continues on AddSample failure (was return)
L10: user.Create logs warning instead of returning error after
successful DB write
L11-L14: Documentation comments for known limitations (HTTP-01
goroutine, GlobalConfig thread-safety, credentialsChanged
thread-safety, SubToken TOCTOU)
L15: useAuth.ts calls fetchMe() to refresh mustChangePassword
L16: Verified access-tokens/client-access endpoints are NOT dead
Verification: go build/vet/test PASS, npm run build PASS, gofmt clean.
2026-08-31 00:19:24 +00:00
Z User 76c81b8261 fix(audit-r3): traffic rule/chains/start fields, ProxyUser telegram fields
Round 3: frontend-backend API contract audit found 2 issues. Fixed.
F-1 (Medium): Traffic page rule column always empty
Frontend Traffic.tsx declares a 'rule' column on connections, but
backend ConnectionView (traffic/types.go) didn't include Rule/Chains/
Start fields. Mihomo's /connections API returns these (api/types.go
Connection struct has Rule, Chains, Start) but the collector dropped
them when building ConnectionView.
Fix: added Rule/Chains/Start to ConnectionView struct; collector now
populates them from conn.Rule/conn.Chains/conn.Start.
F-2 (Low): ProxyUser type missing telegram_id/telegram_name
Backend SafeUser returns telegram_id + telegram_name (added in
Telegram enhancement), but frontend ProxyUser interface didn't
declare them. Also had phantom created_at (backend never returns it).
Fix: replaced created_at with telegram_id + telegram_name in
frontend/src/api/users.ts ProxyUser interface.
Verification: go build/vet PASS, go test PASS, npm run build PASS.
fix(audit-r3): traffic rule/chains/start fields, ProxyUser telegram fields
Round 3: frontend-backend API contract audit found 2 issues. Fixed.
F-1 (Medium): Traffic page rule column always empty
Frontend Traffic.tsx declares a 'rule' column on connections, but
backend ConnectionView (traffic/types.go) didn't include Rule/Chains/
Start fields. Mihomo's /connections API returns these (api/types.go
Connection struct has Rule, Chains, Start) but the collector dropped
them when building ConnectionView.
Fix: added Rule/Chains/Start to ConnectionView struct; collector now
populates them from conn.Rule/conn.Chains/conn.Start.
F-2 (Low): ProxyUser type missing telegram_id/telegram_name
Backend SafeUser returns telegram_id + telegram_name (added in
Telegram enhancement), but frontend ProxyUser interface didn't
declare them. Also had phantom created_at (backend never returns it).
Fix: replaced created_at with telegram_id + telegram_name in
frontend/src/api/users.ts ProxyUser interface.
Verification: go build/vet PASS, go test PASS, npm run build PASS.
fix(audit-r3): traffic rule/chains/start fields, ProxyUser telegram fields
Round 3: frontend-backend API contract audit found 2 issues. Fixed.
F-1 (Medium): Traffic page rule column always empty
Frontend Traffic.tsx declares a 'rule' column on connections, but
backend ConnectionView (traffic/types.go) didn't include Rule/Chains/
Start fields. Mihomo's /connections API returns these (api/types.go
Connection struct has Rule, Chains, Start) but the collector dropped
them when building ConnectionView.
Fix: added Rule/Chains/Start to ConnectionView struct; collector now
populates them from conn.Rule/conn.Chains/conn.Start.
F-2 (Low): ProxyUser type missing telegram_id/telegram_name
Backend SafeUser returns telegram_id + telegram_name (added in
Telegram enhancement), but frontend ProxyUser interface didn't
declare them. Also had phantom created_at (backend never returns it).
Fix: replaced created_at with telegram_id + telegram_name in
frontend/src/api/users.ts ProxyUser interface.
Verification: go build/vet PASS, go test PASS, npm run build PASS.
fix(audit-r3): traffic rule/chains/start fields, ProxyUser telegram fields
Round 3: frontend-backend API contract audit found 2 issues. Fixed.
F-1 (Medium): Traffic page rule column always empty
Frontend Traffic.tsx declares a 'rule' column on connections, but
backend ConnectionView (traffic/types.go) didn't include Rule/Chains/
Start fields. Mihomo's /connections API returns these (api/types.go
Connection struct has Rule, Chains, Start) but the collector dropped
them when building ConnectionView.
Fix: added Rule/Chains/Start to ConnectionView struct; collector now
populates them from conn.Rule/conn.Chains/conn.Start.
F-2 (Low): ProxyUser type missing telegram_id/telegram_name
Backend SafeUser returns telegram_id + telegram_name (added in
Telegram enhancement), but frontend ProxyUser interface didn't
declare them. Also had phantom created_at (backend never returns it).
Fix: replaced created_at with telegram_id + telegram_name in
frontend/src/api/users.ts ProxyUser interface.
Verification: go build/vet PASS, go test PASS, npm run build PASS.
fix(audit-r3): traffic rule/chains/start fields, ProxyUser telegram fields
Round 3: frontend-backend API contract audit found 2 issues. Fixed.
F-1 (Medium): Traffic page rule column always empty
Frontend Traffic.tsx declares a 'rule' column on connections, but
backend ConnectionView (traffic/types.go) didn't include Rule/Chains/
Start fields. Mihomo's /connections API returns these (api/types.go
Connection struct has Rule, Chains, Start) but the collector dropped
them when building ConnectionView.
Fix: added Rule/Chains/Start to ConnectionView struct; collector now
populates them from conn.Rule/conn.Chains/conn.Start.
F-2 (Low): ProxyUser type missing telegram_id/telegram_name
Backend SafeUser returns telegram_id + telegram_name (added in
Telegram enhancement), but frontend ProxyUser interface didn't
declare them. Also had phantom created_at (backend never returns it).
Fix: replaced created_at with telegram_id + telegram_name in
frontend/src/api/users.ts ProxyUser interface.
Verification: go build/vet PASS, go test PASS, npm run build PASS.
fix(audit-r3): traffic rule/chains/start fields, ProxyUser telegram fields
Round 3: frontend-backend API contract audit found 2 issues. Fixed.
F-1 (Medium): Traffic page rule column always empty
Frontend Traffic.tsx declares a 'rule' column on connections, but
backend ConnectionView (traffic/types.go) didn't include Rule/Chains/
Start fields. Mihomo's /connections API returns these (api/types.go
Connection struct has Rule, Chains, Start) but the collector dropped
them when building ConnectionView.
Fix: added Rule/Chains/Start to ConnectionView struct; collector now
populates them from conn.Rule/conn.Chains/conn.Start.
F-2 (Low): ProxyUser type missing telegram_id/telegram_name
Backend SafeUser returns telegram_id + telegram_name (added in
Telegram enhancement), but frontend ProxyUser interface didn't
declare them. Also had phantom created_at (backend never returns it).
Fix: replaced created_at with telegram_id + telegram_name in
frontend/src/api/users.ts ProxyUser interface.
Verification: go build/vet PASS, go test PASS, npm run build PASS.
fix(audit-r3): traffic rule/chains/start fields, ProxyUser telegram fields
Round 3: frontend-backend API contract audit found 2 issues. Fixed.
F-1 (Medium): Traffic page rule column always empty
Frontend Traffic.tsx declares a 'rule' column on connections, but
backend ConnectionView (traffic/types.go) didn't include Rule/Chains/
Start fields. Mihomo's /connections API returns these (api/types.go
Connection struct has Rule, Chains, Start) but the collector dropped
them when building ConnectionView.
Fix: added Rule/Chains/Start to ConnectionView struct; collector now
populates them from conn.Rule/conn.Chains/conn.Start.
F-2 (Low): ProxyUser type missing telegram_id/telegram_name
Backend SafeUser returns telegram_id + telegram_name (added in
Telegram enhancement), but frontend ProxyUser interface didn't
declare them. Also had phantom created_at (backend never returns it).
Fix: replaced created_at with telegram_id + telegram_name in
frontend/src/api/users.ts ProxyUser interface.
Verification: go build/vet PASS, go test PASS, npm run build PASS.
fix(audit-r3): traffic rule/chains/start fields, ProxyUser telegram fields
Round 3: frontend-backend API contract audit found 2 issues. Fixed.
F-1 (Medium): Traffic page rule column always empty
Frontend Traffic.tsx declares a 'rule' column on connections, but
backend ConnectionView (traffic/types.go) didn't include Rule/Chains/
Start fields. Mihomo's /connections API returns these (api/types.go
Connection struct has Rule, Chains, Start) but the collector dropped
them when building ConnectionView.
Fix: added Rule/Chains/Start to ConnectionView struct; collector now
populates them from conn.Rule/conn.Chains/conn.Start.
F-2 (Low): ProxyUser type missing telegram_id/telegram_name
Backend SafeUser returns telegram_id + telegram_name (added in
Telegram enhancement), but frontend ProxyUser interface didn't
declare them. Also had phantom created_at (backend never returns it).
Fix: replaced created_at with telegram_id + telegram_name in
frontend/src/api/users.ts ProxyUser interface.
Verification: go build/vet PASS, go test PASS, npm run build PASS.
fix(audit-r3): traffic rule/chains/start fields, ProxyUser telegram fields
Round 3: frontend-backend API contract audit found 2 issues. Fixed.
F-1 (Medium): Traffic page rule column always empty
Frontend Traffic.tsx declares a 'rule' column on connections, but
backend ConnectionView (traffic/types.go) didn't include Rule/Chains/
Start fields. Mihomo's /connections API returns these (api/types.go
Connection struct has Rule, Chains, Start) but the collector dropped
them when building ConnectionView.
Fix: added Rule/Chains/Start to ConnectionView struct; collector now
populates them from conn.Rule/conn.Chains/conn.Start.
F-2 (Low): ProxyUser type missing telegram_id/telegram_name
Backend SafeUser returns telegram_id + telegram_name (added in
Telegram enhancement), but frontend ProxyUser interface didn't
declare them. Also had phantom created_at (backend never returns it).
Fix: replaced created_at with telegram_id + telegram_name in
frontend/src/api/users.ts ProxyUser interface.
Verification: go build/vet PASS, go test PASS, npm run build PASS.
fix(audit-r3): traffic rule/chains/start fields, ProxyUser telegram fields
Round 3: frontend-backend API contract audit found 2 issues. Fixed.
F-1 (Medium): Traffic page rule column always empty
Frontend Traffic.tsx declares a 'rule' column on connections, but
backend ConnectionView (traffic/types.go) didn't include Rule/Chains/
Start fields. Mihomo's /connections API returns these (api/types.go
Connection struct has Rule, Chains, Start) but the collector dropped
them when building ConnectionView.
Fix: added Rule/Chains/Start to ConnectionView struct; collector now
populates them from conn.Rule/conn.Chains/conn.Start.
F-2 (Low): ProxyUser type missing telegram_id/telegram_name
Backend SafeUser returns telegram_id + telegram_name (added in
Telegram enhancement), but frontend ProxyUser interface didn't
declare them. Also had phantom created_at (backend never returns it).
Fix: replaced created_at with telegram_id + telegram_name in
frontend/src/api/users.ts ProxyUser interface.
Verification: go build/vet PASS, go test PASS, npm run build PASS.
fix(audit-r3): traffic rule/chains/start fields, ProxyUser telegram fields
Round 3: frontend-backend API contract audit found 2 issues. Fixed.
F-1 (Medium): Traffic page rule column always empty
Frontend Traffic.tsx declares a 'rule' column on connections, but
backend ConnectionView (traffic/types.go) didn't include Rule/Chains/
Start fields. Mihomo's /connections API returns these (api/types.go
Connection struct has Rule, Chains, Start) but the collector dropped
them when building ConnectionView.
Fix: added Rule/Chains/Start to ConnectionView struct; collector now
populates them from conn.Rule/conn.Chains/conn.Start.
F-2 (Low): ProxyUser type missing telegram_id/telegram_name
Backend SafeUser returns telegram_id + telegram_name (added in
Telegram enhancement), but frontend ProxyUser interface didn't
declare them. Also had phantom created_at (backend never returns it).
Fix: replaced created_at with telegram_id + telegram_name in
frontend/src/api/users.ts ProxyUser interface.
Verification: go build/vet PASS, go test PASS, npm run build PASS.
fix(audit-r3): traffic rule/chains/start fields, ProxyUser telegram fields
Round 3: frontend-backend API contract audit found 2 issues. Fixed.
F-1 (Medium): Traffic page rule column always empty
Frontend Traffic.tsx declares a 'rule' column on connections, but
backend ConnectionView (traffic/types.go) didn't include Rule/Chains/
Start fields. Mihomo's /connections API returns these (api/types.go
Connection struct has Rule, Chains, Start) but the collector dropped
them when building ConnectionView.
Fix: added Rule/Chains/Start to ConnectionView struct; collector now
populates them from conn.Rule/conn.Chains/conn.Start.
F-2 (Low): ProxyUser type missing telegram_id/telegram_name
Backend SafeUser returns telegram_id + telegram_name (added in
Telegram enhancement), but frontend ProxyUser interface didn't
declare them. Also had phantom created_at (backend never returns it).
Fix: replaced created_at with telegram_id + telegram_name in
frontend/src/api/users.ts ProxyUser interface.
Verification: go build/vet PASS, go test PASS, npm run build PASS.
2026-08-30 15:35:32 +00:00
Z User 7addcdc5c7 fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup
Non-listener comprehensive audit found 4 Medium + 4 Low issues.
All fixed. VLESS+REALITY and listener schema untouched.
== Medium ==
M-NEW-C1: Enforcement doesn't retry failed config regen
traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE
calling RegenerateConfig. If regen failed, lastBlocked was already
set, so next tick saw no change and never retried — over-quota
users stayed connected. Fix: update lastBlocked only after
successful regen; preserve old state on failure for retry.
M-NEW-C2: DB restore orphans running process's SQLite handle
system/api.go RestoreDatabase renames the DB file, but the running
panel keeps writing to the old (unlinked) inode. Added prominent
[WARNING] log + restart_required:true + *** RESTART REQUIRED ***
message in response. In-place GORM reconnect not feasible; restart
is the correct approach.
M-NEW-G1: Backend doesn't send CSP/security HTTP headers
CSP was only in index.html meta tag. Added SecurityHeaders()
middleware setting X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Referrer-Policy: same-origin,
X-XSS-Protection: 1; mode=block on all responses. CSP NOT set
on API responses (meta CSP handles the SPA shell).
M-NEW-A1: Default config.yaml has placeholder secrets
backend/config/config.yaml contained DefaultJWTSecret and
DefaultCredentialKey (rejected by Validate → startup failure for
developers). Replaced with CHANGE_ME placeholders + comments
explaining use install.sh or openssl rand -hex 32.
== Low ==
L-NEW-B1: isSensitiveSettingKey heuristic gaps
panel_settings.go blocklist missing 'key' and 'auth' substrings.
Added (api deliberately excluded — too broad).
L-NEW-D1: enabled_events UI has dead options
Settings.tsx Select included node_down/node_up/block/unblock which
have no backend effect. Removed. Kept only implemented events:
login/cpu/memory/crash/expiry/traffic.
L-NEW-D3: Bot loop hardcodes api.telegram.org
bot.go getUpdates/deleteWebhook hardcoded the official Telegram API
URL. Now takes apiBase parameter; loop() passes tgClient.apiBase()
so custom api_server setting is respected for long-polling.
L-NEW-H1: update.sh duplicate CONFIG_DIR
Line 11-12 had CONFIG_DIR defined twice. Removed duplicate.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- npm run build : PASS
- gofmt : clean
- sh -n update.sh : OK
fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup
Non-listener comprehensive audit found 4 Medium + 4 Low issues.
All fixed. VLESS+REALITY and listener schema untouched.
== Medium ==
M-NEW-C1: Enforcement doesn't retry failed config regen
traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE
calling RegenerateConfig. If regen failed, lastBlocked was already
set, so next tick saw no change and never retried — over-quota
users stayed connected. Fix: update lastBlocked only after
successful regen; preserve old state on failure for retry.
M-NEW-C2: DB restore orphans running process's SQLite handle
system/api.go RestoreDatabase renames the DB file, but the running
panel keeps writing to the old (unlinked) inode. Added prominent
[WARNING] log + restart_required:true + *** RESTART REQUIRED ***
message in response. In-place GORM reconnect not feasible; restart
is the correct approach.
M-NEW-G1: Backend doesn't send CSP/security HTTP headers
CSP was only in index.html meta tag. Added SecurityHeaders()
middleware setting X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Referrer-Policy: same-origin,
X-XSS-Protection: 1; mode=block on all responses. CSP NOT set
on API responses (meta CSP handles the SPA shell).
M-NEW-A1: Default config.yaml has placeholder secrets
backend/config/config.yaml contained DefaultJWTSecret and
DefaultCredentialKey (rejected by Validate → startup failure for
developers). Replaced with CHANGE_ME placeholders + comments
explaining use install.sh or openssl rand -hex 32.
== Low ==
L-NEW-B1: isSensitiveSettingKey heuristic gaps
panel_settings.go blocklist missing 'key' and 'auth' substrings.
Added (api deliberately excluded — too broad).
L-NEW-D1: enabled_events UI has dead options
Settings.tsx Select included node_down/node_up/block/unblock which
have no backend effect. Removed. Kept only implemented events:
login/cpu/memory/crash/expiry/traffic.
L-NEW-D3: Bot loop hardcodes api.telegram.org
bot.go getUpdates/deleteWebhook hardcoded the official Telegram API
URL. Now takes apiBase parameter; loop() passes tgClient.apiBase()
so custom api_server setting is respected for long-polling.
L-NEW-H1: update.sh duplicate CONFIG_DIR
Line 11-12 had CONFIG_DIR defined twice. Removed duplicate.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- npm run build : PASS
- gofmt : clean
- sh -n update.sh : OK
fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup
Non-listener comprehensive audit found 4 Medium + 4 Low issues.
All fixed. VLESS+REALITY and listener schema untouched.
== Medium ==
M-NEW-C1: Enforcement doesn't retry failed config regen
traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE
calling RegenerateConfig. If regen failed, lastBlocked was already
set, so next tick saw no change and never retried — over-quota
users stayed connected. Fix: update lastBlocked only after
successful regen; preserve old state on failure for retry.
M-NEW-C2: DB restore orphans running process's SQLite handle
system/api.go RestoreDatabase renames the DB file, but the running
panel keeps writing to the old (unlinked) inode. Added prominent
[WARNING] log + restart_required:true + *** RESTART REQUIRED ***
message in response. In-place GORM reconnect not feasible; restart
is the correct approach.
M-NEW-G1: Backend doesn't send CSP/security HTTP headers
CSP was only in index.html meta tag. Added SecurityHeaders()
middleware setting X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Referrer-Policy: same-origin,
X-XSS-Protection: 1; mode=block on all responses. CSP NOT set
on API responses (meta CSP handles the SPA shell).
M-NEW-A1: Default config.yaml has placeholder secrets
backend/config/config.yaml contained DefaultJWTSecret and
DefaultCredentialKey (rejected by Validate → startup failure for
developers). Replaced with CHANGE_ME placeholders + comments
explaining use install.sh or openssl rand -hex 32.
== Low ==
L-NEW-B1: isSensitiveSettingKey heuristic gaps
panel_settings.go blocklist missing 'key' and 'auth' substrings.
Added (api deliberately excluded — too broad).
L-NEW-D1: enabled_events UI has dead options
Settings.tsx Select included node_down/node_up/block/unblock which
have no backend effect. Removed. Kept only implemented events:
login/cpu/memory/crash/expiry/traffic.
L-NEW-D3: Bot loop hardcodes api.telegram.org
bot.go getUpdates/deleteWebhook hardcoded the official Telegram API
URL. Now takes apiBase parameter; loop() passes tgClient.apiBase()
so custom api_server setting is respected for long-polling.
L-NEW-H1: update.sh duplicate CONFIG_DIR
Line 11-12 had CONFIG_DIR defined twice. Removed duplicate.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- npm run build : PASS
- gofmt : clean
- sh -n update.sh : OK
fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup
Non-listener comprehensive audit found 4 Medium + 4 Low issues.
All fixed. VLESS+REALITY and listener schema untouched.
== Medium ==
M-NEW-C1: Enforcement doesn't retry failed config regen
traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE
calling RegenerateConfig. If regen failed, lastBlocked was already
set, so next tick saw no change and never retried — over-quota
users stayed connected. Fix: update lastBlocked only after
successful regen; preserve old state on failure for retry.
M-NEW-C2: DB restore orphans running process's SQLite handle
system/api.go RestoreDatabase renames the DB file, but the running
panel keeps writing to the old (unlinked) inode. Added prominent
[WARNING] log + restart_required:true + *** RESTART REQUIRED ***
message in response. In-place GORM reconnect not feasible; restart
is the correct approach.
M-NEW-G1: Backend doesn't send CSP/security HTTP headers
CSP was only in index.html meta tag. Added SecurityHeaders()
middleware setting X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Referrer-Policy: same-origin,
X-XSS-Protection: 1; mode=block on all responses. CSP NOT set
on API responses (meta CSP handles the SPA shell).
M-NEW-A1: Default config.yaml has placeholder secrets
backend/config/config.yaml contained DefaultJWTSecret and
DefaultCredentialKey (rejected by Validate → startup failure for
developers). Replaced with CHANGE_ME placeholders + comments
explaining use install.sh or openssl rand -hex 32.
== Low ==
L-NEW-B1: isSensitiveSettingKey heuristic gaps
panel_settings.go blocklist missing 'key' and 'auth' substrings.
Added (api deliberately excluded — too broad).
L-NEW-D1: enabled_events UI has dead options
Settings.tsx Select included node_down/node_up/block/unblock which
have no backend effect. Removed. Kept only implemented events:
login/cpu/memory/crash/expiry/traffic.
L-NEW-D3: Bot loop hardcodes api.telegram.org
bot.go getUpdates/deleteWebhook hardcoded the official Telegram API
URL. Now takes apiBase parameter; loop() passes tgClient.apiBase()
so custom api_server setting is respected for long-polling.
L-NEW-H1: update.sh duplicate CONFIG_DIR
Line 11-12 had CONFIG_DIR defined twice. Removed duplicate.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- npm run build : PASS
- gofmt : clean
- sh -n update.sh : OK
fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup
Non-listener comprehensive audit found 4 Medium + 4 Low issues.
All fixed. VLESS+REALITY and listener schema untouched.
== Medium ==
M-NEW-C1: Enforcement doesn't retry failed config regen
traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE
calling RegenerateConfig. If regen failed, lastBlocked was already
set, so next tick saw no change and never retried — over-quota
users stayed connected. Fix: update lastBlocked only after
successful regen; preserve old state on failure for retry.
M-NEW-C2: DB restore orphans running process's SQLite handle
system/api.go RestoreDatabase renames the DB file, but the running
panel keeps writing to the old (unlinked) inode. Added prominent
[WARNING] log + restart_required:true + *** RESTART REQUIRED ***
message in response. In-place GORM reconnect not feasible; restart
is the correct approach.
M-NEW-G1: Backend doesn't send CSP/security HTTP headers
CSP was only in index.html meta tag. Added SecurityHeaders()
middleware setting X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Referrer-Policy: same-origin,
X-XSS-Protection: 1; mode=block on all responses. CSP NOT set
on API responses (meta CSP handles the SPA shell).
M-NEW-A1: Default config.yaml has placeholder secrets
backend/config/config.yaml contained DefaultJWTSecret and
DefaultCredentialKey (rejected by Validate → startup failure for
developers). Replaced with CHANGE_ME placeholders + comments
explaining use install.sh or openssl rand -hex 32.
== Low ==
L-NEW-B1: isSensitiveSettingKey heuristic gaps
panel_settings.go blocklist missing 'key' and 'auth' substrings.
Added (api deliberately excluded — too broad).
L-NEW-D1: enabled_events UI has dead options
Settings.tsx Select included node_down/node_up/block/unblock which
have no backend effect. Removed. Kept only implemented events:
login/cpu/memory/crash/expiry/traffic.
L-NEW-D3: Bot loop hardcodes api.telegram.org
bot.go getUpdates/deleteWebhook hardcoded the official Telegram API
URL. Now takes apiBase parameter; loop() passes tgClient.apiBase()
so custom api_server setting is respected for long-polling.
L-NEW-H1: update.sh duplicate CONFIG_DIR
Line 11-12 had CONFIG_DIR defined twice. Removed duplicate.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- npm run build : PASS
- gofmt : clean
- sh -n update.sh : OK
fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup
Non-listener comprehensive audit found 4 Medium + 4 Low issues.
All fixed. VLESS+REALITY and listener schema untouched.
== Medium ==
M-NEW-C1: Enforcement doesn't retry failed config regen
traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE
calling RegenerateConfig. If regen failed, lastBlocked was already
set, so next tick saw no change and never retried — over-quota
users stayed connected. Fix: update lastBlocked only after
successful regen; preserve old state on failure for retry.
M-NEW-C2: DB restore orphans running process's SQLite handle
system/api.go RestoreDatabase renames the DB file, but the running
panel keeps writing to the old (unlinked) inode. Added prominent
[WARNING] log + restart_required:true + *** RESTART REQUIRED ***
message in response. In-place GORM reconnect not feasible; restart
is the correct approach.
M-NEW-G1: Backend doesn't send CSP/security HTTP headers
CSP was only in index.html meta tag. Added SecurityHeaders()
middleware setting X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Referrer-Policy: same-origin,
X-XSS-Protection: 1; mode=block on all responses. CSP NOT set
on API responses (meta CSP handles the SPA shell).
M-NEW-A1: Default config.yaml has placeholder secrets
backend/config/config.yaml contained DefaultJWTSecret and
DefaultCredentialKey (rejected by Validate → startup failure for
developers). Replaced with CHANGE_ME placeholders + comments
explaining use install.sh or openssl rand -hex 32.
== Low ==
L-NEW-B1: isSensitiveSettingKey heuristic gaps
panel_settings.go blocklist missing 'key' and 'auth' substrings.
Added (api deliberately excluded — too broad).
L-NEW-D1: enabled_events UI has dead options
Settings.tsx Select included node_down/node_up/block/unblock which
have no backend effect. Removed. Kept only implemented events:
login/cpu/memory/crash/expiry/traffic.
L-NEW-D3: Bot loop hardcodes api.telegram.org
bot.go getUpdates/deleteWebhook hardcoded the official Telegram API
URL. Now takes apiBase parameter; loop() passes tgClient.apiBase()
so custom api_server setting is respected for long-polling.
L-NEW-H1: update.sh duplicate CONFIG_DIR
Line 11-12 had CONFIG_DIR defined twice. Removed duplicate.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- npm run build : PASS
- gofmt : clean
- sh -n update.sh : OK
fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup
Non-listener comprehensive audit found 4 Medium + 4 Low issues.
All fixed. VLESS+REALITY and listener schema untouched.
== Medium ==
M-NEW-C1: Enforcement doesn't retry failed config regen
traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE
calling RegenerateConfig. If regen failed, lastBlocked was already
set, so next tick saw no change and never retried — over-quota
users stayed connected. Fix: update lastBlocked only after
successful regen; preserve old state on failure for retry.
M-NEW-C2: DB restore orphans running process's SQLite handle
system/api.go RestoreDatabase renames the DB file, but the running
panel keeps writing to the old (unlinked) inode. Added prominent
[WARNING] log + restart_required:true + *** RESTART REQUIRED ***
message in response. In-place GORM reconnect not feasible; restart
is the correct approach.
M-NEW-G1: Backend doesn't send CSP/security HTTP headers
CSP was only in index.html meta tag. Added SecurityHeaders()
middleware setting X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Referrer-Policy: same-origin,
X-XSS-Protection: 1; mode=block on all responses. CSP NOT set
on API responses (meta CSP handles the SPA shell).
M-NEW-A1: Default config.yaml has placeholder secrets
backend/config/config.yaml contained DefaultJWTSecret and
DefaultCredentialKey (rejected by Validate → startup failure for
developers). Replaced with CHANGE_ME placeholders + comments
explaining use install.sh or openssl rand -hex 32.
== Low ==
L-NEW-B1: isSensitiveSettingKey heuristic gaps
panel_settings.go blocklist missing 'key' and 'auth' substrings.
Added (api deliberately excluded — too broad).
L-NEW-D1: enabled_events UI has dead options
Settings.tsx Select included node_down/node_up/block/unblock which
have no backend effect. Removed. Kept only implemented events:
login/cpu/memory/crash/expiry/traffic.
L-NEW-D3: Bot loop hardcodes api.telegram.org
bot.go getUpdates/deleteWebhook hardcoded the official Telegram API
URL. Now takes apiBase parameter; loop() passes tgClient.apiBase()
so custom api_server setting is respected for long-polling.
L-NEW-H1: update.sh duplicate CONFIG_DIR
Line 11-12 had CONFIG_DIR defined twice. Removed duplicate.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- npm run build : PASS
- gofmt : clean
- sh -n update.sh : OK
fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup
Non-listener comprehensive audit found 4 Medium + 4 Low issues.
All fixed. VLESS+REALITY and listener schema untouched.
== Medium ==
M-NEW-C1: Enforcement doesn't retry failed config regen
traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE
calling RegenerateConfig. If regen failed, lastBlocked was already
set, so next tick saw no change and never retried — over-quota
users stayed connected. Fix: update lastBlocked only after
successful regen; preserve old state on failure for retry.
M-NEW-C2: DB restore orphans running process's SQLite handle
system/api.go RestoreDatabase renames the DB file, but the running
panel keeps writing to the old (unlinked) inode. Added prominent
[WARNING] log + restart_required:true + *** RESTART REQUIRED ***
message in response. In-place GORM reconnect not feasible; restart
is the correct approach.
M-NEW-G1: Backend doesn't send CSP/security HTTP headers
CSP was only in index.html meta tag. Added SecurityHeaders()
middleware setting X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Referrer-Policy: same-origin,
X-XSS-Protection: 1; mode=block on all responses. CSP NOT set
on API responses (meta CSP handles the SPA shell).
M-NEW-A1: Default config.yaml has placeholder secrets
backend/config/config.yaml contained DefaultJWTSecret and
DefaultCredentialKey (rejected by Validate → startup failure for
developers). Replaced with CHANGE_ME placeholders + comments
explaining use install.sh or openssl rand -hex 32.
== Low ==
L-NEW-B1: isSensitiveSettingKey heuristic gaps
panel_settings.go blocklist missing 'key' and 'auth' substrings.
Added (api deliberately excluded — too broad).
L-NEW-D1: enabled_events UI has dead options
Settings.tsx Select included node_down/node_up/block/unblock which
have no backend effect. Removed. Kept only implemented events:
login/cpu/memory/crash/expiry/traffic.
L-NEW-D3: Bot loop hardcodes api.telegram.org
bot.go getUpdates/deleteWebhook hardcoded the official Telegram API
URL. Now takes apiBase parameter; loop() passes tgClient.apiBase()
so custom api_server setting is respected for long-polling.
L-NEW-H1: update.sh duplicate CONFIG_DIR
Line 11-12 had CONFIG_DIR defined twice. Removed duplicate.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- npm run build : PASS
- gofmt : clean
- sh -n update.sh : OK
fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup
Non-listener comprehensive audit found 4 Medium + 4 Low issues.
All fixed. VLESS+REALITY and listener schema untouched.
== Medium ==
M-NEW-C1: Enforcement doesn't retry failed config regen
traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE
calling RegenerateConfig. If regen failed, lastBlocked was already
set, so next tick saw no change and never retried — over-quota
users stayed connected. Fix: update lastBlocked only after
successful regen; preserve old state on failure for retry.
M-NEW-C2: DB restore orphans running process's SQLite handle
system/api.go RestoreDatabase renames the DB file, but the running
panel keeps writing to the old (unlinked) inode. Added prominent
[WARNING] log + restart_required:true + *** RESTART REQUIRED ***
message in response. In-place GORM reconnect not feasible; restart
is the correct approach.
M-NEW-G1: Backend doesn't send CSP/security HTTP headers
CSP was only in index.html meta tag. Added SecurityHeaders()
middleware setting X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Referrer-Policy: same-origin,
X-XSS-Protection: 1; mode=block on all responses. CSP NOT set
on API responses (meta CSP handles the SPA shell).
M-NEW-A1: Default config.yaml has placeholder secrets
backend/config/config.yaml contained DefaultJWTSecret and
DefaultCredentialKey (rejected by Validate → startup failure for
developers). Replaced with CHANGE_ME placeholders + comments
explaining use install.sh or openssl rand -hex 32.
== Low ==
L-NEW-B1: isSensitiveSettingKey heuristic gaps
panel_settings.go blocklist missing 'key' and 'auth' substrings.
Added (api deliberately excluded — too broad).
L-NEW-D1: enabled_events UI has dead options
Settings.tsx Select included node_down/node_up/block/unblock which
have no backend effect. Removed. Kept only implemented events:
login/cpu/memory/crash/expiry/traffic.
L-NEW-D3: Bot loop hardcodes api.telegram.org
bot.go getUpdates/deleteWebhook hardcoded the official Telegram API
URL. Now takes apiBase parameter; loop() passes tgClient.apiBase()
so custom api_server setting is respected for long-polling.
L-NEW-H1: update.sh duplicate CONFIG_DIR
Line 11-12 had CONFIG_DIR defined twice. Removed duplicate.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- npm run build : PASS
- gofmt : clean
- sh -n update.sh : OK
fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup
Non-listener comprehensive audit found 4 Medium + 4 Low issues.
All fixed. VLESS+REALITY and listener schema untouched.
== Medium ==
M-NEW-C1: Enforcement doesn't retry failed config regen
traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE
calling RegenerateConfig. If regen failed, lastBlocked was already
set, so next tick saw no change and never retried — over-quota
users stayed connected. Fix: update lastBlocked only after
successful regen; preserve old state on failure for retry.
M-NEW-C2: DB restore orphans running process's SQLite handle
system/api.go RestoreDatabase renames the DB file, but the running
panel keeps writing to the old (unlinked) inode. Added prominent
[WARNING] log + restart_required:true + *** RESTART REQUIRED ***
message in response. In-place GORM reconnect not feasible; restart
is the correct approach.
M-NEW-G1: Backend doesn't send CSP/security HTTP headers
CSP was only in index.html meta tag. Added SecurityHeaders()
middleware setting X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Referrer-Policy: same-origin,
X-XSS-Protection: 1; mode=block on all responses. CSP NOT set
on API responses (meta CSP handles the SPA shell).
M-NEW-A1: Default config.yaml has placeholder secrets
backend/config/config.yaml contained DefaultJWTSecret and
DefaultCredentialKey (rejected by Validate → startup failure for
developers). Replaced with CHANGE_ME placeholders + comments
explaining use install.sh or openssl rand -hex 32.
== Low ==
L-NEW-B1: isSensitiveSettingKey heuristic gaps
panel_settings.go blocklist missing 'key' and 'auth' substrings.
Added (api deliberately excluded — too broad).
L-NEW-D1: enabled_events UI has dead options
Settings.tsx Select included node_down/node_up/block/unblock which
have no backend effect. Removed. Kept only implemented events:
login/cpu/memory/crash/expiry/traffic.
L-NEW-D3: Bot loop hardcodes api.telegram.org
bot.go getUpdates/deleteWebhook hardcoded the official Telegram API
URL. Now takes apiBase parameter; loop() passes tgClient.apiBase()
so custom api_server setting is respected for long-polling.
L-NEW-H1: update.sh duplicate CONFIG_DIR
Line 11-12 had CONFIG_DIR defined twice. Removed duplicate.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- npm run build : PASS
- gofmt : clean
- sh -n update.sh : OK
fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup
Non-listener comprehensive audit found 4 Medium + 4 Low issues.
All fixed. VLESS+REALITY and listener schema untouched.
== Medium ==
M-NEW-C1: Enforcement doesn't retry failed config regen
traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE
calling RegenerateConfig. If regen failed, lastBlocked was already
set, so next tick saw no change and never retried — over-quota
users stayed connected. Fix: update lastBlocked only after
successful regen; preserve old state on failure for retry.
M-NEW-C2: DB restore orphans running process's SQLite handle
system/api.go RestoreDatabase renames the DB file, but the running
panel keeps writing to the old (unlinked) inode. Added prominent
[WARNING] log + restart_required:true + *** RESTART REQUIRED ***
message in response. In-place GORM reconnect not feasible; restart
is the correct approach.
M-NEW-G1: Backend doesn't send CSP/security HTTP headers
CSP was only in index.html meta tag. Added SecurityHeaders()
middleware setting X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Referrer-Policy: same-origin,
X-XSS-Protection: 1; mode=block on all responses. CSP NOT set
on API responses (meta CSP handles the SPA shell).
M-NEW-A1: Default config.yaml has placeholder secrets
backend/config/config.yaml contained DefaultJWTSecret and
DefaultCredentialKey (rejected by Validate → startup failure for
developers). Replaced with CHANGE_ME placeholders + comments
explaining use install.sh or openssl rand -hex 32.
== Low ==
L-NEW-B1: isSensitiveSettingKey heuristic gaps
panel_settings.go blocklist missing 'key' and 'auth' substrings.
Added (api deliberately excluded — too broad).
L-NEW-D1: enabled_events UI has dead options
Settings.tsx Select included node_down/node_up/block/unblock which
have no backend effect. Removed. Kept only implemented events:
login/cpu/memory/crash/expiry/traffic.
L-NEW-D3: Bot loop hardcodes api.telegram.org
bot.go getUpdates/deleteWebhook hardcoded the official Telegram API
URL. Now takes apiBase parameter; loop() passes tgClient.apiBase()
so custom api_server setting is respected for long-polling.
L-NEW-H1: update.sh duplicate CONFIG_DIR
Line 11-12 had CONFIG_DIR defined twice. Removed duplicate.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- npm run build : PASS
- gofmt : clean
- sh -n update.sh : OK
fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup
Non-listener comprehensive audit found 4 Medium + 4 Low issues.
All fixed. VLESS+REALITY and listener schema untouched.
== Medium ==
M-NEW-C1: Enforcement doesn't retry failed config regen
traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE
calling RegenerateConfig. If regen failed, lastBlocked was already
set, so next tick saw no change and never retried — over-quota
users stayed connected. Fix: update lastBlocked only after
successful regen; preserve old state on failure for retry.
M-NEW-C2: DB restore orphans running process's SQLite handle
system/api.go RestoreDatabase renames the DB file, but the running
panel keeps writing to the old (unlinked) inode. Added prominent
[WARNING] log + restart_required:true + *** RESTART REQUIRED ***
message in response. In-place GORM reconnect not feasible; restart
is the correct approach.
M-NEW-G1: Backend doesn't send CSP/security HTTP headers
CSP was only in index.html meta tag. Added SecurityHeaders()
middleware setting X-Content-Type-Options: nosniff,
X-Frame-Options: DENY, Referrer-Policy: same-origin,
X-XSS-Protection: 1; mode=block on all responses. CSP NOT set
on API responses (meta CSP handles the SPA shell).
M-NEW-A1: Default config.yaml has placeholder secrets
backend/config/config.yaml contained DefaultJWTSecret and
DefaultCredentialKey (rejected by Validate → startup failure for
developers). Replaced with CHANGE_ME placeholders + comments
explaining use install.sh or openssl rand -hex 32.
== Low ==
L-NEW-B1: isSensitiveSettingKey heuristic gaps
panel_settings.go blocklist missing 'key' and 'auth' substrings.
Added (api deliberately excluded — too broad).
L-NEW-D1: enabled_events UI has dead options
Settings.tsx Select included node_down/node_up/block/unblock which
have no backend effect. Removed. Kept only implemented events:
login/cpu/memory/crash/expiry/traffic.
L-NEW-D3: Bot loop hardcodes api.telegram.org
bot.go getUpdates/deleteWebhook hardcoded the official Telegram API
URL. Now takes apiBase parameter; loop() passes tgClient.apiBase()
so custom api_server setting is respected for long-polling.
L-NEW-H1: update.sh duplicate CONFIG_DIR
Line 11-12 had CONFIG_DIR defined twice. Removed duplicate.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 16 packages all PASS
- npm run build : PASS
- gofmt : clean
- sh -n update.sh : OK
2026-08-30 11:13:08 +00:00
Z User e445bcd323 fix(audit): P0+P1 — ShadowQUIC defaults, Snell wrappers, SS plugin, VMess encryption, TUIC/Hy2 fields, subscription error leak, frontend forms
Comprehensive re-audit against official Mihomo docs (Meta-Docs-main)
found 3 High + 9 Medium issues. All fixed in this commit.
== P0 High ==
P0-1: ShadowQUIC client defaults violated official docs
converter/client.go unconditionally defaulted quic-versions=[v2],
congestion-controller=bbr, zero-rtt=true. Official defaults are
v1/cubic/false. Removed defaults; fields now pass through only when
present in listener config.
P0-2: Snell listener wrappers not converted to client obfs-opts
Snell+shadow-tls/res-tls/jls-config produced client YAML with only
psk+version. Added applySnellObfsOpts that maps listener wrappers to
client obfs-opts: {mode: shadow-tls/restls/jls, host, password,
version, username, version-hint, restls-script} per proxies/snell.md.
P0-3: SS simple-obfs plugin name mismatch
Emitted plugin: 'simple-obfs' but official docs say plugin: 'obfs'.
Fixed.
== P1 Medium ==
P1-4: VMess encryption field leaked
copyOption(encryption) called for both vmess and vless. Only VLESS
has encryption. Wrapped in if protocol=='vless'.
P1-5: TUIC client missing sni + skip-cert-verify
Added to both v4 (token) and v5 (users) branches.
P1-6: TUIC URI snake_case → kebab-case
congestion_control→congestion-controller, bbr_profile→bbr-profile,
udp_relay_mode→udp-relay-mode, max_udp_relay_packet_size→
max-udp-relay-packet-size.
P1-7: Hysteria2 client missing handshake-timeout + max-idle-time
Added to copyOption list.
P1-8: Subscription endpoint error leak
Replaced err.Error() with generic 'subscription generation failed'
at 4 public endpoints (lines 82/107/122/368). Added server-side
log.Printf for diagnostics.
P1-9: Snell form missing v4/v5
Version dropdown [1,2,3]→[1,2,3,4,5]; initialValue 3→4 (matches
backend autofill default).
P1-10: Sudoku form missing httpmask + custom-table + fallback
Added EnableSection for httpmask (disable/mode/path-root) +
custom-table/custom-tables/fallback Form.Items. Wired through
configToFormValues + formValuesToConfig + FORM_OWNED_KEYS.
P1-11: ShadowQUIC form missing 4 fields
Added max-datagram-frame-size, recv-window-conn, recv-window,
disable-mtu-discovery.
P1-12: Unconditional support-x25519mlkem768
Removed unconditional true; now only set when listener config
explicitly provides it.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean (on modified files)
fix(audit): P0+P1 — ShadowQUIC defaults, Snell wrappers, SS plugin, VMess encryption, TUIC/Hy2 fields, subscription error leak, frontend forms
Comprehensive re-audit against official Mihomo docs (Meta-Docs-main)
found 3 High + 9 Medium issues. All fixed in this commit.
== P0 High ==
P0-1: ShadowQUIC client defaults violated official docs
converter/client.go unconditionally defaulted quic-versions=[v2],
congestion-controller=bbr, zero-rtt=true. Official defaults are
v1/cubic/false. Removed defaults; fields now pass through only when
present in listener config.
P0-2: Snell listener wrappers not converted to client obfs-opts
Snell+shadow-tls/res-tls/jls-config produced client YAML with only
psk+version. Added applySnellObfsOpts that maps listener wrappers to
client obfs-opts: {mode: shadow-tls/restls/jls, host, password,
version, username, version-hint, restls-script} per proxies/snell.md.
P0-3: SS simple-obfs plugin name mismatch
Emitted plugin: 'simple-obfs' but official docs say plugin: 'obfs'.
Fixed.
== P1 Medium ==
P1-4: VMess encryption field leaked
copyOption(encryption) called for both vmess and vless. Only VLESS
has encryption. Wrapped in if protocol=='vless'.
P1-5: TUIC client missing sni + skip-cert-verify
Added to both v4 (token) and v5 (users) branches.
P1-6: TUIC URI snake_case → kebab-case
congestion_control→congestion-controller, bbr_profile→bbr-profile,
udp_relay_mode→udp-relay-mode, max_udp_relay_packet_size→
max-udp-relay-packet-size.
P1-7: Hysteria2 client missing handshake-timeout + max-idle-time
Added to copyOption list.
P1-8: Subscription endpoint error leak
Replaced err.Error() with generic 'subscription generation failed'
at 4 public endpoints (lines 82/107/122/368). Added server-side
log.Printf for diagnostics.
P1-9: Snell form missing v4/v5
Version dropdown [1,2,3]→[1,2,3,4,5]; initialValue 3→4 (matches
backend autofill default).
P1-10: Sudoku form missing httpmask + custom-table + fallback
Added EnableSection for httpmask (disable/mode/path-root) +
custom-table/custom-tables/fallback Form.Items. Wired through
configToFormValues + formValuesToConfig + FORM_OWNED_KEYS.
P1-11: ShadowQUIC form missing 4 fields
Added max-datagram-frame-size, recv-window-conn, recv-window,
disable-mtu-discovery.
P1-12: Unconditional support-x25519mlkem768
Removed unconditional true; now only set when listener config
explicitly provides it.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean (on modified files)
fix(audit): P0+P1 — ShadowQUIC defaults, Snell wrappers, SS plugin, VMess encryption, TUIC/Hy2 fields, subscription error leak, frontend forms
Comprehensive re-audit against official Mihomo docs (Meta-Docs-main)
found 3 High + 9 Medium issues. All fixed in this commit.
== P0 High ==
P0-1: ShadowQUIC client defaults violated official docs
converter/client.go unconditionally defaulted quic-versions=[v2],
congestion-controller=bbr, zero-rtt=true. Official defaults are
v1/cubic/false. Removed defaults; fields now pass through only when
present in listener config.
P0-2: Snell listener wrappers not converted to client obfs-opts
Snell+shadow-tls/res-tls/jls-config produced client YAML with only
psk+version. Added applySnellObfsOpts that maps listener wrappers to
client obfs-opts: {mode: shadow-tls/restls/jls, host, password,
version, username, version-hint, restls-script} per proxies/snell.md.
P0-3: SS simple-obfs plugin name mismatch
Emitted plugin: 'simple-obfs' but official docs say plugin: 'obfs'.
Fixed.
== P1 Medium ==
P1-4: VMess encryption field leaked
copyOption(encryption) called for both vmess and vless. Only VLESS
has encryption. Wrapped in if protocol=='vless'.
P1-5: TUIC client missing sni + skip-cert-verify
Added to both v4 (token) and v5 (users) branches.
P1-6: TUIC URI snake_case → kebab-case
congestion_control→congestion-controller, bbr_profile→bbr-profile,
udp_relay_mode→udp-relay-mode, max_udp_relay_packet_size→
max-udp-relay-packet-size.
P1-7: Hysteria2 client missing handshake-timeout + max-idle-time
Added to copyOption list.
P1-8: Subscription endpoint error leak
Replaced err.Error() with generic 'subscription generation failed'
at 4 public endpoints (lines 82/107/122/368). Added server-side
log.Printf for diagnostics.
P1-9: Snell form missing v4/v5
Version dropdown [1,2,3]→[1,2,3,4,5]; initialValue 3→4 (matches
backend autofill default).
P1-10: Sudoku form missing httpmask + custom-table + fallback
Added EnableSection for httpmask (disable/mode/path-root) +
custom-table/custom-tables/fallback Form.Items. Wired through
configToFormValues + formValuesToConfig + FORM_OWNED_KEYS.
P1-11: ShadowQUIC form missing 4 fields
Added max-datagram-frame-size, recv-window-conn, recv-window,
disable-mtu-discovery.
P1-12: Unconditional support-x25519mlkem768
Removed unconditional true; now only set when listener config
explicitly provides it.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean (on modified files)
fix(audit): P0+P1 — ShadowQUIC defaults, Snell wrappers, SS plugin, VMess encryption, TUIC/Hy2 fields, subscription error leak, frontend forms
Comprehensive re-audit against official Mihomo docs (Meta-Docs-main)
found 3 High + 9 Medium issues. All fixed in this commit.
== P0 High ==
P0-1: ShadowQUIC client defaults violated official docs
converter/client.go unconditionally defaulted quic-versions=[v2],
congestion-controller=bbr, zero-rtt=true. Official defaults are
v1/cubic/false. Removed defaults; fields now pass through only when
present in listener config.
P0-2: Snell listener wrappers not converted to client obfs-opts
Snell+shadow-tls/res-tls/jls-config produced client YAML with only
psk+version. Added applySnellObfsOpts that maps listener wrappers to
client obfs-opts: {mode: shadow-tls/restls/jls, host, password,
version, username, version-hint, restls-script} per proxies/snell.md.
P0-3: SS simple-obfs plugin name mismatch
Emitted plugin: 'simple-obfs' but official docs say plugin: 'obfs'.
Fixed.
== P1 Medium ==
P1-4: VMess encryption field leaked
copyOption(encryption) called for both vmess and vless. Only VLESS
has encryption. Wrapped in if protocol=='vless'.
P1-5: TUIC client missing sni + skip-cert-verify
Added to both v4 (token) and v5 (users) branches.
P1-6: TUIC URI snake_case → kebab-case
congestion_control→congestion-controller, bbr_profile→bbr-profile,
udp_relay_mode→udp-relay-mode, max_udp_relay_packet_size→
max-udp-relay-packet-size.
P1-7: Hysteria2 client missing handshake-timeout + max-idle-time
Added to copyOption list.
P1-8: Subscription endpoint error leak
Replaced err.Error() with generic 'subscription generation failed'
at 4 public endpoints (lines 82/107/122/368). Added server-side
log.Printf for diagnostics.
P1-9: Snell form missing v4/v5
Version dropdown [1,2,3]→[1,2,3,4,5]; initialValue 3→4 (matches
backend autofill default).
P1-10: Sudoku form missing httpmask + custom-table + fallback
Added EnableSection for httpmask (disable/mode/path-root) +
custom-table/custom-tables/fallback Form.Items. Wired through
configToFormValues + formValuesToConfig + FORM_OWNED_KEYS.
P1-11: ShadowQUIC form missing 4 fields
Added max-datagram-frame-size, recv-window-conn, recv-window,
disable-mtu-discovery.
P1-12: Unconditional support-x25519mlkem768
Removed unconditional true; now only set when listener config
explicitly provides it.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean (on modified files)
fix(audit): P0+P1 — ShadowQUIC defaults, Snell wrappers, SS plugin, VMess encryption, TUIC/Hy2 fields, subscription error leak, frontend forms
Comprehensive re-audit against official Mihomo docs (Meta-Docs-main)
found 3 High + 9 Medium issues. All fixed in this commit.
== P0 High ==
P0-1: ShadowQUIC client defaults violated official docs
converter/client.go unconditionally defaulted quic-versions=[v2],
congestion-controller=bbr, zero-rtt=true. Official defaults are
v1/cubic/false. Removed defaults; fields now pass through only when
present in listener config.
P0-2: Snell listener wrappers not converted to client obfs-opts
Snell+shadow-tls/res-tls/jls-config produced client YAML with only
psk+version. Added applySnellObfsOpts that maps listener wrappers to
client obfs-opts: {mode: shadow-tls/restls/jls, host, password,
version, username, version-hint, restls-script} per proxies/snell.md.
P0-3: SS simple-obfs plugin name mismatch
Emitted plugin: 'simple-obfs' but official docs say plugin: 'obfs'.
Fixed.
== P1 Medium ==
P1-4: VMess encryption field leaked
copyOption(encryption) called for both vmess and vless. Only VLESS
has encryption. Wrapped in if protocol=='vless'.
P1-5: TUIC client missing sni + skip-cert-verify
Added to both v4 (token) and v5 (users) branches.
P1-6: TUIC URI snake_case → kebab-case
congestion_control→congestion-controller, bbr_profile→bbr-profile,
udp_relay_mode→udp-relay-mode, max_udp_relay_packet_size→
max-udp-relay-packet-size.
P1-7: Hysteria2 client missing handshake-timeout + max-idle-time
Added to copyOption list.
P1-8: Subscription endpoint error leak
Replaced err.Error() with generic 'subscription generation failed'
at 4 public endpoints (lines 82/107/122/368). Added server-side
log.Printf for diagnostics.
P1-9: Snell form missing v4/v5
Version dropdown [1,2,3]→[1,2,3,4,5]; initialValue 3→4 (matches
backend autofill default).
P1-10: Sudoku form missing httpmask + custom-table + fallback
Added EnableSection for httpmask (disable/mode/path-root) +
custom-table/custom-tables/fallback Form.Items. Wired through
configToFormValues + formValuesToConfig + FORM_OWNED_KEYS.
P1-11: ShadowQUIC form missing 4 fields
Added max-datagram-frame-size, recv-window-conn, recv-window,
disable-mtu-discovery.
P1-12: Unconditional support-x25519mlkem768
Removed unconditional true; now only set when listener config
explicitly provides it.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean (on modified files)
fix(audit): P0+P1 — ShadowQUIC defaults, Snell wrappers, SS plugin, VMess encryption, TUIC/Hy2 fields, subscription error leak, frontend forms
Comprehensive re-audit against official Mihomo docs (Meta-Docs-main)
found 3 High + 9 Medium issues. All fixed in this commit.
== P0 High ==
P0-1: ShadowQUIC client defaults violated official docs
converter/client.go unconditionally defaulted quic-versions=[v2],
congestion-controller=bbr, zero-rtt=true. Official defaults are
v1/cubic/false. Removed defaults; fields now pass through only when
present in listener config.
P0-2: Snell listener wrappers not converted to client obfs-opts
Snell+shadow-tls/res-tls/jls-config produced client YAML with only
psk+version. Added applySnellObfsOpts that maps listener wrappers to
client obfs-opts: {mode: shadow-tls/restls/jls, host, password,
version, username, version-hint, restls-script} per proxies/snell.md.
P0-3: SS simple-obfs plugin name mismatch
Emitted plugin: 'simple-obfs' but official docs say plugin: 'obfs'.
Fixed.
== P1 Medium ==
P1-4: VMess encryption field leaked
copyOption(encryption) called for both vmess and vless. Only VLESS
has encryption. Wrapped in if protocol=='vless'.
P1-5: TUIC client missing sni + skip-cert-verify
Added to both v4 (token) and v5 (users) branches.
P1-6: TUIC URI snake_case → kebab-case
congestion_control→congestion-controller, bbr_profile→bbr-profile,
udp_relay_mode→udp-relay-mode, max_udp_relay_packet_size→
max-udp-relay-packet-size.
P1-7: Hysteria2 client missing handshake-timeout + max-idle-time
Added to copyOption list.
P1-8: Subscription endpoint error leak
Replaced err.Error() with generic 'subscription generation failed'
at 4 public endpoints (lines 82/107/122/368). Added server-side
log.Printf for diagnostics.
P1-9: Snell form missing v4/v5
Version dropdown [1,2,3]→[1,2,3,4,5]; initialValue 3→4 (matches
backend autofill default).
P1-10: Sudoku form missing httpmask + custom-table + fallback
Added EnableSection for httpmask (disable/mode/path-root) +
custom-table/custom-tables/fallback Form.Items. Wired through
configToFormValues + formValuesToConfig + FORM_OWNED_KEYS.
P1-11: ShadowQUIC form missing 4 fields
Added max-datagram-frame-size, recv-window-conn, recv-window,
disable-mtu-discovery.
P1-12: Unconditional support-x25519mlkem768
Removed unconditional true; now only set when listener config
explicitly provides it.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean (on modified files)
fix(audit): P0+P1 — ShadowQUIC defaults, Snell wrappers, SS plugin, VMess encryption, TUIC/Hy2 fields, subscription error leak, frontend forms
Comprehensive re-audit against official Mihomo docs (Meta-Docs-main)
found 3 High + 9 Medium issues. All fixed in this commit.
== P0 High ==
P0-1: ShadowQUIC client defaults violated official docs
converter/client.go unconditionally defaulted quic-versions=[v2],
congestion-controller=bbr, zero-rtt=true. Official defaults are
v1/cubic/false. Removed defaults; fields now pass through only when
present in listener config.
P0-2: Snell listener wrappers not converted to client obfs-opts
Snell+shadow-tls/res-tls/jls-config produced client YAML with only
psk+version. Added applySnellObfsOpts that maps listener wrappers to
client obfs-opts: {mode: shadow-tls/restls/jls, host, password,
version, username, version-hint, restls-script} per proxies/snell.md.
P0-3: SS simple-obfs plugin name mismatch
Emitted plugin: 'simple-obfs' but official docs say plugin: 'obfs'.
Fixed.
== P1 Medium ==
P1-4: VMess encryption field leaked
copyOption(encryption) called for both vmess and vless. Only VLESS
has encryption. Wrapped in if protocol=='vless'.
P1-5: TUIC client missing sni + skip-cert-verify
Added to both v4 (token) and v5 (users) branches.
P1-6: TUIC URI snake_case → kebab-case
congestion_control→congestion-controller, bbr_profile→bbr-profile,
udp_relay_mode→udp-relay-mode, max_udp_relay_packet_size→
max-udp-relay-packet-size.
P1-7: Hysteria2 client missing handshake-timeout + max-idle-time
Added to copyOption list.
P1-8: Subscription endpoint error leak
Replaced err.Error() with generic 'subscription generation failed'
at 4 public endpoints (lines 82/107/122/368). Added server-side
log.Printf for diagnostics.
P1-9: Snell form missing v4/v5
Version dropdown [1,2,3]→[1,2,3,4,5]; initialValue 3→4 (matches
backend autofill default).
P1-10: Sudoku form missing httpmask + custom-table + fallback
Added EnableSection for httpmask (disable/mode/path-root) +
custom-table/custom-tables/fallback Form.Items. Wired through
configToFormValues + formValuesToConfig + FORM_OWNED_KEYS.
P1-11: ShadowQUIC form missing 4 fields
Added max-datagram-frame-size, recv-window-conn, recv-window,
disable-mtu-discovery.
P1-12: Unconditional support-x25519mlkem768
Removed unconditional true; now only set when listener config
explicitly provides it.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean (on modified files)
fix(audit): P0+P1 — ShadowQUIC defaults, Snell wrappers, SS plugin, VMess encryption, TUIC/Hy2 fields, subscription error leak, frontend forms
Comprehensive re-audit against official Mihomo docs (Meta-Docs-main)
found 3 High + 9 Medium issues. All fixed in this commit.
== P0 High ==
P0-1: ShadowQUIC client defaults violated official docs
converter/client.go unconditionally defaulted quic-versions=[v2],
congestion-controller=bbr, zero-rtt=true. Official defaults are
v1/cubic/false. Removed defaults; fields now pass through only when
present in listener config.
P0-2: Snell listener wrappers not converted to client obfs-opts
Snell+shadow-tls/res-tls/jls-config produced client YAML with only
psk+version. Added applySnellObfsOpts that maps listener wrappers to
client obfs-opts: {mode: shadow-tls/restls/jls, host, password,
version, username, version-hint, restls-script} per proxies/snell.md.
P0-3: SS simple-obfs plugin name mismatch
Emitted plugin: 'simple-obfs' but official docs say plugin: 'obfs'.
Fixed.
== P1 Medium ==
P1-4: VMess encryption field leaked
copyOption(encryption) called for both vmess and vless. Only VLESS
has encryption. Wrapped in if protocol=='vless'.
P1-5: TUIC client missing sni + skip-cert-verify
Added to both v4 (token) and v5 (users) branches.
P1-6: TUIC URI snake_case → kebab-case
congestion_control→congestion-controller, bbr_profile→bbr-profile,
udp_relay_mode→udp-relay-mode, max_udp_relay_packet_size→
max-udp-relay-packet-size.
P1-7: Hysteria2 client missing handshake-timeout + max-idle-time
Added to copyOption list.
P1-8: Subscription endpoint error leak
Replaced err.Error() with generic 'subscription generation failed'
at 4 public endpoints (lines 82/107/122/368). Added server-side
log.Printf for diagnostics.
P1-9: Snell form missing v4/v5
Version dropdown [1,2,3]→[1,2,3,4,5]; initialValue 3→4 (matches
backend autofill default).
P1-10: Sudoku form missing httpmask + custom-table + fallback
Added EnableSection for httpmask (disable/mode/path-root) +
custom-table/custom-tables/fallback Form.Items. Wired through
configToFormValues + formValuesToConfig + FORM_OWNED_KEYS.
P1-11: ShadowQUIC form missing 4 fields
Added max-datagram-frame-size, recv-window-conn, recv-window,
disable-mtu-discovery.
P1-12: Unconditional support-x25519mlkem768
Removed unconditional true; now only set when listener config
explicitly provides it.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean (on modified files)
fix(audit): P0+P1 — ShadowQUIC defaults, Snell wrappers, SS plugin, VMess encryption, TUIC/Hy2 fields, subscription error leak, frontend forms
Comprehensive re-audit against official Mihomo docs (Meta-Docs-main)
found 3 High + 9 Medium issues. All fixed in this commit.
== P0 High ==
P0-1: ShadowQUIC client defaults violated official docs
converter/client.go unconditionally defaulted quic-versions=[v2],
congestion-controller=bbr, zero-rtt=true. Official defaults are
v1/cubic/false. Removed defaults; fields now pass through only when
present in listener config.
P0-2: Snell listener wrappers not converted to client obfs-opts
Snell+shadow-tls/res-tls/jls-config produced client YAML with only
psk+version. Added applySnellObfsOpts that maps listener wrappers to
client obfs-opts: {mode: shadow-tls/restls/jls, host, password,
version, username, version-hint, restls-script} per proxies/snell.md.
P0-3: SS simple-obfs plugin name mismatch
Emitted plugin: 'simple-obfs' but official docs say plugin: 'obfs'.
Fixed.
== P1 Medium ==
P1-4: VMess encryption field leaked
copyOption(encryption) called for both vmess and vless. Only VLESS
has encryption. Wrapped in if protocol=='vless'.
P1-5: TUIC client missing sni + skip-cert-verify
Added to both v4 (token) and v5 (users) branches.
P1-6: TUIC URI snake_case → kebab-case
congestion_control→congestion-controller, bbr_profile→bbr-profile,
udp_relay_mode→udp-relay-mode, max_udp_relay_packet_size→
max-udp-relay-packet-size.
P1-7: Hysteria2 client missing handshake-timeout + max-idle-time
Added to copyOption list.
P1-8: Subscription endpoint error leak
Replaced err.Error() with generic 'subscription generation failed'
at 4 public endpoints (lines 82/107/122/368). Added server-side
log.Printf for diagnostics.
P1-9: Snell form missing v4/v5
Version dropdown [1,2,3]→[1,2,3,4,5]; initialValue 3→4 (matches
backend autofill default).
P1-10: Sudoku form missing httpmask + custom-table + fallback
Added EnableSection for httpmask (disable/mode/path-root) +
custom-table/custom-tables/fallback Form.Items. Wired through
configToFormValues + formValuesToConfig + FORM_OWNED_KEYS.
P1-11: ShadowQUIC form missing 4 fields
Added max-datagram-frame-size, recv-window-conn, recv-window,
disable-mtu-discovery.
P1-12: Unconditional support-x25519mlkem768
Removed unconditional true; now only set when listener config
explicitly provides it.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean (on modified files)
fix(audit): P0+P1 — ShadowQUIC defaults, Snell wrappers, SS plugin, VMess encryption, TUIC/Hy2 fields, subscription error leak, frontend forms
Comprehensive re-audit against official Mihomo docs (Meta-Docs-main)
found 3 High + 9 Medium issues. All fixed in this commit.
== P0 High ==
P0-1: ShadowQUIC client defaults violated official docs
converter/client.go unconditionally defaulted quic-versions=[v2],
congestion-controller=bbr, zero-rtt=true. Official defaults are
v1/cubic/false. Removed defaults; fields now pass through only when
present in listener config.
P0-2: Snell listener wrappers not converted to client obfs-opts
Snell+shadow-tls/res-tls/jls-config produced client YAML with only
psk+version. Added applySnellObfsOpts that maps listener wrappers to
client obfs-opts: {mode: shadow-tls/restls/jls, host, password,
version, username, version-hint, restls-script} per proxies/snell.md.
P0-3: SS simple-obfs plugin name mismatch
Emitted plugin: 'simple-obfs' but official docs say plugin: 'obfs'.
Fixed.
== P1 Medium ==
P1-4: VMess encryption field leaked
copyOption(encryption) called for both vmess and vless. Only VLESS
has encryption. Wrapped in if protocol=='vless'.
P1-5: TUIC client missing sni + skip-cert-verify
Added to both v4 (token) and v5 (users) branches.
P1-6: TUIC URI snake_case → kebab-case
congestion_control→congestion-controller, bbr_profile→bbr-profile,
udp_relay_mode→udp-relay-mode, max_udp_relay_packet_size→
max-udp-relay-packet-size.
P1-7: Hysteria2 client missing handshake-timeout + max-idle-time
Added to copyOption list.
P1-8: Subscription endpoint error leak
Replaced err.Error() with generic 'subscription generation failed'
at 4 public endpoints (lines 82/107/122/368). Added server-side
log.Printf for diagnostics.
P1-9: Snell form missing v4/v5
Version dropdown [1,2,3]→[1,2,3,4,5]; initialValue 3→4 (matches
backend autofill default).
P1-10: Sudoku form missing httpmask + custom-table + fallback
Added EnableSection for httpmask (disable/mode/path-root) +
custom-table/custom-tables/fallback Form.Items. Wired through
configToFormValues + formValuesToConfig + FORM_OWNED_KEYS.
P1-11: ShadowQUIC form missing 4 fields
Added max-datagram-frame-size, recv-window-conn, recv-window,
disable-mtu-discovery.
P1-12: Unconditional support-x25519mlkem768
Removed unconditional true; now only set when listener config
explicitly provides it.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean (on modified files)
fix(audit): P0+P1 — ShadowQUIC defaults, Snell wrappers, SS plugin, VMess encryption, TUIC/Hy2 fields, subscription error leak, frontend forms
Comprehensive re-audit against official Mihomo docs (Meta-Docs-main)
found 3 High + 9 Medium issues. All fixed in this commit.
== P0 High ==
P0-1: ShadowQUIC client defaults violated official docs
converter/client.go unconditionally defaulted quic-versions=[v2],
congestion-controller=bbr, zero-rtt=true. Official defaults are
v1/cubic/false. Removed defaults; fields now pass through only when
present in listener config.
P0-2: Snell listener wrappers not converted to client obfs-opts
Snell+shadow-tls/res-tls/jls-config produced client YAML with only
psk+version. Added applySnellObfsOpts that maps listener wrappers to
client obfs-opts: {mode: shadow-tls/restls/jls, host, password,
version, username, version-hint, restls-script} per proxies/snell.md.
P0-3: SS simple-obfs plugin name mismatch
Emitted plugin: 'simple-obfs' but official docs say plugin: 'obfs'.
Fixed.
== P1 Medium ==
P1-4: VMess encryption field leaked
copyOption(encryption) called for both vmess and vless. Only VLESS
has encryption. Wrapped in if protocol=='vless'.
P1-5: TUIC client missing sni + skip-cert-verify
Added to both v4 (token) and v5 (users) branches.
P1-6: TUIC URI snake_case → kebab-case
congestion_control→congestion-controller, bbr_profile→bbr-profile,
udp_relay_mode→udp-relay-mode, max_udp_relay_packet_size→
max-udp-relay-packet-size.
P1-7: Hysteria2 client missing handshake-timeout + max-idle-time
Added to copyOption list.
P1-8: Subscription endpoint error leak
Replaced err.Error() with generic 'subscription generation failed'
at 4 public endpoints (lines 82/107/122/368). Added server-side
log.Printf for diagnostics.
P1-9: Snell form missing v4/v5
Version dropdown [1,2,3]→[1,2,3,4,5]; initialValue 3→4 (matches
backend autofill default).
P1-10: Sudoku form missing httpmask + custom-table + fallback
Added EnableSection for httpmask (disable/mode/path-root) +
custom-table/custom-tables/fallback Form.Items. Wired through
configToFormValues + formValuesToConfig + FORM_OWNED_KEYS.
P1-11: ShadowQUIC form missing 4 fields
Added max-datagram-frame-size, recv-window-conn, recv-window,
disable-mtu-discovery.
P1-12: Unconditional support-x25519mlkem768
Removed unconditional true; now only set when listener config
explicitly provides it.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean (on modified files)
fix(audit): P0+P1 — ShadowQUIC defaults, Snell wrappers, SS plugin, VMess encryption, TUIC/Hy2 fields, subscription error leak, frontend forms
Comprehensive re-audit against official Mihomo docs (Meta-Docs-main)
found 3 High + 9 Medium issues. All fixed in this commit.
== P0 High ==
P0-1: ShadowQUIC client defaults violated official docs
converter/client.go unconditionally defaulted quic-versions=[v2],
congestion-controller=bbr, zero-rtt=true. Official defaults are
v1/cubic/false. Removed defaults; fields now pass through only when
present in listener config.
P0-2: Snell listener wrappers not converted to client obfs-opts
Snell+shadow-tls/res-tls/jls-config produced client YAML with only
psk+version. Added applySnellObfsOpts that maps listener wrappers to
client obfs-opts: {mode: shadow-tls/restls/jls, host, password,
version, username, version-hint, restls-script} per proxies/snell.md.
P0-3: SS simple-obfs plugin name mismatch
Emitted plugin: 'simple-obfs' but official docs say plugin: 'obfs'.
Fixed.
== P1 Medium ==
P1-4: VMess encryption field leaked
copyOption(encryption) called for both vmess and vless. Only VLESS
has encryption. Wrapped in if protocol=='vless'.
P1-5: TUIC client missing sni + skip-cert-verify
Added to both v4 (token) and v5 (users) branches.
P1-6: TUIC URI snake_case → kebab-case
congestion_control→congestion-controller, bbr_profile→bbr-profile,
udp_relay_mode→udp-relay-mode, max_udp_relay_packet_size→
max-udp-relay-packet-size.
P1-7: Hysteria2 client missing handshake-timeout + max-idle-time
Added to copyOption list.
P1-8: Subscription endpoint error leak
Replaced err.Error() with generic 'subscription generation failed'
at 4 public endpoints (lines 82/107/122/368). Added server-side
log.Printf for diagnostics.
P1-9: Snell form missing v4/v5
Version dropdown [1,2,3]→[1,2,3,4,5]; initialValue 3→4 (matches
backend autofill default).
P1-10: Sudoku form missing httpmask + custom-table + fallback
Added EnableSection for httpmask (disable/mode/path-root) +
custom-table/custom-tables/fallback Form.Items. Wired through
configToFormValues + formValuesToConfig + FORM_OWNED_KEYS.
P1-11: ShadowQUIC form missing 4 fields
Added max-datagram-frame-size, recv-window-conn, recv-window,
disable-mtu-discovery.
P1-12: Unconditional support-x25519mlkem768
Removed unconditional true; now only set when listener config
explicitly provides it.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean (on modified files)
2026-08-30 10:34:48 +00:00
Z User fac3d88929 fix(settings): port change now shows explicit restart warning
Problem: Changing the panel port in Settings → Panel/NAT only saved
to config.yaml and updated in-memory cfg.Server.Port, but the HTTP
server kept listening on the old port. The success toast said 'restart
service to apply' but was easy to miss — users expected the port
change to take effect immediately.
Root cause: the HTTP server (gin r.Run) binds once at startup to
cfg.Server.Port. Changing the port in-memory doesn't rebind. A panel
restart is required. The response included restart_required:true and
a message, but the frontend only showed a generic success toast.
Fix: when the port actually changes (prevPort != newPort), show a
Modal.warning with:
- old port → new port
- explicit 'systemctl restart 3m-ui' command
- new access URL http://IP:newPort
When only listen/public_url changes (port unchanged), keep the
existing success toast.
fix(settings): port change now shows explicit restart warning
Problem: Changing the panel port in Settings → Panel/NAT only saved
to config.yaml and updated in-memory cfg.Server.Port, but the HTTP
server kept listening on the old port. The success toast said 'restart
service to apply' but was easy to miss — users expected the port
change to take effect immediately.
Root cause: the HTTP server (gin r.Run) binds once at startup to
cfg.Server.Port. Changing the port in-memory doesn't rebind. A panel
restart is required. The response included restart_required:true and
a message, but the frontend only showed a generic success toast.
Fix: when the port actually changes (prevPort != newPort), show a
Modal.warning with:
- old port → new port
- explicit 'systemctl restart 3m-ui' command
- new access URL http://IP:newPort
When only listen/public_url changes (port unchanged), keep the
existing success toast.
fix(settings): port change now shows explicit restart warning
Problem: Changing the panel port in Settings → Panel/NAT only saved
to config.yaml and updated in-memory cfg.Server.Port, but the HTTP
server kept listening on the old port. The success toast said 'restart
service to apply' but was easy to miss — users expected the port
change to take effect immediately.
Root cause: the HTTP server (gin r.Run) binds once at startup to
cfg.Server.Port. Changing the port in-memory doesn't rebind. A panel
restart is required. The response included restart_required:true and
a message, but the frontend only showed a generic success toast.
Fix: when the port actually changes (prevPort != newPort), show a
Modal.warning with:
- old port → new port
- explicit 'systemctl restart 3m-ui' command
- new access URL http://IP:newPort
When only listen/public_url changes (port unchanged), keep the
existing success toast.
fix(settings): port change now shows explicit restart warning
Problem: Changing the panel port in Settings → Panel/NAT only saved
to config.yaml and updated in-memory cfg.Server.Port, but the HTTP
server kept listening on the old port. The success toast said 'restart
service to apply' but was easy to miss — users expected the port
change to take effect immediately.
Root cause: the HTTP server (gin r.Run) binds once at startup to
cfg.Server.Port. Changing the port in-memory doesn't rebind. A panel
restart is required. The response included restart_required:true and
a message, but the frontend only showed a generic success toast.
Fix: when the port actually changes (prevPort != newPort), show a
Modal.warning with:
- old port → new port
- explicit 'systemctl restart 3m-ui' command
- new access URL http://IP:newPort
When only listen/public_url changes (port unchanged), keep the
existing success toast.
fix(settings): port change now shows explicit restart warning
Problem: Changing the panel port in Settings → Panel/NAT only saved
to config.yaml and updated in-memory cfg.Server.Port, but the HTTP
server kept listening on the old port. The success toast said 'restart
service to apply' but was easy to miss — users expected the port
change to take effect immediately.
Root cause: the HTTP server (gin r.Run) binds once at startup to
cfg.Server.Port. Changing the port in-memory doesn't rebind. A panel
restart is required. The response included restart_required:true and
a message, but the frontend only showed a generic success toast.
Fix: when the port actually changes (prevPort != newPort), show a
Modal.warning with:
- old port → new port
- explicit 'systemctl restart 3m-ui' command
- new access URL http://IP:newPort
When only listen/public_url changes (port unchanged), keep the
existing success toast.
fix(settings): port change now shows explicit restart warning
Problem: Changing the panel port in Settings → Panel/NAT only saved
to config.yaml and updated in-memory cfg.Server.Port, but the HTTP
server kept listening on the old port. The success toast said 'restart
service to apply' but was easy to miss — users expected the port
change to take effect immediately.
Root cause: the HTTP server (gin r.Run) binds once at startup to
cfg.Server.Port. Changing the port in-memory doesn't rebind. A panel
restart is required. The response included restart_required:true and
a message, but the frontend only showed a generic success toast.
Fix: when the port actually changes (prevPort != newPort), show a
Modal.warning with:
- old port → new port
- explicit 'systemctl restart 3m-ui' command
- new access URL http://IP:newPort
When only listen/public_url changes (port unchanged), keep the
existing success toast.
fix(settings): port change now shows explicit restart warning
Problem: Changing the panel port in Settings → Panel/NAT only saved
to config.yaml and updated in-memory cfg.Server.Port, but the HTTP
server kept listening on the old port. The success toast said 'restart
service to apply' but was easy to miss — users expected the port
change to take effect immediately.
Root cause: the HTTP server (gin r.Run) binds once at startup to
cfg.Server.Port. Changing the port in-memory doesn't rebind. A panel
restart is required. The response included restart_required:true and
a message, but the frontend only showed a generic success toast.
Fix: when the port actually changes (prevPort != newPort), show a
Modal.warning with:
- old port → new port
- explicit 'systemctl restart 3m-ui' command
- new access URL http://IP:newPort
When only listen/public_url changes (port unchanged), keep the
existing success toast.
fix(settings): port change now shows explicit restart warning
Problem: Changing the panel port in Settings → Panel/NAT only saved
to config.yaml and updated in-memory cfg.Server.Port, but the HTTP
server kept listening on the old port. The success toast said 'restart
service to apply' but was easy to miss — users expected the port
change to take effect immediately.
Root cause: the HTTP server (gin r.Run) binds once at startup to
cfg.Server.Port. Changing the port in-memory doesn't rebind. A panel
restart is required. The response included restart_required:true and
a message, but the frontend only showed a generic success toast.
Fix: when the port actually changes (prevPort != newPort), show a
Modal.warning with:
- old port → new port
- explicit 'systemctl restart 3m-ui' command
- new access URL http://IP:newPort
When only listen/public_url changes (port unchanged), keep the
existing success toast.
fix(settings): port change now shows explicit restart warning
Problem: Changing the panel port in Settings → Panel/NAT only saved
to config.yaml and updated in-memory cfg.Server.Port, but the HTTP
server kept listening on the old port. The success toast said 'restart
service to apply' but was easy to miss — users expected the port
change to take effect immediately.
Root cause: the HTTP server (gin r.Run) binds once at startup to
cfg.Server.Port. Changing the port in-memory doesn't rebind. A panel
restart is required. The response included restart_required:true and
a message, but the frontend only showed a generic success toast.
Fix: when the port actually changes (prevPort != newPort), show a
Modal.warning with:
- old port → new port
- explicit 'systemctl restart 3m-ui' command
- new access URL http://IP:newPort
When only listen/public_url changes (port unchanged), keep the
existing success toast.
fix(settings): port change now shows explicit restart warning
Problem: Changing the panel port in Settings → Panel/NAT only saved
to config.yaml and updated in-memory cfg.Server.Port, but the HTTP
server kept listening on the old port. The success toast said 'restart
service to apply' but was easy to miss — users expected the port
change to take effect immediately.
Root cause: the HTTP server (gin r.Run) binds once at startup to
cfg.Server.Port. Changing the port in-memory doesn't rebind. A panel
restart is required. The response included restart_required:true and
a message, but the frontend only showed a generic success toast.
Fix: when the port actually changes (prevPort != newPort), show a
Modal.warning with:
- old port → new port
- explicit 'systemctl restart 3m-ui' command
- new access URL http://IP:newPort
When only listen/public_url changes (port unchanged), keep the
existing success toast.
fix(settings): port change now shows explicit restart warning
Problem: Changing the panel port in Settings → Panel/NAT only saved
to config.yaml and updated in-memory cfg.Server.Port, but the HTTP
server kept listening on the old port. The success toast said 'restart
service to apply' but was easy to miss — users expected the port
change to take effect immediately.
Root cause: the HTTP server (gin r.Run) binds once at startup to
cfg.Server.Port. Changing the port in-memory doesn't rebind. A panel
restart is required. The response included restart_required:true and
a message, but the frontend only showed a generic success toast.
Fix: when the port actually changes (prevPort != newPort), show a
Modal.warning with:
- old port → new port
- explicit 'systemctl restart 3m-ui' command
- new access URL http://IP:newPort
When only listen/public_url changes (port unchanged), keep the
existing success toast.
fix(settings): port change now shows explicit restart warning
Problem: Changing the panel port in Settings → Panel/NAT only saved
to config.yaml and updated in-memory cfg.Server.Port, but the HTTP
server kept listening on the old port. The success toast said 'restart
service to apply' but was easy to miss — users expected the port
change to take effect immediately.
Root cause: the HTTP server (gin r.Run) binds once at startup to
cfg.Server.Port. Changing the port in-memory doesn't rebind. A panel
restart is required. The response included restart_required:true and
a message, but the frontend only showed a generic success toast.
Fix: when the port actually changes (prevPort != newPort), show a
Modal.warning with:
- old port → new port
- explicit 'systemctl restart 3m-ui' command
- new access URL http://IP:newPort
When only listen/public_url changes (port unchanged), keep the
existing success toast.
2026-08-30 09:25:11 +00:00
Z User fad6e612a5 fix(audit): mux-option UI fields, shadowquic/trusttunnel URI, sudoku aliases
Final re-audit against official Mihomo docs (Meta-Docs-main) found 4
remaining issues. All fixed.
== C1 Critical: mux-option frontend writes smux client fields ==
The frontend mux-option form wrote enable/protocol/max-connections/
min-streams/max-streams/statistic/only-tcp (smux client format) into
the listener mux-option block. The backend listener schema only accepts
padding + brutal.{enabled,up,down} per official docs. Saving a listener
with Mux enabled failed validation.
Fix: removed the 7 smux client fields from the frontend form and the
formValuesToConfig serializer. Only padding + brutal remain (matching
the listener schema). The smux fields belong on the proxy client side,
not the listener.
== C2 Critical: shadowquic URI export broken ==
shadowQUICURIs read userMap(cfg) but shadowquic uses array-form
users: [{username, password}] per official docs. The map read always
returned empty → URI export failed with 'requires at least one user'.
Fix: use userRows(cfg) (array form) matching the compiler/autofill.
== C3 Critical: trusttunnel URI export broken ==
Same bug class as C2. trustTunnelURIs read userMap(cfg) but trusttunnel
uses array-form users per official docs.
Fix: use userRows(cfg).
== M Minor: sudoku missing flat compatibility aliases ==
Official sudoku doc lists http-mask-mode and path-root as flat
compatibility aliases for httpmask.{mode, path-root}. The schema only
had disable-http-mask (top-level). Added http-mask-mode and path-root
to the sudoku Fields allowlist.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean
fix(audit): mux-option UI fields, shadowquic/trusttunnel URI, sudoku aliases
Final re-audit against official Mihomo docs (Meta-Docs-main) found 4
remaining issues. All fixed.
== C1 Critical: mux-option frontend writes smux client fields ==
The frontend mux-option form wrote enable/protocol/max-connections/
min-streams/max-streams/statistic/only-tcp (smux client format) into
the listener mux-option block. The backend listener schema only accepts
padding + brutal.{enabled,up,down} per official docs. Saving a listener
with Mux enabled failed validation.
Fix: removed the 7 smux client fields from the frontend form and the
formValuesToConfig serializer. Only padding + brutal remain (matching
the listener schema). The smux fields belong on the proxy client side,
not the listener.
== C2 Critical: shadowquic URI export broken ==
shadowQUICURIs read userMap(cfg) but shadowquic uses array-form
users: [{username, password}] per official docs. The map read always
returned empty → URI export failed with 'requires at least one user'.
Fix: use userRows(cfg) (array form) matching the compiler/autofill.
== C3 Critical: trusttunnel URI export broken ==
Same bug class as C2. trustTunnelURIs read userMap(cfg) but trusttunnel
uses array-form users per official docs.
Fix: use userRows(cfg).
== M Minor: sudoku missing flat compatibility aliases ==
Official sudoku doc lists http-mask-mode and path-root as flat
compatibility aliases for httpmask.{mode, path-root}. The schema only
had disable-http-mask (top-level). Added http-mask-mode and path-root
to the sudoku Fields allowlist.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean
fix(audit): mux-option UI fields, shadowquic/trusttunnel URI, sudoku aliases
Final re-audit against official Mihomo docs (Meta-Docs-main) found 4
remaining issues. All fixed.
== C1 Critical: mux-option frontend writes smux client fields ==
The frontend mux-option form wrote enable/protocol/max-connections/
min-streams/max-streams/statistic/only-tcp (smux client format) into
the listener mux-option block. The backend listener schema only accepts
padding + brutal.{enabled,up,down} per official docs. Saving a listener
with Mux enabled failed validation.
Fix: removed the 7 smux client fields from the frontend form and the
formValuesToConfig serializer. Only padding + brutal remain (matching
the listener schema). The smux fields belong on the proxy client side,
not the listener.
== C2 Critical: shadowquic URI export broken ==
shadowQUICURIs read userMap(cfg) but shadowquic uses array-form
users: [{username, password}] per official docs. The map read always
returned empty → URI export failed with 'requires at least one user'.
Fix: use userRows(cfg) (array form) matching the compiler/autofill.
== C3 Critical: trusttunnel URI export broken ==
Same bug class as C2. trustTunnelURIs read userMap(cfg) but trusttunnel
uses array-form users per official docs.
Fix: use userRows(cfg).
== M Minor: sudoku missing flat compatibility aliases ==
Official sudoku doc lists http-mask-mode and path-root as flat
compatibility aliases for httpmask.{mode, path-root}. The schema only
had disable-http-mask (top-level). Added http-mask-mode and path-root
to the sudoku Fields allowlist.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean
fix(audit): mux-option UI fields, shadowquic/trusttunnel URI, sudoku aliases
Final re-audit against official Mihomo docs (Meta-Docs-main) found 4
remaining issues. All fixed.
== C1 Critical: mux-option frontend writes smux client fields ==
The frontend mux-option form wrote enable/protocol/max-connections/
min-streams/max-streams/statistic/only-tcp (smux client format) into
the listener mux-option block. The backend listener schema only accepts
padding + brutal.{enabled,up,down} per official docs. Saving a listener
with Mux enabled failed validation.
Fix: removed the 7 smux client fields from the frontend form and the
formValuesToConfig serializer. Only padding + brutal remain (matching
the listener schema). The smux fields belong on the proxy client side,
not the listener.
== C2 Critical: shadowquic URI export broken ==
shadowQUICURIs read userMap(cfg) but shadowquic uses array-form
users: [{username, password}] per official docs. The map read always
returned empty → URI export failed with 'requires at least one user'.
Fix: use userRows(cfg) (array form) matching the compiler/autofill.
== C3 Critical: trusttunnel URI export broken ==
Same bug class as C2. trustTunnelURIs read userMap(cfg) but trusttunnel
uses array-form users per official docs.
Fix: use userRows(cfg).
== M Minor: sudoku missing flat compatibility aliases ==
Official sudoku doc lists http-mask-mode and path-root as flat
compatibility aliases for httpmask.{mode, path-root}. The schema only
had disable-http-mask (top-level). Added http-mask-mode and path-root
to the sudoku Fields allowlist.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean
fix(audit): mux-option UI fields, shadowquic/trusttunnel URI, sudoku aliases
Final re-audit against official Mihomo docs (Meta-Docs-main) found 4
remaining issues. All fixed.
== C1 Critical: mux-option frontend writes smux client fields ==
The frontend mux-option form wrote enable/protocol/max-connections/
min-streams/max-streams/statistic/only-tcp (smux client format) into
the listener mux-option block. The backend listener schema only accepts
padding + brutal.{enabled,up,down} per official docs. Saving a listener
with Mux enabled failed validation.
Fix: removed the 7 smux client fields from the frontend form and the
formValuesToConfig serializer. Only padding + brutal remain (matching
the listener schema). The smux fields belong on the proxy client side,
not the listener.
== C2 Critical: shadowquic URI export broken ==
shadowQUICURIs read userMap(cfg) but shadowquic uses array-form
users: [{username, password}] per official docs. The map read always
returned empty → URI export failed with 'requires at least one user'.
Fix: use userRows(cfg) (array form) matching the compiler/autofill.
== C3 Critical: trusttunnel URI export broken ==
Same bug class as C2. trustTunnelURIs read userMap(cfg) but trusttunnel
uses array-form users per official docs.
Fix: use userRows(cfg).
== M Minor: sudoku missing flat compatibility aliases ==
Official sudoku doc lists http-mask-mode and path-root as flat
compatibility aliases for httpmask.{mode, path-root}. The schema only
had disable-http-mask (top-level). Added http-mask-mode and path-root
to the sudoku Fields allowlist.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean
fix(audit): mux-option UI fields, shadowquic/trusttunnel URI, sudoku aliases
Final re-audit against official Mihomo docs (Meta-Docs-main) found 4
remaining issues. All fixed.
== C1 Critical: mux-option frontend writes smux client fields ==
The frontend mux-option form wrote enable/protocol/max-connections/
min-streams/max-streams/statistic/only-tcp (smux client format) into
the listener mux-option block. The backend listener schema only accepts
padding + brutal.{enabled,up,down} per official docs. Saving a listener
with Mux enabled failed validation.
Fix: removed the 7 smux client fields from the frontend form and the
formValuesToConfig serializer. Only padding + brutal remain (matching
the listener schema). The smux fields belong on the proxy client side,
not the listener.
== C2 Critical: shadowquic URI export broken ==
shadowQUICURIs read userMap(cfg) but shadowquic uses array-form
users: [{username, password}] per official docs. The map read always
returned empty → URI export failed with 'requires at least one user'.
Fix: use userRows(cfg) (array form) matching the compiler/autofill.
== C3 Critical: trusttunnel URI export broken ==
Same bug class as C2. trustTunnelURIs read userMap(cfg) but trusttunnel
uses array-form users per official docs.
Fix: use userRows(cfg).
== M Minor: sudoku missing flat compatibility aliases ==
Official sudoku doc lists http-mask-mode and path-root as flat
compatibility aliases for httpmask.{mode, path-root}. The schema only
had disable-http-mask (top-level). Added http-mask-mode and path-root
to the sudoku Fields allowlist.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean
fix(audit): mux-option UI fields, shadowquic/trusttunnel URI, sudoku aliases
Final re-audit against official Mihomo docs (Meta-Docs-main) found 4
remaining issues. All fixed.
== C1 Critical: mux-option frontend writes smux client fields ==
The frontend mux-option form wrote enable/protocol/max-connections/
min-streams/max-streams/statistic/only-tcp (smux client format) into
the listener mux-option block. The backend listener schema only accepts
padding + brutal.{enabled,up,down} per official docs. Saving a listener
with Mux enabled failed validation.
Fix: removed the 7 smux client fields from the frontend form and the
formValuesToConfig serializer. Only padding + brutal remain (matching
the listener schema). The smux fields belong on the proxy client side,
not the listener.
== C2 Critical: shadowquic URI export broken ==
shadowQUICURIs read userMap(cfg) but shadowquic uses array-form
users: [{username, password}] per official docs. The map read always
returned empty → URI export failed with 'requires at least one user'.
Fix: use userRows(cfg) (array form) matching the compiler/autofill.
== C3 Critical: trusttunnel URI export broken ==
Same bug class as C2. trustTunnelURIs read userMap(cfg) but trusttunnel
uses array-form users per official docs.
Fix: use userRows(cfg).
== M Minor: sudoku missing flat compatibility aliases ==
Official sudoku doc lists http-mask-mode and path-root as flat
compatibility aliases for httpmask.{mode, path-root}. The schema only
had disable-http-mask (top-level). Added http-mask-mode and path-root
to the sudoku Fields allowlist.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean
fix(audit): mux-option UI fields, shadowquic/trusttunnel URI, sudoku aliases
Final re-audit against official Mihomo docs (Meta-Docs-main) found 4
remaining issues. All fixed.
== C1 Critical: mux-option frontend writes smux client fields ==
The frontend mux-option form wrote enable/protocol/max-connections/
min-streams/max-streams/statistic/only-tcp (smux client format) into
the listener mux-option block. The backend listener schema only accepts
padding + brutal.{enabled,up,down} per official docs. Saving a listener
with Mux enabled failed validation.
Fix: removed the 7 smux client fields from the frontend form and the
formValuesToConfig serializer. Only padding + brutal remain (matching
the listener schema). The smux fields belong on the proxy client side,
not the listener.
== C2 Critical: shadowquic URI export broken ==
shadowQUICURIs read userMap(cfg) but shadowquic uses array-form
users: [{username, password}] per official docs. The map read always
returned empty → URI export failed with 'requires at least one user'.
Fix: use userRows(cfg) (array form) matching the compiler/autofill.
== C3 Critical: trusttunnel URI export broken ==
Same bug class as C2. trustTunnelURIs read userMap(cfg) but trusttunnel
uses array-form users per official docs.
Fix: use userRows(cfg).
== M Minor: sudoku missing flat compatibility aliases ==
Official sudoku doc lists http-mask-mode and path-root as flat
compatibility aliases for httpmask.{mode, path-root}. The schema only
had disable-http-mask (top-level). Added http-mask-mode and path-root
to the sudoku Fields allowlist.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean
fix(audit): mux-option UI fields, shadowquic/trusttunnel URI, sudoku aliases
Final re-audit against official Mihomo docs (Meta-Docs-main) found 4
remaining issues. All fixed.
== C1 Critical: mux-option frontend writes smux client fields ==
The frontend mux-option form wrote enable/protocol/max-connections/
min-streams/max-streams/statistic/only-tcp (smux client format) into
the listener mux-option block. The backend listener schema only accepts
padding + brutal.{enabled,up,down} per official docs. Saving a listener
with Mux enabled failed validation.
Fix: removed the 7 smux client fields from the frontend form and the
formValuesToConfig serializer. Only padding + brutal remain (matching
the listener schema). The smux fields belong on the proxy client side,
not the listener.
== C2 Critical: shadowquic URI export broken ==
shadowQUICURIs read userMap(cfg) but shadowquic uses array-form
users: [{username, password}] per official docs. The map read always
returned empty → URI export failed with 'requires at least one user'.
Fix: use userRows(cfg) (array form) matching the compiler/autofill.
== C3 Critical: trusttunnel URI export broken ==
Same bug class as C2. trustTunnelURIs read userMap(cfg) but trusttunnel
uses array-form users per official docs.
Fix: use userRows(cfg).
== M Minor: sudoku missing flat compatibility aliases ==
Official sudoku doc lists http-mask-mode and path-root as flat
compatibility aliases for httpmask.{mode, path-root}. The schema only
had disable-http-mask (top-level). Added http-mask-mode and path-root
to the sudoku Fields allowlist.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean
fix(audit): mux-option UI fields, shadowquic/trusttunnel URI, sudoku aliases
Final re-audit against official Mihomo docs (Meta-Docs-main) found 4
remaining issues. All fixed.
== C1 Critical: mux-option frontend writes smux client fields ==
The frontend mux-option form wrote enable/protocol/max-connections/
min-streams/max-streams/statistic/only-tcp (smux client format) into
the listener mux-option block. The backend listener schema only accepts
padding + brutal.{enabled,up,down} per official docs. Saving a listener
with Mux enabled failed validation.
Fix: removed the 7 smux client fields from the frontend form and the
formValuesToConfig serializer. Only padding + brutal remain (matching
the listener schema). The smux fields belong on the proxy client side,
not the listener.
== C2 Critical: shadowquic URI export broken ==
shadowQUICURIs read userMap(cfg) but shadowquic uses array-form
users: [{username, password}] per official docs. The map read always
returned empty → URI export failed with 'requires at least one user'.
Fix: use userRows(cfg) (array form) matching the compiler/autofill.
== C3 Critical: trusttunnel URI export broken ==
Same bug class as C2. trustTunnelURIs read userMap(cfg) but trusttunnel
uses array-form users per official docs.
Fix: use userRows(cfg).
== M Minor: sudoku missing flat compatibility aliases ==
Official sudoku doc lists http-mask-mode and path-root as flat
compatibility aliases for httpmask.{mode, path-root}. The schema only
had disable-http-mask (top-level). Added http-mask-mode and path-root
to the sudoku Fields allowlist.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean
fix(audit): mux-option UI fields, shadowquic/trusttunnel URI, sudoku aliases
Final re-audit against official Mihomo docs (Meta-Docs-main) found 4
remaining issues. All fixed.
== C1 Critical: mux-option frontend writes smux client fields ==
The frontend mux-option form wrote enable/protocol/max-connections/
min-streams/max-streams/statistic/only-tcp (smux client format) into
the listener mux-option block. The backend listener schema only accepts
padding + brutal.{enabled,up,down} per official docs. Saving a listener
with Mux enabled failed validation.
Fix: removed the 7 smux client fields from the frontend form and the
formValuesToConfig serializer. Only padding + brutal remain (matching
the listener schema). The smux fields belong on the proxy client side,
not the listener.
== C2 Critical: shadowquic URI export broken ==
shadowQUICURIs read userMap(cfg) but shadowquic uses array-form
users: [{username, password}] per official docs. The map read always
returned empty → URI export failed with 'requires at least one user'.
Fix: use userRows(cfg) (array form) matching the compiler/autofill.
== C3 Critical: trusttunnel URI export broken ==
Same bug class as C2. trustTunnelURIs read userMap(cfg) but trusttunnel
uses array-form users per official docs.
Fix: use userRows(cfg).
== M Minor: sudoku missing flat compatibility aliases ==
Official sudoku doc lists http-mask-mode and path-root as flat
compatibility aliases for httpmask.{mode, path-root}. The schema only
had disable-http-mask (top-level). Added http-mask-mode and path-root
to the sudoku Fields allowlist.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean
fix(audit): mux-option UI fields, shadowquic/trusttunnel URI, sudoku aliases
Final re-audit against official Mihomo docs (Meta-Docs-main) found 4
remaining issues. All fixed.
== C1 Critical: mux-option frontend writes smux client fields ==
The frontend mux-option form wrote enable/protocol/max-connections/
min-streams/max-streams/statistic/only-tcp (smux client format) into
the listener mux-option block. The backend listener schema only accepts
padding + brutal.{enabled,up,down} per official docs. Saving a listener
with Mux enabled failed validation.
Fix: removed the 7 smux client fields from the frontend form and the
formValuesToConfig serializer. Only padding + brutal remain (matching
the listener schema). The smux fields belong on the proxy client side,
not the listener.
== C2 Critical: shadowquic URI export broken ==
shadowQUICURIs read userMap(cfg) but shadowquic uses array-form
users: [{username, password}] per official docs. The map read always
returned empty → URI export failed with 'requires at least one user'.
Fix: use userRows(cfg) (array form) matching the compiler/autofill.
== C3 Critical: trusttunnel URI export broken ==
Same bug class as C2. trustTunnelURIs read userMap(cfg) but trusttunnel
uses array-form users per official docs.
Fix: use userRows(cfg).
== M Minor: sudoku missing flat compatibility aliases ==
Official sudoku doc lists http-mask-mode and path-root as flat
compatibility aliases for httpmask.{mode, path-root}. The schema only
had disable-http-mask (top-level). Added http-mask-mode and path-root
to the sudoku Fields allowlist.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS
- gofmt : clean
2026-08-30 08:30:38 +00:00
風雪 f434b6293a fix(listeners): transport options per protocol + clean autofill hint + remove dead hy2 fields
Second-round audit against https://wiki.metacubex.one/config/inbound/
found and fixed 4 issues:
== 1. XHTTP transport option shown for trojan/vmess (frontend) ==
Official Mihomo docs explicitly state:
'xhttp: 仅 VLESS 支持 xhttp 传输层,请勿在其他协议上使用'
'mkcp: 仅 VMess 支持 mKCP 传输层'
'mekya: 仅 VMess 支持 Mekya 传输层'
The transport_layer Radio.Group showed TCP/ws/grpc/xhttp for ALL
protocols in TRANSPORT_PROTOCOLS (vmess/vless/trojan). Wrapped the
xhttp Radio.Button in {XHTTP_PROTOCOLS.has(protocol) && ...} so it
only appears for vless. TCP/ws/grpc remain shared. mekya/mkcp for
vmess already render via dedicated EnableSection blocks (unchanged).
== 2. Autofill hint was SS/Snell-centric (frontend i18n) ==
The global autoGenerateHint Alert mentioned only 'REALITY/SS/Snell/Hy2'
but the autofill logic now covers ALL protocols (sudoku key, snell
version, trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel users).
Updated zh+en i18n strings to enumerate all autofilled fields so the
hint is consistent and accurate regardless of which protocol form is
open.
== 3. hysteria2 dead fields obfs-min/max-packet-size (backend+frontend) ==
These fields are NOT in the Mihomo hysteria2 listener source (appear
copied from hysteria v1). Removed from:
- schema registry Fields allowlist (backend)
- ListenerConfigFields.tsx FORM_OWNED_KEYS + set() calls + Form.Item
- mui/protocol/hysteria2.go struct fields + struct literals
- mui/domain/node.go Hysteria2Spec fields
Kept max-idle-time + handshake-timeout (present in Mihomo source, just
wiki-undocumented). Kept obfs/obfs-password (salamander, wiki-documented).
== 4. trojan ss-option verified (no change) ==
Confirmed present in Mihomo trojan listener source (wraps trojan in a
shadowsocks layer). Real feature, wiki-undocumented. Kept as-is.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
fix(listeners): transport options per protocol + clean autofill hint + remove dead hy2 fields
Second-round audit against https://wiki.metacubex.one/config/inbound/
found and fixed 4 issues:
== 1. XHTTP transport option shown for trojan/vmess (frontend) ==
Official Mihomo docs explicitly state:
'xhttp: 仅 VLESS 支持 xhttp 传输层,请勿在其他协议上使用'
'mkcp: 仅 VMess 支持 mKCP 传输层'
'mekya: 仅 VMess 支持 Mekya 传输层'
The transport_layer Radio.Group showed TCP/ws/grpc/xhttp for ALL
protocols in TRANSPORT_PROTOCOLS (vmess/vless/trojan). Wrapped the
xhttp Radio.Button in {XHTTP_PROTOCOLS.has(protocol) && ...} so it
only appears for vless. TCP/ws/grpc remain shared. mekya/mkcp for
vmess already render via dedicated EnableSection blocks (unchanged).
== 2. Autofill hint was SS/Snell-centric (frontend i18n) ==
The global autoGenerateHint Alert mentioned only 'REALITY/SS/Snell/Hy2'
but the autofill logic now covers ALL protocols (sudoku key, snell
version, trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel users).
Updated zh+en i18n strings to enumerate all autofilled fields so the
hint is consistent and accurate regardless of which protocol form is
open.
== 3. hysteria2 dead fields obfs-min/max-packet-size (backend+frontend) ==
These fields are NOT in the Mihomo hysteria2 listener source (appear
copied from hysteria v1). Removed from:
- schema registry Fields allowlist (backend)
- ListenerConfigFields.tsx FORM_OWNED_KEYS + set() calls + Form.Item
- mui/protocol/hysteria2.go struct fields + struct literals
- mui/domain/node.go Hysteria2Spec fields
Kept max-idle-time + handshake-timeout (present in Mihomo source, just
wiki-undocumented). Kept obfs/obfs-password (salamander, wiki-documented).
== 4. trojan ss-option verified (no change) ==
Confirmed present in Mihomo trojan listener source (wraps trojan in a
shadowsocks layer). Real feature, wiki-undocumented. Kept as-is.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
fix(listeners): transport options per protocol + clean autofill hint + remove dead hy2 fields
Second-round audit against https://wiki.metacubex.one/config/inbound/
found and fixed 4 issues:
== 1. XHTTP transport option shown for trojan/vmess (frontend) ==
Official Mihomo docs explicitly state:
'xhttp: 仅 VLESS 支持 xhttp 传输层,请勿在其他协议上使用'
'mkcp: 仅 VMess 支持 mKCP 传输层'
'mekya: 仅 VMess 支持 Mekya 传输层'
The transport_layer Radio.Group showed TCP/ws/grpc/xhttp for ALL
protocols in TRANSPORT_PROTOCOLS (vmess/vless/trojan). Wrapped the
xhttp Radio.Button in {XHTTP_PROTOCOLS.has(protocol) && ...} so it
only appears for vless. TCP/ws/grpc remain shared. mekya/mkcp for
vmess already render via dedicated EnableSection blocks (unchanged).
== 2. Autofill hint was SS/Snell-centric (frontend i18n) ==
The global autoGenerateHint Alert mentioned only 'REALITY/SS/Snell/Hy2'
but the autofill logic now covers ALL protocols (sudoku key, snell
version, trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel users).
Updated zh+en i18n strings to enumerate all autofilled fields so the
hint is consistent and accurate regardless of which protocol form is
open.
== 3. hysteria2 dead fields obfs-min/max-packet-size (backend+frontend) ==
These fields are NOT in the Mihomo hysteria2 listener source (appear
copied from hysteria v1). Removed from:
- schema registry Fields allowlist (backend)
- ListenerConfigFields.tsx FORM_OWNED_KEYS + set() calls + Form.Item
- mui/protocol/hysteria2.go struct fields + struct literals
- mui/domain/node.go Hysteria2Spec fields
Kept max-idle-time + handshake-timeout (present in Mihomo source, just
wiki-undocumented). Kept obfs/obfs-password (salamander, wiki-documented).
== 4. trojan ss-option verified (no change) ==
Confirmed present in Mihomo trojan listener source (wraps trojan in a
shadowsocks layer). Real feature, wiki-undocumented. Kept as-is.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
fix(listeners): transport options per protocol + clean autofill hint + remove dead hy2 fields
Second-round audit against https://wiki.metacubex.one/config/inbound/
found and fixed 4 issues:
== 1. XHTTP transport option shown for trojan/vmess (frontend) ==
Official Mihomo docs explicitly state:
'xhttp: 仅 VLESS 支持 xhttp 传输层,请勿在其他协议上使用'
'mkcp: 仅 VMess 支持 mKCP 传输层'
'mekya: 仅 VMess 支持 Mekya 传输层'
The transport_layer Radio.Group showed TCP/ws/grpc/xhttp for ALL
protocols in TRANSPORT_PROTOCOLS (vmess/vless/trojan). Wrapped the
xhttp Radio.Button in {XHTTP_PROTOCOLS.has(protocol) && ...} so it
only appears for vless. TCP/ws/grpc remain shared. mekya/mkcp for
vmess already render via dedicated EnableSection blocks (unchanged).
== 2. Autofill hint was SS/Snell-centric (frontend i18n) ==
The global autoGenerateHint Alert mentioned only 'REALITY/SS/Snell/Hy2'
but the autofill logic now covers ALL protocols (sudoku key, snell
version, trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel users).
Updated zh+en i18n strings to enumerate all autofilled fields so the
hint is consistent and accurate regardless of which protocol form is
open.
== 3. hysteria2 dead fields obfs-min/max-packet-size (backend+frontend) ==
These fields are NOT in the Mihomo hysteria2 listener source (appear
copied from hysteria v1). Removed from:
- schema registry Fields allowlist (backend)
- ListenerConfigFields.tsx FORM_OWNED_KEYS + set() calls + Form.Item
- mui/protocol/hysteria2.go struct fields + struct literals
- mui/domain/node.go Hysteria2Spec fields
Kept max-idle-time + handshake-timeout (present in Mihomo source, just
wiki-undocumented). Kept obfs/obfs-password (salamander, wiki-documented).
== 4. trojan ss-option verified (no change) ==
Confirmed present in Mihomo trojan listener source (wraps trojan in a
shadowsocks layer). Real feature, wiki-undocumented. Kept as-is.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
fix(listeners): transport options per protocol + clean autofill hint + remove dead hy2 fields
Second-round audit against https://wiki.metacubex.one/config/inbound/
found and fixed 4 issues:
== 1. XHTTP transport option shown for trojan/vmess (frontend) ==
Official Mihomo docs explicitly state:
'xhttp: 仅 VLESS 支持 xhttp 传输层,请勿在其他协议上使用'
'mkcp: 仅 VMess 支持 mKCP 传输层'
'mekya: 仅 VMess 支持 Mekya 传输层'
The transport_layer Radio.Group showed TCP/ws/grpc/xhttp for ALL
protocols in TRANSPORT_PROTOCOLS (vmess/vless/trojan). Wrapped the
xhttp Radio.Button in {XHTTP_PROTOCOLS.has(protocol) && ...} so it
only appears for vless. TCP/ws/grpc remain shared. mekya/mkcp for
vmess already render via dedicated EnableSection blocks (unchanged).
== 2. Autofill hint was SS/Snell-centric (frontend i18n) ==
The global autoGenerateHint Alert mentioned only 'REALITY/SS/Snell/Hy2'
but the autofill logic now covers ALL protocols (sudoku key, snell
version, trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel users).
Updated zh+en i18n strings to enumerate all autofilled fields so the
hint is consistent and accurate regardless of which protocol form is
open.
== 3. hysteria2 dead fields obfs-min/max-packet-size (backend+frontend) ==
These fields are NOT in the Mihomo hysteria2 listener source (appear
copied from hysteria v1). Removed from:
- schema registry Fields allowlist (backend)
- ListenerConfigFields.tsx FORM_OWNED_KEYS + set() calls + Form.Item
- mui/protocol/hysteria2.go struct fields + struct literals
- mui/domain/node.go Hysteria2Spec fields
Kept max-idle-time + handshake-timeout (present in Mihomo source, just
wiki-undocumented). Kept obfs/obfs-password (salamander, wiki-documented).
== 4. trojan ss-option verified (no change) ==
Confirmed present in Mihomo trojan listener source (wraps trojan in a
shadowsocks layer). Real feature, wiki-undocumented. Kept as-is.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
fix(listeners): transport options per protocol + clean autofill hint + remove dead hy2 fields
Second-round audit against https://wiki.metacubex.one/config/inbound/
found and fixed 4 issues:
== 1. XHTTP transport option shown for trojan/vmess (frontend) ==
Official Mihomo docs explicitly state:
'xhttp: 仅 VLESS 支持 xhttp 传输层,请勿在其他协议上使用'
'mkcp: 仅 VMess 支持 mKCP 传输层'
'mekya: 仅 VMess 支持 Mekya 传输层'
The transport_layer Radio.Group showed TCP/ws/grpc/xhttp for ALL
protocols in TRANSPORT_PROTOCOLS (vmess/vless/trojan). Wrapped the
xhttp Radio.Button in {XHTTP_PROTOCOLS.has(protocol) && ...} so it
only appears for vless. TCP/ws/grpc remain shared. mekya/mkcp for
vmess already render via dedicated EnableSection blocks (unchanged).
== 2. Autofill hint was SS/Snell-centric (frontend i18n) ==
The global autoGenerateHint Alert mentioned only 'REALITY/SS/Snell/Hy2'
but the autofill logic now covers ALL protocols (sudoku key, snell
version, trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel users).
Updated zh+en i18n strings to enumerate all autofilled fields so the
hint is consistent and accurate regardless of which protocol form is
open.
== 3. hysteria2 dead fields obfs-min/max-packet-size (backend+frontend) ==
These fields are NOT in the Mihomo hysteria2 listener source (appear
copied from hysteria v1). Removed from:
- schema registry Fields allowlist (backend)
- ListenerConfigFields.tsx FORM_OWNED_KEYS + set() calls + Form.Item
- mui/protocol/hysteria2.go struct fields + struct literals
- mui/domain/node.go Hysteria2Spec fields
Kept max-idle-time + handshake-timeout (present in Mihomo source, just
wiki-undocumented). Kept obfs/obfs-password (salamander, wiki-documented).
== 4. trojan ss-option verified (no change) ==
Confirmed present in Mihomo trojan listener source (wraps trojan in a
shadowsocks layer). Real feature, wiki-undocumented. Kept as-is.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
fix(listeners): transport options per protocol + clean autofill hint + remove dead hy2 fields
Second-round audit against https://wiki.metacubex.one/config/inbound/
found and fixed 4 issues:
== 1. XHTTP transport option shown for trojan/vmess (frontend) ==
Official Mihomo docs explicitly state:
'xhttp: 仅 VLESS 支持 xhttp 传输层,请勿在其他协议上使用'
'mkcp: 仅 VMess 支持 mKCP 传输层'
'mekya: 仅 VMess 支持 Mekya 传输层'
The transport_layer Radio.Group showed TCP/ws/grpc/xhttp for ALL
protocols in TRANSPORT_PROTOCOLS (vmess/vless/trojan). Wrapped the
xhttp Radio.Button in {XHTTP_PROTOCOLS.has(protocol) && ...} so it
only appears for vless. TCP/ws/grpc remain shared. mekya/mkcp for
vmess already render via dedicated EnableSection blocks (unchanged).
== 2. Autofill hint was SS/Snell-centric (frontend i18n) ==
The global autoGenerateHint Alert mentioned only 'REALITY/SS/Snell/Hy2'
but the autofill logic now covers ALL protocols (sudoku key, snell
version, trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel users).
Updated zh+en i18n strings to enumerate all autofilled fields so the
hint is consistent and accurate regardless of which protocol form is
open.
== 3. hysteria2 dead fields obfs-min/max-packet-size (backend+frontend) ==
These fields are NOT in the Mihomo hysteria2 listener source (appear
copied from hysteria v1). Removed from:
- schema registry Fields allowlist (backend)
- ListenerConfigFields.tsx FORM_OWNED_KEYS + set() calls + Form.Item
- mui/protocol/hysteria2.go struct fields + struct literals
- mui/domain/node.go Hysteria2Spec fields
Kept max-idle-time + handshake-timeout (present in Mihomo source, just
wiki-undocumented). Kept obfs/obfs-password (salamander, wiki-documented).
== 4. trojan ss-option verified (no change) ==
Confirmed present in Mihomo trojan listener source (wraps trojan in a
shadowsocks layer). Real feature, wiki-undocumented. Kept as-is.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
fix(listeners): transport options per protocol + clean autofill hint + remove dead hy2 fields
Second-round audit against https://wiki.metacubex.one/config/inbound/
found and fixed 4 issues:
== 1. XHTTP transport option shown for trojan/vmess (frontend) ==
Official Mihomo docs explicitly state:
'xhttp: 仅 VLESS 支持 xhttp 传输层,请勿在其他协议上使用'
'mkcp: 仅 VMess 支持 mKCP 传输层'
'mekya: 仅 VMess 支持 Mekya 传输层'
The transport_layer Radio.Group showed TCP/ws/grpc/xhttp for ALL
protocols in TRANSPORT_PROTOCOLS (vmess/vless/trojan). Wrapped the
xhttp Radio.Button in {XHTTP_PROTOCOLS.has(protocol) && ...} so it
only appears for vless. TCP/ws/grpc remain shared. mekya/mkcp for
vmess already render via dedicated EnableSection blocks (unchanged).
== 2. Autofill hint was SS/Snell-centric (frontend i18n) ==
The global autoGenerateHint Alert mentioned only 'REALITY/SS/Snell/Hy2'
but the autofill logic now covers ALL protocols (sudoku key, snell
version, trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel users).
Updated zh+en i18n strings to enumerate all autofilled fields so the
hint is consistent and accurate regardless of which protocol form is
open.
== 3. hysteria2 dead fields obfs-min/max-packet-size (backend+frontend) ==
These fields are NOT in the Mihomo hysteria2 listener source (appear
copied from hysteria v1). Removed from:
- schema registry Fields allowlist (backend)
- ListenerConfigFields.tsx FORM_OWNED_KEYS + set() calls + Form.Item
- mui/protocol/hysteria2.go struct fields + struct literals
- mui/domain/node.go Hysteria2Spec fields
Kept max-idle-time + handshake-timeout (present in Mihomo source, just
wiki-undocumented). Kept obfs/obfs-password (salamander, wiki-documented).
== 4. trojan ss-option verified (no change) ==
Confirmed present in Mihomo trojan listener source (wraps trojan in a
shadowsocks layer). Real feature, wiki-undocumented. Kept as-is.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
fix(listeners): transport options per protocol + clean autofill hint + remove dead hy2 fields
Second-round audit against https://wiki.metacubex.one/config/inbound/
found and fixed 4 issues:
== 1. XHTTP transport option shown for trojan/vmess (frontend) ==
Official Mihomo docs explicitly state:
'xhttp: 仅 VLESS 支持 xhttp 传输层,请勿在其他协议上使用'
'mkcp: 仅 VMess 支持 mKCP 传输层'
'mekya: 仅 VMess 支持 Mekya 传输层'
The transport_layer Radio.Group showed TCP/ws/grpc/xhttp for ALL
protocols in TRANSPORT_PROTOCOLS (vmess/vless/trojan). Wrapped the
xhttp Radio.Button in {XHTTP_PROTOCOLS.has(protocol) && ...} so it
only appears for vless. TCP/ws/grpc remain shared. mekya/mkcp for
vmess already render via dedicated EnableSection blocks (unchanged).
== 2. Autofill hint was SS/Snell-centric (frontend i18n) ==
The global autoGenerateHint Alert mentioned only 'REALITY/SS/Snell/Hy2'
but the autofill logic now covers ALL protocols (sudoku key, snell
version, trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel users).
Updated zh+en i18n strings to enumerate all autofilled fields so the
hint is consistent and accurate regardless of which protocol form is
open.
== 3. hysteria2 dead fields obfs-min/max-packet-size (backend+frontend) ==
These fields are NOT in the Mihomo hysteria2 listener source (appear
copied from hysteria v1). Removed from:
- schema registry Fields allowlist (backend)
- ListenerConfigFields.tsx FORM_OWNED_KEYS + set() calls + Form.Item
- mui/protocol/hysteria2.go struct fields + struct literals
- mui/domain/node.go Hysteria2Spec fields
Kept max-idle-time + handshake-timeout (present in Mihomo source, just
wiki-undocumented). Kept obfs/obfs-password (salamander, wiki-documented).
== 4. trojan ss-option verified (no change) ==
Confirmed present in Mihomo trojan listener source (wraps trojan in a
shadowsocks layer). Real feature, wiki-undocumented. Kept as-is.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
fix(listeners): transport options per protocol + clean autofill hint + remove dead hy2 fields
Second-round audit against https://wiki.metacubex.one/config/inbound/
found and fixed 4 issues:
== 1. XHTTP transport option shown for trojan/vmess (frontend) ==
Official Mihomo docs explicitly state:
'xhttp: 仅 VLESS 支持 xhttp 传输层,请勿在其他协议上使用'
'mkcp: 仅 VMess 支持 mKCP 传输层'
'mekya: 仅 VMess 支持 Mekya 传输层'
The transport_layer Radio.Group showed TCP/ws/grpc/xhttp for ALL
protocols in TRANSPORT_PROTOCOLS (vmess/vless/trojan). Wrapped the
xhttp Radio.Button in {XHTTP_PROTOCOLS.has(protocol) && ...} so it
only appears for vless. TCP/ws/grpc remain shared. mekya/mkcp for
vmess already render via dedicated EnableSection blocks (unchanged).
== 2. Autofill hint was SS/Snell-centric (frontend i18n) ==
The global autoGenerateHint Alert mentioned only 'REALITY/SS/Snell/Hy2'
but the autofill logic now covers ALL protocols (sudoku key, snell
version, trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel users).
Updated zh+en i18n strings to enumerate all autofilled fields so the
hint is consistent and accurate regardless of which protocol form is
open.
== 3. hysteria2 dead fields obfs-min/max-packet-size (backend+frontend) ==
These fields are NOT in the Mihomo hysteria2 listener source (appear
copied from hysteria v1). Removed from:
- schema registry Fields allowlist (backend)
- ListenerConfigFields.tsx FORM_OWNED_KEYS + set() calls + Form.Item
- mui/protocol/hysteria2.go struct fields + struct literals
- mui/domain/node.go Hysteria2Spec fields
Kept max-idle-time + handshake-timeout (present in Mihomo source, just
wiki-undocumented). Kept obfs/obfs-password (salamander, wiki-documented).
== 4. trojan ss-option verified (no change) ==
Confirmed present in Mihomo trojan listener source (wraps trojan in a
shadowsocks layer). Real feature, wiki-undocumented. Kept as-is.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
fix(listeners): transport options per protocol + clean autofill hint + remove dead hy2 fields
Second-round audit against https://wiki.metacubex.one/config/inbound/
found and fixed 4 issues:
== 1. XHTTP transport option shown for trojan/vmess (frontend) ==
Official Mihomo docs explicitly state:
'xhttp: 仅 VLESS 支持 xhttp 传输层,请勿在其他协议上使用'
'mkcp: 仅 VMess 支持 mKCP 传输层'
'mekya: 仅 VMess 支持 Mekya 传输层'
The transport_layer Radio.Group showed TCP/ws/grpc/xhttp for ALL
protocols in TRANSPORT_PROTOCOLS (vmess/vless/trojan). Wrapped the
xhttp Radio.Button in {XHTTP_PROTOCOLS.has(protocol) && ...} so it
only appears for vless. TCP/ws/grpc remain shared. mekya/mkcp for
vmess already render via dedicated EnableSection blocks (unchanged).
== 2. Autofill hint was SS/Snell-centric (frontend i18n) ==
The global autoGenerateHint Alert mentioned only 'REALITY/SS/Snell/Hy2'
but the autofill logic now covers ALL protocols (sudoku key, snell
version, trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel users).
Updated zh+en i18n strings to enumerate all autofilled fields so the
hint is consistent and accurate regardless of which protocol form is
open.
== 3. hysteria2 dead fields obfs-min/max-packet-size (backend+frontend) ==
These fields are NOT in the Mihomo hysteria2 listener source (appear
copied from hysteria v1). Removed from:
- schema registry Fields allowlist (backend)
- ListenerConfigFields.tsx FORM_OWNED_KEYS + set() calls + Form.Item
- mui/protocol/hysteria2.go struct fields + struct literals
- mui/domain/node.go Hysteria2Spec fields
Kept max-idle-time + handshake-timeout (present in Mihomo source, just
wiki-undocumented). Kept obfs/obfs-password (salamander, wiki-documented).
== 4. trojan ss-option verified (no change) ==
Confirmed present in Mihomo trojan listener source (wraps trojan in a
shadowsocks layer). Real feature, wiki-undocumented. Kept as-is.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
fix(listeners): transport options per protocol + clean autofill hint + remove dead hy2 fields
Second-round audit against https://wiki.metacubex.one/config/inbound/
found and fixed 4 issues:
== 1. XHTTP transport option shown for trojan/vmess (frontend) ==
Official Mihomo docs explicitly state:
'xhttp: 仅 VLESS 支持 xhttp 传输层,请勿在其他协议上使用'
'mkcp: 仅 VMess 支持 mKCP 传输层'
'mekya: 仅 VMess 支持 Mekya 传输层'
The transport_layer Radio.Group showed TCP/ws/grpc/xhttp for ALL
protocols in TRANSPORT_PROTOCOLS (vmess/vless/trojan). Wrapped the
xhttp Radio.Button in {XHTTP_PROTOCOLS.has(protocol) && ...} so it
only appears for vless. TCP/ws/grpc remain shared. mekya/mkcp for
vmess already render via dedicated EnableSection blocks (unchanged).
== 2. Autofill hint was SS/Snell-centric (frontend i18n) ==
The global autoGenerateHint Alert mentioned only 'REALITY/SS/Snell/Hy2'
but the autofill logic now covers ALL protocols (sudoku key, snell
version, trojan/hy2/anytls/mieru/tuic/shadowquic/trusttunnel users).
Updated zh+en i18n strings to enumerate all autofilled fields so the
hint is consistent and accurate regardless of which protocol form is
open.
== 3. hysteria2 dead fields obfs-min/max-packet-size (backend+frontend) ==
These fields are NOT in the Mihomo hysteria2 listener source (appear
copied from hysteria v1). Removed from:
- schema registry Fields allowlist (backend)
- ListenerConfigFields.tsx FORM_OWNED_KEYS + set() calls + Form.Item
- mui/protocol/hysteria2.go struct fields + struct literals
- mui/domain/node.go Hysteria2Spec fields
Kept max-idle-time + handshake-timeout (present in Mihomo source, just
wiki-undocumented). Kept obfs/obfs-password (salamander, wiki-documented).
== 4. trojan ss-option verified (no change) ==
Confirmed present in Mihomo trojan listener source (wraps trojan in a
shadowsocks layer). Real feature, wiki-undocumented. Kept as-is.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : 15 packages all PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
2026-08-29 14:18:07 +00:00
風雪 dc13e69951 feat(telegram): full bot enhancement with inline menus, cron reports, events
Reference: https://docs.sanaei.dev/zh/docs/operations/telegram-bot/
Field naming follows existing 3m-ui snake_case convention (not sanaei's
tg* prefix) per project maintainer's request.
== Data layer ==
- ProxyUser: add TelegramID (indexed) + TelegramName fields for linking
proxy users to their Telegram accounts (self-service queries).
- SafeUser + ToSafeUser: expose both new fields.
- user.Service: BindTelegram / UnbindTelegram / GetByTelegramID methods.
- user API: GET /by-telegram/:tgid, PUT /:id/telegram, DELETE /:id/telegram
(registered before /:id to avoid path capture).
- UpdateInput/CreateInput: accept TelegramID + TelegramName.
== Settings expansion (8 new fields, snake_case) ==
- schedule: cron expr or @daily/@every 6h for periodic reports (default @daily)
- attach_backup: include DB backup zip in periodic report
- language: bot message language zh/en (default zh)
- enabled_events: comma-sep event list (default 'login,cpu,crash')
Supports: login,cpu,memory,crash,node_down,node_up,block,unblock,expiry,traffic
- expiry_warn_days: remind N days before expiry (0=off)
- traffic_warn_gb: remind when remaining traffic < N GB (0=off)
- proxy_url: socks5:// or http:// proxy for Telegram API
- api_server: custom Bot API server (empty=official)
- EventEnabled(name) helper for per-event gating.
== Client enhancements ==
- Proxy support: http(s):// via http.Transport.Proxy; socks5:// via
golang.org/x/net/proxy SOCKS5 dialer. Parse failure → direct fallback.
- Custom API server (apiBase() replaces hardcoded URL).
- SendDocument: multipart sendDocument for backup file attachment.
- GetMe + SetMyCommands: for bot-info + set-my-commands endpoints.
- sendWithKeyboard: sendMessage with reply_markup (inline keyboards).
- answerCallbackQuery: clear callback loading spinner.
== Bot commands + inline menus ==
- /id: reply with sender's Telegram numeric ID.
- /usage: admin → search users; bound user → self-query traffic/expiry/sub URL.
- /start + /help: send inline keyboard menu (admin menu for admin chats,
user menu for bound users, plain text for unbound).
- Admin menu: 8 buttons (status/users/traffic/online/listeners/deldepleted/
backup/restart) in 4 rows.
- User menu: my_usage + my_sub buttons.
- callback_query handling: answerCallbackQuery + dispatch on callback_data.
- Access control: admin chats always pass; non-admin chats pass only when
FromID matches a ProxyUser.TelegramID.
- Bot struct: added userSvc field for GetByTelegramID lookups.
- NewBot signature: NewBot(db, mihomoSvc, userSvc).
== Cron scheduler (new telegram/scheduler.go) ==
- Periodic report goroutine driven by Settings.Schedule.
- Supports @hourly/@daily/@weekly/@monthly/@every <dur>/5-field cron.
- Report: host, version, uptime, CPU/mem/disk, user stats, expiring+depleted
counts.
- Optional backup: AttachBackup → system.WriteZip → SendDocument.
- Settings reload on every tick (no restart needed for schedule changes).
- NotifyEvent(event, details): generic event emitter for one-shot alerts.
== Events layer ==
- login.attempt: NotifyLogin (success) + NotifyLoginFailed (failure).
Failure path added to auth/api.go Login handler. Both gated by
EventEnabled('login') — admins can mute success spam yet keep failure alerts.
- memory.high: emitMemoryWarning via gopsutil/v4/mem. Reuses CPUWarnPct as
shared 'resource warn %' threshold. 1-hour cooldown dedup.
- crash: mihomo ProcessManager.SetCrashHandler → telegram.NotifyCrash.
Fires on non-zero exit while desired==true (unexpected crash, not admin stop).
Wired in app/container.go.
- Dual thresholds: expiry (hours + days), traffic (percent + GB) with
separate dedup keys so neither suppresses the other.
== API endpoints ==
- POST /telegram/set-my-commands: registers 12-command menu with Telegram.
- GET /telegram/bot-info: returns bot username/first_name/id.
== Frontend ==
- telegram.ts: 8 new fields in TelegramSettings interface + setTelegramCommands
+ fetchTelegramBotInfo API functions.
- Settings.tsx: 8 new form controls (schedule Input, language Select,
enabled_events multi-Select with 10 options, expiry_warn_days + traffic_warn_gb
InputNumber, attach_backup Switch, proxy_url Input, api_server Input).
Added 'Set bot commands' button calling setTelegramCommands.
- i18n: 11 new keys (zh + en) for the new settings labels.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : all 15 packages PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
feat(telegram): full bot enhancement with inline menus, cron reports, events
Reference: https://docs.sanaei.dev/zh/docs/operations/telegram-bot/
Field naming follows existing 3m-ui snake_case convention (not sanaei's
tg* prefix) per project maintainer's request.
== Data layer ==
- ProxyUser: add TelegramID (indexed) + TelegramName fields for linking
proxy users to their Telegram accounts (self-service queries).
- SafeUser + ToSafeUser: expose both new fields.
- user.Service: BindTelegram / UnbindTelegram / GetByTelegramID methods.
- user API: GET /by-telegram/:tgid, PUT /:id/telegram, DELETE /:id/telegram
(registered before /:id to avoid path capture).
- UpdateInput/CreateInput: accept TelegramID + TelegramName.
== Settings expansion (8 new fields, snake_case) ==
- schedule: cron expr or @daily/@every 6h for periodic reports (default @daily)
- attach_backup: include DB backup zip in periodic report
- language: bot message language zh/en (default zh)
- enabled_events: comma-sep event list (default 'login,cpu,crash')
Supports: login,cpu,memory,crash,node_down,node_up,block,unblock,expiry,traffic
- expiry_warn_days: remind N days before expiry (0=off)
- traffic_warn_gb: remind when remaining traffic < N GB (0=off)
- proxy_url: socks5:// or http:// proxy for Telegram API
- api_server: custom Bot API server (empty=official)
- EventEnabled(name) helper for per-event gating.
== Client enhancements ==
- Proxy support: http(s):// via http.Transport.Proxy; socks5:// via
golang.org/x/net/proxy SOCKS5 dialer. Parse failure → direct fallback.
- Custom API server (apiBase() replaces hardcoded URL).
- SendDocument: multipart sendDocument for backup file attachment.
- GetMe + SetMyCommands: for bot-info + set-my-commands endpoints.
- sendWithKeyboard: sendMessage with reply_markup (inline keyboards).
- answerCallbackQuery: clear callback loading spinner.
== Bot commands + inline menus ==
- /id: reply with sender's Telegram numeric ID.
- /usage: admin → search users; bound user → self-query traffic/expiry/sub URL.
- /start + /help: send inline keyboard menu (admin menu for admin chats,
user menu for bound users, plain text for unbound).
- Admin menu: 8 buttons (status/users/traffic/online/listeners/deldepleted/
backup/restart) in 4 rows.
- User menu: my_usage + my_sub buttons.
- callback_query handling: answerCallbackQuery + dispatch on callback_data.
- Access control: admin chats always pass; non-admin chats pass only when
FromID matches a ProxyUser.TelegramID.
- Bot struct: added userSvc field for GetByTelegramID lookups.
- NewBot signature: NewBot(db, mihomoSvc, userSvc).
== Cron scheduler (new telegram/scheduler.go) ==
- Periodic report goroutine driven by Settings.Schedule.
- Supports @hourly/@daily/@weekly/@monthly/@every <dur>/5-field cron.
- Report: host, version, uptime, CPU/mem/disk, user stats, expiring+depleted
counts.
- Optional backup: AttachBackup → system.WriteZip → SendDocument.
- Settings reload on every tick (no restart needed for schedule changes).
- NotifyEvent(event, details): generic event emitter for one-shot alerts.
== Events layer ==
- login.attempt: NotifyLogin (success) + NotifyLoginFailed (failure).
Failure path added to auth/api.go Login handler. Both gated by
EventEnabled('login') — admins can mute success spam yet keep failure alerts.
- memory.high: emitMemoryWarning via gopsutil/v4/mem. Reuses CPUWarnPct as
shared 'resource warn %' threshold. 1-hour cooldown dedup.
- crash: mihomo ProcessManager.SetCrashHandler → telegram.NotifyCrash.
Fires on non-zero exit while desired==true (unexpected crash, not admin stop).
Wired in app/container.go.
- Dual thresholds: expiry (hours + days), traffic (percent + GB) with
separate dedup keys so neither suppresses the other.
== API endpoints ==
- POST /telegram/set-my-commands: registers 12-command menu with Telegram.
- GET /telegram/bot-info: returns bot username/first_name/id.
== Frontend ==
- telegram.ts: 8 new fields in TelegramSettings interface + setTelegramCommands
+ fetchTelegramBotInfo API functions.
- Settings.tsx: 8 new form controls (schedule Input, language Select,
enabled_events multi-Select with 10 options, expiry_warn_days + traffic_warn_gb
InputNumber, attach_backup Switch, proxy_url Input, api_server Input).
Added 'Set bot commands' button calling setTelegramCommands.
- i18n: 11 new keys (zh + en) for the new settings labels.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : all 15 packages PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
feat(telegram): full bot enhancement with inline menus, cron reports, events
Reference: https://docs.sanaei.dev/zh/docs/operations/telegram-bot/
Field naming follows existing 3m-ui snake_case convention (not sanaei's
tg* prefix) per project maintainer's request.
== Data layer ==
- ProxyUser: add TelegramID (indexed) + TelegramName fields for linking
proxy users to their Telegram accounts (self-service queries).
- SafeUser + ToSafeUser: expose both new fields.
- user.Service: BindTelegram / UnbindTelegram / GetByTelegramID methods.
- user API: GET /by-telegram/:tgid, PUT /:id/telegram, DELETE /:id/telegram
(registered before /:id to avoid path capture).
- UpdateInput/CreateInput: accept TelegramID + TelegramName.
== Settings expansion (8 new fields, snake_case) ==
- schedule: cron expr or @daily/@every 6h for periodic reports (default @daily)
- attach_backup: include DB backup zip in periodic report
- language: bot message language zh/en (default zh)
- enabled_events: comma-sep event list (default 'login,cpu,crash')
Supports: login,cpu,memory,crash,node_down,node_up,block,unblock,expiry,traffic
- expiry_warn_days: remind N days before expiry (0=off)
- traffic_warn_gb: remind when remaining traffic < N GB (0=off)
- proxy_url: socks5:// or http:// proxy for Telegram API
- api_server: custom Bot API server (empty=official)
- EventEnabled(name) helper for per-event gating.
== Client enhancements ==
- Proxy support: http(s):// via http.Transport.Proxy; socks5:// via
golang.org/x/net/proxy SOCKS5 dialer. Parse failure → direct fallback.
- Custom API server (apiBase() replaces hardcoded URL).
- SendDocument: multipart sendDocument for backup file attachment.
- GetMe + SetMyCommands: for bot-info + set-my-commands endpoints.
- sendWithKeyboard: sendMessage with reply_markup (inline keyboards).
- answerCallbackQuery: clear callback loading spinner.
== Bot commands + inline menus ==
- /id: reply with sender's Telegram numeric ID.
- /usage: admin → search users; bound user → self-query traffic/expiry/sub URL.
- /start + /help: send inline keyboard menu (admin menu for admin chats,
user menu for bound users, plain text for unbound).
- Admin menu: 8 buttons (status/users/traffic/online/listeners/deldepleted/
backup/restart) in 4 rows.
- User menu: my_usage + my_sub buttons.
- callback_query handling: answerCallbackQuery + dispatch on callback_data.
- Access control: admin chats always pass; non-admin chats pass only when
FromID matches a ProxyUser.TelegramID.
- Bot struct: added userSvc field for GetByTelegramID lookups.
- NewBot signature: NewBot(db, mihomoSvc, userSvc).
== Cron scheduler (new telegram/scheduler.go) ==
- Periodic report goroutine driven by Settings.Schedule.
- Supports @hourly/@daily/@weekly/@monthly/@every <dur>/5-field cron.
- Report: host, version, uptime, CPU/mem/disk, user stats, expiring+depleted
counts.
- Optional backup: AttachBackup → system.WriteZip → SendDocument.
- Settings reload on every tick (no restart needed for schedule changes).
- NotifyEvent(event, details): generic event emitter for one-shot alerts.
== Events layer ==
- login.attempt: NotifyLogin (success) + NotifyLoginFailed (failure).
Failure path added to auth/api.go Login handler. Both gated by
EventEnabled('login') — admins can mute success spam yet keep failure alerts.
- memory.high: emitMemoryWarning via gopsutil/v4/mem. Reuses CPUWarnPct as
shared 'resource warn %' threshold. 1-hour cooldown dedup.
- crash: mihomo ProcessManager.SetCrashHandler → telegram.NotifyCrash.
Fires on non-zero exit while desired==true (unexpected crash, not admin stop).
Wired in app/container.go.
- Dual thresholds: expiry (hours + days), traffic (percent + GB) with
separate dedup keys so neither suppresses the other.
== API endpoints ==
- POST /telegram/set-my-commands: registers 12-command menu with Telegram.
- GET /telegram/bot-info: returns bot username/first_name/id.
== Frontend ==
- telegram.ts: 8 new fields in TelegramSettings interface + setTelegramCommands
+ fetchTelegramBotInfo API functions.
- Settings.tsx: 8 new form controls (schedule Input, language Select,
enabled_events multi-Select with 10 options, expiry_warn_days + traffic_warn_gb
InputNumber, attach_backup Switch, proxy_url Input, api_server Input).
Added 'Set bot commands' button calling setTelegramCommands.
- i18n: 11 new keys (zh + en) for the new settings labels.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : all 15 packages PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
feat(telegram): full bot enhancement with inline menus, cron reports, events
Reference: https://docs.sanaei.dev/zh/docs/operations/telegram-bot/
Field naming follows existing 3m-ui snake_case convention (not sanaei's
tg* prefix) per project maintainer's request.
== Data layer ==
- ProxyUser: add TelegramID (indexed) + TelegramName fields for linking
proxy users to their Telegram accounts (self-service queries).
- SafeUser + ToSafeUser: expose both new fields.
- user.Service: BindTelegram / UnbindTelegram / GetByTelegramID methods.
- user API: GET /by-telegram/:tgid, PUT /:id/telegram, DELETE /:id/telegram
(registered before /:id to avoid path capture).
- UpdateInput/CreateInput: accept TelegramID + TelegramName.
== Settings expansion (8 new fields, snake_case) ==
- schedule: cron expr or @daily/@every 6h for periodic reports (default @daily)
- attach_backup: include DB backup zip in periodic report
- language: bot message language zh/en (default zh)
- enabled_events: comma-sep event list (default 'login,cpu,crash')
Supports: login,cpu,memory,crash,node_down,node_up,block,unblock,expiry,traffic
- expiry_warn_days: remind N days before expiry (0=off)
- traffic_warn_gb: remind when remaining traffic < N GB (0=off)
- proxy_url: socks5:// or http:// proxy for Telegram API
- api_server: custom Bot API server (empty=official)
- EventEnabled(name) helper for per-event gating.
== Client enhancements ==
- Proxy support: http(s):// via http.Transport.Proxy; socks5:// via
golang.org/x/net/proxy SOCKS5 dialer. Parse failure → direct fallback.
- Custom API server (apiBase() replaces hardcoded URL).
- SendDocument: multipart sendDocument for backup file attachment.
- GetMe + SetMyCommands: for bot-info + set-my-commands endpoints.
- sendWithKeyboard: sendMessage with reply_markup (inline keyboards).
- answerCallbackQuery: clear callback loading spinner.
== Bot commands + inline menus ==
- /id: reply with sender's Telegram numeric ID.
- /usage: admin → search users; bound user → self-query traffic/expiry/sub URL.
- /start + /help: send inline keyboard menu (admin menu for admin chats,
user menu for bound users, plain text for unbound).
- Admin menu: 8 buttons (status/users/traffic/online/listeners/deldepleted/
backup/restart) in 4 rows.
- User menu: my_usage + my_sub buttons.
- callback_query handling: answerCallbackQuery + dispatch on callback_data.
- Access control: admin chats always pass; non-admin chats pass only when
FromID matches a ProxyUser.TelegramID.
- Bot struct: added userSvc field for GetByTelegramID lookups.
- NewBot signature: NewBot(db, mihomoSvc, userSvc).
== Cron scheduler (new telegram/scheduler.go) ==
- Periodic report goroutine driven by Settings.Schedule.
- Supports @hourly/@daily/@weekly/@monthly/@every <dur>/5-field cron.
- Report: host, version, uptime, CPU/mem/disk, user stats, expiring+depleted
counts.
- Optional backup: AttachBackup → system.WriteZip → SendDocument.
- Settings reload on every tick (no restart needed for schedule changes).
- NotifyEvent(event, details): generic event emitter for one-shot alerts.
== Events layer ==
- login.attempt: NotifyLogin (success) + NotifyLoginFailed (failure).
Failure path added to auth/api.go Login handler. Both gated by
EventEnabled('login') — admins can mute success spam yet keep failure alerts.
- memory.high: emitMemoryWarning via gopsutil/v4/mem. Reuses CPUWarnPct as
shared 'resource warn %' threshold. 1-hour cooldown dedup.
- crash: mihomo ProcessManager.SetCrashHandler → telegram.NotifyCrash.
Fires on non-zero exit while desired==true (unexpected crash, not admin stop).
Wired in app/container.go.
- Dual thresholds: expiry (hours + days), traffic (percent + GB) with
separate dedup keys so neither suppresses the other.
== API endpoints ==
- POST /telegram/set-my-commands: registers 12-command menu with Telegram.
- GET /telegram/bot-info: returns bot username/first_name/id.
== Frontend ==
- telegram.ts: 8 new fields in TelegramSettings interface + setTelegramCommands
+ fetchTelegramBotInfo API functions.
- Settings.tsx: 8 new form controls (schedule Input, language Select,
enabled_events multi-Select with 10 options, expiry_warn_days + traffic_warn_gb
InputNumber, attach_backup Switch, proxy_url Input, api_server Input).
Added 'Set bot commands' button calling setTelegramCommands.
- i18n: 11 new keys (zh + en) for the new settings labels.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : all 15 packages PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
feat(telegram): full bot enhancement with inline menus, cron reports, events
Reference: https://docs.sanaei.dev/zh/docs/operations/telegram-bot/
Field naming follows existing 3m-ui snake_case convention (not sanaei's
tg* prefix) per project maintainer's request.
== Data layer ==
- ProxyUser: add TelegramID (indexed) + TelegramName fields for linking
proxy users to their Telegram accounts (self-service queries).
- SafeUser + ToSafeUser: expose both new fields.
- user.Service: BindTelegram / UnbindTelegram / GetByTelegramID methods.
- user API: GET /by-telegram/:tgid, PUT /:id/telegram, DELETE /:id/telegram
(registered before /:id to avoid path capture).
- UpdateInput/CreateInput: accept TelegramID + TelegramName.
== Settings expansion (8 new fields, snake_case) ==
- schedule: cron expr or @daily/@every 6h for periodic reports (default @daily)
- attach_backup: include DB backup zip in periodic report
- language: bot message language zh/en (default zh)
- enabled_events: comma-sep event list (default 'login,cpu,crash')
Supports: login,cpu,memory,crash,node_down,node_up,block,unblock,expiry,traffic
- expiry_warn_days: remind N days before expiry (0=off)
- traffic_warn_gb: remind when remaining traffic < N GB (0=off)
- proxy_url: socks5:// or http:// proxy for Telegram API
- api_server: custom Bot API server (empty=official)
- EventEnabled(name) helper for per-event gating.
== Client enhancements ==
- Proxy support: http(s):// via http.Transport.Proxy; socks5:// via
golang.org/x/net/proxy SOCKS5 dialer. Parse failure → direct fallback.
- Custom API server (apiBase() replaces hardcoded URL).
- SendDocument: multipart sendDocument for backup file attachment.
- GetMe + SetMyCommands: for bot-info + set-my-commands endpoints.
- sendWithKeyboard: sendMessage with reply_markup (inline keyboards).
- answerCallbackQuery: clear callback loading spinner.
== Bot commands + inline menus ==
- /id: reply with sender's Telegram numeric ID.
- /usage: admin → search users; bound user → self-query traffic/expiry/sub URL.
- /start + /help: send inline keyboard menu (admin menu for admin chats,
user menu for bound users, plain text for unbound).
- Admin menu: 8 buttons (status/users/traffic/online/listeners/deldepleted/
backup/restart) in 4 rows.
- User menu: my_usage + my_sub buttons.
- callback_query handling: answerCallbackQuery + dispatch on callback_data.
- Access control: admin chats always pass; non-admin chats pass only when
FromID matches a ProxyUser.TelegramID.
- Bot struct: added userSvc field for GetByTelegramID lookups.
- NewBot signature: NewBot(db, mihomoSvc, userSvc).
== Cron scheduler (new telegram/scheduler.go) ==
- Periodic report goroutine driven by Settings.Schedule.
- Supports @hourly/@daily/@weekly/@monthly/@every <dur>/5-field cron.
- Report: host, version, uptime, CPU/mem/disk, user stats, expiring+depleted
counts.
- Optional backup: AttachBackup → system.WriteZip → SendDocument.
- Settings reload on every tick (no restart needed for schedule changes).
- NotifyEvent(event, details): generic event emitter for one-shot alerts.
== Events layer ==
- login.attempt: NotifyLogin (success) + NotifyLoginFailed (failure).
Failure path added to auth/api.go Login handler. Both gated by
EventEnabled('login') — admins can mute success spam yet keep failure alerts.
- memory.high: emitMemoryWarning via gopsutil/v4/mem. Reuses CPUWarnPct as
shared 'resource warn %' threshold. 1-hour cooldown dedup.
- crash: mihomo ProcessManager.SetCrashHandler → telegram.NotifyCrash.
Fires on non-zero exit while desired==true (unexpected crash, not admin stop).
Wired in app/container.go.
- Dual thresholds: expiry (hours + days), traffic (percent + GB) with
separate dedup keys so neither suppresses the other.
== API endpoints ==
- POST /telegram/set-my-commands: registers 12-command menu with Telegram.
- GET /telegram/bot-info: returns bot username/first_name/id.
== Frontend ==
- telegram.ts: 8 new fields in TelegramSettings interface + setTelegramCommands
+ fetchTelegramBotInfo API functions.
- Settings.tsx: 8 new form controls (schedule Input, language Select,
enabled_events multi-Select with 10 options, expiry_warn_days + traffic_warn_gb
InputNumber, attach_backup Switch, proxy_url Input, api_server Input).
Added 'Set bot commands' button calling setTelegramCommands.
- i18n: 11 new keys (zh + en) for the new settings labels.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : all 15 packages PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
feat(telegram): full bot enhancement with inline menus, cron reports, events
Reference: https://docs.sanaei.dev/zh/docs/operations/telegram-bot/
Field naming follows existing 3m-ui snake_case convention (not sanaei's
tg* prefix) per project maintainer's request.
== Data layer ==
- ProxyUser: add TelegramID (indexed) + TelegramName fields for linking
proxy users to their Telegram accounts (self-service queries).
- SafeUser + ToSafeUser: expose both new fields.
- user.Service: BindTelegram / UnbindTelegram / GetByTelegramID methods.
- user API: GET /by-telegram/:tgid, PUT /:id/telegram, DELETE /:id/telegram
(registered before /:id to avoid path capture).
- UpdateInput/CreateInput: accept TelegramID + TelegramName.
== Settings expansion (8 new fields, snake_case) ==
- schedule: cron expr or @daily/@every 6h for periodic reports (default @daily)
- attach_backup: include DB backup zip in periodic report
- language: bot message language zh/en (default zh)
- enabled_events: comma-sep event list (default 'login,cpu,crash')
Supports: login,cpu,memory,crash,node_down,node_up,block,unblock,expiry,traffic
- expiry_warn_days: remind N days before expiry (0=off)
- traffic_warn_gb: remind when remaining traffic < N GB (0=off)
- proxy_url: socks5:// or http:// proxy for Telegram API
- api_server: custom Bot API server (empty=official)
- EventEnabled(name) helper for per-event gating.
== Client enhancements ==
- Proxy support: http(s):// via http.Transport.Proxy; socks5:// via
golang.org/x/net/proxy SOCKS5 dialer. Parse failure → direct fallback.
- Custom API server (apiBase() replaces hardcoded URL).
- SendDocument: multipart sendDocument for backup file attachment.
- GetMe + SetMyCommands: for bot-info + set-my-commands endpoints.
- sendWithKeyboard: sendMessage with reply_markup (inline keyboards).
- answerCallbackQuery: clear callback loading spinner.
== Bot commands + inline menus ==
- /id: reply with sender's Telegram numeric ID.
- /usage: admin → search users; bound user → self-query traffic/expiry/sub URL.
- /start + /help: send inline keyboard menu (admin menu for admin chats,
user menu for bound users, plain text for unbound).
- Admin menu: 8 buttons (status/users/traffic/online/listeners/deldepleted/
backup/restart) in 4 rows.
- User menu: my_usage + my_sub buttons.
- callback_query handling: answerCallbackQuery + dispatch on callback_data.
- Access control: admin chats always pass; non-admin chats pass only when
FromID matches a ProxyUser.TelegramID.
- Bot struct: added userSvc field for GetByTelegramID lookups.
- NewBot signature: NewBot(db, mihomoSvc, userSvc).
== Cron scheduler (new telegram/scheduler.go) ==
- Periodic report goroutine driven by Settings.Schedule.
- Supports @hourly/@daily/@weekly/@monthly/@every <dur>/5-field cron.
- Report: host, version, uptime, CPU/mem/disk, user stats, expiring+depleted
counts.
- Optional backup: AttachBackup → system.WriteZip → SendDocument.
- Settings reload on every tick (no restart needed for schedule changes).
- NotifyEvent(event, details): generic event emitter for one-shot alerts.
== Events layer ==
- login.attempt: NotifyLogin (success) + NotifyLoginFailed (failure).
Failure path added to auth/api.go Login handler. Both gated by
EventEnabled('login') — admins can mute success spam yet keep failure alerts.
- memory.high: emitMemoryWarning via gopsutil/v4/mem. Reuses CPUWarnPct as
shared 'resource warn %' threshold. 1-hour cooldown dedup.
- crash: mihomo ProcessManager.SetCrashHandler → telegram.NotifyCrash.
Fires on non-zero exit while desired==true (unexpected crash, not admin stop).
Wired in app/container.go.
- Dual thresholds: expiry (hours + days), traffic (percent + GB) with
separate dedup keys so neither suppresses the other.
== API endpoints ==
- POST /telegram/set-my-commands: registers 12-command menu with Telegram.
- GET /telegram/bot-info: returns bot username/first_name/id.
== Frontend ==
- telegram.ts: 8 new fields in TelegramSettings interface + setTelegramCommands
+ fetchTelegramBotInfo API functions.
- Settings.tsx: 8 new form controls (schedule Input, language Select,
enabled_events multi-Select with 10 options, expiry_warn_days + traffic_warn_gb
InputNumber, attach_backup Switch, proxy_url Input, api_server Input).
Added 'Set bot commands' button calling setTelegramCommands.
- i18n: 11 new keys (zh + en) for the new settings labels.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : all 15 packages PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
feat(telegram): full bot enhancement with inline menus, cron reports, events
Reference: https://docs.sanaei.dev/zh/docs/operations/telegram-bot/
Field naming follows existing 3m-ui snake_case convention (not sanaei's
tg* prefix) per project maintainer's request.
== Data layer ==
- ProxyUser: add TelegramID (indexed) + TelegramName fields for linking
proxy users to their Telegram accounts (self-service queries).
- SafeUser + ToSafeUser: expose both new fields.
- user.Service: BindTelegram / UnbindTelegram / GetByTelegramID methods.
- user API: GET /by-telegram/:tgid, PUT /:id/telegram, DELETE /:id/telegram
(registered before /:id to avoid path capture).
- UpdateInput/CreateInput: accept TelegramID + TelegramName.
== Settings expansion (8 new fields, snake_case) ==
- schedule: cron expr or @daily/@every 6h for periodic reports (default @daily)
- attach_backup: include DB backup zip in periodic report
- language: bot message language zh/en (default zh)
- enabled_events: comma-sep event list (default 'login,cpu,crash')
Supports: login,cpu,memory,crash,node_down,node_up,block,unblock,expiry,traffic
- expiry_warn_days: remind N days before expiry (0=off)
- traffic_warn_gb: remind when remaining traffic < N GB (0=off)
- proxy_url: socks5:// or http:// proxy for Telegram API
- api_server: custom Bot API server (empty=official)
- EventEnabled(name) helper for per-event gating.
== Client enhancements ==
- Proxy support: http(s):// via http.Transport.Proxy; socks5:// via
golang.org/x/net/proxy SOCKS5 dialer. Parse failure → direct fallback.
- Custom API server (apiBase() replaces hardcoded URL).
- SendDocument: multipart sendDocument for backup file attachment.
- GetMe + SetMyCommands: for bot-info + set-my-commands endpoints.
- sendWithKeyboard: sendMessage with reply_markup (inline keyboards).
- answerCallbackQuery: clear callback loading spinner.
== Bot commands + inline menus ==
- /id: reply with sender's Telegram numeric ID.
- /usage: admin → search users; bound user → self-query traffic/expiry/sub URL.
- /start + /help: send inline keyboard menu (admin menu for admin chats,
user menu for bound users, plain text for unbound).
- Admin menu: 8 buttons (status/users/traffic/online/listeners/deldepleted/
backup/restart) in 4 rows.
- User menu: my_usage + my_sub buttons.
- callback_query handling: answerCallbackQuery + dispatch on callback_data.
- Access control: admin chats always pass; non-admin chats pass only when
FromID matches a ProxyUser.TelegramID.
- Bot struct: added userSvc field for GetByTelegramID lookups.
- NewBot signature: NewBot(db, mihomoSvc, userSvc).
== Cron scheduler (new telegram/scheduler.go) ==
- Periodic report goroutine driven by Settings.Schedule.
- Supports @hourly/@daily/@weekly/@monthly/@every <dur>/5-field cron.
- Report: host, version, uptime, CPU/mem/disk, user stats, expiring+depleted
counts.
- Optional backup: AttachBackup → system.WriteZip → SendDocument.
- Settings reload on every tick (no restart needed for schedule changes).
- NotifyEvent(event, details): generic event emitter for one-shot alerts.
== Events layer ==
- login.attempt: NotifyLogin (success) + NotifyLoginFailed (failure).
Failure path added to auth/api.go Login handler. Both gated by
EventEnabled('login') — admins can mute success spam yet keep failure alerts.
- memory.high: emitMemoryWarning via gopsutil/v4/mem. Reuses CPUWarnPct as
shared 'resource warn %' threshold. 1-hour cooldown dedup.
- crash: mihomo ProcessManager.SetCrashHandler → telegram.NotifyCrash.
Fires on non-zero exit while desired==true (unexpected crash, not admin stop).
Wired in app/container.go.
- Dual thresholds: expiry (hours + days), traffic (percent + GB) with
separate dedup keys so neither suppresses the other.
== API endpoints ==
- POST /telegram/set-my-commands: registers 12-command menu with Telegram.
- GET /telegram/bot-info: returns bot username/first_name/id.
== Frontend ==
- telegram.ts: 8 new fields in TelegramSettings interface + setTelegramCommands
+ fetchTelegramBotInfo API functions.
- Settings.tsx: 8 new form controls (schedule Input, language Select,
enabled_events multi-Select with 10 options, expiry_warn_days + traffic_warn_gb
InputNumber, attach_backup Switch, proxy_url Input, api_server Input).
Added 'Set bot commands' button calling setTelegramCommands.
- i18n: 11 new keys (zh + en) for the new settings labels.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : all 15 packages PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
feat(telegram): full bot enhancement with inline menus, cron reports, events
Reference: https://docs.sanaei.dev/zh/docs/operations/telegram-bot/
Field naming follows existing 3m-ui snake_case convention (not sanaei's
tg* prefix) per project maintainer's request.
== Data layer ==
- ProxyUser: add TelegramID (indexed) + TelegramName fields for linking
proxy users to their Telegram accounts (self-service queries).
- SafeUser + ToSafeUser: expose both new fields.
- user.Service: BindTelegram / UnbindTelegram / GetByTelegramID methods.
- user API: GET /by-telegram/:tgid, PUT /:id/telegram, DELETE /:id/telegram
(registered before /:id to avoid path capture).
- UpdateInput/CreateInput: accept TelegramID + TelegramName.
== Settings expansion (8 new fields, snake_case) ==
- schedule: cron expr or @daily/@every 6h for periodic reports (default @daily)
- attach_backup: include DB backup zip in periodic report
- language: bot message language zh/en (default zh)
- enabled_events: comma-sep event list (default 'login,cpu,crash')
Supports: login,cpu,memory,crash,node_down,node_up,block,unblock,expiry,traffic
- expiry_warn_days: remind N days before expiry (0=off)
- traffic_warn_gb: remind when remaining traffic < N GB (0=off)
- proxy_url: socks5:// or http:// proxy for Telegram API
- api_server: custom Bot API server (empty=official)
- EventEnabled(name) helper for per-event gating.
== Client enhancements ==
- Proxy support: http(s):// via http.Transport.Proxy; socks5:// via
golang.org/x/net/proxy SOCKS5 dialer. Parse failure → direct fallback.
- Custom API server (apiBase() replaces hardcoded URL).
- SendDocument: multipart sendDocument for backup file attachment.
- GetMe + SetMyCommands: for bot-info + set-my-commands endpoints.
- sendWithKeyboard: sendMessage with reply_markup (inline keyboards).
- answerCallbackQuery: clear callback loading spinner.
== Bot commands + inline menus ==
- /id: reply with sender's Telegram numeric ID.
- /usage: admin → search users; bound user → self-query traffic/expiry/sub URL.
- /start + /help: send inline keyboard menu (admin menu for admin chats,
user menu for bound users, plain text for unbound).
- Admin menu: 8 buttons (status/users/traffic/online/listeners/deldepleted/
backup/restart) in 4 rows.
- User menu: my_usage + my_sub buttons.
- callback_query handling: answerCallbackQuery + dispatch on callback_data.
- Access control: admin chats always pass; non-admin chats pass only when
FromID matches a ProxyUser.TelegramID.
- Bot struct: added userSvc field for GetByTelegramID lookups.
- NewBot signature: NewBot(db, mihomoSvc, userSvc).
== Cron scheduler (new telegram/scheduler.go) ==
- Periodic report goroutine driven by Settings.Schedule.
- Supports @hourly/@daily/@weekly/@monthly/@every <dur>/5-field cron.
- Report: host, version, uptime, CPU/mem/disk, user stats, expiring+depleted
counts.
- Optional backup: AttachBackup → system.WriteZip → SendDocument.
- Settings reload on every tick (no restart needed for schedule changes).
- NotifyEvent(event, details): generic event emitter for one-shot alerts.
== Events layer ==
- login.attempt: NotifyLogin (success) + NotifyLoginFailed (failure).
Failure path added to auth/api.go Login handler. Both gated by
EventEnabled('login') — admins can mute success spam yet keep failure alerts.
- memory.high: emitMemoryWarning via gopsutil/v4/mem. Reuses CPUWarnPct as
shared 'resource warn %' threshold. 1-hour cooldown dedup.
- crash: mihomo ProcessManager.SetCrashHandler → telegram.NotifyCrash.
Fires on non-zero exit while desired==true (unexpected crash, not admin stop).
Wired in app/container.go.
- Dual thresholds: expiry (hours + days), traffic (percent + GB) with
separate dedup keys so neither suppresses the other.
== API endpoints ==
- POST /telegram/set-my-commands: registers 12-command menu with Telegram.
- GET /telegram/bot-info: returns bot username/first_name/id.
== Frontend ==
- telegram.ts: 8 new fields in TelegramSettings interface + setTelegramCommands
+ fetchTelegramBotInfo API functions.
- Settings.tsx: 8 new form controls (schedule Input, language Select,
enabled_events multi-Select with 10 options, expiry_warn_days + traffic_warn_gb
InputNumber, attach_backup Switch, proxy_url Input, api_server Input).
Added 'Set bot commands' button calling setTelegramCommands.
- i18n: 11 new keys (zh + en) for the new settings labels.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : all 15 packages PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
feat(telegram): full bot enhancement with inline menus, cron reports, events
Reference: https://docs.sanaei.dev/zh/docs/operations/telegram-bot/
Field naming follows existing 3m-ui snake_case convention (not sanaei's
tg* prefix) per project maintainer's request.
== Data layer ==
- ProxyUser: add TelegramID (indexed) + TelegramName fields for linking
proxy users to their Telegram accounts (self-service queries).
- SafeUser + ToSafeUser: expose both new fields.
- user.Service: BindTelegram / UnbindTelegram / GetByTelegramID methods.
- user API: GET /by-telegram/:tgid, PUT /:id/telegram, DELETE /:id/telegram
(registered before /:id to avoid path capture).
- UpdateInput/CreateInput: accept TelegramID + TelegramName.
== Settings expansion (8 new fields, snake_case) ==
- schedule: cron expr or @daily/@every 6h for periodic reports (default @daily)
- attach_backup: include DB backup zip in periodic report
- language: bot message language zh/en (default zh)
- enabled_events: comma-sep event list (default 'login,cpu,crash')
Supports: login,cpu,memory,crash,node_down,node_up,block,unblock,expiry,traffic
- expiry_warn_days: remind N days before expiry (0=off)
- traffic_warn_gb: remind when remaining traffic < N GB (0=off)
- proxy_url: socks5:// or http:// proxy for Telegram API
- api_server: custom Bot API server (empty=official)
- EventEnabled(name) helper for per-event gating.
== Client enhancements ==
- Proxy support: http(s):// via http.Transport.Proxy; socks5:// via
golang.org/x/net/proxy SOCKS5 dialer. Parse failure → direct fallback.
- Custom API server (apiBase() replaces hardcoded URL).
- SendDocument: multipart sendDocument for backup file attachment.
- GetMe + SetMyCommands: for bot-info + set-my-commands endpoints.
- sendWithKeyboard: sendMessage with reply_markup (inline keyboards).
- answerCallbackQuery: clear callback loading spinner.
== Bot commands + inline menus ==
- /id: reply with sender's Telegram numeric ID.
- /usage: admin → search users; bound user → self-query traffic/expiry/sub URL.
- /start + /help: send inline keyboard menu (admin menu for admin chats,
user menu for bound users, plain text for unbound).
- Admin menu: 8 buttons (status/users/traffic/online/listeners/deldepleted/
backup/restart) in 4 rows.
- User menu: my_usage + my_sub buttons.
- callback_query handling: answerCallbackQuery + dispatch on callback_data.
- Access control: admin chats always pass; non-admin chats pass only when
FromID matches a ProxyUser.TelegramID.
- Bot struct: added userSvc field for GetByTelegramID lookups.
- NewBot signature: NewBot(db, mihomoSvc, userSvc).
== Cron scheduler (new telegram/scheduler.go) ==
- Periodic report goroutine driven by Settings.Schedule.
- Supports @hourly/@daily/@weekly/@monthly/@every <dur>/5-field cron.
- Report: host, version, uptime, CPU/mem/disk, user stats, expiring+depleted
counts.
- Optional backup: AttachBackup → system.WriteZip → SendDocument.
- Settings reload on every tick (no restart needed for schedule changes).
- NotifyEvent(event, details): generic event emitter for one-shot alerts.
== Events layer ==
- login.attempt: NotifyLogin (success) + NotifyLoginFailed (failure).
Failure path added to auth/api.go Login handler. Both gated by
EventEnabled('login') — admins can mute success spam yet keep failure alerts.
- memory.high: emitMemoryWarning via gopsutil/v4/mem. Reuses CPUWarnPct as
shared 'resource warn %' threshold. 1-hour cooldown dedup.
- crash: mihomo ProcessManager.SetCrashHandler → telegram.NotifyCrash.
Fires on non-zero exit while desired==true (unexpected crash, not admin stop).
Wired in app/container.go.
- Dual thresholds: expiry (hours + days), traffic (percent + GB) with
separate dedup keys so neither suppresses the other.
== API endpoints ==
- POST /telegram/set-my-commands: registers 12-command menu with Telegram.
- GET /telegram/bot-info: returns bot username/first_name/id.
== Frontend ==
- telegram.ts: 8 new fields in TelegramSettings interface + setTelegramCommands
+ fetchTelegramBotInfo API functions.
- Settings.tsx: 8 new form controls (schedule Input, language Select,
enabled_events multi-Select with 10 options, expiry_warn_days + traffic_warn_gb
InputNumber, attach_backup Switch, proxy_url Input, api_server Input).
Added 'Set bot commands' button calling setTelegramCommands.
- i18n: 11 new keys (zh + en) for the new settings labels.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : all 15 packages PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
feat(telegram): full bot enhancement with inline menus, cron reports, events
Reference: https://docs.sanaei.dev/zh/docs/operations/telegram-bot/
Field naming follows existing 3m-ui snake_case convention (not sanaei's
tg* prefix) per project maintainer's request.
== Data layer ==
- ProxyUser: add TelegramID (indexed) + TelegramName fields for linking
proxy users to their Telegram accounts (self-service queries).
- SafeUser + ToSafeUser: expose both new fields.
- user.Service: BindTelegram / UnbindTelegram / GetByTelegramID methods.
- user API: GET /by-telegram/:tgid, PUT /:id/telegram, DELETE /:id/telegram
(registered before /:id to avoid path capture).
- UpdateInput/CreateInput: accept TelegramID + TelegramName.
== Settings expansion (8 new fields, snake_case) ==
- schedule: cron expr or @daily/@every 6h for periodic reports (default @daily)
- attach_backup: include DB backup zip in periodic report
- language: bot message language zh/en (default zh)
- enabled_events: comma-sep event list (default 'login,cpu,crash')
Supports: login,cpu,memory,crash,node_down,node_up,block,unblock,expiry,traffic
- expiry_warn_days: remind N days before expiry (0=off)
- traffic_warn_gb: remind when remaining traffic < N GB (0=off)
- proxy_url: socks5:// or http:// proxy for Telegram API
- api_server: custom Bot API server (empty=official)
- EventEnabled(name) helper for per-event gating.
== Client enhancements ==
- Proxy support: http(s):// via http.Transport.Proxy; socks5:// via
golang.org/x/net/proxy SOCKS5 dialer. Parse failure → direct fallback.
- Custom API server (apiBase() replaces hardcoded URL).
- SendDocument: multipart sendDocument for backup file attachment.
- GetMe + SetMyCommands: for bot-info + set-my-commands endpoints.
- sendWithKeyboard: sendMessage with reply_markup (inline keyboards).
- answerCallbackQuery: clear callback loading spinner.
== Bot commands + inline menus ==
- /id: reply with sender's Telegram numeric ID.
- /usage: admin → search users; bound user → self-query traffic/expiry/sub URL.
- /start + /help: send inline keyboard menu (admin menu for admin chats,
user menu for bound users, plain text for unbound).
- Admin menu: 8 buttons (status/users/traffic/online/listeners/deldepleted/
backup/restart) in 4 rows.
- User menu: my_usage + my_sub buttons.
- callback_query handling: answerCallbackQuery + dispatch on callback_data.
- Access control: admin chats always pass; non-admin chats pass only when
FromID matches a ProxyUser.TelegramID.
- Bot struct: added userSvc field for GetByTelegramID lookups.
- NewBot signature: NewBot(db, mihomoSvc, userSvc).
== Cron scheduler (new telegram/scheduler.go) ==
- Periodic report goroutine driven by Settings.Schedule.
- Supports @hourly/@daily/@weekly/@monthly/@every <dur>/5-field cron.
- Report: host, version, uptime, CPU/mem/disk, user stats, expiring+depleted
counts.
- Optional backup: AttachBackup → system.WriteZip → SendDocument.
- Settings reload on every tick (no restart needed for schedule changes).
- NotifyEvent(event, details): generic event emitter for one-shot alerts.
== Events layer ==
- login.attempt: NotifyLogin (success) + NotifyLoginFailed (failure).
Failure path added to auth/api.go Login handler. Both gated by
EventEnabled('login') — admins can mute success spam yet keep failure alerts.
- memory.high: emitMemoryWarning via gopsutil/v4/mem. Reuses CPUWarnPct as
shared 'resource warn %' threshold. 1-hour cooldown dedup.
- crash: mihomo ProcessManager.SetCrashHandler → telegram.NotifyCrash.
Fires on non-zero exit while desired==true (unexpected crash, not admin stop).
Wired in app/container.go.
- Dual thresholds: expiry (hours + days), traffic (percent + GB) with
separate dedup keys so neither suppresses the other.
== API endpoints ==
- POST /telegram/set-my-commands: registers 12-command menu with Telegram.
- GET /telegram/bot-info: returns bot username/first_name/id.
== Frontend ==
- telegram.ts: 8 new fields in TelegramSettings interface + setTelegramCommands
+ fetchTelegramBotInfo API functions.
- Settings.tsx: 8 new form controls (schedule Input, language Select,
enabled_events multi-Select with 10 options, expiry_warn_days + traffic_warn_gb
InputNumber, attach_backup Switch, proxy_url Input, api_server Input).
Added 'Set bot commands' button calling setTelegramCommands.
- i18n: 11 new keys (zh + en) for the new settings labels.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : all 15 packages PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
feat(telegram): full bot enhancement with inline menus, cron reports, events
Reference: https://docs.sanaei.dev/zh/docs/operations/telegram-bot/
Field naming follows existing 3m-ui snake_case convention (not sanaei's
tg* prefix) per project maintainer's request.
== Data layer ==
- ProxyUser: add TelegramID (indexed) + TelegramName fields for linking
proxy users to their Telegram accounts (self-service queries).
- SafeUser + ToSafeUser: expose both new fields.
- user.Service: BindTelegram / UnbindTelegram / GetByTelegramID methods.
- user API: GET /by-telegram/:tgid, PUT /:id/telegram, DELETE /:id/telegram
(registered before /:id to avoid path capture).
- UpdateInput/CreateInput: accept TelegramID + TelegramName.
== Settings expansion (8 new fields, snake_case) ==
- schedule: cron expr or @daily/@every 6h for periodic reports (default @daily)
- attach_backup: include DB backup zip in periodic report
- language: bot message language zh/en (default zh)
- enabled_events: comma-sep event list (default 'login,cpu,crash')
Supports: login,cpu,memory,crash,node_down,node_up,block,unblock,expiry,traffic
- expiry_warn_days: remind N days before expiry (0=off)
- traffic_warn_gb: remind when remaining traffic < N GB (0=off)
- proxy_url: socks5:// or http:// proxy for Telegram API
- api_server: custom Bot API server (empty=official)
- EventEnabled(name) helper for per-event gating.
== Client enhancements ==
- Proxy support: http(s):// via http.Transport.Proxy; socks5:// via
golang.org/x/net/proxy SOCKS5 dialer. Parse failure → direct fallback.
- Custom API server (apiBase() replaces hardcoded URL).
- SendDocument: multipart sendDocument for backup file attachment.
- GetMe + SetMyCommands: for bot-info + set-my-commands endpoints.
- sendWithKeyboard: sendMessage with reply_markup (inline keyboards).
- answerCallbackQuery: clear callback loading spinner.
== Bot commands + inline menus ==
- /id: reply with sender's Telegram numeric ID.
- /usage: admin → search users; bound user → self-query traffic/expiry/sub URL.
- /start + /help: send inline keyboard menu (admin menu for admin chats,
user menu for bound users, plain text for unbound).
- Admin menu: 8 buttons (status/users/traffic/online/listeners/deldepleted/
backup/restart) in 4 rows.
- User menu: my_usage + my_sub buttons.
- callback_query handling: answerCallbackQuery + dispatch on callback_data.
- Access control: admin chats always pass; non-admin chats pass only when
FromID matches a ProxyUser.TelegramID.
- Bot struct: added userSvc field for GetByTelegramID lookups.
- NewBot signature: NewBot(db, mihomoSvc, userSvc).
== Cron scheduler (new telegram/scheduler.go) ==
- Periodic report goroutine driven by Settings.Schedule.
- Supports @hourly/@daily/@weekly/@monthly/@every <dur>/5-field cron.
- Report: host, version, uptime, CPU/mem/disk, user stats, expiring+depleted
counts.
- Optional backup: AttachBackup → system.WriteZip → SendDocument.
- Settings reload on every tick (no restart needed for schedule changes).
- NotifyEvent(event, details): generic event emitter for one-shot alerts.
== Events layer ==
- login.attempt: NotifyLogin (success) + NotifyLoginFailed (failure).
Failure path added to auth/api.go Login handler. Both gated by
EventEnabled('login') — admins can mute success spam yet keep failure alerts.
- memory.high: emitMemoryWarning via gopsutil/v4/mem. Reuses CPUWarnPct as
shared 'resource warn %' threshold. 1-hour cooldown dedup.
- crash: mihomo ProcessManager.SetCrashHandler → telegram.NotifyCrash.
Fires on non-zero exit while desired==true (unexpected crash, not admin stop).
Wired in app/container.go.
- Dual thresholds: expiry (hours + days), traffic (percent + GB) with
separate dedup keys so neither suppresses the other.
== API endpoints ==
- POST /telegram/set-my-commands: registers 12-command menu with Telegram.
- GET /telegram/bot-info: returns bot username/first_name/id.
== Frontend ==
- telegram.ts: 8 new fields in TelegramSettings interface + setTelegramCommands
+ fetchTelegramBotInfo API functions.
- Settings.tsx: 8 new form controls (schedule Input, language Select,
enabled_events multi-Select with 10 options, expiry_warn_days + traffic_warn_gb
InputNumber, attach_backup Switch, proxy_url Input, api_server Input).
Added 'Set bot commands' button calling setTelegramCommands.
- i18n: 11 new keys (zh + en) for the new settings labels.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : all 15 packages PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
feat(telegram): full bot enhancement with inline menus, cron reports, events
Reference: https://docs.sanaei.dev/zh/docs/operations/telegram-bot/
Field naming follows existing 3m-ui snake_case convention (not sanaei's
tg* prefix) per project maintainer's request.
== Data layer ==
- ProxyUser: add TelegramID (indexed) + TelegramName fields for linking
proxy users to their Telegram accounts (self-service queries).
- SafeUser + ToSafeUser: expose both new fields.
- user.Service: BindTelegram / UnbindTelegram / GetByTelegramID methods.
- user API: GET /by-telegram/:tgid, PUT /:id/telegram, DELETE /:id/telegram
(registered before /:id to avoid path capture).
- UpdateInput/CreateInput: accept TelegramID + TelegramName.
== Settings expansion (8 new fields, snake_case) ==
- schedule: cron expr or @daily/@every 6h for periodic reports (default @daily)
- attach_backup: include DB backup zip in periodic report
- language: bot message language zh/en (default zh)
- enabled_events: comma-sep event list (default 'login,cpu,crash')
Supports: login,cpu,memory,crash,node_down,node_up,block,unblock,expiry,traffic
- expiry_warn_days: remind N days before expiry (0=off)
- traffic_warn_gb: remind when remaining traffic < N GB (0=off)
- proxy_url: socks5:// or http:// proxy for Telegram API
- api_server: custom Bot API server (empty=official)
- EventEnabled(name) helper for per-event gating.
== Client enhancements ==
- Proxy support: http(s):// via http.Transport.Proxy; socks5:// via
golang.org/x/net/proxy SOCKS5 dialer. Parse failure → direct fallback.
- Custom API server (apiBase() replaces hardcoded URL).
- SendDocument: multipart sendDocument for backup file attachment.
- GetMe + SetMyCommands: for bot-info + set-my-commands endpoints.
- sendWithKeyboard: sendMessage with reply_markup (inline keyboards).
- answerCallbackQuery: clear callback loading spinner.
== Bot commands + inline menus ==
- /id: reply with sender's Telegram numeric ID.
- /usage: admin → search users; bound user → self-query traffic/expiry/sub URL.
- /start + /help: send inline keyboard menu (admin menu for admin chats,
user menu for bound users, plain text for unbound).
- Admin menu: 8 buttons (status/users/traffic/online/listeners/deldepleted/
backup/restart) in 4 rows.
- User menu: my_usage + my_sub buttons.
- callback_query handling: answerCallbackQuery + dispatch on callback_data.
- Access control: admin chats always pass; non-admin chats pass only when
FromID matches a ProxyUser.TelegramID.
- Bot struct: added userSvc field for GetByTelegramID lookups.
- NewBot signature: NewBot(db, mihomoSvc, userSvc).
== Cron scheduler (new telegram/scheduler.go) ==
- Periodic report goroutine driven by Settings.Schedule.
- Supports @hourly/@daily/@weekly/@monthly/@every <dur>/5-field cron.
- Report: host, version, uptime, CPU/mem/disk, user stats, expiring+depleted
counts.
- Optional backup: AttachBackup → system.WriteZip → SendDocument.
- Settings reload on every tick (no restart needed for schedule changes).
- NotifyEvent(event, details): generic event emitter for one-shot alerts.
== Events layer ==
- login.attempt: NotifyLogin (success) + NotifyLoginFailed (failure).
Failure path added to auth/api.go Login handler. Both gated by
EventEnabled('login') — admins can mute success spam yet keep failure alerts.
- memory.high: emitMemoryWarning via gopsutil/v4/mem. Reuses CPUWarnPct as
shared 'resource warn %' threshold. 1-hour cooldown dedup.
- crash: mihomo ProcessManager.SetCrashHandler → telegram.NotifyCrash.
Fires on non-zero exit while desired==true (unexpected crash, not admin stop).
Wired in app/container.go.
- Dual thresholds: expiry (hours + days), traffic (percent + GB) with
separate dedup keys so neither suppresses the other.
== API endpoints ==
- POST /telegram/set-my-commands: registers 12-command menu with Telegram.
- GET /telegram/bot-info: returns bot username/first_name/id.
== Frontend ==
- telegram.ts: 8 new fields in TelegramSettings interface + setTelegramCommands
+ fetchTelegramBotInfo API functions.
- Settings.tsx: 8 new form controls (schedule Input, language Select,
enabled_events multi-Select with 10 options, expiry_warn_days + traffic_warn_gb
InputNumber, attach_backup Switch, proxy_url Input, api_server Input).
Added 'Set bot commands' button calling setTelegramCommands.
- i18n: 11 new keys (zh + en) for the new settings labels.
== Verification ==
- go build ./... : PASS
- go vet ./... : PASS
- go test ./... : all 15 packages PASS
- npm run build (tsc + vite) : PASS, 3241 modules
- gofmt : clean
2026-08-29 12:19:46 +00:00
風雪 2f3856704b fix(frontend): local QR generation, JWT expiry, CSP, axios CVE
- H-1: Subscription URIs / sub_tokens were sent to the third-party
api.qrserver.com via GET query, leaking SS passwords / VMess UUIDs /
sub_tokens to an untrusted service. Replace with a new QRCode.tsx
component using the 'qrcode' npm package (pure client-side canvas).
Remove the QR <img> from the backend subpage defaultHTML too.
- H-2: Add isTokenExpired() helper parsing JWT exp; useAuth now
proactively logs out when the token has already expired instead of
waiting for a 401. Full httpOnly-cookie migration documented as a
follow-up (requires backend API changes).
- MED-2: axios ^1.7.9 -> ^1.8.4 (CVE-2025-27152).
- MED-3: Add CSP, X-Frame-Options: DENY, X-Content-Type-Options,
referrer=same-origin meta tags to index.html.
- MED-6: Remove leftover console.log(r.data) in Settings.tsx.
- MED-7: Commit package-lock.json for reproducible builds (npm ci).
- LOW-1: Simplify keep_token to !values.bot_token (consistent with
Cluster.tsx).
- LOW-2: Remove dead share Modal + state in Users.tsx.
- LOW-6: Unify formatBytes in utils/format.ts (4 duplicates removed).
- LOW-7: ChangePassword min length 6 -> 8.
fix(frontend): local QR generation, JWT expiry, CSP, axios CVE
- H-1: Subscription URIs / sub_tokens were sent to the third-party
api.qrserver.com via GET query, leaking SS passwords / VMess UUIDs /
sub_tokens to an untrusted service. Replace with a new QRCode.tsx
component using the 'qrcode' npm package (pure client-side canvas).
Remove the QR <img> from the backend subpage defaultHTML too.
- H-2: Add isTokenExpired() helper parsing JWT exp; useAuth now
proactively logs out when the token has already expired instead of
waiting for a 401. Full httpOnly-cookie migration documented as a
follow-up (requires backend API changes).
- MED-2: axios ^1.7.9 -> ^1.8.4 (CVE-2025-27152).
- MED-3: Add CSP, X-Frame-Options: DENY, X-Content-Type-Options,
referrer=same-origin meta tags to index.html.
- MED-6: Remove leftover console.log(r.data) in Settings.tsx.
- MED-7: Commit package-lock.json for reproducible builds (npm ci).
- LOW-1: Simplify keep_token to !values.bot_token (consistent with
Cluster.tsx).
- LOW-2: Remove dead share Modal + state in Users.tsx.
- LOW-6: Unify formatBytes in utils/format.ts (4 duplicates removed).
- LOW-7: ChangePassword min length 6 -> 8.
fix(frontend): local QR generation, JWT expiry, CSP, axios CVE
- H-1: Subscription URIs / sub_tokens were sent to the third-party
api.qrserver.com via GET query, leaking SS passwords / VMess UUIDs /
sub_tokens to an untrusted service. Replace with a new QRCode.tsx
component using the 'qrcode' npm package (pure client-side canvas).
Remove the QR <img> from the backend subpage defaultHTML too.
- H-2: Add isTokenExpired() helper parsing JWT exp; useAuth now
proactively logs out when the token has already expired instead of
waiting for a 401. Full httpOnly-cookie migration documented as a
follow-up (requires backend API changes).
- MED-2: axios ^1.7.9 -> ^1.8.4 (CVE-2025-27152).
- MED-3: Add CSP, X-Frame-Options: DENY, X-Content-Type-Options,
referrer=same-origin meta tags to index.html.
- MED-6: Remove leftover console.log(r.data) in Settings.tsx.
- MED-7: Commit package-lock.json for reproducible builds (npm ci).
- LOW-1: Simplify keep_token to !values.bot_token (consistent with
Cluster.tsx).
- LOW-2: Remove dead share Modal + state in Users.tsx.
- LOW-6: Unify formatBytes in utils/format.ts (4 duplicates removed).
- LOW-7: ChangePassword min length 6 -> 8.
fix(frontend): local QR generation, JWT expiry, CSP, axios CVE
- H-1: Subscription URIs / sub_tokens were sent to the third-party
api.qrserver.com via GET query, leaking SS passwords / VMess UUIDs /
sub_tokens to an untrusted service. Replace with a new QRCode.tsx
component using the 'qrcode' npm package (pure client-side canvas).
Remove the QR <img> from the backend subpage defaultHTML too.
- H-2: Add isTokenExpired() helper parsing JWT exp; useAuth now
proactively logs out when the token has already expired instead of
waiting for a 401. Full httpOnly-cookie migration documented as a
follow-up (requires backend API changes).
- MED-2: axios ^1.7.9 -> ^1.8.4 (CVE-2025-27152).
- MED-3: Add CSP, X-Frame-Options: DENY, X-Content-Type-Options,
referrer=same-origin meta tags to index.html.
- MED-6: Remove leftover console.log(r.data) in Settings.tsx.
- MED-7: Commit package-lock.json for reproducible builds (npm ci).
- LOW-1: Simplify keep_token to !values.bot_token (consistent with
Cluster.tsx).
- LOW-2: Remove dead share Modal + state in Users.tsx.
- LOW-6: Unify formatBytes in utils/format.ts (4 duplicates removed).
- LOW-7: ChangePassword min length 6 -> 8.
fix(frontend): local QR generation, JWT expiry, CSP, axios CVE
- H-1: Subscription URIs / sub_tokens were sent to the third-party
api.qrserver.com via GET query, leaking SS passwords / VMess UUIDs /
sub_tokens to an untrusted service. Replace with a new QRCode.tsx
component using the 'qrcode' npm package (pure client-side canvas).
Remove the QR <img> from the backend subpage defaultHTML too.
- H-2: Add isTokenExpired() helper parsing JWT exp; useAuth now
proactively logs out when the token has already expired instead of
waiting for a 401. Full httpOnly-cookie migration documented as a
follow-up (requires backend API changes).
- MED-2: axios ^1.7.9 -> ^1.8.4 (CVE-2025-27152).
- MED-3: Add CSP, X-Frame-Options: DENY, X-Content-Type-Options,
referrer=same-origin meta tags to index.html.
- MED-6: Remove leftover console.log(r.data) in Settings.tsx.
- MED-7: Commit package-lock.json for reproducible builds (npm ci).
- LOW-1: Simplify keep_token to !values.bot_token (consistent with
Cluster.tsx).
- LOW-2: Remove dead share Modal + state in Users.tsx.
- LOW-6: Unify formatBytes in utils/format.ts (4 duplicates removed).
- LOW-7: ChangePassword min length 6 -> 8.
fix(frontend): local QR generation, JWT expiry, CSP, axios CVE
- H-1: Subscription URIs / sub_tokens were sent to the third-party
api.qrserver.com via GET query, leaking SS passwords / VMess UUIDs /
sub_tokens to an untrusted service. Replace with a new QRCode.tsx
component using the 'qrcode' npm package (pure client-side canvas).
Remove the QR <img> from the backend subpage defaultHTML too.
- H-2: Add isTokenExpired() helper parsing JWT exp; useAuth now
proactively logs out when the token has already expired instead of
waiting for a 401. Full httpOnly-cookie migration documented as a
follow-up (requires backend API changes).
- MED-2: axios ^1.7.9 -> ^1.8.4 (CVE-2025-27152).
- MED-3: Add CSP, X-Frame-Options: DENY, X-Content-Type-Options,
referrer=same-origin meta tags to index.html.
- MED-6: Remove leftover console.log(r.data) in Settings.tsx.
- MED-7: Commit package-lock.json for reproducible builds (npm ci).
- LOW-1: Simplify keep_token to !values.bot_token (consistent with
Cluster.tsx).
- LOW-2: Remove dead share Modal + state in Users.tsx.
- LOW-6: Unify formatBytes in utils/format.ts (4 duplicates removed).
- LOW-7: ChangePassword min length 6 -> 8.
fix(frontend): local QR generation, JWT expiry, CSP, axios CVE
- H-1: Subscription URIs / sub_tokens were sent to the third-party
api.qrserver.com via GET query, leaking SS passwords / VMess UUIDs /
sub_tokens to an untrusted service. Replace with a new QRCode.tsx
component using the 'qrcode' npm package (pure client-side canvas).
Remove the QR <img> from the backend subpage defaultHTML too.
- H-2: Add isTokenExpired() helper parsing JWT exp; useAuth now
proactively logs out when the token has already expired instead of
waiting for a 401. Full httpOnly-cookie migration documented as a
follow-up (requires backend API changes).
- MED-2: axios ^1.7.9 -> ^1.8.4 (CVE-2025-27152).
- MED-3: Add CSP, X-Frame-Options: DENY, X-Content-Type-Options,
referrer=same-origin meta tags to index.html.
- MED-6: Remove leftover console.log(r.data) in Settings.tsx.
- MED-7: Commit package-lock.json for reproducible builds (npm ci).
- LOW-1: Simplify keep_token to !values.bot_token (consistent with
Cluster.tsx).
- LOW-2: Remove dead share Modal + state in Users.tsx.
- LOW-6: Unify formatBytes in utils/format.ts (4 duplicates removed).
- LOW-7: ChangePassword min length 6 -> 8.
fix(frontend): local QR generation, JWT expiry, CSP, axios CVE
- H-1: Subscription URIs / sub_tokens were sent to the third-party
api.qrserver.com via GET query, leaking SS passwords / VMess UUIDs /
sub_tokens to an untrusted service. Replace with a new QRCode.tsx
component using the 'qrcode' npm package (pure client-side canvas).
Remove the QR <img> from the backend subpage defaultHTML too.
- H-2: Add isTokenExpired() helper parsing JWT exp; useAuth now
proactively logs out when the token has already expired instead of
waiting for a 401. Full httpOnly-cookie migration documented as a
follow-up (requires backend API changes).
- MED-2: axios ^1.7.9 -> ^1.8.4 (CVE-2025-27152).
- MED-3: Add CSP, X-Frame-Options: DENY, X-Content-Type-Options,
referrer=same-origin meta tags to index.html.
- MED-6: Remove leftover console.log(r.data) in Settings.tsx.
- MED-7: Commit package-lock.json for reproducible builds (npm ci).
- LOW-1: Simplify keep_token to !values.bot_token (consistent with
Cluster.tsx).
- LOW-2: Remove dead share Modal + state in Users.tsx.
- LOW-6: Unify formatBytes in utils/format.ts (4 duplicates removed).
- LOW-7: ChangePassword min length 6 -> 8.
fix(frontend): local QR generation, JWT expiry, CSP, axios CVE
- H-1: Subscription URIs / sub_tokens were sent to the third-party
api.qrserver.com via GET query, leaking SS passwords / VMess UUIDs /
sub_tokens to an untrusted service. Replace with a new QRCode.tsx
component using the 'qrcode' npm package (pure client-side canvas).
Remove the QR <img> from the backend subpage defaultHTML too.
- H-2: Add isTokenExpired() helper parsing JWT exp; useAuth now
proactively logs out when the token has already expired instead of
waiting for a 401. Full httpOnly-cookie migration documented as a
follow-up (requires backend API changes).
- MED-2: axios ^1.7.9 -> ^1.8.4 (CVE-2025-27152).
- MED-3: Add CSP, X-Frame-Options: DENY, X-Content-Type-Options,
referrer=same-origin meta tags to index.html.
- MED-6: Remove leftover console.log(r.data) in Settings.tsx.
- MED-7: Commit package-lock.json for reproducible builds (npm ci).
- LOW-1: Simplify keep_token to !values.bot_token (consistent with
Cluster.tsx).
- LOW-2: Remove dead share Modal + state in Users.tsx.
- LOW-6: Unify formatBytes in utils/format.ts (4 duplicates removed).
- LOW-7: ChangePassword min length 6 -> 8.
fix(frontend): local QR generation, JWT expiry, CSP, axios CVE
- H-1: Subscription URIs / sub_tokens were sent to the third-party
api.qrserver.com via GET query, leaking SS passwords / VMess UUIDs /
sub_tokens to an untrusted service. Replace with a new QRCode.tsx
component using the 'qrcode' npm package (pure client-side canvas).
Remove the QR <img> from the backend subpage defaultHTML too.
- H-2: Add isTokenExpired() helper parsing JWT exp; useAuth now
proactively logs out when the token has already expired instead of
waiting for a 401. Full httpOnly-cookie migration documented as a
follow-up (requires backend API changes).
- MED-2: axios ^1.7.9 -> ^1.8.4 (CVE-2025-27152).
- MED-3: Add CSP, X-Frame-Options: DENY, X-Content-Type-Options,
referrer=same-origin meta tags to index.html.
- MED-6: Remove leftover console.log(r.data) in Settings.tsx.
- MED-7: Commit package-lock.json for reproducible builds (npm ci).
- LOW-1: Simplify keep_token to !values.bot_token (consistent with
Cluster.tsx).
- LOW-2: Remove dead share Modal + state in Users.tsx.
- LOW-6: Unify formatBytes in utils/format.ts (4 duplicates removed).
- LOW-7: ChangePassword min length 6 -> 8.
fix(frontend): local QR generation, JWT expiry, CSP, axios CVE
- H-1: Subscription URIs / sub_tokens were sent to the third-party
api.qrserver.com via GET query, leaking SS passwords / VMess UUIDs /
sub_tokens to an untrusted service. Replace with a new QRCode.tsx
component using the 'qrcode' npm package (pure client-side canvas).
Remove the QR <img> from the backend subpage defaultHTML too.
- H-2: Add isTokenExpired() helper parsing JWT exp; useAuth now
proactively logs out when the token has already expired instead of
waiting for a 401. Full httpOnly-cookie migration documented as a
follow-up (requires backend API changes).
- MED-2: axios ^1.7.9 -> ^1.8.4 (CVE-2025-27152).
- MED-3: Add CSP, X-Frame-Options: DENY, X-Content-Type-Options,
referrer=same-origin meta tags to index.html.
- MED-6: Remove leftover console.log(r.data) in Settings.tsx.
- MED-7: Commit package-lock.json for reproducible builds (npm ci).
- LOW-1: Simplify keep_token to !values.bot_token (consistent with
Cluster.tsx).
- LOW-2: Remove dead share Modal + state in Users.tsx.
- LOW-6: Unify formatBytes in utils/format.ts (4 duplicates removed).
- LOW-7: ChangePassword min length 6 -> 8.
fix(frontend): local QR generation, JWT expiry, CSP, axios CVE
- H-1: Subscription URIs / sub_tokens were sent to the third-party
api.qrserver.com via GET query, leaking SS passwords / VMess UUIDs /
sub_tokens to an untrusted service. Replace with a new QRCode.tsx
component using the 'qrcode' npm package (pure client-side canvas).
Remove the QR <img> from the backend subpage defaultHTML too.
- H-2: Add isTokenExpired() helper parsing JWT exp; useAuth now
proactively logs out when the token has already expired instead of
waiting for a 401. Full httpOnly-cookie migration documented as a
follow-up (requires backend API changes).
- MED-2: axios ^1.7.9 -> ^1.8.4 (CVE-2025-27152).
- MED-3: Add CSP, X-Frame-Options: DENY, X-Content-Type-Options,
referrer=same-origin meta tags to index.html.
- MED-6: Remove leftover console.log(r.data) in Settings.tsx.
- MED-7: Commit package-lock.json for reproducible builds (npm ci).
- LOW-1: Simplify keep_token to !values.bot_token (consistent with
Cluster.tsx).
- LOW-2: Remove dead share Modal + state in Users.tsx.
- LOW-6: Unify formatBytes in utils/format.ts (4 duplicates removed).
- LOW-7: ChangePassword min length 6 -> 8.
2026-08-29 09:39:12 +00:00
風雪 1b497d09ff fix(sub): serve HTML page for target=html without subconverter 2026-08-29 14:17:34 +08:00
風雪 a88e7e581f fix(i18n): move share strings inside export default (CI tsc) 2026-08-29 14:08:08 +08:00
風雪 fbf5f6df27 fix(i18n): move share strings inside export default (CI tsc) 2026-08-29 14:08:07 +08:00
風雪 ad6d2b4ecf feat(ui): unified Share/Subscription page (links + node URIs) 2026-08-29 13:44:17 +08:00
風雪 9a88f64849 feat(ui): unified Share/Subscription page (links + node URIs) 2026-08-29 13:44:16 +08:00
風雪 94fd22b657 feat(ui): unified Share/Subscription page (links + node URIs) 2026-08-29 13:44:15 +08:00
風雪 1c4bbd30da feat(ui): unified Share/Subscription page (links + node URIs) 2026-08-29 13:44:14 +08:00
風雪 396cd6427d feat(ui): unified Share/Subscription page (links + node URIs) 2026-08-29 13:44:13 +08:00
風雪 da07d85f14 feat(ui): unified Share/Subscription page (links + node URIs) 2026-08-29 13:44:13 +08:00
風雪 e017d40b35 feat(ui): unified Share/Subscription page (links + node URIs) 2026-08-29 13:44:12 +08:00
風雪 2a63bed00e i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
2026-08-29 13:08:58 +08:00
風雪 f7c5e6109e i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
i18n: autofill and reality key hints
2026-08-29 13:08:57 +08:00
風雪 f909238e57 fix(share): derive Reality pbk for standard URIs; fix listen port; clarify autofill UI 2026-08-29 13:08:41 +08:00
風雪 4ffca2ef4b feat(nat): custom panel port/listen/public_url via env, API, settings UI, install.sh 2026-08-29 12:33:32 +08:00
風雪 f52207df5d feat(nat): custom panel port/listen/public_url via env, API, settings UI, install.sh 2026-08-29 12:33:31 +08:00
風雪 a18429b169 feat(nat): custom panel port/listen/public_url via env, API, settings UI, install.sh 2026-08-29 12:33:30 +08:00
風雪 43099ea84d i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
2026-08-29 12:07:23 +08:00
風雪 deee84ccc3 i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
i18n: auto-generate hints for create node
2026-08-29 12:07:23 +08:00
風雪 7628696f76 feat(ui): create-node auto-generate secrets (REALITY/password/psk); relax required fields 2026-08-29 12:07:21 +08:00
風雪 53cccdedb8 feat(ui): create-node auto-generate secrets (REALITY/password/psk); relax required fields 2026-08-29 12:07:20 +08:00
風雪 02ecc231e7 feat: remove quick-setup; autofill secrets on create + generate helper 2026-08-29 10:54:03 +08:00
風雪 c5dbac1510 feat: remove quick-setup; autofill secrets on create + generate helper 2026-08-29 10:54:02 +08:00
風雪 3faf7227d4 feat: remove quick-setup; autofill secrets on create + generate helper 2026-08-29 10:54:01 +08:00