Commit Graph

69 Commits

Author SHA1 Message Date
ruslan b838c814ba fix: change anti-idle interval from 30s to 3min 2026-05-04 06:20:55 +00:00
ruslan 359a0c7636 fix: add shift key press in anti-idle loop for OS that ignore mouse movement 2026-05-03 12:10:59 +00:00
ruslan 3d8ccd30b6 fix: add hash_password to auth imports in main.py 2026-05-01 16:47:48 +00:00
ruslan dc90569631 fix: call connect_rdp_slot on session reuse
Previously connect_rdp_slot was only called when creating a new session.
If the API container restarted, existing sessions had should_be_connected=false
and xfreerdp never started. Now connect is triggered on every /go/<slug> visit
when an RDP session already exists.
2026-05-01 16:11:30 +00:00
ruslan fc3a4c6efb fix: increase mouse jiggle to 10px for reliable screensaver prevention 2026-05-01 16:06:13 +00:00
ruslan ccf7401f71 fix: anti-idle uses mouse jiggle instead of Shift key
Mouse movement works universally on any remote OS (Windows, Ubuntu,
RED OS, Astra). Alternates between (960,540) and (961,541) every 30s
inside xfreerdp window via xdotool mousemove --window.
2026-05-01 16:05:04 +00:00
ruslan 34972af7c0 fix: add tini as PID 1 to prevent zombie processes in containers 2026-05-01 14:58:51 +00:00
ruslan 96b7dff7cd fix: anti-idle uses xdotool --window; remove creds from URL
rdp-proxy/manager.py: anti_idle_loop gets window ID first, then sends
key --window ID --clearmodifiers shift (was broken chain syntax).
universal-runtime/manager.py: removed credentials from URL - they break
SPA fetch() calls causing white screen (e.g. CGP).
2026-05-01 14:44:08 +00:00
ruslan 38dc206f5a fix: add missing user_is_valid import from auth in main.py 2026-05-01 12:56:19 +00:00
ruslan fb4af8cfe6 fix: add missing import secrets in main.py 2026-05-01 12:55:02 +00:00
ruslan 4ab49cd10f fix: restore anti-idle in manager.py, fix ENTRYPOINT quoting in Dockerfile
- manager.py: anti_idle_loop sends xdotool Shift to xfreerdp window every 30s
  while session is active; mousemove fallback if window not found
- Dockerfile: restore xdotool package; fix ENTRYPOINT JSON quoting lost in heredoc
2026-05-01 11:16:53 +00:00
ruslan 58cb8b1035 feat: on-demand RDP - connect xfreerdp only when session opens
Replaces always-on xfreerdp with on-demand model (load 12 to under 1 at idle).
- rdp-proxy/manager.py: HTTP server port 7001 managing xfreerdp lifecycle
- rdp-proxy/entrypoint.sh: starts Xvfb+x11vnc+websockify+manager, no auto-connect
- rdp-proxy/Dockerfile: adds python3, copies manager.py, exposes 7001
- runtime.py: connect_rdp_slot and disconnect_rdp_slot via manager HTTP API
- terminate_session_record: disconnect instead of container restart
- main.py: calls connect_rdp_slot in background thread on session create
- maintenance.py: cleanup_loop disconnects on expire, run_maintenance_service
  includes RDP slot init, maintenance_runner fixed to import maintenance
2026-05-01 10:12:52 +00:00
ruslan 82024a36c4 fix: add missing sqlalchemy imports (select, text, delete, update) to main.py 2026-05-01 09:51:24 +00:00
ruslan b8dd023233 fix: add missing runtime imports (route_ready, docker_client, ensure_universal_pool, get_universal_pool_status) 2026-05-01 09:48:42 +00:00
ruslan 1c7caec021 fix: add missing config imports to main.py (GO_*_LOCK_TIMEOUT, WEB_POOL_BUFFER) 2026-05-01 09:45:55 +00:00
ruslan c8c77048c7 refactor: split main.py into modules (config, database, models, utils, auth, runtime, maintenance)
main.py was ~3000 lines with models, routes, Docker ops, maintenance all mixed.
Split into 7 focused modules:
- config.py: env vars and constants
- database.py: SQLAlchemy engine, SessionLocal, Base, get_db
- models.py: ORM models and enums
- utils.py: logging, formatting, icon handling, misc helpers
- auth.py: password hashing, cookies, CSRF, user dependency
- runtime.py: all Docker operations, pool management, session lifecycle
- maintenance.py: cleanup loop, schema bootstrap, startup logic
- main.py: FastAPI app, middleware, all route handlers only
2026-05-01 09:40:06 +00:00
ruslan 9bd38ed6db Improve autofill extension: better field detection + Basic Auth support
- Split filled flag into userFilled/passFilled for independent tracking
- Add findUserFieldNearPassword() for DOM-relative lookup near password field
- Add isVisible() helper to skip disabled/hidden/offscreen inputs
- Add console.log tracing for debugging
- Add background.js service worker with webRequest.onAuthRequired for Basic Auth
- Add _url_with_credentials() to embed login:pass in URL for HTTP Basic Auth
- Use /usr/lib/chromium/chromium binary directly (bypass Debian wrapper)
- Add --enable-logging=stderr for console.log capture in chromium logs
2026-05-01 04:44:15 +00:00
ruslan d57acb416b Replace Login Data injection with autofill via Chromium extension
The previous approach pre-populated Chromiums Login Data SQLite with
schema version 30 and AES-128-CBC v10 encrypted passwords. Chromium 147
expects schema version 43, fails to migrate (Unable to migrate database
from 30 to 43), and refuses to open Login Data altogether. Result: the
row was written but Chromium never read it, so autofill never worked.

Instead generate a tiny Manifest V3 extension per session in a temp dir
with a content_script that finds username and password fields, sets
their values, and dispatches input/change events. Pass it via
--load-extension and --disable-extensions-except so it is the only
extension loaded.

Benefits:
- Independent of Chromium version and Login Database schema
- Works on SPAs (MutationObserver re-runs on DOM changes)
- Credentials live only in a temp file alongside the profile, removed
  on session end via _stop_current
- No SQLite or cryptography dependency
- Removes the silent failure mode of Login Data migration

Removes _chrome_encrypt_v10, sqlite3, hashlib, urlparse imports.
Adds _create_autofill_extension and tracks extension_dir alongside
profile_dir in _state for cleanup symmetry.
2026-04-30 17:47:10 +00:00
ruslan cf68bc848f Fix CSRF SameSite=Strict breaking login on iPad/Safari
Safari (iPadOS/iOS) blocks SameSite=Strict cookies on the initial
top-level navigation when it considers the request cross-site (links
from messengers, email, QR codes). The CSRF cookie was therefore never
set on first visit, and the subsequent login POST failed with 403
"CSRF failed".

Switch the CSRF cookie to SameSite=Lax — this is the OWASP recommended
default and matches industry practice. The auth (session) cookie keeps
SameSite=Strict, since it is only issued after a successful first-party
login POST and needs the stricter binding.
2026-04-30 17:38:20 +00:00
ruslan be65be8fdb Fix Chromium autofill timing bug + RDP anti-idle to prevent lock screen
- universal-runtime: set _state[profile_dir] AFTER _start_process so
  _stop_current does not delete the freshly-created profile before
  Chromium reads it. Without this, Login Data was being wiped.
- rdp-proxy: add xdotool dependency and background anti_idle_loop that
  sends Shift to the xfreerdp window every 30s, forwarded over RDP to
  reset the remote idle timer and keep the lock screen from kicking in.
2026-04-30 14:05:04 +00:00
ruslan 23c1f6e342 Chromium: Russian language, autofill passwords from svc_login/svc_password via Login Data 2026-04-30 07:22:31 +00:00
ruslan d7c956e10b rdp-proxy: monitor xfreerdp, auto-restart container on disconnect 2026-04-30 06:53:27 +00:00
ruslan 3f20fe5991 Fix: category delete button broken by double-quote conflict in onclick attr 2026-04-28 21:07:14 +00:00
ruslan 154ec35384 Block mobile devices: show desktop-only page 2026-04-28 20:52:24 +00:00
ruslan 8f3617afdd Remove copy buttons from credentials panels 2026-04-28 13:49:47 +00:00
ruslan beb6828520 Add credentials panel on view page; remove copy buttons from dashboard cards 2026-04-28 13:47:46 +00:00
ruslan 4cc19b32d8 chore: ignore uploaded service icons 2026-04-28 13:32:28 +00:00
ruslan 2d65d98116 fix: link z-index:0, tile z-index:1 pointer-events:none, scroll restored 2026-04-28 13:29:53 +00:00
ruslan 1ab5af28b5 fix: link overlay pattern — credentials never trigger navigation 2026-04-28 13:17:32 +00:00
ruslan bfcf5f565b fix: card height 672px (-30%) 2026-04-28 13:08:29 +00:00
ruslan af02c0d059 fix: card height 960px, credentials always visible, only comment scrolls 2026-04-28 13:02:19 +00:00
ruslan 530d901a45 fix: fixed card height 480px, icon constrained, info-area scrolls 2026-04-28 12:57:21 +00:00
ruslan 7a7c6e30e3 fix: tile-info-area shares space between credentials+comment, scroll restored 2026-04-28 12:48:59 +00:00
ruslan 6ccba89216 fix: equal card height — categories always at bottom, comment clips to fill 2026-04-28 12:33:12 +00:00
ruslan a64d49a8c1 feat: reorder card layout + svc_cred_hint field for credentials note 2026-04-28 12:20:37 +00:00
ruslan b06620a793 fix: move credentials and comment inside card, right below icon 2026-04-28 12:16:02 +00:00
ruslan b9d13733c9 feat: service credentials (login/password) on dashboard cards with copy button 2026-04-28 12:10:40 +00:00
ruslan fa88f7f4e4 feat: full Markdown support in service card comments (mistune) 2026-04-28 11:57:44 +00:00
ruslan b951f6c68e docs: add full deployment guide in Russian 2026-04-28 07:08:05 +00:00
ruslan d0ff949828 fix: remove stale Xvfb lock file on container restart
On restart, /tmp/.X1-lock remains from previous run causing Xvfb to fail
with 'Server is already active for display 1', which then breaks xfreerdp
and x11vnc. Clean up lock and socket before starting Xvfb.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 06:50:56 +00:00
ruslan a4a96c45b0 fix: RDP slot occupancy and cleanup_loop always running
- admin_page: slot shown as occupied based on ACTIVE status only (no time cutoff)
- go_service: busy slots checked by ACTIVE status (no cutoff) — cleanup_loop handles expiry
- startup_event: cleanup_loop starts regardless of ENABLE_STARTUP_MAINTENANCE flag;
  pool/container init guarded by the flag separately
- cleanup_loop: RDPSLOT sessions expire correctly and trigger container restart

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 06:48:00 +00:00
ruslan 552898e3e9 fix: cleanup_loop correctly frees and restarts RDP slots on session expiry
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 06:34:04 +00:00
ruslan 6847cbc078 feat: RDP slot pool — multi-user RDP with per-account containers
- New RdpSlot model (rdp_slots table): service_id, rdp_username,
  rdp_password, container_name
- Each slot gets a dedicated portal-rdpslot-<slug>-<id> container with
  Traefik route /rdp/<slot_id>/ and restart_policy=unless-stopped
- go_service: RDP services with slots use pool allocation — finds first
  free slot (not occupied by active session), returns 503 if all busy
- session_status + session_view: handle RDPSLOT: container_id prefix
- terminate_session_record: restarts slot container in background on close
- session_redirect_url: RDPSLOT sessions redirect to /s/<id>/view
- startup_event: starts containers for all configured slots on boot
- Admin: POST /api/admin/services/{id}/rdp-slots, DELETE /api/admin/rdp-slots/{id}
- admin.html: slot management UI (list, add, delete); removed ACL exclusivity
- set_acl: removed RDP 1-user exclusivity — RDP services now assignable to many

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 06:32:02 +00:00
ruslan 67d361c5c9 feat: login page polish — remove welcome text, add spacer, resize corner brand
- login.html: removed 'Добро пожаловать' heading, added 3.5rem spacer below logo
- style.css: .login-corner-brand font-size set to 1.5rem

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 19:31:24 +00:00
ruslan 56cf2d6830 feat: responsive mobile wall with logo and footer
- #mobile-wall uses width:100vw/height:100vh/overflow:hidden to prevent
  text overflow on narrow screens
- All font sizes via clamp(), word-break:break-word on text elements
- MONT logo pinned to top (position:absolute), height clamp(4rem,16vw,6rem)
- Made by Galyaviev footer pinned to bottom

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 19:20:35 +00:00
ruslan 6f17193312 feat: loading overlay on dashboard, RDP pooled session routing fix
- dashboard.html: overlay div moved before <script> so getElementById works;
  double rAF ensures browser paints spinner before navigation
- main.py: pooled_rdp route fix — session_status now returns /svc/<slug>/
  route and redirect_url for POOL: RDP sessions (was always ready instantly)
- docker-compose.yml: parametrise env vars via .env for easier tuning

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 19:07:55 +00:00
ruslan 419b495020 feat: RDP ACL exclusivity, mobile wall, nav buttons, resolution xrandr
- RDP сервис может быть назначен только одному пользователю в ACL
- Мобильная заглушка на dashboard при ширине < 1024px
- rdp-proxy: кнопки навигации, спиннер Ожидайте, реконнект
- session_wait_page: тёмная тема, CSS спиннер
- kiosk/universal-runtime manager.py: xrandr + cvt --newmode для resolution
- Dockerfiles: x11-xserver-utils, x11-utils
2026-04-27 18:49:06 +00:00
ruslan 445d025de2 Add configurable X11VNC_FLAGS env passthrough 2026-04-25 19:07:49 +00:00
ruslan d927e1c947 chore: stop tracking local project context doc 2026-04-25 18:45:44 +00:00
ruslan bd4350b2e0 docs: add ncache-related resolution issue note 2026-04-25 18:44:37 +00:00