Home
Softono
pb-ext

pb-ext

Open source MIT Go
113
Stars
8
Forks
0
Issues
4
Watchers
1 month
Last Commit

About pb-ext

pocketbase boilerplate

Platforms

Web Self-hosted

Languages

Go

pb-ext

Enhanced PocketBase server with monitoring, logging & API docs.

pb-ext Screenshot_2026-02-10_14-42-37 Screenshot_2026-02-20_18-19-39

Ask DeepWiki

Core Features

  • API Schema: Auto-generates OpenAPI docs UI for your endpoints
  • Cron Tracking: Logs and monitors scheduled cron jobs
  • System Monitoring: Real-time CPU, memory, disk, network, and runtime metrics
  • Structured Logging: Complete logging with error tracking and request tracing
  • Visitor Analytics: Track GDPR compliant visitors, page views, device types, and browsers
  • PocketBase Integration: Uses PocketBase's auth system and styling

Access

  • Admin panel:
    127.0.0.1:8090/_
  • pb-ext dashboard:
    127.0.0.1:8090/_/_

    Quick Start

🆕 New to Golang and/or PocketBase? Read this beginner tutorial.

package main

import (
    "flag"
    "log"

    app "github.com/magooney-loon/pb-ext/core"
    "github.com/pocketbase/pocketbase/core"
)

func main() {
    devMode := flag.Bool("dev", false, "Run in developer mode")
    generateSpecsDir := flag.String("generate-specs-dir", "", "Generate OpenAPI specs into the provided directory and exit")
    generateSpecVersion := flag.String("generate-spec-version", "", "Optional API version to generate (requires --generate-specs-dir)")
    validateSpecsDir := flag.String("validate-specs-dir", "", "Validate OpenAPI specs from the provided directory and exit")
    flag.Parse()

    if *generateSpecsDir != "" {
        gen := app.NewSpecGeneratorWithInitializer(func() (*app.APIVersionManager, error) {
            return initVersionedSystem(), nil
        })
        if err := gen.Generate(*generateSpecsDir, *generateSpecVersion); err != nil {
            log.Fatal(err)
        }
        return
    }

    if *validateSpecsDir != "" {
        gen := app.NewSpecGeneratorWithInitializer(func() (*app.APIVersionManager, error) {
            return initVersionedSystem(), nil
        })
        if err := gen.Validate(*validateSpecsDir); err != nil {
            log.Fatal(err)
        }
        return
    }

    initApp(*devMode)
}

func initApp(devMode bool) {
    var opts []app.Option

    if devMode {
        opts = append(opts, app.InDeveloperMode())
    } else {
        opts = append(opts, app.InNormalMode())
    }

    // Option 1: Use a custom PocketBase config
    // pbConfig := &pocketbase.Config{
    //  DefaultDev:     true,
    //  DefaultDataDir: "./custom_pb_data",
    // }
    // opts = append(opts, app.WithConfig(pbConfig))

    // Option 2: Use an existing PocketBase instance
    // pb := pocketbase.New()
    // opts = append(opts, app.WithPocketbase(pb))

    // Set custom port programmatically
    // os.Args = []string{"app", "serve", "--http=127.0.0.1:9090"}

    // Note: WithConfig and WithPocketbase cannot be used together

    srv := app.New(opts...)

    app.SetupLogging(srv)

    registerCollections(srv.App())
    registerRoutes(srv.App())
    registerJobs(srv.App())

    srv.App().OnServe().BindFunc(func(e *core.ServeEvent) error {
        app.SetupRecovery(srv.App(), e)
        return e.Next()
    })

    if err := srv.Start(); err != nil {
        srv.App().Logger().Error("Fatal application error",
            "error", err,
            "uptime", srv.Stats().StartTime,
            "total_requests", srv.Stats().TotalRequests.Load(),
            "active_connections", srv.Stats().ActiveConnections.Load(),
            "last_request_time", srv.Stats().LastRequestTime.Load(),
        )
        log.Fatal(err)
    }
}

// Example models in cmd/server/collections.go
// Example routes in cmd/server/routes.go
// Example handlers in cmd/server/handlers.go
// Example cron jobs in cmd/server/jobs.go
//
// You can restructure Your project as You wish,
// just keep this main.go in cmd/server/main.go
//
// Build toolchain (pb-cli):
// go install github.com/magooney-loon/pb-ext/cmd/pb-cli@latest
//
// Need a pre-built Svelte5Kit starter template?
// https://github.com/magooney-loon/svelte-gui
//
// Ready for a production build deployment?
// https://github.com/magooney-loon/pb-deployer
go mod tidy
go install github.com/magooney-loon/pb-ext/cmd/pb-cli@latest
pb-cli --run-only

See **/*/README.md for detailed docs.

OpenAPI Spec Generation

Dev vs Production

  • Development: Specs are generated at runtime via AST parsing - no disk files needed
  • Production: Specs are generated at build time and read from disk (dist/specs/)

Build pipeline

The pb-cli toolchain runs OpenAPI generation automatically for production builds:

pb-cli              # Development mode (no spec generation)
pb-cli --build-only # Build frontend + generate specs
pb-cli --production # Production build with specs

For programmatic usage, see pkg/scripts/README.md.

Having issues with Your API Docs?

127.0.0.1:8090/api/docs/debug/ast

Reserved Collections

pb-ext creates the following PocketBase system collections automatically on startup. Do not create collections with these names in your own code.

Collection Purpose
_analytics Daily aggregated page view counters (one row per path/date/device/browser). Retention: 90 days.
_analytics_sessions Ring buffer of the 50 most recent visits for the Recent Activity display. No PII stored.
_job_logs Cron job execution logs (start time, end time, duration, status, output). Retention: 72 hours.

Schema notes:

  • All three collections are system collections (hidden from the PocketBase Collections UI).
  • _analytics and _analytics_sessions store no personal data — no IP, no user agent, no visitor ID. GDPR-compliant by design.
  • On upgrade from an old pb-ext version, incompatible schemas are automatically migrated at startup with no manual steps required.

Reserved Routes

pb-ext registers the following routes. Do not register your own routes at these paths.

Dashboard

Method Path Auth Description
GET /_/_ Superuser pb-ext health, analytics & jobs dashboard

Cron Job API

All routes require superuser authentication.

Method Path Description
GET /api/cron/jobs List registered cron jobs
POST /api/cron/jobs/{id}/run Trigger a job manually
DELETE /api/cron/jobs/{id} Remove a job from the scheduler
GET /api/cron/status Cron scheduler status
POST /api/cron/config/timezone Update scheduler timezone
GET /api/cron/logs Paginated job execution logs
GET /api/cron/logs/{job_id} Logs for a specific job
GET /api/cron/logs/analytics Aggregated job log statistics

API Docs

Method Path Description
GET /api/docs/versions List registered API versions
GET /api/docs/debug/ast AST parsing debug info
GET /api/docs/v{n} Version metadata
GET /api/docs/v{n}/openapi.json OpenAPI 3.0 spec
GET /api/docs/v{n}/swagger Swagger UI

Internal System Jobs

pb-ext registers these cron jobs automatically. They appear in the dashboard with the "System" badge.

Job ID Schedule Description
__pbExtLogClean__ 0 0 * * * (daily midnight) Purge _job_logs records older than 72 hours
__pbExtAnalyticsClean__ 0 3 * * * (daily 3 AM) Purge _analytics rows older than 90 days