A Magic: The Gathering boardstate tracker built with GraphQL, Vue, and Go.
โโโ โโโโโโโโโโโโโโโโโโ โโโ โโโ
โโโ โโโโโโโโโโโโโโโโโโโโโโ โโโ
โโโ โโโโโโโโโ โโโ โโโโโโโโโโโ
โโโโ โโโโโโโโโโ โโโ โโโโโโโโโโโ
โโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโ
โโโโโ โโโโโโโโโโโโโโโ โโโ โโโ
Pronounced "vee-dee-aych" The virtual expressive deck handler.
Prerequisites:
- Make
- Go v1.17
- PostgreSQL 14.15.0
- Node 16
You will need to configure .vedh.env and .pg.env environment files at your project root.
# .vedh.env example
# Database connection URI
DATABASE_URL=""
# Optional listener port
PORT="8080"
# Authentication secret
JWT_SECRET=""
# Allowed browser origins for HTTP + websocket GraphQL traffic
ALLOWED_ORIGINS="http://localhost:5173,http://127.0.0.1:5173"
# Optional Prometheus exposure. Metrics stay disabled unless both are set.
METRICS_ENABLED="false"
METRICS_TOKEN=""
# Optional: structured logging
# Levels: debug, info, warn, error
LOG_LEVEL="info"# .pg.env example
POSTGRESQL_USERNAME=""
POSTGRESQL_PASSWORD=""
POSTGRESQL_DATABASE=""The front end uses Node 16 and won't build with any other version.
I recommend NVM to manage the environment for it.
The front end also supports an environment file, .env.local.
NODE_ENV="development"
VITE_GRAPHQL_WS="ws://127.0.0.1:8080/graphql"
VITE_GRAPHQL_HTTP="http://127.0.0.1:8080/graphql"You can quickly start the persistence dependencies by running make persistence
This will boot up Postgres database.
Then run the server with our Makefile by running make run
The server will attempt to run all migrations and then start up. If it can't run migrations, it will rollback the database and noisily fail.
You can run the server as if it's in prod with this same config, so you can switch between local and prod as long as you've configured your environment variables correctly.
A copy of the server environment file for development is included in this repository.
The front end is a Vue & Apollo GraphQL application that is statically served in production.
To run the current web app:
> cd ./app
> npm install
> npm run devThe current frontend lives in app/.
npm run devto run the dev servernpm testto run the frontend unit and helper tests oncenpm run test:watchto run Vitest in watch mode
Use the smallest layer that proves the change you made:
- Frontend unit/helper tests (
cd app && npm test): proves Vue components, stores, and small browser helpers behave correctly in isolation. Runs once and exits cleanly. Requires Node and frontend deps installed; no server or database. - Backend integration tests (
make test-api): proves the Go API works against its real persistence and GraphQL paths. Requires local Postgres onlocalhost:5432and any test fixture/config expected by the current Go tests. - API smoke (Rust, preferred) (
make test-smoke-rustorcargo run --manifest-path tools/smoke/Cargo.toml --): proves a create/join flow works against a running GraphQL API with minimal end-to-end setup. Defaults tohttp://127.0.0.1:8080/graphqland respectsVEDH_GRAPHQL_URL/VEDH_SMOKE_TIMEOUT_MS. For production, point it athttps://api.vedh.xyz/graphql. - API smoke (legacy JS) (
cd app && npm run test:smoke): older create/join smoke runner against a running API. Keep only as fallback while the Rust path settles. - Browser E2E (
cd app && npm run test:e2eornpm run test:e2e:headed): proves the browser experience works through real UI flows. Requires frontend deps, a running app/API target for Playwright, and browser binaries installed.
If you only need fast feedback, start with unit/helper tests. Reach for backend integration, the Rust smoke runner, or browser E2E when you need confidence across process boundaries.
The server emits structured JSON logs to stdout.
- Control log verbosity with
LOG_LEVEL(defaults toinfo). - Each HTTP request gets an
X-Request-Id:- If you send
X-Request-Id, the server will reuse it. - Otherwise, the server generates one and returns it in the response.
- Logs include
request_id,method,path,status, andduration_ms.
- If you send
LOG_LEVEL=debug make run- Postgres stores application and card data.
- Golang for the server
- Migrate CLI for managing database migrations.
- GraphQL as a realtime API layer
The current production deploys use Dokku on dokku@192.241.142.53.
Production is split across two Dokku apps:
appserves the frontend SPA onhttps://vedh.xyz(alsowww.vedh.xyz/app.vedh.xyz)vedh-apiserves the Go GraphQL API onhttps://api.vedh.xyz/graphql
The frontend is built with these production env vars:
VITE_GRAPHQL_HTTP="https://api.vedh.xyz/graphql"
VITE_GRAPHQL_WS="wss://api.vedh.xyz/graphql"Ensure your local deploy remote targets the frontend Dokku app when shipping app/ changes:
git remote remove dokku-app 2>/dev/null || true
git remote add dokku-app dokku@192.241.142.53:appgit add $CHANGES
git commit -m "server changes"
git push dokku maingit add $CHANGES
git commit -m "app changes"
git subtree push --prefix app dokku-app main- How to connect to a postgres instance inside of docker
- How to import an SQL dump into Postgres
- Make sure when you rows.Scan() you don't point it at a nil value
app/.env.local sets local environemnt variables and is used when yarn start is run.
app/.env.production sets production environment variables and it used for yarn build.
A copy of the frontend environment file for development is included in this repository.
The current Go server reads these env vars in code:
DATABASE_URLPORTALLOWED_ORIGINSMETRICS_ENABLEDMETRICS_TOKENLOG_LEVEL
/prometheus is only exposed when both of these are set:
METRICS_ENABLED=trueMETRICS_TOKENis non-empty
Even then, prefer to keep the route behind ingress/network restriction instead of relying on bearer auth alone.
Full ./server tests currently expect:
- local Postgres on
localhost:5432 - the card import fixture path (
../All Printings.jsonby default)
For a fast listener/origin/metrics smoke check that does not go through server/main_test.go, run:
cd /root/.openclaw/workspace/vedh
/usr/local/go/bin/go test $(find server -maxdepth 1 -name '*.go' ! -name '*_test.go' | sort) server/graphql_metrics_test.go server/graphql_origin_test.go -run 'TestGraphQLServer_|TestParseAllowedOrigins' -vUse full make test-api only when the local DB + card fixture prerequisites are available.
A few route expectations that matter when debugging prod:
https://vedh.xyzis the frontend SPA, not the GraphQL APIhttps://api.vedh.xyz/graphqlis the real live GraphQL endpointGET https://api.vedh.xyz/graphqlmay return a GraphQL validation error when no operation is supplied; that is expectedPOST https://api.vedh.xyz/graphqlis the meaningful API smoke targethttps://api.vedh.xyz/playgroundshould load the GraphQL playground- there is currently no dedicated
/healthendpoint in the Go app