Popplio/.github at master · PlexiOSS/Popplio · GitHub
Skip to content

Latest commit

 

History

History

Folders and files

README.md

Popplio

Popplio is the API backend for Omniplex (Previously: Infinity Bot List). It also hosts the Arcadia staff panel API and staff Discord bot, ported into this repository as the arcadia/ package, and their combined background tasks and Discord bots run in the same process as the public API.

Licensed under the AGPL-3.0. See LICENSE.

Requirements

  • Go 1.23 or later
  • PostgreSQL (connection string set in config.yaml)
  • Redis (used for caching)
  • A Discord bot application (for the public API's own bot) and, separately, a second Discord bot application for Arcadia's staff bot, since it runs its own gateway connection under its own identity

No platform-specific build tooling is required; Popplio is a pure Go module with no CGO dependencies (CGO_ENABLED=0 is used in production builds).

Configuration

Popplio is configured via config.yaml at the repository root, which is gitignored since it holds real credentials.

config.yaml.sample documents every available key and is regenerated automatically from the config schema (config/config.go) at the start of every startup, before config.yaml itself is read — so it always reflects the current set of fields, defaults, and comments, and stays in sync on its own whenever the schema changes. On a bare checkout with no config.yaml yet, running go run . will still (re)write config.yaml.sample before it fails on the missing config.yaml, so it's always safe to run first just to get an up-to-date sample.

Copy config.yaml.sample to config.yaml and fill in the blanks. Fields without a default in the sample (tokens, client secrets, API keys) are required and Popplio will refuse to start without them (validator tags enforce this at startup).

Environment

Popplio runs as a single deployment, there is no build-time staging/beta/dev split. Every config key takes one plain value; there's nothing to keep in sync across environment variants. For local development, just point config.yaml at your own Discord bot application(s) (discord_auth.token, arcadia.token) and your own database.

The one thing that still varies per caller rather than per deployment is the Bug Hunter-only sign-in restriction: it checks the calling frontend's OAuth redirect_uri (or, for already-authenticated requests, the Origin header) against the configured production frontend (sites.frontend), so a non-production frontend pointed at the same shared backend can still be restricted to Bug Hunters without needing a separate deployment.

Building and running

go build -o popplio .
./popplio

or during development:

go run .

Popplio listens on the port configured in meta.port. On Linux and macOS it uses tableflip for zero-downtime restarts on SIGHUP; on other platforms (including Windows) it falls back to a plain http.ListenAndServe with a startup warning, since tableflip's socket handoff is not supported there. Windows is fine for local development but is not a production target.

Database migrations

Schema changes are versioned goose migrations under db/migrations/, applied via cmd/migrate:

go run ./cmd/migrate status   # see what's applied / pending
go run ./cmd/migrate up       # apply every pending migration
go run ./cmd/migrate create add_some_column

or make migrate / make migrate-status. This connects using the same config.yaml DSN as the rest of Popplio, and applying migrations is a deliberate manual step — never wired into a deploy, so nothing runs silently as a side effect of restarting the service. See db/migrations/README.md.

Everything the module reads or writes goes through sqlc-generated code (db/queries/*.sqldb/), with a small number of documented exceptions for queries sqlc's static analysis can't model (dynamic table/column dispatch, template-built search SQL, and similar) — each one has a comment at the call site explaining why.

Tests

make tests

Runs go test ./... with coverage output to coverage.out.

Repository layout

This is illustrative, not exhaustive — read the actual directory before assuming a package exists.

Path Contents
main.go Process entrypoint: mounts every router, starts Arcadia, background loops, and the HTTP server
api/ Shared response helpers (resp.*) used across route handlers
routes/ Public API route handlers, one package per resource (bots, servers, teams, packs, users, votes, webhooks, ...)
types/ Request/response types shared across routes
state/ Process-wide globals (Postgres pool, Redis client, Discord session, logger, parsed config) initialised once at startup
config/ Configuration schema (config.go)
perms/ The permission model: the declared catalogues of flat permissions (review_entities, edit_team_members), resolution, checking, and the staff permission loader
teams/ Team/entity permission resolution (built on perms/)
webhooks/ Outbound webhook delivery
notifications/ Push notifications and vote reminders
votes/, shop/, payments/ (under routes/) Voting, shop/coupons, PayPal/Stripe payment handling
arcadia/ The staff panel API and staff Discord bot, ported from the standalone Rust Arcadia service. See arcadia/CONFORMANCE.md for what was intentionally reproduced byte-for-byte versus fixed during the port
cmd/kitehelper/ Standalone operational CLI: rebuilds foreign keys, checks referential integrity, seed/test tooling — separate go.mod, built independently. Used to also carry a migrate subcommand; superseded by cmd/migrate
cmd/migrate/, db/migrations/ Schema migrations — see Database migrations above
db/queries/, db/ sqlc query definitions and the generated query package — see Database migrations above
data/docs.html Template served at /docs

API documentation

Live OpenAPI docs are served by the running instance at /docs (production: https://api.omniplex.gg/docs).

Working with Discord users

Always fetch Discord user data through dovewing.GetUser, not a raw Discord API call, it transparently handles gateway cache, Redis, and in-memory caching, and every other part of the codebase assumes user data went through it.