waitlist

package
v0.39.15 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package waitlist registers a viral, points-based waiting-list plugin on a Base app.

The mechanic is one number: an entry's POINTS. Position on the list is the entry's competition rank by points (ORDER BY points DESC, earlier joiners break ties). Points are earned from events — referrals, shares, invites, verified social follows/joins, running hanzod, admin/service boosts — and every award is an append-only row in `waitlist_events`, so a UNIQUE(entry, dedupKey) index is the anti-fraud spine (one follow = one award). There is exactly one place points change: award().

Access is a separate, orthogonal gate: an entry has access when the list is Open, when it was granted access (sticky), or when its points-derived rank falls within AccessCapacity.

Endpoints under /v1/waitlist:

POST /join          register an entry, credit a referrer atomically
GET  /status        one entry's rank, points, per-source breakdown, access
GET  /neighborhood  the rank +/- window around an entry (the scalable view)
GET  /list          leaderboard page (top-N; masked emails)
GET  /activity      recent event feed
POST /track-share   award share points (deduped per platform per day)
POST /invite        award invite points; credit conversions on join
POST /boost         service-authed points boost (superuser/AdminSecret,
                    e.g. hanzod), a caller-amount award through the seam
POST /award         server-to-server award for a VERIFIED event
                    (social/hanzod), gated by AwardSecret — the seam the
                    cloud automations connectors call after verifying
GET  /export        admin-only CSV

Backing storage is Base SQLite collections auto-created on bootstrap. No Redis, no external store: SQL transactions provide atomicity, an index on points provides O(log n) neighborhood seeks.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func MustRegister

func MustRegister(app core.App, cfg Config)

MustRegister installs the waitlist plugin on the given app and panics on error. Suitable for a Base process's startup wiring.

func Register

func Register(app core.App, cfg Config) error

Register installs the points-based waitlist plugin.

On OnBootstrap it auto-creates/upgrades three collections (`waitlists`, `waitlist_entries`, `waitlist_events`) and seeds any configured default waitlists. On OnServe it mounts the REST surface under /v1/waitlist. The plugin owns its collections (no public CRUD); the dashboard remains available to superusers.

Types

type Config

type Config struct {
	// Enabled toggles the whole plugin. A zero-value Config is disabled;
	// callers must set Enabled:true explicitly to opt in.
	Enabled bool

	// CollectionPrefix lets multiple waitlist plugins coexist on one Base.
	// Default empty -> `waitlists`, `waitlist_entries`, `waitlist_events`.
	CollectionPrefix string

	// TurnstileSecret enables Cloudflare Turnstile verification on /join.
	// Resolved at boot from TURNSTILE_SECRET_KEY if empty.
	TurnstileSecret string

	// JoinRateLimit caps /join (and other public writes) by source IP. Zero ->
	// default 5/window. Negative disables.
	JoinRateLimit int

	// JoinRateWindow is the sliding window for JoinRateLimit. Zero -> 1h.
	JoinRateWindow time.Duration

	// AdminSecret guards /export and the service-authed /boost. Resolved from
	// WAITLIST_ADMIN_SECRET if empty. If still empty, those endpoints require a
	// superuser session (else 404).
	AdminSecret string

	// AwardSecret guards the server-to-server POST /award. Resolved from
	// WAITLIST_AWARD_SECRET if empty. If still empty, /award is disabled (404)
	// — a verified-event award can never be forged by a public client.
	AwardSecret string

	// Points is the award schedule. Zero values resolve to sane defaults /
	// the POINTS_* environment.
	Points PointValues

	// InviteMaxBatch caps emails per /invite call. Zero -> 50.
	InviteMaxBatch int

	// DisposableDomains, if non-nil, replaces the built-in disposable
	// e-mail blocklist. Empty slice disables blocking.
	DisposableDomains []string

	// DefaultSlugs are waitlist slugs seeded on bootstrap. Each becomes a
	// waitlist row (name = slug with its first letter upper-cased) if absent.
	// Resolved at boot from WAITLIST_DEFAULT_SLUGS (comma-separated). Empty
	// seeds nothing.
	DefaultSlugs []string

	// AccessCapacity auto-grants product access to the top-N entries by rank.
	// Resolved at boot from WAITLIST_ACCESS_CAPACITY. Zero (default) grants
	// none automatically.
	AccessCapacity int

	// Open is the master switch that ends the waitlist: when true EVERYONE
	// has access. Resolved at boot from WAITLIST_OPEN (1/true/yes,
	// case-insensitive). Default false.
	Open bool
}

Config controls plugin registration.

type PointValues

type PointValues struct {
	Referral        int // someone joins via your refCode
	Share           int // a share click (per platform, per day)
	InviteSent      int // per valid email you invite
	InviteConverted int // an invited email actually joins
	Social          int // a verified social follow/join (X, Discord, ...)
	Hanzod          int // running a hanzod node (verified)
	Signup          int // a plain signup (default 0)
}

PointValues is the award schedule — the currency the business controls. Every value is overridable from the environment so a consumer can tune the economy without a redeploy of the plugin.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL