News called /api/v1/client/news through apiGet(), which sends the
webtoken Bearer obtained from POST /api/v1/client/user/webtoken. But
that route is a defineClientEventHandler -- it verifies the clients own
short-lived signed JWT and never consults the webtokens ACLs at all, so
it rejected the request outright. Store and Library have always worked
because they already use the JWT path.
Adds api_get_jwt on the Rust side and apiGetClient() in the composable,
and points News at it. Verified working against the live server.
Two auth mechanisms coexist here and picking the wrong one yields a
clean 403 that looks like a permissions problem: /api/v1/client/* wants
the client JWT, while routes gated by aclManager.getUserIdACL want the
webtoken plus the right ACL.
0.4.2 was built two minutes before the webtoken fix landed, so that
artifact carries the broken JSON parse and cannot authenticate any
community API call. Bumping so the fixed build is unambiguous.
The server route returns a bare string (return token.token), which h3
sends as a raw text/plain body rather than a JSON-quoted string. Parsing
it with .json::<String>() failed against the live server -- serde_json
read the UUID leading hex as a number literal and errored with "invalid
number at line 1 column 7".
Without this the client cannot obtain a web token at all, so every
community API call (news, friends, notifications) fails to authenticate
regardless of the page implementation.
The News page was a hardcoded "under construction" stub, and the Friends/
Alerts header icons did nothing -- both replaced with real Vue pages/
widgets backed by the server's existing news/notifications/community APIs,
reached through a generic authenticated REST bridge (community_api.rs:
api_get/api_post/api_delete) and a websocket bridge for live chat/presence
(community_ws.rs, mirrors the reqwest_websocket pattern remote.rs already
uses for the auth-code exchange).
- News: GET /api/v1/client/news, rendered with micromark, with real
loading/empty/error states.
- Alerts: GET/POST /api/v1/notifications/*, polled (not the notifications
websocket -- see notifications.ts for why), unread badge + mark
one/all read.
- Friends: full friends composable (list/requests/search/presence) plus a
header dropdown and a /community/friends management page.
- Chat: /community/chat, rooms (server/global/title/DM) over the live
community websocket with a REST history/read-state load on room switch.
- Achievements: per-game card on the library detail page and a per-user
summary on the new read-only profile page (/community/profile/:username).
- The Community tab's "opens in your system browser" page is gone;
/community is now a real in-app hub (activity feed + server list) linking
into friends/chat.
Also fixes a real auth gap found by actually running the built client
against the live server: notifications/community routes are gated by
aclManager.getUserIdACL, which only accepts a session cookie or an opaque
Bearer APIToken -- never the client's own short-lived signed JWT
(generate_authorization_header). The client already had a bridge for this
(POST /api/v1/client/user/webtoken, JWT-authenticated, mints an opaque
token), but CLIENT_WEBTOKEN_ACLS never granted it the notifications/
community scopes, so the exchange succeeded and the minted token still got
403'd on every one of these routes. Extended that ACL list
(04.auth-init.ts) and switched community_api.rs/community_ws.rs to mint and
use that webtoken instead of the JWT for these specific calls.
Client version bumped to 0.4.2 (tauri.conf.json).
Cargo.toml previously built reqwest with default-features=false and no
compression feature, so the client never sent Accept-Encoding on chunk
GETs and never decoded a compressed response -- Caddy-side encode would
have been a silent no-op. zstd is listed first to match the measured
best cost/ratio tradeoff (zstd-3 ~0.70 ratio in 0.18s/64MB chunk vs
zstd-19's 10.8s); gzip stays as the fallback for any proxy hop that
only speaks gzip.
Community was never a separate community.quasarke.net host -- it lives
inside the Drop server itself at {server}/community, same as the server-side
fold-in two commits up. The desktop client's own community.rs, models.rs,
and the two community.vue pages still assumed the old sidecar design
(Settings.communityUrl/communityEndpoint defaulting to
https://community.quasarke.net), so this corrects them to match:
- community.rs now posts to {server}/api/v1/ingest/{session-start,
session-stop,achievements} (the fixed contract, matching the wrapper) via
the same generate_url()/make_authenticated_post_json() helpers
cloud_saves_commands.rs already uses, instead of an unauthenticated
bespoke HTTP client pointed at a hardcoded second host. {server} is
DB.base_url -- whatever Drop server the user actually signed into.
- database/src/models.rs: dropped the now-meaningless community_endpoint/
community_url Settings fields; kept enable_community_telemetry as the
only toggle.
- community.vue (the tab) and settings/community.vue: resolve the page URL
via the existing gen_drop_url Tauri command (same one the admin-panel
link already uses) instead of a stored/hardcoded URL.
- error.vue: the generic pre-auth error page can render before any server
is configured, so it can't resolve a per-server community link the way
community.vue does -- pointed its "Support" link at our docs instead of
the retired community.quasarke.net.
Also fixes a real release-blocking bug in process_manager.rs found while
verifying this compiles: the drop://game-launch telemetry emit held
install_dir (a reference borrowed out of db_lock via game_status) live past
the db_lock.applications.transient_statuses.insert() mutable borrow a few
lines above it -- an E0502 that fails every build, release or debug.
Fixed by cloning install_dir to an owned String before that mutable borrow
point and using the clone in the emit instead.
Verified: cargo check -p drop-app passes; full `cargo tauri build` release
build succeeds and produces a working NSIS installer (7z archive-test:
"Everything is Ok").
cloud_saves was a real, working crate (tar+zstd archive build/extract,
per-platform save-path translation) that was never reachable from the app:
a workspace member but not a drop-app dependency, no Tauri commands, no UI.
Adds six commands (cloud_saves_commands.rs) covering slot list/create/delete
against the server's existing /api/v1/client/saves/* routes (which the
client's handshake already declares the cloudSaves capability for -- see
remote/src/auth.rs) plus push/pull of an archive, and a Saves tab in the
game options modal to drive them.
One thing this does NOT do, because there is nothing to wire it to:
cloud_saves::resolver + BackupManager/CloudSaveMetadata/GameFile expect an
auto-detected, Ludusavi-style per-game save-file manifest
(CloudSaveMetadata.files), but nothing in Drop produces that manifest --
not the GameVersion Prisma model, not any admin UI, not any API response.
So push/pull here archive a directory the user picks explicitly rather than
auto-locating save files. The archive format (tar under a "data/" prefix,
zstd-compressed) stays compatible with what cloud_saves::resolver writes,
so a future manifest-driven auto-detect pass can still read what gets
pushed today.
Also: Drop's saves API only exposes a push (upload) route server-side, no
saves-specific download -- pull reuses the same authenticated
/api/v1/client/object/{id} route the client already uses for depot/image
assets, fetching the most recent entry in a slot's historyObjectIds.
Retarget every upstream Drop-OSS/discord/droposs.org reference to our own
infra (github.com/Drop-OSS -> devops.quasarke.net/drop-oss, Discord ->
community.quasarke.net), rename the product to "Drop Desktop Client
(Quasarke Edition)" with a distinct app identifier so it can't collide with
a genuine upstream install, and note in updates.rs that there was never a
self-updater to repoint (no tauri-plugin-updater dependency exists anywhere
in this client -- confirmed via Cargo.toml/tauri.conf.json).
Community tab (desktop/main/pages/community.vue) replaces the upstream
"Under construction" stub: opens community.quasarke.net in the system
browser via tauri-plugin-shell (already covered by the existing
shell:allow-open capability, no CSP/capability changes needed).
Native play-session + achievements telemetry (desktop/src-tauri/src/community.rs):
process_manager now emits drop://game-launch / drop://game-exit tauri events
around every game launch, picked up by listeners in src/lib.rs that POST
session-start / session-stop / achievements to the community service,
matching homelab-compose's drop-launch-wrapper.ps1 contract exactly
(same endpoint paths, same {"ok":true} ack requirement, same
%LOCALAPPDATA%\drop-community\queue offline-queue format/location so a
machine running both the client and the per-game wrapper shares one
flush point). This gives every game launched through the client the same
tracking the wrapper gives individually-wrapped games, without requiring
the wrapper to be installed.
The community endpoint is a single configurable Settings field
(communityEndpoint/communityUrl/enableCommunityTelemetry in
database/src/models.rs, exposed via a new Settings > Community tab), not
hardcoded at each call site.
* Fix Windows and Linux launch
* Add process handler selector, pin Prisma
* Regenerate lcofkiel
* Fix torrential inclusion in image
* Fix layouting
* Implement tree kill for Windows
* Fix server lint
* add basic git files to root
* make server part of monorepo
* import promo
* import libraries base
* import docs
* import desktop
* move docs and promo
* chore: Major refactoring
Still needs a massive go-over because there shouldn't be anything referencing tauri in any of the workspaces except the original one. Process manager has been refactored as an example
Signed-off-by: quexeky <git@quexeky.dev>
* fix: Remote tauri dependency from process
Signed-off-by: quexeky <git@quexeky.dev>
* refactor: Improvements to src-tauri
Signed-off-by: quexeky <git@quexeky.dev>
* refactor: Builds, but some logic still left to move back
Signed-off-by: quexeky <git@quexeky.dev>
* refactor: Finish refactor
Signed-off-by: quexeky <git@quexeky.dev>
* chore: Run cargo clippy && cargo fmt
Signed-off-by: quexeky <git@quexeky.dev>
* refactor: Move everything into src-tauri
Signed-off-by: quexeky <git@quexeky.dev>
---------
Signed-off-by: quexeky <git@quexeky.dev>
* fix: Add lint and remove all unwraps from lib.rs
Signed-off-by: quexeky <git@quexeky.dev>
* chore: Remove all unwraps from util.rs and add state_lock macro
Signed-off-by: quexeky <git@quexeky.dev>
* chore: Add CacheError and remove unwraps from fetch_object
Signed-off-by: quexeky <git@quexeky.dev>
* chore: Remove unwraps from fetch_object and server_proto
Signed-off-by: quexeky <git@quexeky.dev>
* chore: Remove unwraps from auth.rs
Signed-off-by: quexeky <git@quexeky.dev>
* chore: Remove unwraps from process_handlers
Signed-off-by: quexeky <git@quexeky.dev>
* chore: Clippy unwrap linting
Signed-off-by: quexeky <git@quexeky.dev>
* chore: Remove lint
Because not everything is actually resolved yet: will be resolved with a restructure of the library
Signed-off-by: quexeky <git@quexeky.dev>
* chore: Make the rest of clippy happy
Signed-off-by: quexeky <git@quexeky.dev>
* fix: Send download signal instead of triggering self.on_error
Signed-off-by: quexeky <git@quexeky.dev>
* fix: Corrupted state should panic
Signed-off-by: quexeky <git@quexeky.dev>
* fix: Use debug instead of display for specific errors
Signed-off-by: quexeky <git@quexeky.dev>
* fix: Settings now log error instead of panicking
Signed-off-by: quexeky <git@quexeky.dev>
---------
Signed-off-by: quexeky <git@quexeky.dev>
* feat: different local path in dev #73
* feat: better error output for downloads
* feat: collections in library view
* feat: improve download manager reliability
* feat: new download UI, more stable downloads
* fix: clippy
* fix: only show admin link if user is admin
* feat: check for libs before building