Commit graph

686 commits

Author SHA1 Message Date
wdunn001
62708af762 Merge branch 'develop' of forgejo:drop-oss/drop into develop
Some checks failed
Server CI / Lint (push) Failing after 16s
Server CI / Typecheck (push) Failing after 19s
2026-08-20 23:32:39 -04:00
wdunn001
5278bce000 feat(profile): user avatar upload endpoint, UserAvatar in header and profile, account home rework 2026-08-20 23:32:27 -04:00
wdunn001
927f9aae21 feat(game-accounts): add account recovery action, optional password on provision, settings page panel and design doc update 2026-08-20 23:32:26 -04:00
wdunn001
5516453b7f server: tell pre-0.4 clients they are outdated instead of a bare 403
Some checks failed
Server CI / Lint (push) Failing after 2m6s
Server CI / Typecheck (push) Successful in 4m2s
Old clients pair fine (the handshake is unauthenticated) then fail every authenticated call with a message-less 403, which the desktop app renders as an opaque 'Unrecoverable error'. Gate /client/auth/initiate on the User-Agent the 0.4+ remote crate always sends, and give the non-JWT auth fallthrough an explicit statusMessage/message pointing at cdn.quasarke.net/drop-client.
2026-08-14 12:21:57 -04:00
wdunn001
a74ed9eded feat(community): SSO to native game account bridge, phases 2 and 3
Some checks failed
Server CI / Lint (push) Failing after 2m0s
Server CI / Typecheck (push) Successful in 3m28s
Adds the player-facing half of game account management: a panel on
/community/servers/[id] that shows your linked account for that server,
creates one, and changes its password.

None of these emulators speak OIDC and none will - each ships its own
login against its own auth database. So this is a bridge, not SSO: one
Drop identity tied to one native account per server, enforced by a unique
index on (userId, serverSlug). That constraint IS the feature.

Drop owns identity, authorization, the link table and the UI. The
per-game DB access and password crypto stay in mmo-portal's provider API
(SRP6 verifiers, EQEmu hashing) - code that writes into live auth DBs and
fails silently when subtly wrong.

The password never touches Drop's database; it goes straight through to
the provider, which turns it into whatever that emulator wants.

Notable details:
- GameServer.slug names the game, provider.key names the emulator. They
  agree for eqemu and daoc but wow != trinitycore, so the mapping is an
  explicit three-entry constant, not an assumption.
- Panel renders NOTHING when a server has no account backend (Impostor is
  a lobby with no identity), rather than a dead button.
- 'backend unreachable' is kept distinct from 'you have no account' - the
  latter would invite a duplicate during an outage.
- Provision creates in the game first, then links. The reverse would
  leave a link pointing at a nonexistent account, which reads as
  corruption to every later call.
- Authorization is just an authenticated Drop session with the ACL: Drop
  access and game access already imply each other, so a third gate could
  only disagree with them.

Typecheck clean.
2026-08-12 17:04:33 -04:00
wdunn001
e8d3df032c desktop: native News, Friends, and Alerts pages; retire the browser-linkout Community tab
Some checks failed
Server CI / Lint (push) Failing after 9m53s
Server CI / Typecheck (push) Successful in 11m40s
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).
2026-08-03 19:42:50 -04:00
wdunn001
3f89ddd11f community: fix Spire login path, add staleness cutoff for DAoC serverstats
Some checks failed
Server CI / Typecheck (push) Successful in 5m37s
Server CI / Lint (push) Failing after 7m17s
- Spire's auth controller is mounted at bare /auth, not /api/v1/login or
  /api/v1/auth/login (confirmed live via Spire's own unauthenticated
  GET /api/v1/routes introspection endpoint). Two earlier guesses both
  405'd. Default login_path corrected to /auth/login.
- mysql_direct gains value_column/observed_at_column/max_staleness_sec:
  OpenDAoC's serverstats is a periodic snapshot (written once a minute by
  the server's own StatSave.cs), not a live aggregate, so a query must read
  the newest row and discard it as unavailable once it's too old rather
  than presenting a stale reading -- possibly from a since-crashed server
  -- as a current live count.
- Registry updated for both now-confirmed-live pieces: daoc-mysql's
  serverproperty.statsave_interval is enabled (1 minute) and verified
  writing; the drop-service Spire account exists (user ID 3), credentials
  in OpenBao kv/eqemu/spire, materialized host-side into
  /opt/drop/spire-service.env (not yet mounted -- compose change is
  separate, see homelab-compose).
2026-08-03 12:55:53 -04:00
wdunn001
904f0f3351 community: replace SSH game-server probes with direct DB/API reads over mmo-net
Some checks failed
Server CI / Lint (push) Failing after 4m46s
Server CI / Typecheck (push) Successful in 6m10s
WoW/TrinityCore and OpenDAoC population is now read via a direct mysql2
connection to trinitycore-mysql/daoc-mysql over the mmo-net bridge (drop
joins that network in homelab-compose), the same shape Drop already uses
for RomM's catalog (rommAdapter.ts) and mmo-portal already uses for these
exact databases. Each query is copied from that game's own management
tooling rather than invented against the schema: WoW's matches FusionCMS's
Characters_model.php::getOnlineCount() verbatim; OpenDAoC's matches
upstream OpenDAoC-Core's own StatSave.cs/stats.php reading serverstats.

EQEmu has no DB-backed online-player source (re-verified live: peq has no
who_all_cache, no characters.online-equivalent, no count anywhere) --
corrects an earlier "genuinely unavailable" claim, which was wrong about
there being no source at all. The real source is World's in-memory client
list, exposed safely through Spire's own authenticated HTTP API
(GET /api/v1/eqemuserver/server-stats, backed by a telnet admin console
that must stay loopback-only). Added a spire_session_api probe method that
logs in as a dedicated service account and reuses the JWT; degrades to
"unavailable" honestly until that account is provisioned.

Removes the ssh_mysql_query and ssh_docker_logs_regex methods -- the
deployed image never had an ssh client, so both always failed. Does not
touch presence_inferred (Goldberg relay), which already degrades to
"unknown" without ssh, by design; not fixed here per explicit instruction
not to add openssh-client to the runtime image or probe the relay's game
ports.
2026-08-03 12:48:57 -04:00
wdunn001
0106a6bd34 community: game-server information provider contract (design + interfaces)
Some checks failed
Server CI / Lint (push) Failing after 11m32s
Server CI / Typecheck (push) Successful in 13m16s
One contract, many providers. Design document plus the TypeScript
interfaces; no implementation, no changes to the existing panel or
services.

Two structural rules, both aimed at bugs we actually hit:

- Population is a discriminated union, not `number | null`. A provider
  that declares population unsupported has no code path to a number, so
  the "0 online now" the panel showed for EverQuest (whose count is
  genuinely unobtainable: Spire needs an authenticated session, the PEQ
  schema has no online column) becomes unrepresentable rather than
  merely fixed.
- "Down" and "could not check" are separate outcomes. `unreachable`
  requires evidence; a transport failure yields `indeterminate`. The
  float ConnectTimeout bug that reported a live relay as DOWN would now
  produce "status unknown".

Providers declare capabilities (liveness, population, player list,
instances, realms, map/mode, latency, version, join info) and the panel
renders from those declarations, so a new game needs no panel edit.
Every fact carries provenance: measured, queried_side_channel, inferred,
or self_reported.

Four topologies modelled without flattening: persistent_world (EQEmu,
TrinityCore, OpenDAoC), instance_fleet (Quake, Counter-Strike),
lobby_host (Hawken, Impostor -- an instance_fleet whose members have no
own address), peer_mesh (the Goldberg relay). ProbeDiscipline carries
neverContactEndpoint, enforced by the runner rather than by a comment,
because gbe_fork registers any prober as a peer.

Survey findings folded in: we run zero Quake/CS servers today, gamedig
gives direct query of all those protocols for free, dpmaster is the only
credible self-hostable master and Source 2 / CS2 has none at all, so the
recommendation is a configured endpoint list plus direct query rather
than running a master. The relay's journald peer log is a readable,
zero-packet occupancy source we were not using.

Files are new and self-contained; migration is four additive phases
keeping the existing GameServer columns working throughout.
2026-08-03 11:06:21 -04:00
wdunn001
180241c266 community: wire server registry deep links + main nav (friends widget, Community submenu, profile link)
Some checks failed
Server CI / Lint (push) Has been cancelled
Server CI / Typecheck (push) Has been cancelled
- Resolve gameServerRegistry.json's null deep_link entries against the
  live drop-db catalog (WoW, EverQuest, Among Us match real Game rows;
  OpenDAoC and the Goldberg relay genuinely have none, left null and
  documented why).
- Fix the "0 online now" panel bug: the community landing page's
  persistent-world quick-strip was inferring presence from PlaySession
  name-matching (presenceService.getServerPresence), which hardcodes
  available=true even with zero matches -- always showing a fake "0"
  for EQEmu instead of the honest unavailable state. Switched it to the
  same probe-backed GameServer feed ServerStatusPanel already uses
  (joined by the shared wow/eqemu/daoc slug), so known-zero and
  count-unavailable never render the same.
- Header people icon now opens a friends flyout (list, incoming
  requests with accept/decline, entry into chat with a friend) instead
  of going nowhere, mirroring the existing notifications bell's
  Menu/MenuButton/MenuItems pattern; badge shows pending request count.
  Mobile sidebar links it straight to /community/friends.
- Community gets a hover submenu in the main nav (desktop) / inline
  sublist (mobile) listing Friends, Chat, Devices, Mods, Servers,
  Issues, Achievements.
- User dropdown menu gets a "Your profile" entry resolved from the
  signed-in user's own username (/community/profile/<username>), not
  hardcoded.
2026-08-03 11:02:58 -04:00
wdunn001
feb5f29190 community: fix presenceAvailable/playing type mismatch breaking the build
Some checks failed
Server CI / Typecheck (push) Successful in 3m8s
Server CI / Lint (push) Failing after 40m17s
getFriendsActivity's declared return type omitted presenceAvailable
entirely (both the early-return-on-no-friends branch and the type
annotation), and community/index.vue's local FriendActivityEntry.playing
was typed {gameId, name} when presenceService.ts's actual FriendPlaying
shape is {platform, id, name} -- the template only ever reads `.name`, so
only the type needed to change. This was breaking `nuxt build`'s typecheck
step on plain `develop` (confirmed via a clean checkout before any M4 code
was involved), which meant nothing downstream of M3's chat/presence
landing could build. Fixed forward rather than reverting anything.
2026-08-03 03:53:18 -04:00
wdunn001
7021af50fb community: M4 -- add GameServer.slug, regenerate migration against M2/M3/M5 baseline
Some checks failed
Server CI / Typecheck (push) Has been cancelled
Server CI / Lint (push) Has been cancelled
Stable slugs (wow/eqemu/daoc) match M3's chatService.ts KNOWN_SERVERS keys
exactly, so ChatRoom.gameServerId's reserved backfill can match on slug
equality later. Migration timestamp moved past M2/M3/M5's so it applies
last; regenerated via schema-to-schema diff (not `prisma migrate dev`) so it
only contains this milestone's delta, not a whitespace sweep of models this
milestone doesn't own.
2026-08-03 03:23:41 -04:00
wdunn001
0348d10602 community: M4 -- game-server registry and issue reports
GameServer/GameServerStatusHistory/Issue/IssueComment models, a
strategy-per-probe_type ServerProbe (tcp_connect/http_get/presence_inferred,
mirroring script-library's game-server-registry-probe.py including the
integer ConnectTimeout fix), the bundled verified registry seed (WoW, EQEmu,
the Goldberg relay, Impostor, OpenDAoC), and full player+admin UI at
/community/servers and /community/issues. Featured flag pins the
persistent-world servers (WoW/EQEmu/OpenDAoC) to a compact status panel
surfaced on the community landing page. Scheduled sweep respects a
circuit-breaker backoff and never probes on page load; the relay is never
network-probed, only liveness-checked over ssh.
2026-08-03 03:19:54 -04:00
wdunn001
dcf6507af5 community/chat: register a websocket error handler
Some checks failed
Server CI / Typecheck (push) Failing after 6m26s
Server CI / Lint (push) Failing after 6m32s
WebSocketHandler throws on the literal "unauthenticated" server response
when no .error() handler is registered, before its listen() callback ever
runs -- and since this composable connects at module scope (Nuxt bundles
auto-imported composables into a shared chunk), that happened on every
signed-out page, /auth/signin included. Found live: a browser check of
/auth/signin hung on an uncaught exception from this exact path.
2026-08-03 01:55:23 -04:00
wdunn001
e80033e68e community: M3 -- friends, presence, chat
Some checks failed
Server CI / Lint (push) Failing after 2m23s
Server CI / Typecheck (push) Failing after 2m56s
Friendship (request/accept/decline/remove, self-request and crossing-request
handling, DB-level pair uniqueness via a hand-written expression index),
ChatRoom/ChatMessage/ChatReadState backed by Nitro's built-in websocket
(server/api/v1/community/ws.get.ts) behind a ChatTransport seam
(server/internal/community/chatTransport.ts) with a REST send fallback.

Mid-milestone product redirect: chat's primary job is coordinating the
persistent-world servers (EQEmu/WoW/DAoC), not per-title discussion --
ChatRoomKind gets a first-class `server` case and chatService.ts ships a
static KNOWN_SERVERS registry keyed the same way M4's GameServer will be,
once that lands (gameServerId reserved for that wiring-up pass).

Presence has no new table -- "online" is a live websocket registry
(presenceRuntime.ts), "playing X" reads through an isolated query module
(presenceService.ts) that degrades to an empty, clearly-labeled result if
PlaySession's shape doesn't match what it expects.

Pages: /community/friends, /community/chat, plus a friends/servers strip on
/community itself. Migration 20260803120000_m3_friends_chat.
2026-08-03 01:39:04 -04:00
wdunn001
e668c41eef community/mods: fix intra-batch dependency resolution ordering
Some checks failed
Server CI / Lint (push) Failing after 2m42s
Server CI / Typecheck (push) Successful in 4m33s
syncModsFromPipeline resolved each mod's dependencies inline as it upserted
that mod, so a mod listed before its own dependency in catalog.json (e.g.
BroMaker, which appears before RocketLib) resolved against a Mod row that
didn't exist yet on a first-ever sync -- landing as "unresolved" until a
second sync run picked it up. Verified live: /community/mods/bromaker's
install plan showed both deps unresolved after the first sync, and
RocketLib correctly resolved (with its own transitive unresolved UMM dep)
after a second.

Split into two passes: upsert every Mod row first, then resolve
versions/dependencies against the now-complete set. Makes a single sync run
order-independent regardless of catalog.json's array order.
2026-08-03 01:31:35 -04:00
wdunn001
933ff37ccb community: M5 -- mods catalog and achievement definitions/display
Some checks failed
Server CI / Lint (push) Failing after 6m33s
Server CI / Typecheck (push) Successful in 7m42s
Adds the M5 slice of the community layer: Mod/ModVersion/ModDependency/
ModInstallation/ModInstalledFile/AchievementDefinition (migration
m5_mods_achievements). Gods/Users are referenced as plain soft columns
(gameId/uploaderId/identityId), not Prisma relations, following the
concurrent-edit-avoidance pattern devices.prisma established -- content.prisma
and user.prisma only grow a comment each.

Mods: syncs from script-library/scripts/archive-mod.py's _index/catalog.json
(read-only bind mount, homelab-compose/drop-stack/docker-compose.yml) into
Mod/ModVersion/ModDependency, resolves dependency refs into real Mod rows
where archived, and exposes a topologically-ordered install-plan resolver
with cycle/conflict/unresolved-dependency detection
(GET /mods/:id/versions/:v/plan). The install manifest
(ModInstallation/ModInstalledFile) records created/overwrote/skipped per
file so an uninstall plan is exact, computed server-side from what was
recorded, applied by the caller. Downloads stream directly off the mount
(GET /mods/:id/versions/:v/download) since cdn.quasarke.net has no
file_server wired to /mnt/local24/game-mods yet -- documented as a known gap
in server/internal/community/README.md, deliberately not fixed by editing
the live Caddyfile. A manual add form covers the DESIGN.md open-question #4
fallback (no upload write path exists).

Achievements: imports gbe_fork's archived generate_emu_config output
(/mnt/local24/game-tools/goldberg/schemas, also read-only mounted) via
goldbergAdapter.ts, copying icons into Drop's own object store
(objectHandler.createFromSource, same mechanism as Steam cover art) so
nothing is hotlinked. Display joins AchievementDefinition against M2's
already-landed AchievementUnlock by (steamAppId, apiName), exactly the join
key M2 designed it around -- no stub needed, M2's table was already in the
tree. Verified against real fixtures: appid 245170 (Skullgirls) importing
45/45 definitions with 90 icons, and 274190 (Broforce) importing 17/17.

Pages: /community/mods, /community/mods/:slug, /community/mods/new,
/community/achievements, /community/games/:gameId (achievement grid +
per-title mods, completion %). Small additive nav links on /community's
index rather than touching the actively-changing shared game-detail page.
2026-08-03 01:16:25 -04:00
wdunn001
c21e69aa5f community: M2 -- device enrollment, wrapper ingest, playtime, played-only listing
Some checks failed
Server CI / Lint (push) Failing after 3m38s
Server CI / Typecheck (push) Successful in 5m36s
Adds the M2 slice of the community layer: Device/DeviceToken/EnrollmentCode/
PlaySession/AchievementUnlock (migration m2_devices_playtime), the
device-token-authenticated ingest surface (POST /api/v1/ingest/enroll,
session-start, session-stop, achievements -- every success acks 2xx +
{"ok":true}, idempotent on sessionId), the /community/devices enrollment UI,
a PowerShell launch wrapper (homelab-compose/drop-stack/wrapper), and unified
playtime rails across Drop + RomM on the profile page.

Also implements the community titles listing change: default view is now
played-only (PlaySession + native Playtime, both platforms), sorted by most
recently played, with a friendly empty state and a "browse all" toggle back
to the full catalog. Adds a live-activity panel (now playing / recently
played) sourced from PlaySession, and a clearly-labeled server-status slot
for M4 to fill in.

PlaySession is the join table for M3/M4/M5: userId, deviceId, titlePlatform
('drop'|'romm'), gameId, communityTitleId, steamAppId, sessionId (unique),
startedAt, endedAt, durationSeconds, endReason, exitCode. No stale-session
sweeper in this pass -- "currently playing" is endedAt IS NULL, filtered to
sessions started within the last 12h for the live panel.
2026-08-03 01:08:39 -04:00
wdunn001
e20b029cb2 community: fix sync's P2029 param-limit crash, harden browse page
Some checks failed
Server CI / Lint (push) Failing after 5m34s
Server CI / Typecheck (push) Successful in 1h21m1s
runCommunityTitleSync's "hide titles that disappeared from RomM" step
built `externalId: { notIn: Array.from(seenExternalIds) }` -- on a
first sync essentially every title is "seen", so that list is ~34.5k
entries, each becoming a bound parameter and blowing Postgres's query
parameter limit (Prisma P2029: "query parameter limit... is exceeded").
Caught live: the first real "Sync now" click 500'd immediately.

Replaced with a timestamp comparison: capture syncStartedAt before any
row is touched, let every create/update set (or DB-default) lastSyncedAt
to now, then hide anything still older than syncStartedAt. Same "wasn't
touched by this run" semantics as the id-set check, O(1) parameters
regardless of catalog size.

Also hardens server/pages/community/index.vue's initial title fetch and
admin sync-status fetch with try/catch instead of an unguarded top-level
await -- a request that throws for any reason (this bug, a future one,
a transient adapter hiccup) should degrade to an empty/quiet state, not
take the whole page down via Nuxt's full-page error boundary. This is
exactly how a *different*, now-fixed bug (an arktype schema requiring
the `platform` query key to be present) surfaced: a plain 400 from the
API turned into a hard page crash purely because nothing caught it.
2026-08-02 14:13:32 -04:00
wdunn001
5efbd01880 community: fix titles.get.ts 400 on a bare /community browse request
Some checks failed
Server CI / Typecheck (push) Has been cancelled
Server CI / Lint (push) Has been cancelled
Arktype needs the KEY suffixed with "?" to make a query param
optional (same pattern already used correctly for `q: "string?"`,
which marks the VALUE nullable, not the key absent) -- marking only the
*value* type as "'drop' | 'romm' | undefined" still requires the
`platform` key to be present, just allowing it to hold undefined. Since
getQuery() simply omits absent query keys rather than setting them to
literal `undefined`, every browse request without an explicit platform
filter -- i.e. every normal page load of /community, both the initial
SSR request and the client-side fetch behind it -- failed arktype
validation with "platform must be 'drop', 'romm' or undefined (was
missing)" and 400'd. browseService.ts already treats platform as an
optional key (`platform?: "drop" | "romm"`) and handles it being absent
correctly (`wantsDrop = !platform || platform === "drop"`), so this was
purely a schema-declaration bug in the API route, not a design issue.

Caught by browser-driving drop.quasarke.net/community end-to-end (not
just curl -- a plain GET returns real content either way, so this
needed an actual page load to surface).
2026-08-02 13:22:48 -04:00
wdunn001
0a41fd429e Dockerfile: add a memory-pressure escape hatch for the typecheck step
Some checks failed
Server CI / Typecheck (push) Failing after 3h14m3s
Server CI / Lint (push) Failing after 3h10m52s
Observed on .88 (2026-08-02): docker build got through deps install,
Prisma generate, and the client bundle fine, then got OOM-killed (exit 143)
during vue-tsc's full-project typecheck -- the host had ~600Mi free and a
fully-exhausted 2GB swap at the time (osrm-routed alone holds ~47GB RSS on
that shared box). This isn't a code problem; it's the single most
memory-hungry step of `nuxt build` landing on an already-saturated host.

--build-arg SKIP_TYPECHECK=true (env NUXT_BUILD_SKIP_TYPECHECK) skips it.
Off by default -- this is a resource-pressure escape hatch for a
contended host, not a reason to stop typechecking; re-run
`pnpm run typecheck` separately once there's headroom.
2026-08-02 01:03:09 -04:00
wdunn001
f59fd03b47 community: fix source-build type errors (missing ACL descriptions, JSON cast)
Some checks are pending
Server CI / Typecheck (push) Waiting to run
Server CI / Lint (push) Waiting to run
Caught by an actual `docker build` of the fork from source on .88:
descriptions.ts requires an exhaustive description for every ACL name
(missed community:read/community:sync when adding them), and
titleSyncService's update() call needed the same Prisma.InputJsonValue
cast its createMany() sibling already had for the metadata column.
2026-08-02 00:34:51 -04:00
wdunn001
faa8b7d61c community: fold /community into the fork, source-build the three JS patches
Some checks are pending
Deploy website to GitHub Pages / deploy (push) Blocked by required conditions
Deploy website to GitHub Pages / build (push) Waiting to run
Server CI / Typecheck (push) Waiting to run
Server CI / Lint (push) Waiting to run
Replaces the server/pages/community.vue stub ({{ $t("todo") }}) with real
Nuxt pages: a unified Drop+RomM title browser, an admin catalog-sync panel,
and a profile page with playtime rails. Corrects the original architecture
mistake of building this as a separate service at community.quasarke.net --
now that we own the fork's source, community is ordinary Nuxt pages instead
of a sidecar with its own auth/DB/vhost.

Data model: one new table, CommunityTitle, for the RomM catalog (ported
from drop-community M1's Title spine). Drop's own Game/User/Playtime models
already ARE the title/identity/playtime spine for Drop content, so nothing
new was needed there -- profile playtime rails read real, already-existing
Playtime rows, no wrapper/ingest milestone required to populate them.
RommAdapter (server/server/internal/community/rommAdapter.ts) reads RomM's
MariaDB directly via the community_ro read-only role, same deviation and
same reasoning as M1's adapter (RomM's /api/roms needs a browser session,
no service-account key). TitleSyncService runs every 6h
(scheduledTasks.communityTitleSync) and via an admin "Sync now" button
(system:community:sync ACL) -- never deletes, hides instead.

Also folds the three post-build JS patches
(homelab-compose/drop-stack/patch-{launcher-button,telemetry-snippet,
footer-links}.js) into source now that the fork builds from source instead
of patching upstream's prebuilt image:
  - library/game/[id]/index.vue: the "Open in Launcher" button gets a real
    @click handler (drop://open with a timeout fallback to /download).
  - plugins/telemetry.client.ts: OpenPanel RUM injected via a Nuxt client
    plugin instead of appending to the compiled entry chunk.
  - UserFooter.vue + admin/library source-help links + error.vue + the
    Weblate link: repointed at our own infra. The Discord/social link
    used to point at community.quasarke.net; that service is retired, so
    it now points at /community directly.
2026-08-02 00:12:06 -04:00
dependabot[bot]
f59bf9fd85
chore(deps): bump nuxt from 3.21.6 to 3.21.7 (#424)
Bumps [nuxt](https://github.com/nuxt/nuxt/tree/HEAD/packages/nuxt) from 3.21.6 to 3.21.7.
- [Release notes](https://github.com/nuxt/nuxt/releases)
- [Commits](https://github.com/nuxt/nuxt/commits/v3.21.7/packages/nuxt)

---
updated-dependencies:
- dependency-name: nuxt
  dependency-version: 3.21.7
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-29 21:00:46 +10:00
dependabot[bot]
0eefae69cf
chore(deps): bump shescape from 2.1.11 to 2.1.14 (#458)
Bumps [shescape](https://github.com/ericcornelissen/shescape) from 2.1.11 to 2.1.14.
- [Release notes](https://github.com/ericcornelissen/shescape/releases)
- [Changelog](https://github.com/ericcornelissen/shescape/blob/v2.1.14/CHANGELOG.md)
- [Commits](https://github.com/ericcornelissen/shescape/compare/v2.1.11...v2.1.14)

---
updated-dependencies:
- dependency-name: shescape
  dependency-version: 2.1.14
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-29 18:47:10 +10:00
AgentScrubbles
97c6f2c8a6
Add age ratings (#451)
* Base age rating system

* Cleanup enum leftovers

* Missed files

* GB importer

* Linter settings

* Translations

* Prettier and linting

---------

Co-authored-by: Robert Clabough <robert@clabough.tech>
2026-07-26 11:42:16 +10:00
AgentScrubbles
8e75a0bf56
Autofill launch name on version import (#373) (#447)
Co-authored-by: Robert Clabough <robert@clabough.tech>
2026-07-24 07:38:04 +10:00
DecDuck
d0c3b2fdb6
Fix admin UI user bar overlap (#439) 2026-07-10 14:53:48 +10:00
DecDuck
38c11567ef
Variety of bug fixes (#432)
* Fix #414

* Implement #268

* Add #269
2026-06-21 20:07:59 +10:00
DecDuck
cbecd1161d
Publish docs, update links (#431)
* Publish docs, update links

* Fix sitemap gen

* Migrate to Astro v6

* Fix server lint
2026-06-21 16:39:34 +10:00
DecDuck
9185089c99
Fix v0.4.0 process handler, add override menu (#430)
* 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
2026-06-21 15:24:33 +10:00
DecDuck
2e86422004
Add lints, new website publish (#428)
* Add lints and new website

* Fix droplet CI

* Fix droplet ci again

* Fix clippy lints
2026-06-21 11:16:39 +10:00
DecDuck
796abf478f
Fix GitHub Actions build (#427)
* Fix server build

* Remove server drop-base submod

* Update lockfile

* Use debian images for build

* Fix pino errors, lint

* Fix macOS keychain lookup
2026-06-21 10:37:54 +10:00
dependabot[bot]
8637ff52ef
chore(deps): bump nuxt from 3.21.2 to 3.21.6 (#417)
Bumps [nuxt](https://github.com/nuxt/nuxt/tree/HEAD/packages/nuxt) from 3.21.2 to 3.21.6.
- [Release notes](https://github.com/nuxt/nuxt/releases)
- [Commits](https://github.com/nuxt/nuxt/commits/v3.21.6/packages/nuxt)

---
updated-dependencies:
- dependency-name: nuxt
  dependency-version: 3.21.6
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 08:12:37 +10:00
dependabot[bot]
a3974f6137
chore(deps-dev): bump nitropack from 2.13.3 to 2.13.4 (#410)
Bumps [nitropack](https://github.com/nitrojs/nitro) from 2.13.3 to 2.13.4.
- [Release notes](https://github.com/nitrojs/nitro/releases)
- [Changelog](https://github.com/nitrojs/nitro/blob/main/changelog.config.ts)
- [Commits](https://github.com/nitrojs/nitro/compare/v2.13.3...v2.13.4)

---
updated-dependencies:
- dependency-name: nitropack
  dependency-version: 2.13.4
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 12:28:18 +10:00
DecDuck
bf7ce5927f
Attempt fix monorepo build (#404)
* add latest changes and fix launcher

* add optional tag specify

* fix client release

* empty commit
2026-04-27 15:38:05 +10:00
Husky
ff1144e016
Improve repo tooling (#398)
* add basic git files to root

* make server part of monorepo

* import promo

* import libraries base

* import docs

* import desktop

* move docs and promo
2026-04-20 11:44:38 +10:00
DecDuck
5bbe406e4c
disable proxy buffering 2026-04-19 09:38:42 +10:00
DecDuck
2dd90fbc44 New v0.4.0 website 2026-04-03 01:25:10 +00:00
DecDuck
50106d5fa2
Add final packages and fix torrential launch 2026-03-30 19:47:53 +11:00
DecDuck
e881da5ce3
move base to libraries 2026-03-30 19:38:14 +11:00
DecDuck
6b294949b9
Add and fix missing projects 2026-03-30 19:34:34 +11:00
DecDuck
f82ec017d9
Update readmes for monorepo 2026-03-30 19:04:39 +11:00
NicoVIII
22dc94b920 Add z-index to ComboboxOptions (#271) (#375)
I never used headlessui before, so I have no idea why this is needed, but it looks like this fixes the problem. I think a high z-index is alright, because there shouldn't be things that should lie above the options.
2026-03-29 21:24:45 +11:00
dependabot[bot]
f03fed0b8a chore(deps): bump shescape from 2.1.8 to 2.1.10 (#368)
Bumps [shescape](https://github.com/ericcornelissen/shescape) from 2.1.8 to 2.1.10.
- [Release notes](https://github.com/ericcornelissen/shescape/releases)
- [Changelog](https://github.com/ericcornelissen/shescape/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ericcornelissen/shescape/compare/v2.1.8...v2.1.10)

---
updated-dependencies:
- dependency-name: shescape
  dependency-version: 2.1.10
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-13 08:12:57 +11:00
Paco
f3e90bd033 Fix non-authenticated users being counted as sessions (#362) 2026-03-03 08:02:58 +11:00
DecDuck
236022e99f Check integrity task (#364) 2026-03-01 21:49:34 +11:00
DecDuck
83516d83fd OIDC validation & issuer fixes (#363)
* fix: validation and issuer checks

* feat: query param util

* fix: lint
2026-03-01 21:25:55 +11:00
DecDuck
14f4135071 Tag connect & disconnect fix (#360)
* fix: tag connect/disconnect

* fix: lint

* fix: oidc typo fix
2026-02-27 15:15:27 +11:00
DecDuck
d7fffe59f9 OIDC & store fixes (#358)
* fix: typos

* fix: platform filtering

* feat: fix tags and create option
2026-02-27 09:15:19 +11:00