mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
Document admin page and refresh architecture docs
This commit is contained in:
@@ -4,8 +4,13 @@ Native Android/iOS admin app for DorkNet, built with .NET MAUI.
|
|||||||
|
|
||||||
In the microservices deployment, point the app at the public admin host
|
In the microservices deployment, point the app at the public admin host
|
||||||
for the stack, usually `https://admin.<domain>`. Cloudflare and
|
for the stack, usually `https://admin.<domain>`. Cloudflare and
|
||||||
`DorkNet.Gateway` keep the public URL stable while admin API requests are
|
`DorkNet.Gateway` keep the public URL stable. Requests made through the
|
||||||
routed to the owning backend slice.
|
admin host are handled by the `web` service's same-origin admin route;
|
||||||
|
the `/api/admin/*` path family is also exposed by the moderation slice
|
||||||
|
for non-admin-host routing.
|
||||||
|
|
||||||
|
The browser admin page, route map, and troubleshooting flow are
|
||||||
|
documented in [`../docs/admin.md`](../docs/admin.md).
|
||||||
|
|
||||||
## Authentication
|
## Authentication
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ Both were collapsed in 2026-05-25:
|
|||||||
no per-feature patches needed.
|
no per-feature patches needed.
|
||||||
- The PhotonRegion coerce (the only data transform in the block)
|
- The PhotonRegion coerce (the only data transform in the block)
|
||||||
was moved into `LegacyUpgrades.CoercePhotonRegionAsync`.
|
was moved into `LegacyUpgrades.CoercePhotonRegionAsync`.
|
||||||
- The Storage admin page (`admin-ui/src/pages/Storage.tsx`) and
|
- The legacy storage backfill page (`admin-ui/src/pages/Storage.tsx`) and
|
||||||
`StorageBackfillController.cs` were deleted — they were the UI for
|
`StorageBackfillController.cs` were deleted — they were the UI for
|
||||||
draining the legacy `RoomDataBlobs.Bytes` column into S3, which is
|
draining the legacy `RoomDataBlobs.Bytes` column into S3, which is
|
||||||
no longer needed.
|
no longer needed.
|
||||||
|
|||||||
@@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
Generated: 2026-05-09. Source: client URLs from `Cpp2IL_ISIL` ISIL dump (curated list provided to this report); server routes harvested via grep of `[HttpGet]`/`[HttpPost]`/`[HttpPut]`/`[HttpDelete]`/`[Route]` attributes across `Controllers/**/*.cs` plus the namespace-wildcard fall-throughs in `ApiNamespaceStubsController` and the global `ApiCatchAllController` / `GlobalCatchAllController`.
|
Generated: 2026-05-09. Source: client URLs from `Cpp2IL_ISIL` ISIL dump (curated list provided to this report); server routes harvested via grep of `[HttpGet]`/`[HttpPost]`/`[HttpPut]`/`[HttpDelete]`/`[Route]` attributes across `Controllers/**/*.cs` plus the namespace-wildcard fall-throughs in `ApiNamespaceStubsController` and the global `ApiCatchAllController` / `GlobalCatchAllController`.
|
||||||
|
|
||||||
|
> Snapshot scope: this file is a historical controller-coverage matrix,
|
||||||
|
> not the live gateway route table. The dedicated microservice hosts
|
||||||
|
> still reuse the shared controller stack behind route guards, so the
|
||||||
|
> endpoint shape notes remain useful. For current service ownership, use
|
||||||
|
> `DorkNet.Contracts/DorkNetRouteOwnership.cs` or the gateway
|
||||||
|
> `/internal/routes` endpoint.
|
||||||
|
|
||||||
## How this matrix is built
|
## How this matrix is built
|
||||||
|
|
||||||
Each client URL is matched against the server's full route table. ASP.NET Core routes resolve "specific wins over wildcard", so a route literally registered as `api/foo/v1/bar` always wins over `api/foo/{*path}`. The `Status` column reflects the route that actually wins:
|
Each client URL is matched against the server's full route table. ASP.NET Core routes resolve "specific wins over wildcard", so a route literally registered as `api/foo/v1/bar` always wins over `api/foo/{*path}`. The `Status` column reflects the route that actually wins:
|
||||||
|
|||||||
@@ -245,6 +245,9 @@ For deployment details, including Dokploy env vars, Cloudflare Tunnel
|
|||||||
routing, external Postgres, and S3 setup, see
|
routing, external Postgres, and S3 setup, see
|
||||||
[docs/deploy.md](docs/deploy.md).
|
[docs/deploy.md](docs/deploy.md).
|
||||||
|
|
||||||
|
For the browser admin console, its routes, auth model, and production
|
||||||
|
troubleshooting, see [docs/admin.md](docs/admin.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Database & migrations
|
## Database & migrations
|
||||||
|
|||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
# DorkNet Admin Page
|
||||||
|
|
||||||
|
The admin page is the browser operations console for DorkNet. In the
|
||||||
|
microservices deployment it lives at `https://admin.<domain>/` and is
|
||||||
|
served by the `web` service from the built Vite SPA assets.
|
||||||
|
|
||||||
|
The admin backend endpoints are under `/api/admin/v1`. Requests from the
|
||||||
|
SPA are same-origin, so `https://admin.<domain>/api/admin/v1/*` is also
|
||||||
|
handled by the `web` service route on the admin host. The same
|
||||||
|
`/api/admin/*` route family is available as a path-owned moderation slice
|
||||||
|
for non-admin-host routing, so `/internal/routes` can show moderation as
|
||||||
|
the owner for that path family.
|
||||||
|
|
||||||
|
## Runtime Routing
|
||||||
|
|
||||||
|
| Item | Value |
|
||||||
|
|---|---|
|
||||||
|
| SPA source | `DorkNet.Server/admin-ui` |
|
||||||
|
| Build output | `DorkNet.Server/wwwroot/admin` |
|
||||||
|
| Container path | `/app/wwwroot/admin` |
|
||||||
|
| Public browser host | `admin.<domain>` |
|
||||||
|
| API base used by the SPA | `/api/admin/v1` |
|
||||||
|
| Login endpoint | `POST /api/admin/v1/login` |
|
||||||
|
| Same-origin admin-host service | `web` |
|
||||||
|
| Path-routed admin API slice | `moderation` |
|
||||||
|
|
||||||
|
`DorkNet.Gateway` preserves the original Host header when it proxies to
|
||||||
|
backend services. That matters for the admin page: the `admin.<domain>`
|
||||||
|
host route must reach `web`, and `web` must contain
|
||||||
|
`/app/wwwroot/admin/index.html`.
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
The first account created on a fresh database is promoted to admin so
|
||||||
|
there is always a bootstrap operator. The login form posts to
|
||||||
|
`/api/admin/v1/login`; the SPA stores the returned values in browser
|
||||||
|
`localStorage`:
|
||||||
|
|
||||||
|
| Key | Contents |
|
||||||
|
|---|---|
|
||||||
|
| `dorknet.admin.token` | DorkNet admin JWT |
|
||||||
|
| `dorknet.admin.me` | Admin identity object used by the layout |
|
||||||
|
|
||||||
|
Every admin API request sends `Authorization: Bearer <token>`. Protected
|
||||||
|
actions are guarded by `AdminOnlyAttribute`, which checks the resolved
|
||||||
|
player row still has `IsAdmin = true`. A `401` clears the local session
|
||||||
|
and sends the browser back to login.
|
||||||
|
|
||||||
|
If the admin host is public, put it behind Cloudflare Access or an
|
||||||
|
equivalent outer access control. The DorkNet JWT is still required after
|
||||||
|
that outer check.
|
||||||
|
|
||||||
|
## Navigation
|
||||||
|
|
||||||
|
| Section | Routes | Workflows |
|
||||||
|
|---|---|---|
|
||||||
|
| Overview | `/` | Live ops dashboard, online players, active sessions, quick kick/ban/broadcast actions |
|
||||||
|
| Moderation | `/players` | Player directory, bans, reports, per-player ban/grant/gift/password/avatar/account actions |
|
||||||
|
| Activity | `/activity` | Admin audit log and per-player request logs |
|
||||||
|
| Content | `/rooms`, `/rooms/:id`, `/import-room`, `/content` | Room list/detail, room import, instances, leaderboards, community board, loading tips |
|
||||||
|
| Operations | `/broadcast`, `/settings` | Server broadcast, server toggles, signup codes, weekly challenges, Play menu tags, Rec Center doors, game config values |
|
||||||
|
|
||||||
|
Several older admin URLs are kept as redirects:
|
||||||
|
|
||||||
|
| Old route | Current route |
|
||||||
|
|---|---|
|
||||||
|
| `/bans` | `/players?tab=bans` |
|
||||||
|
| `/reports` | `/players?tab=reports` |
|
||||||
|
| `/gift`, `/passwords`, `/grants` | `/players` |
|
||||||
|
| `/audit` | `/activity?tab=audit` |
|
||||||
|
| `/logs` | `/activity?tab=logs` |
|
||||||
|
| `/rr-originals`, `/instances`, `/leaderboards` | `/rooms` |
|
||||||
|
| `/community` | `/content?tab=community` |
|
||||||
|
| `/loading-tips` | `/content?tab=tips` |
|
||||||
|
| `/signup-codes` | `/settings?tab=signup` |
|
||||||
|
|
||||||
|
`/import-room-legacy` still exists for the legacy room importer.
|
||||||
|
|
||||||
|
## Build And Deploy
|
||||||
|
|
||||||
|
The service Dockerfile builds the admin SPA for service images that need
|
||||||
|
static assets. In the microservices stack the `web` image must include:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/app/wwwroot/admin/index.html
|
||||||
|
/app/wwwroot/admin/assets/*
|
||||||
|
```
|
||||||
|
|
||||||
|
The admin SPA calls relative URLs, so it does not need a configured API
|
||||||
|
origin as long as the browser is opened on `https://admin.<domain>/`.
|
||||||
|
|
||||||
|
The native admin mobile app points at the same public admin host; see
|
||||||
|
[`../DorkNet.AdminMobile/README.md`](../DorkNet.AdminMobile/README.md).
|
||||||
|
|
||||||
|
## Smoke Checks
|
||||||
|
|
||||||
|
Check the static host:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -I https://admin.yourdomain.com/
|
||||||
|
```
|
||||||
|
|
||||||
|
Check that protected admin API routing is alive. A `401` is expected
|
||||||
|
without a valid admin token:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -i https://admin.yourdomain.com/api/admin/v1/stats
|
||||||
|
```
|
||||||
|
|
||||||
|
Check the built assets inside the running `web` container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec <web-container> sh -lc 'find /app/wwwroot/admin -maxdepth 2 -type f | sort | head -50'
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful logs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker logs <stack>-gateway-1 --tail=200
|
||||||
|
docker logs <stack>-web-1 --tail=200
|
||||||
|
docker logs <stack>-moderation-1 --tail=200
|
||||||
|
```
|
||||||
|
|
||||||
|
For normal browser admin traffic, start with `gateway` and `web`.
|
||||||
|
Check `moderation` when you are testing `/api/admin/*` through a
|
||||||
|
non-admin host such as `api.<domain>`.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Symptom | Check |
|
||||||
|
|---|---|
|
||||||
|
| `admin.<domain>/` returns 404 | `web` logs for `probe="/app/wwwroot/admin/index.html" exists=False`; rebuild/redeploy the `web` image |
|
||||||
|
| Admin page loads but actions 404 | Gateway `/internal/routes`, then whether the request host is `admin.<domain>` or a path-routed host |
|
||||||
|
| Login or actions return 401 | Token expired, local session cleared, or the player no longer has `IsAdmin = true` |
|
||||||
|
| Browser spins forever | Open the browser network tab, then check `gateway` and `web` logs for the stuck URL |
|
||||||
|
| Upload/import fails near 100 MB | Use the chunked importer; the SPA uses 50 MB chunks to stay below Cloudflare request limits |
|
||||||
@@ -45,8 +45,8 @@ DorkNet.sln
|
|||||||
| `DorkNet.Services.Social` | Clubs, groups, announcements, player events, subscriptions | .NET 9 |
|
| `DorkNet.Services.Social` | Clubs, groups, announcements, player events, subscriptions | .NET 9 |
|
||||||
| `DorkNet.Services.Commerce` | Catalog, storefronts, econ, inventory, inventions | .NET 9 |
|
| `DorkNet.Services.Commerce` | Catalog, storefronts, econ, inventory, inventions | .NET 9 |
|
||||||
| `DorkNet.Services.Platform` | Service URLs, config, version checks, geo, strings, telemetry-style surfaces | .NET 9 |
|
| `DorkNet.Services.Platform` | Service URLs, config, version checks, geo, strings, telemetry-style surfaces | .NET 9 |
|
||||||
| `DorkNet.Services.Moderation` | Bug reporting, player reporting, sanitize, admin API, testcase routes | .NET 9 |
|
| `DorkNet.Services.Moderation` | Bug reporting, player reporting, sanitize, path-routed admin API, testcase routes | .NET 9 |
|
||||||
| `DorkNet.Services.Web` | Public site, admin static host, feed static host, site API | .NET 9 |
|
| `DorkNet.Services.Web` | Public site, admin static host, same-origin admin browser API, feed static host, site API | .NET 9 |
|
||||||
| `DorkNet.ClientMod` | MelonLoader 0.6.x IL2CPP mod — the client patcher, JSON config | .NET 6 |
|
| `DorkNet.ClientMod` | MelonLoader 0.6.x IL2CPP mod — the client patcher, JSON config | .NET 6 |
|
||||||
|
|
||||||
`ClientMod` applies the client-side patches needed to point the 2020
|
`ClientMod` applies the client-side patches needed to point the 2020
|
||||||
@@ -119,6 +119,10 @@ SPAs for service images that need static assets, publishes the selected
|
|||||||
.NET project, and runs that DLL in the runtime image. The root
|
.NET project, and runs that DLL in the runtime image. The root
|
||||||
`Dockerfile` builds only the standalone/fallback `DorkNet.Server` image.
|
`Dockerfile` builds only the standalone/fallback `DorkNet.Server` image.
|
||||||
|
|
||||||
|
For the admin browser console specifically, including the host routing
|
||||||
|
split between the admin static host and `/api/admin/v1`, see
|
||||||
|
[`admin.md`](admin.md).
|
||||||
|
|
||||||
### Controllers/ groupings
|
### Controllers/ groupings
|
||||||
|
|
||||||
20 top-level groups, each responsible for one product surface the
|
20 top-level groups, each responsible for one product surface the
|
||||||
|
|||||||
+10
-2
@@ -29,8 +29,8 @@ while route slices move out of the fallback server.
|
|||||||
| `social` | Clubs, groups, announcements, player events, subscriptions |
|
| `social` | Clubs, groups, announcements, player events, subscriptions |
|
||||||
| `commerce` | Catalog, storefronts, econ, inventory, inventions |
|
| `commerce` | Catalog, storefronts, econ, inventory, inventions |
|
||||||
| `platform` | Service directory, config, version checks, geo, strings |
|
| `platform` | Service directory, config, version checks, geo, strings |
|
||||||
| `moderation` | Bug reports, player reports, sanitize, admin API, testcase routes |
|
| `moderation` | Bug reports, player reports, sanitize, path-routed admin API, testcase routes |
|
||||||
| `web` | Apex/www/admin/feed static hosts and site API |
|
| `web` | Apex/www/admin/feed static hosts, same-origin admin browser API, and site API |
|
||||||
| `monolith` | Fallback shared server for route families not split yet |
|
| `monolith` | Fallback shared server for route families not split yet |
|
||||||
| `postgres` | Bundled Postgres for the compose network |
|
| `postgres` | Bundled Postgres for the compose network |
|
||||||
| `redis` | Bundled Redis for ephemeral state and fan-out |
|
| `redis` | Bundled Redis for ephemeral state and fan-out |
|
||||||
@@ -240,11 +240,15 @@ Expected key files:
|
|||||||
slice or to the monolith fallback. `/internal/services/health` reports
|
slice or to the monolith fallback. `/internal/services/health` reports
|
||||||
gateway-visible health for each backend service.
|
gateway-visible health for each backend service.
|
||||||
|
|
||||||
|
For the admin page's browser routes, auth flow, same-origin API calls,
|
||||||
|
and focused troubleshooting, see [`admin.md`](admin.md).
|
||||||
|
|
||||||
### Useful Logs
|
### Useful Logs
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker logs <stack>-gateway-1 --tail=200
|
docker logs <stack>-gateway-1 --tail=200
|
||||||
docker logs <stack>-web-1 --tail=200
|
docker logs <stack>-web-1 --tail=200
|
||||||
|
docker logs <stack>-moderation-1 --tail=200
|
||||||
docker logs <stack>-platform-1 --tail=200
|
docker logs <stack>-platform-1 --tail=200
|
||||||
docker logs <stack>-cloudflared-1 --tail=200
|
docker logs <stack>-cloudflared-1 --tail=200
|
||||||
```
|
```
|
||||||
@@ -260,6 +264,10 @@ That means the `web` image did not include the built SPA assets or the
|
|||||||
old container is still running. Force a clean Dokploy rebuild on the
|
old container is still running. Force a clean Dokploy rebuild on the
|
||||||
latest branch commit.
|
latest branch commit.
|
||||||
|
|
||||||
|
Admin browser traffic on `admin.<domain>` is routed to `web`. The
|
||||||
|
`moderation` service is still useful when testing the `/api/admin/*`
|
||||||
|
path family through non-admin hosts.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Local Microservices
|
## Local Microservices
|
||||||
|
|||||||
+15
-12
@@ -3,7 +3,9 @@
|
|||||||
Cloudflare Tunnel is used in two DorkNet workflows:
|
Cloudflare Tunnel is used in two DorkNet workflows:
|
||||||
|
|
||||||
- Local standalone testing, where cloudflared forwards `*.localhost`
|
- Local standalone testing, where cloudflared forwards `*.localhost`
|
||||||
traffic to a locally running `DorkNet.Server`.
|
traffic to a locally running `DorkNet.Server`. The default origin is
|
||||||
|
`http://localhost:8080`; use local HTTPS only when you intentionally
|
||||||
|
run the standalone server with a wildcard dev certificate.
|
||||||
- Dokploy production microservices, where the compose sidecar forwards
|
- Dokploy production microservices, where the compose sidecar forwards
|
||||||
the apex and wildcard hostnames to `http://gateway:8080`.
|
the apex and wildcard hostnames to `http://gateway:8080`.
|
||||||
|
|
||||||
@@ -26,10 +28,9 @@ friends to reach it through Cloudflare.
|
|||||||
1. **`localhost` is on a Cloudflare account you control.** The DNS
|
1. **`localhost` is on a Cloudflare account you control.** The DNS
|
||||||
nameservers for the domain need to be Cloudflare's. Check at
|
nameservers for the domain need to be Cloudflare's. Check at
|
||||||
<https://dash.cloudflare.com/> — the zone should be Active.
|
<https://dash.cloudflare.com/> — the zone should be Active.
|
||||||
2. **Local standalone server running** on `0.0.0.0:443` with the wildcard cert
|
2. **Local standalone server running.** The current quickstart listens
|
||||||
that covers `*.localhost` (re-run `tools/patch-client.ps1` if your
|
on `http://localhost:8080`. Older local TLS setups can still run on
|
||||||
cert is still the `+1` rec.net-only one — it'll regenerate as
|
`0.0.0.0:443` with a wildcard certificate that covers `*.localhost`.
|
||||||
`+3` covering both domains).
|
|
||||||
3. **Patched client** with `tools/patch-domain.ps1` so the in-game
|
3. **Patched client** with `tools/patch-domain.ps1` so the in-game
|
||||||
URLs resolve to `*.localhost`. (Players using the public deployment
|
URLs resolve to `*.localhost`. (Players using the public deployment
|
||||||
need to run this on their own client too.)
|
need to run this on their own client too.)
|
||||||
@@ -97,7 +98,7 @@ visitor -> https://api.localhost/api/versioncheck/v4
|
|||||||
-> Cloudflare edge
|
-> Cloudflare edge
|
||||||
-> cloudflared tunnel
|
-> cloudflared tunnel
|
||||||
-> cloudflared running on your machine
|
-> cloudflared running on your machine
|
||||||
-> https://localhost:443 (Host preserved)
|
-> http://localhost:8080 (Host preserved)
|
||||||
-> DorkNet.Server
|
-> DorkNet.Server
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -112,12 +113,14 @@ visitor -> https://api.yourdomain.com/api/versioncheck/v4
|
|||||||
-> dedicated service slice or monolith fallback
|
-> dedicated service slice or monolith fallback
|
||||||
```
|
```
|
||||||
|
|
||||||
For the local standalone template, the leg from cloudflared to localhost
|
For default local testing, point the local cloudflared origin at
|
||||||
has `noTLSVerify: true` because the local server's cert is from mkcert
|
`http://localhost:8080`. If you intentionally test a local HTTPS origin,
|
||||||
(private CA only trusted on your machine). The Cloudflare-edge to
|
the leg from cloudflared to localhost can use `noTLSVerify: true`
|
||||||
cloudflared leg is still TLS-encrypted with Cloudflare's cert. In the
|
because that certificate is usually from a private dev CA. The
|
||||||
Dokploy microservices stack, the tunnel sidecar uses plain HTTP on the
|
Cloudflare-edge to cloudflared leg is still TLS-encrypted with
|
||||||
private Compose network and sends everything to the gateway.
|
Cloudflare's cert. In the Dokploy microservices stack, the tunnel
|
||||||
|
sidecar uses plain HTTP on the private Compose network and sends
|
||||||
|
everything to the gateway.
|
||||||
|
|
||||||
## Tearing down
|
## Tearing down
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user