pb-ext

March 3, 2026 ยท View on GitHub

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.

CollectionPurpose
_analyticsDaily aggregated page view counters (one row per path/date/device/browser). Retention: 90 days.
_analytics_sessionsRing buffer of the 50 most recent visits for the Recent Activity display. No PII stored.
_job_logsCron 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

MethodPathAuthDescription
GET/_/_Superuserpb-ext health, analytics & jobs dashboard

Cron Job API

All routes require superuser authentication.

MethodPathDescription
GET/api/cron/jobsList registered cron jobs
POST/api/cron/jobs/{id}/runTrigger a job manually
DELETE/api/cron/jobs/{id}Remove a job from the scheduler
GET/api/cron/statusCron scheduler status
POST/api/cron/config/timezoneUpdate scheduler timezone
GET/api/cron/logsPaginated job execution logs
GET/api/cron/logs/{job_id}Logs for a specific job
GET/api/cron/logs/analyticsAggregated job log statistics

API Docs

MethodPathDescription
GET/api/docs/versionsList registered API versions
GET/api/docs/debug/astAST parsing debug info
GET/api/docs/v{n}Version metadata
GET/api/docs/v{n}/openapi.jsonOpenAPI 3.0 spec
GET/api/docs/v{n}/swaggerSwagger UI

Internal System Jobs

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

Job IDScheduleDescription
__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