28 Commits
Author SHA1 Message Date
yuanyuanxianganddeepseek-flash 39c8daa9b0 Fix: Keep the audit log open across WebSocket reconnects
Hiding the browser tab while the audit log was open closed the socket,
because the visibility handler treats only the remote screen, terminal
and wall pages as streaming pages. Returning to the tab reconnected the
socket, and the onopen restore path took its catch-all branch, which
navigated to the device list. The audit log therefore vanished whenever
the browser was minimized.

The catch-all branch now leaves the active page alone when it is the
audit log, and only refreshes the device list in the background. Every
other page keeps the previous behaviour: the login page advances with a
cached token, and the terminal page falls back to the device list
because its session died with the socket. Audit results already rendered
stay on screen, so the admin can keep reading without re-querying.

Co-Authored-By: deepseek-flash
2026-10-19 10:39:36 +02:00
yuanyuanxianganddeepseek-flash cd4f3675fd Improve: Replace native browser prompts with themed in-app dialogs
Native alert() and confirm() render their buttons in the browser or OS
locale, so on a German system prompts such as the Clear Screen Wall
confirmation showed German labels that disagreed with the rest of the
web UI. Both are now served by a single themed modal that reuses the
logout confirmation styling: an info variant with one OK button for
notices, and a danger variant with Cancel/OK for destructive actions.

The alert helper is fire-and-forget, while the confirm helper returns a
Promise, so the two call sites that need an answer (clearWall and
deleteUser) are now async. Dialog title and message are written with
textContent so device names, user names and server error strings can
never be interpreted as markup. A pending dialog is resolved as
cancelled when another one supersedes it, keeping callers from hanging,
and clicking the backdrop or pressing Escape closes the dialog. Remote
keyboard forwarding now ignores keys while a dialog is open.

Co-Authored-By: deepseek-flash
2026-10-19 10:27:09 +02:00
yuanyuanxianganddeepseek-flash bbb3d8c2fa Fix: Recover proxied client IPs and fit audit filters on phones
The MFC web server now resolves the real client address from the
WebSocket upgrade request when the TCP peer is loopback, so audit records
and login rate limiting stop reporting the reverse proxy's 127.0.0.1.
X-Forwarded-For is preferred over X-Real-IP because its last hop is the
address the local proxy appended; a client-supplied header list cannot
forge it. Forwarded headers are ignored for non-loopback peers so a
client connecting directly over the internet cannot spoof its identity
or rotate addresses to bypass throttling. FRP deployments can keep using
the existing Proxy Protocol v2 support instead.

Collapse the audit log search conditions behind a toolbar toggle that
starts closed and switches to an accent-colored "Hide filters" state
while the panel is open. On portrait phones the panel uses one control
per row, and From/To move their border and padding onto a wrapper so the
iOS WebKit datetime-local width miscalculation cannot overflow the
viewport.

Co-Authored-By: deepseek-flash
2026-10-19 10:05:58 +02:00
yuanyuanxianganddeepseek-flash ca671d6453 Feature: In-memory web audit log for the MFC server
Add a process-local security audit trail to the Windows server's web
console, mirroring the Go server's event names and reply shape. The ring
keeps the most recent 1000 events since process start and is never
persisted.

- New query_audit WebSocket command, admin-only, with time/event/actor/
  IP/device/keyword/warn-only filters and pagination; the reply is the
  audit_result payload the existing Audit Log page already understands.
  The challenge advertises caps.auditLog so browsers enable the button.
- Record logins (success, failure, throttling, salt probes), WebSocket
  connect/close, screen and terminal session start/stop/denials,
  clipboard transfers (byte counts only), RDP resets, and admin user
  mutations.
- Repeated failed logins and username probes are de-duplicated per
  connection so scanners cannot flood the ring.
- Audit writes never hold m_ClientsMutex: disconnect paths snapshot the
  client identity before the map entry is erased, and offline-device
  stops are audited after the clients lock is released.

Co-Authored-By: deepseek-flash
2026-10-18 19:16:17 +02:00
yuanyuanxianganddeepseek-flash a522424491 Feature: Security audit logging with admin web query page
Add a dedicated security audit trail to the Go server. Login attempts,
authorization denials, WebSocket lifecycle, remote screen and terminal
sessions, clipboard operations, device authentication and admin mutations
now emit stable JSON-line events (audit=true, event=<name>) to a separate
logs/audit.log, mirrored to server.log. The path is configurable through
YAMA_AUDIT_LOG and the separate file can be switched off. Records never
contain passcodes, password hashes, tokens or clipboard contents, and
untrusted string fields are stripped of control characters and truncated
so they cannot forge log lines.

Expose the trail to administrators through a query_audit WebSocket command
and an Audit Log page in the web UI. The page filters by time range, event
(exact or prefix), actor, IP, device id, keyword and warning level, pages
newest-first, and stays hidden unless the server advertises the auditLog
capability, so older C++ servers degrade cleanly. Each query scans at most
the newest 32 MB of the current file to bound latency, records itself as
admin.audit.query, and tolerates torn or non-audit lines.

Add unit tests for event emission and redaction, query filtering, paging,
malformed lines, missing or disabled audit files, and handler
authorization.

Co-Authored-By: deepseek-flash
2026-10-18 18:44:17 +02:00
yuanyuanxianganddeepseek-flash b1ddec1b8a Feature: Offline device history with delete and remote uninstall
Persist every device that has ever logged in to the Go server in a small
JSON store (devices.json, overridable with YAMA_DEVICES_FILE and capped by
YAMA_DEVICES_MAX, default 1000 newest). The store follows the users.json
convention: atomic temp-file write with fsync and rename, a 500 ms debounce
for bursts of reconnects, first_seen preserved across logins, and pruning
that evicts offline records before devices that are still connected.
Devices connected at shutdown are stamped offline before the final flush so
last_seen stays accurate across restarts.

The server advertises an offlineDevices capability and device_list now
appends historical devices that are not online, marked with online:false and
last_seen. The web UI shows an Offline checkbox after Total/Showing (state
kept in localStorage), renders offline cards dimmed and dashed, and always
lists them in the screen-wall sidebar. Older servers without the capability
keep the online-only list.

Grid and List cards gain a delete button after the terminal button, admin
only, behind a confirmation dialog. Deleting an offline device removes its
history record; deleting an online device first sends the single-byte
TOKEN_UNINSTALL command (matching the MFC OnOnlineUninstall flow) and then
removes the record too, so the device does not come back as a dead offline
entry. The agent's own disconnect then runs the existing cleanup path:
screen and terminal sub-connections are closed, the hub entry is removed,
and browsers receive device_offline to release their watch sets, wall
sessions, and stream references. Wall mode exposes no delete action.

Tests cover history persistence/pruning/recovery, offline ordering, the
device_list merge, and all three delete outcomes (offline removal, online
uninstall with a single 0x3F byte, and viewer rejection).

Co-Authored-By: deepseek-flash
2026-10-18 17:54:30 +02:00
yuanyuanxianganddeepseek-flash 8eab26c1f6 Feature: Web screen wall for multi-device live monitoring
Add a third view to the web UI, the screen wall, which shows several
devices at once in one tab with 1x1 / 2x2 / 3x3 / 4x4 / 5x5 layouts and a
device sidebar on the left. Tiles are monitoring-only; clicking one
enlarges that device into the existing single-screen page for control, and
Back returns to the wall. The same device can be added several times: all
tiles of one device share a single decoder and a single stream.

The wall decodes at most 9 streams on desktop and 4 on touch devices.
Tiles beyond the budget keep receiving frames but have their decoder
closed and show the last frame plus a Paused badge; they are promoted
automatically when a live tile goes away. Subscriptions are reference
counted on the client (streams map with owner sets), so a stream is only
connected on the 0 -> 1 transition and disconnected on 1 -> 0, and the
wall and the single-screen page can share one device without tearing the
stream down.

Server side, WebClient now holds a set of watched devices instead of a
single id. A new multiWatch command flag selects set semantics, while
clients without it keep the old replace-all behaviour, so an old frontend
against the new server is unchanged. connect and disconnect responses echo
id and req so the frontend can route them, input commands carry an id and
are resolved against the watch set (dropped when the set has several
devices and no id), and the per-client watch set is capped at 32.

For fast recovery after a pause or when joining late, the server caches
each device's raw IDR payload and replays it on connect and on a new
keyframe command, then asks the client to force a new IDR. The client gets
COMMAND_SCREEN_KEYFRAME_REQ = 169 and an atomic request flag that is only
cleared after an IDR actually came out of the encoder, so a failed capture
or EAGAIN does not lose the request. The frontend retries the keyframe
request up to three times; an old client that ignores the command simply
recovers on the next natural GOP.

Also fix the keyframe cache double wrapping: BroadcastH264Frame used to
cache the full packet header and HandleConnect wrapped it again. The cache
now stores data + 9, len - 9 and every sender wraps it through
BuildFramePacket, and the cache is cleared on cold start and on resolution
change. StopRemoteDesktop is always called outside m_ClientsMutex, with
the ids collected under the lock first.

Co-Authored-By: deepseek-flash
2026-10-18 11:03:17 +02:00
yuanyuanxianganddeepseek-v4-pro 04f4eb9947 Fix: share web login session across browser tabs
The login token and role were stored in sessionStorage, which is
isolated per tab, so opening a second tab forced a fresh password
login. Switch every token/role read and write to localStorage so all
tabs on the same origin share the session and a new tab auto-resumes
it on load. Expiry (device_list ok:false) and explicit logout still
clear it as before.

Co-Authored-By: deepseek-v4-pro
2026-10-17 22:44:50 +02:00
yuanyuanxianganddeepseek-v4-pro 5cea1167be Feature: Web remote desktop phone IME input box ("I")
Add an "I" button to the mobile shortcut bar that opens a chat-style
input box pinned above the virtual keyboard. Text is delivered to the
remote through the existing clipboard bridge (clipboard_set + synthetic
Ctrl+V); an empty box turns Enter/Backspace into remote Enter/Backspace
so the phone can submit and edit text in the focused window. The box
soft-wraps and auto-grows up to four lines.

The box is a <textarea>, so the desktop key-forwarding handlers now skip
TEXTAREA targets as well, which stops an empty-box Backspace from being
forwarded twice (once by imeBackspace, once by the document handler).

Co-Authored-By: deepseek-v4-pro
2026-10-17 22:06:46 +02:00
yuanyuanxianganddeepseek-v4-pro 0c04f7516b Feature: Web remote desktop bidirectional text clipboard sync
Add keyboard-shortcut (Ctrl+C / Ctrl+X / Ctrl+V) text clipboard transfer
between the browser and the remote desktop, reusing the clipboard protocol
that already exists in the native viewer (COMMAND_SCREEN_GET_CLIPBOARD=24,
COMMAND_SCREEN_SET_CLIPBOARD=25, TOKEN_CLIPBOARD_TEXT=118) so the client
needs no change.

Local -> remote: pressing Ctrl+V reads the browser clipboard, pushes it to
the remote clipboard (UTF-8 -> GBK, matching the client's CF_TEXT/ANSI
handling) and then synthesizes a remote Ctrl+V so the pasted text is the
newly delivered one.

Remote -> local: Ctrl+C or Ctrl+X is forwarded to the remote as usual and,
after a short delay, the remote clipboard is pulled back and written to the
browser clipboard for a later local Ctrl+V.

Both the MFC and Go servers get the same three clipboard signaling paths
(clipboard_set, clipboard_get, clipboard_text) so the shared single-page
frontend behaves identically on either backend.

Co-Authored-By: deepseek-v4-pro
2026-10-17 19:52:38 +02:00
yuanyuanxianganddeepseek-v4-pro 4ff965a2b4 Feat: White-label web UI branding for subpath deployments
When the web remote desktop is mounted under another domain's subpath
(YAMA_WEB_BASE_PATH non-empty), read the product title from
YAMA_WEB_BRAND_NAME, substitute it for the SimpleRemoter titles and
PWA manifest name, and strip the security notice and website links.
The brand name is JSON- and HTML-escaped so operator input can't
produce malformed output. Served from the domain root, the official
SimpleRemoter branding is left unchanged.

Co-Authored-By: deepseek-v4-pro
2026-10-15 19:43:58 +02:00
yuanyuanxianganddeepseek-v4-pro 53b7151fb7 Feature: Add admin-only SSO bridge to web UI
Bridge an already-authenticated portal session into a YAMA web session via
a new /sso endpoint, so portal administrators can enter the remote-desktop
UI without a second login. Only the "admin" portal role is bridged; any
other role (viewer, unknown) still falls back to the built-in login form,
so a low-privileged portal account cannot bypass authentication.

- wsauth: extract IssueToken from VerifyLogin so an out-of-band-authenticated
  identity (SSO) can mint a session token without the challenge/response.
- web: add the SSODetector interface, a fail-closed JWT detector (HMAC-only),
  and the /sso handler gated to the admin role.
- cmd: enable the JWT detector via YAMA_SSO_JWT_SECRET, the shared HS256
  secret matching the parent portal.
- web UI: attempt SSO before showing the login form, falling back to login
  when the server returns 401.
- docs: document SSO (admin-only) and license trial-vs-paid troubleshooting
  in README and the env example.

Co-Authored-By: deepseek-v4-pro
2026-10-13 15:46:04 +02:00
yuanyuanxianganddeepseek-v4-pro f3a05c14b1 Feature: Play remote audio on iPhone via WebAudio + self-hosted Opus WASM
iOS WebKit lacks Media Source Extensions with WebM/Opus, so remote audio
was silent on iPhone Safari/Chrome even though audio frames kept flowing.
Add a Web Audio API fallback path that decodes Opus packets with the
`opus-decoder` WASM library and schedules them onto an AudioContext.

The decoder bundle is now self-hosted — served at /static/opus-decoder.min.js
by both the Go and C++ servers — instead of pulled from a CDN, which was
unreachable from some networks.

Gotcha fixed during bring-up: the unscoped `opus-decoder` build's
`OpusDecoder.decodeFrame` is synchronous and returns {channelData,
samplesDecoded, sampleRate, errors} directly, not a Promise, so it must be
called in try/catch rather than via .then().

Also remove the on-screen diagnostic overlay (audio toast + frame counter)
used while debugging the phone without a console; diagnostics now go to
console.* only.

Co-Authored-By: deepseek-v4-pro
2026-10-03 18:23:10 +02:00
yuanyuanxianganddeepseek-v4-pro 42df128f62 Feature: Support subpath deployment for Go web UI
Add optional YAMA_WEB_BASE_PATH to mount the whole web UI (index, manifest,
static assets, /health, /api/devices, /ws) under a URL prefix such as
/remoter behind a reverse proxy. index.html now uses a __BASE_PATH__
placeholder substituted at serve time, the PWA manifest start_url is rooted
at the base path, and a mount wrapper redirects the bare prefix to prefix/
and strips it before routing. Include nginx, systemd, and env-file deployment
examples for the subpath setup.

The shared placeholder is also handled by the MFC server, which serves from
the domain root and strips __BASE_PATH__ (to "") so its web remote desktop
URLs stay rooted.

Co-Authored-By: deepseek-v4-pro
2026-09-11 12:48:21 +02:00
yuanyuanxiang f146af121a Fix: prevent mobile web remote desktop freeze on Safari app-switch
by forcing clean WS reconnect on foreground return;

add 30-second grace period on server to avoid cold-start on quick reconnects
2026-07-10 16:08:02 +02:00
yuanyuanxiang 45553ec5b6 Feat: Android client Phase 0-3 full implementation 2026-06-21 23:00:05 +02:00
yuanyuanxiang fc0be64880 Fix(macOS): restore dblclick for MAC touch, fix scroll speed (10px→40px per notch) 2026-06-04 15:33:12 +02:00
yuanyuanxiang dc83c2df42 Feature(web): show host remark alongside hostname 2026-06-02 22:07:56 +02:00
yuanyuanxiang a52874fe08 Feature(web): bandwidth read-out + collapsible fullscreen toolbar 2026-06-02 21:50:21 +02:00
yuanyuanxiang 498c7d15b3 Feature(web): Add toolbar audio toggle button 2026-06-02 20:52:20 +02:00
yuanyuanxiang 9aca587654 Feature(audio): forward client PCM to web viewers with continuous playback 2026-06-02 12:09:57 +02:00
yuanyuanxiang a5a04aaab7 fix(web): improve touch double-click reliability across platforms
Increase touch move threshold to prevent accidental drag detection.

Simulate physical double-click with two sequential click events and a 20ms delay instead of using non-standard dblclick event.

Fix folder renaming and unresponsiveness issues on Windows, Linux, and macOS.
2026-05-30 23:41:03 +02:00
yuanyuanxiang 8c7f612449 Feature: Implement H.264 and AV1 hardware encoding for remote control
Remark: Need to update FFmpeg static libraries to take effort
2026-05-30 00:12:38 +02:00
yuanyuanxiang 707dcdbbb4 Fix(Web): exit remote-desktop fullscreen blocks device-list clicks 2026-05-19 22:20:58 +02:00
yuanyuanxiang cd43caafb2 Fix: Web remote desktop reliability and UX
- Server: clamp web session adaptive quality to H264-only levels (>=Good) in EvaluateQuality and ApplyQualityLevel; Ultra/High (DIFF/RGB565) caused the browser to freeze ~1 min into a session
- Server: move session-type detection to the top of ScreenSpyDlg::OnInitDialog and skip SetWindowPlacement/EnterFullScreen for hidden web sessions, eliminating the MFC dialog flash on web-triggered opens
- Linux client: default QualityLevel from QUALITY_ADAPTIVE to QUALITY_GOOD to match Windows/macOS so the server's adaptive controller doesn't auto-upgrade to non-H264 algorithms
- Web: clear the floating quick-action toolbar on fullscreen exit so its row of buttons (RDP reset / Mouse / Close) doesn't stay pinned to the top of the page
- Web: route F11 to the remote in control mode instead of toggling local fullscreen
- Web: route Esc to the remote in control mode via the Keyboard Lock API instead of exiting native fullscreen
2026-05-19 18:39:16 +02:00
yuanyuanxiang ccab37658a Improve(Web): Touch-mode visual cursor follows remote IDC_* state 2026-05-17 20:02:10 +02:00
yuanyuanxiang 4e0627e6a3 Fix(Web): Align touchpad cursor overlay to SVG arrow tip 2026-05-17 19:12:55 +02:00
yuanyuanxiang dc48091d5b Refactor(Web): Extract embedded HTML to server/web/index.html 2026-05-17 18:46:21 +02:00