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.
- 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.
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.
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.
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.
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.
* Adds settings for server name and logo
* Implements ApplicationLogo and replaces site name based on settings
* Refactors component for changing the company logo
* Removes unused variable
* Uses message instead of statusMessage
* Replaces favicon with logo if set
Currently translated at 100.0% (522 of 522 strings)
Translated using Weblate (German)
Currently translated at 100.0% (522 of 522 strings)
Translated using Weblate (German)
Currently translated at 100.0% (522 of 522 strings)
Translated using Weblate (French)
Currently translated at 100.0% (522 of 522 strings)
Translated using Weblate (French)
Currently translated at 100.0% (518 of 518 strings)
Translate-URL: https://translate.droposs.org/projects/drop/drop/de/
Translate-URL: https://translate.droposs.org/projects/drop/drop/fr/
Translation: Drop/Drop
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Hicks <hicksgaming99+weblate@gmail.com>
Co-authored-by: Ribemont Francois <ribemont.francois+weblate@gmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
* Adds new tile on the admin home page with system data. Also fixes the active users bug in the pie chart
* Fixes missing parentheses
* Updates user stats cache when signing in
* Reads active number of users from session provider
* Removes unused variable
* Small improvements
* Removes acl properties from system data websocket and performs initial push of data
* fix: remove acl fetch
---------
Co-authored-by: DecDuck <declanahofmeyr@gmail.com>
Currently translated at 100.0% (529 of 529 strings)
Translated using Weblate (Polish)
Currently translated at 100.0% (529 of 529 strings)
Translated using Weblate (Polish)
Currently translated at 100.0% (523 of 523 strings)
Translated using Weblate (Polish)
Currently translated at 24.4% (128 of 523 strings)
Translated using Weblate (Polish)
Currently translated at 22.7% (119 of 523 strings)
Added translation using Weblate (Polish)
Translated using Weblate (German)
Currently translated at 100.0% (523 of 523 strings)
Translated using Weblate (English (en_PIRATE))
Currently translated at 73.8% (386 of 523 strings)
Translate-URL: http://translate.droposs.org/projects/drop/drop/de/
Translate-URL: http://translate.droposs.org/projects/drop/drop/en_PIRATE/
Translate-URL: http://translate.droposs.org/projects/drop/drop/pl/
Translate-URL: https://translate.droposs.org/projects/drop/drop/fr/
Translation: Drop/Drop
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Andus <resres2007@gmail.com>
Co-authored-by: Hicks <hicksgaming99+weblate@gmail.com>
Co-authored-by: Ribemont Francois <ribemont.francois+weblate@gmail.com>
* Add user page and API endpoint
* add: /user/[id] page
* add: /api/v1/user/[id] API endpoint
* Change loading message in user profile page
* Fix build errors, prettier code
Currently translated at 100.0% (522 of 522 strings)
Translated using Weblate (German)
Currently translated at 100.0% (522 of 522 strings)
Translated using Weblate (German)
Currently translated at 100.0% (522 of 522 strings)
Translated using Weblate (German)
Currently translated at 96.3% (503 of 522 strings)
Translated using Weblate (German)
Currently translated at 96.3% (503 of 522 strings)
Translated using Weblate (French)
Currently translated at 100.0% (522 of 522 strings)
Translated using Weblate (French)
Currently translated at 100.0% (522 of 522 strings)
Translate-URL: http://translate.droposs.org/projects/drop/drop/de/
Translate-URL: http://translate.droposs.org/projects/drop/drop/fr/
Translation: Drop/Drop
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Hicks <hicksgaming99+weblate@gmail.com>
Co-authored-by: Ribemont Francois <ribemont.francois+weblate@gmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Currently translated at 100.0% (499 of 499 strings)
Translated using Weblate (French)
Currently translated at 96.9% (484 of 499 strings)
Co-authored-by: Ribemont Francois <ribemont.francois+weblate@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.droposs.org/projects/drop/drop/fr/
Translation: Drop/Drop
* First iteration on the new PieChart component
* #128 Adds new admin home page
* Fixes code after merging conflicts
* Removes empty file
* Uses real data for admin home page, and improves style
* Reverts debugging code
* Defines missing variable
* Caches user stats data for admin home page
* Typo
* Styles improvements
* Invalidates cache on signup/signin
* Implements top 5 biggest games
* Improves styling
* Improves style
* Using generateManifest to get the proper size
* Reading data from cache
* Removes unnecessary import
* Improves caching mechanism for game sizes
* Removes lint errors
* Replaces piechart tooltip with colors in legend
* Fixes caching
* Fixes caching and slight improvement on pie chart colours
* Fixes a few bugs related to caching
* Fixes bug where app signin didn't refresh cache
* feat: style improvements
* fix: lint
---------
Co-authored-by: DecDuck <declanahofmeyr@gmail.com>
* Translated using Weblate (German)
Currently translated at 66.5% (314 of 472 strings)
Translation: Drop/Drop
Translate-URL: http://translate.droposs.org/projects/drop/drop/de/
* Translated using Weblate (French)
Currently translated at 93.1% (465 of 499 strings)
Translation: Drop/Drop
Translate-URL: http://translate.droposs.org/projects/drop/drop/fr/
* Translated using Weblate (Russian)
Currently translated at 16.0% (80 of 499 strings)
Translation: Drop/Drop
Translate-URL: http://translate.droposs.org/projects/drop/drop/ru/
* Translated using Weblate (German)
Currently translated at 62.9% (314 of 499 strings)
Translation: Drop/Drop
Translate-URL: http://translate.droposs.org/projects/drop/drop/de/
* Translated using Weblate (German)
Currently translated at 62.9% (314 of 499 strings)
Translation: Drop/Drop
Translate-URL: http://translate.droposs.org/projects/drop/drop/de/
* Translated using Weblate (German)
Currently translated at 62.9% (314 of 499 strings)
Translation: Drop/Drop
Translate-URL: http://translate.droposs.org/projects/drop/drop/de/
* Translated using Weblate (German)
Currently translated at 81.7% (408 of 499 strings)
Translation: Drop/Drop
Translate-URL: http://translate.droposs.org/projects/drop/drop/de/
* Translated using Weblate (German)
Currently translated at 81.7% (408 of 499 strings)
Translation: Drop/Drop
Translate-URL: http://translate.droposs.org/projects/drop/drop/de/
* Translated using Weblate (German)
Currently translated at 81.7% (408 of 499 strings)
Translation: Drop/Drop
Translate-URL: http://translate.droposs.org/projects/drop/drop/de/
* Translated using Weblate (German)
Currently translated at 100.0% (499 of 499 strings)
Translation: Drop/Drop
Translate-URL: http://translate.droposs.org/projects/drop/drop/de/
---------
Co-authored-by: Niklas Eifler <droposs@eiflerstrom.de>
Co-authored-by: pVDWNwffCRw2B2inHGs# <farmouss@gmail.com>
Co-authored-by: D3 <sl4yerenter@protonmail.com>
Co-authored-by: Weblate Translation Memory <noreply-mt-weblate-translation-memory@weblate.org>
Co-authored-by: Kuschiniko <nico.kusch@outlook.de>
Co-authored-by: Hicks <hicksgaming99+weblate@gmail.com>
* fix: small fixes to request util and version update endpoint
* feat: api token creation and management
* fix: lint
* fix: remove unneeded sidebar component
* feat: small library tweaks + company page
* feat: new store view
* fix: ci merge error
* feat: add genres to store page
* feat: sorting
* feat: lock game/version imports while their tasks are running
* feat: feature games
* feat: tag based filtering
* fix: make tags alphabetical
* refactor: move a bunch of i18n to common
* feat: add localizations for everything
* fix: title description on panel
* fix: feature carousel text
* fix: i18n footer strings
* feat: add tag page
* fix: develop merge
* feat: offline games support (don't error out if provider throws)
* feat: tag management
* feat: show library next to game import + small fixes
* feat: most of the company and tag managers
* feat: company text field editing
* fix: small fixes + tsgo experiemental
* feat: upload icon and banner
* feat: store infinite scrolling and bulk import mode
* fix: lint
* fix: add drop-base to prettier ignore
* fix: save task as Json rather than string
* fix: pull objects before creating game in database
* fix: strips relative dirs from version information
* fix: #132
* fix: lint
* fix: news object ids and small tweaks
* fix: notification styling errors
* fix: lint
* #51 Adds settings page with showTitleDescriptionOnGamePanel
* Removes console.log
* Renames isHidden to system, adds missing system column on Game and fixes nitro plugin on fresh database
* Implements a different way to handle the placeholder image
* Removes system column on Game
* Groups settings keys together
* Removes unused i18n keys
* fix: fix eslints and other small tweaks
---------
Co-authored-by: Francois Ribemont <ribemont.francois@gmail.com>
* feat: set lang in html head
* fix: add # in front of git ref
* fix: remove unused vars from example env
* fix: package name and license field
* fix: enable sourcemap for client and server
* fix: emojis not showing in prod
this is extremely cursed, but it works
* chore: refactor auth manager
* feat: disable invitations if simple auth disabled
* feat: add drop version to footer
* feat: translate auth endpoints
* chore: move oidc module
* feat: add weekly tasks
enabled object cleanup as weekly task
* feat: add timestamp to task log msgs
* feat: add guard to prevent invalid progress %
* fix: add missing global scope to i18n components
* feat: set base url for i18n
* feat: switch task log to json format
* ci: run ci on develop branch only
* fix: UserWidget text not updating #109
* fix: EXTERNAL_URL being computed at build
* feat: add basic language outlines for translation
* feat: add more english dialects