Previous behavior: login-based accounts (Doodstream/VOE/Vidmoly) reported
'Login hinterlegt — Bereit' as long as username/password were non-empty.
Entering nonsense (asdas@web.de / anything) passed. Now:
- Vidmoly: POST /api/auth/login with JSON and verify /api/upload/config
is reachable afterwards — 401/403 or non-OK message → BadCredentials.
- Doodstream: login_ajax POST, success when either Dashboard HTML comes
back or json.status == 'success'; OTP-required is surfaced as
'Login gültig (OTP erforderlich)'.
- VOE: Laravel CSRF scrape + POST /login, then verify /file-upload
renders a fresh CSRF (only present when logged in).
- Clouddrop: 401/403 now mapped to BadCredentials instead of generic.
- Byse: parse JSON status field (server returns HTTP 200 + status:403
on bad keys) and map accordingly.
Bogus credentials now correctly show a red 'Fehler' state.
- error.rs: 3 tests for the account-specific / transient-network /
file-rejected classifiers
- throttle.rs: 2 tests for unlimited passthrough + rate updates
- folder_monitor.rs: 4 tests for extension parsing + include/exclude
filter + empty-list behavior
- updater.rs: 3 tests for semver compare edge cases
- upload_log: now also emits log-path-auto-updated after persisting
a working fallback so the renderer's input field updates live.
Test count: 3 → 15 (all pass). Live smoke test: cold + warm start
both land at 28 MB RAM with clean shutdown (0 orphans).
- src/tray.rs: system tray with show/hide/quit menu, left-click
toggles main window visibility (minimize-to-tray parity with v1).
- src/shutdown.rs: 60s countdown with per-second 'shutdown-countdown'
event; sleep/shutdown/restart via rundll32/shutdown on Windows,
cancel-aware.
- cancel_shutdown + set_shutdown_after_finish commands now drive the
scheduler so the renderer's existing countdown UI works unchanged.
- Cargo features + tray-icon + image-png added.
- Updater pointed at new Gitea repo Administrator/Multi-Hoster-Upload-2.
OTP (Doodstream two-factor):
- src/otp.rs: OtpBroker registers per-request oneshot channels with
180s timeout, survives abort via Cancelled answer.
- Doodstream login now loops: first attempt without OTP; if server
says OTP required, emit 'otp-required' event to the renderer with
a request_id, wait for provide_otp/cancel_otp commands, re-POST
with the code. Renderer can pop a modal on otp-required.
- UploadCtx carries the broker + app handle so any future hoster can
do the same pattern.
Drop-target floating window:
- src/drop-target.html: minimal always-on-top borderless window with
dashed drop-zone. Emits 'drop-target-files' to the main window on
drag-drop.
- show_drop_target / hide_drop_target commands create/close the
'drop-target' webview on demand.
- Capabilities updated for dual-window use.
In-app auto-update:
- updater::download_and_launch: fetches the NSIS/MSI from Gitea to
%TEMP%, launches detached, exits the app so the installer can
replace the running exe.
- Commands install_update + install_update_now both go through the
new helper. Renderer clicks 'Install Update' → Rust downloads and
hands off, then process exit.
Härtetest results:
- exe: 7.54 MB
- NSIS: 2.70 MB
- MSI: 3.69 MB
- RAM idle: 33 MB (vs Electron ~300 MB)
- All 3 unit tests pass (secret encryption round-trips).
- Doodstream: login_ajax + sess_id scrape from /?op=upload page +
upload_server + multipart upload + XFS-style fn field + filecode
extraction. Skips OTP path (v1 still has the full flow).
- VOE: login page CSRF scrape + POST /login + fresh CSRF from
/file-upload + /engine/delivery-node for CDN server + baseline
my-files snapshot + multipart upload + file-list polling fallback
when response is empty.
Both wire into the existing dispatcher (hosters::upload_file) and
pick up the same rotation/classifier layer as the other uploaders.
Release build clean: exe 7.0 MB, NSIS 2.5 MB, MSI 3.4 MB.