Documentation
¶
Overview ¶
Package vukem is a full-stack web framework for Go inspired by Laravel and AdonisJS, built on net/http and the standard library.
An App is built by New from functional options (WithRoutes, WithViews, WithMigrations, WithJobs, WithProviders...) and holds the configuration, logger, router and the lazily opened services (DB, Cache, Queue, Mailer, Sessions). Providers register services in two phases (Register, then Boot); runners such as the HTTP server run until the context is cancelled and shut down in reverse order. Main is the entry point of a generated project: it parses the command line (serve, migrate, queue:work, schedule:work...) and runs the matching command.
func main() { os.Exit(vukem.Main(bootstrap.New)) }
There are no globals, no init() registration and no reflection-based dependency injection: everything an app uses is wired in code.
See https://github.com/AlexGalhardo/vukem/blob/main/docs/architecture.md.
Example ¶
package main
import (
"fmt"
"io"
"log/slog"
"net/http"
"net/http/httptest"
"github.com/AlexGalhardo/vukem"
"github.com/AlexGalhardo/vukem/config"
"github.com/AlexGalhardo/vukem/router"
"github.com/AlexGalhardo/vukem/web"
)
func main() {
// Apps read APP_KEY and the rest from the environment and .env files;
// a map source keeps the example self-contained.
src := config.MapSource(map[string]string{"APP_KEY": config.GenerateKey(), "APP_ENV": "test"})
app, err := vukem.New(
vukem.WithSource(src),
vukem.WithLogger(slog.New(slog.DiscardHandler)),
vukem.WithRoutes(func(r *router.Router, _ *vukem.App) error {
r.Get("/hello/{name}", func(c *web.Ctx) error {
return c.String(http.StatusOK, "Hello, "+c.Param("name")+"!")
})
return nil
}),
)
if err != nil {
fmt.Println(err)
return
}
h, err := app.Handler()
if err != nil {
fmt.Println(err)
return
}
rec := httptest.NewRecorder()
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/hello/gopher", http.NoBody))
body, _ := io.ReadAll(rec.Body)
fmt.Println(rec.Code, string(body))
}
Output: 200 Hello, gopher!
Index ¶
- Constants
- Variables
- func APIMiddleware(app *App, r *router.Router) error
- func APIMiddlewareWith(app *App, r *router.Router, opts APIMiddlewareOptions) error
- func DefaultMiddleware(app *App, r *router.Router) error
- func DefaultMiddlewareWith(app *App, r *router.Router, opts MiddlewareOptions) error
- func Main(build BuildFunc) int
- func RunMain(ctx context.Context, args []string, getenv func(string) (string, bool), ...) error
- func ServeOpenAPI(app *App, r *router.Router, path string) *router.Route
- func ServeOpenAPIWith(app *App, r *router.Router, path string, opts OpenAPIOptions) *router.Route
- type APIMiddlewareOptions
- type App
- func (a *App) AddCommand(cmds ...Command)
- func (a *App) AddHealthCheck(name string, check HealthCheck)
- func (a *App) AddHealthCheckTimeout(name string, timeout time.Duration, check HealthCheck)
- func (a *App) AddRunner(name string, r Runner)
- func (a *App) Addr() string
- func (a *App) Boot(ctx context.Context) error
- func (a *App) Cache() (*cache.Cache, error)
- func (a *App) Call(ctx context.Context, name string, args []string) error
- func (a *App) CheckHealth(ctx context.Context) map[string]error
- func (a *App) Commands() []Command
- func (a *App) DB() (*database.DB, error)
- func (a *App) DeriveKey(purpose string, n int) ([]byte, error)
- func (a *App) DerivePreviousKeys(purpose string, n int) [][]byte
- func (a *App) Flags() (*flags.Flags, error)
- func (a *App) Handler() (http.Handler, error)
- func (a *App) Instrument(in Instrumentation) error
- func (a *App) MailLater(ctx context.Context, mailable mail.Mailable, opts ...queue.Option) error
- func (a *App) MailLaterTx(ctx context.Context, ex database.Executor, mailable mail.Mailable, ...) error
- func (a *App) Mailer() (*mail.Mailer, error)
- func (a *App) Migrator() (*schema.Migrator, error)
- func (a *App) Now() time.Time
- func (a *App) OnShutdown(fn func(context.Context) error)
- func (a *App) OpenAPI() (*openapi.Document, error)
- func (a *App) Queue() (*queue.Queue, error)
- func (a *App) RateLimiters() (ratelimit.Factory, error)
- func (a *App) ReadyHandler() http.Handler
- func (a *App) RequireMailer() error
- func (a *App) Run(ctx context.Context) error
- func (a *App) Schedule() (*schedule.Schedule, error)
- func (a *App) Seed(ctx context.Context) error
- func (a *App) Sessions() (*session.Manager, error)
- func (a *App) SetSessionStore(store session.Store)
- func (a *App) Shutdown(ctx context.Context) error
- func (a *App) Stderr() io.Writer
- func (a *App) Stdin() io.Reader
- func (a *App) Stdout() io.Writer
- type AppSeeder
- type AppSeederFunc
- type Booter
- type BuildFunc
- type CacheConfig
- type Command
- type CommandFunc
- type Config
- type DatabaseConfig
- type FlagSeeder
- type HTTPServer
- type HealthCheck
- type Instrumentation
- type Kind
- type KindFilter
- type MailConfig
- type MiddlewareOptions
- type OpenAPIOptions
- type Option
- func WithAppSeeders(seeders ...AppSeeder) Option
- func WithCacheStore(s cache.Store) Option
- func WithCacheStoreFunc(fn func(app *App) (cache.Store, error)) Option
- func WithClock(now func() time.Time) Option
- func WithDatabaseDrivers(drivers ...database.Driver) Option
- func WithDevtools() Option
- func WithEnv(fsys fs.FS, getenv func(string) (string, bool)) Option
- func WithEventsFake(f *events.Fake) Option
- func WithFlags(list ...flags.AnyFlag) Option
- func WithHealthCheck(name string, check HealthCheck) Option
- func WithInertia(cfg inertia.Config) Option
- func WithJobs(regs ...queue.Registration) Option
- func WithJobsFunc(fn func(app *App) []queue.Registration) Option
- func WithKind(k Kind) Option
- func WithListeners(regs ...events.Registration) Option
- func WithLogOutput(w io.Writer) Option
- func WithLogger(l *slog.Logger) Option
- func WithMailTransport(t mail.Transport) Option
- func WithMigrations(migrations []schema.Migration) Option
- func WithOpenAPI(info openapi.Info) Option
- func WithProviders(p ...Provider) Option
- func WithQueryObservers(obs ...database.QueryObserver) Option
- func WithQueueDriver(d queue.Driver) Option
- func WithQueueDriverFunc(fn func(app *App) (queue.Driver, error)) Option
- func WithRateLimiters(f ratelimit.Factory) Option
- func WithReady(ch chan<- string) Option
- func WithRoutes(fn func(r *router.Router, app *App) error) Option
- func WithRoutesAfterBoot(fn func(r *router.Router, app *App) error) Option
- func WithRunner(name string, r Runner) Option
- func WithSchedule(fn func(s *schedule.Schedule, app *App)) Option
- func WithSeeders(seeders ...Seeder) Option
- func WithSessionStore(s session.Store) Option
- func WithSessionStoreFunc(fn func(app *App) (session.Store, error)) Option
- func WithSource(src *config.Source) Option
- func WithStderr(w io.Writer) Option
- func WithStdin(r io.Reader) Option
- func WithStdout(w io.Writer) Option
- func WithTelegram(fn func(cfg *telegram.Config)) Option
- func WithTranslations(fsys fs.FS) Option
- func WithViewFuncs(funcs template.FuncMap) Option
- func WithViews(fsys fs.FS) Option
- func WithVite(fsys fs.FS) Option
- func WithoutHTTP() Option
- type Provider
- type ProviderFunc
- type QueueConfig
- type RateLimitConfig
- type Runner
- type RunnerFunc
- type ScheduleConfig
- type Seeder
- type SeederFunc
- type SessionConfig
- type Shutdowner
- type TelegramConfig
Examples ¶
Constants ¶
const ( DefaultReadHeaderTimeout = 10 * time.Second DefaultReadTimeout = 30 * time.Second DefaultWriteTimeout = 60 * time.Second DefaultIdleTimeout = 120 * time.Second // DefaultMaxHeaderBytes is 64 KiB, not Go's 1 MiB // (http.DefaultMaxHeaderBytes, which is only the ceiling // HTTP_MAX_HEADER_BYTES accepts): it bounds the // memory each connection can make the server buffer, and is still far // above what browsers send and common proxies forward (nginx 8 KiB per // line, Node.js 16 KiB in total). HTTP_MAX_HEADER_BYTES overrides it. DefaultMaxHeaderBytes = 64 << 10 )
Server timeouts applied by NewHTTPServer. Zero timeouts are never used (Slowloris protection, docs/security.md A02).
const DefaultHealthCheckTimeout = 3 * time.Second
DefaultHealthCheckTimeout bounds a health check registered with AddHealthCheck.
const MailJobKind = "vukem.mail"
MailJobKind is the queue job that sends queued mail (App.MailLater).
Variables ¶
var ErrInstrumentTooLate = errors.New(
"vukem: Instrument must run before DB(), Queue() or Mailer() is first used (call it from a provider's Register)",
)
ErrInstrumentTooLate is returned by App.Instrument when a hook targets a service that was already built: it would never be called.
var ErrLegacyMail = errors.New("vukem: clear-text mail job refused (QUEUE_ALLOW_LEGACY_MAIL is off)")
ErrLegacyMail fails a clear-text mail job when QUEUE_ALLOW_LEGACY_MAIL is off.
var ErrNoFailedStore = errors.New("vukem: the queue driver does not keep failed jobs (use QUEUE_CONNECTION=database)")
ErrNoFailedStore is returned by the failed-job commands for drivers that do not keep failed jobs.
var ErrNotBooted = errors.New("vukem: routes after boot are pending; call Boot before Handler")
ErrNotBooted is returned by Handler when routes from WithRoutesAfterBoot are pending because Boot has not run: serving then would answer 404 for them.
var ErrRoutesCompiled = errors.New("vukem: router compiled before Boot, so routes after boot would be lost; " +
"call Boot before Handler/Router.URL; avoid URL() inside WithRoutes")
ErrRoutesCompiled is returned by Boot when routes from WithRoutesAfterBoot are pending but the router was already compiled (Handler, Router.URL or a request before Boot), so they would never be served.
var ErrUseDefault = errors.New("vukem: use the configured default store")
ErrUseDefault is returned by a WithCacheStoreFunc, WithSessionStoreFunc or WithQueueDriverFunc function (directly or wrapped) to use the store CACHE_STORE, SESSION_DRIVER or QUEUE_CONNECTION configures instead, e.g. when REDIS_URL is not set. The function must return it with a nil store. Any other error, and a nil store with a nil error, fail instead: a service that is configured but broken never falls back silently.
var ErrWarmupRun = errors.New("vukem: a warmup app cannot boot or run")
ErrWarmupRun is returned by Boot (and so Run) on a KindWarmup app: it is built only to read its routes and may lack APP_KEY, so it never opens connections or serves. Never expose a warmup app's Router or Handler.
Functions ¶
func APIMiddleware ¶ added in v0.10.0
APIMiddleware installs the stack of JSON-only apps: recover, request id, security headers (the same set as DefaultMiddleware), maintenance mode and logging on the server; the body limit on routes. There are no sessions or CSRF: API clients authenticate with tokens (see auth.TokenGuard), and CORS is configured by the routes. APIMiddlewareWith customizes the stack.
func APIMiddlewareWith ¶ added in v0.25.0
func APIMiddlewareWith(app *App, r *router.Router, opts APIMiddlewareOptions) error
APIMiddlewareWith installs the APIMiddleware stack with opts, plus the i18n locale resolver when WithTranslations was given, so 422 messages follow the request locale.
func DefaultMiddleware ¶
DefaultMiddleware installs Vukem's recommended stacks on r:
- server: Recover, RequestID, SecureHeaders, Maintenance, Logger, MethodOverride;
- router: BodyLimit(10 MiB), sessions, CSRF (CrossOriginProtection, trusted origins from CSRF_TRUSTED_ORIGINS), and the i18n locale resolver when WithTranslations was given.
It returns configuration errors (insecure production cookies, bad origins) so they fail at boot. Starters call it from bootstrap. DefaultMiddlewareWith customizes the stack.
func DefaultMiddlewareWith ¶ added in v0.24.0
func DefaultMiddlewareWith(app *App, r *router.Router, opts MiddlewareOptions) error
DefaultMiddlewareWith installs the DefaultMiddleware stacks with opts.
func Main ¶
Main runs the application from os.Args and the process environment and returns the exit code. Use it as: func main() { os.Exit(vukem.Main(bootstrap.New)) }.
Example ¶
package main
import (
"os"
"github.com/AlexGalhardo/vukem"
)
func main() {
// The main function of a generated project: Main parses the command
// line (serve, migrate, queue:work...) and returns the exit code.
os.Exit(vukem.Main(func(opts ...vukem.Option) (*vukem.App, error) {
return vukem.New(append(opts, vukem.WithProviders(vukem.ProviderFunc(func(app *vukem.App) error {
app.Log.Info("registering services")
return nil
})))...)
}))
}
Output:
func RunMain ¶
func RunMain(ctx context.Context, args []string, getenv func(string) (string, bool), build BuildFunc, opts ...Option, ) error
RunMain is the testable body of main: it builds the app with the given environment and runs the command in args (default "serve").
func ServeOpenAPI ¶ added in v0.15.0
ServeOpenAPI registers GET path (e.g. "/openapi.json") serving the document. In production it registers nothing (and returns nil) unless OPENAPI_IN_PRODUCTION=true: the document maps the whole API surface.
func ServeOpenAPIWith ¶ added in v0.25.0
ServeOpenAPIWith is ServeOpenAPI with options: a document per path prefix (one per API version) and a per-document production opt-in. The route is unnamed, so several documents can coexist; name it if needed.
vukem.ServeOpenAPIWith(app, r, "/api/v1/openapi.json",
vukem.OpenAPIOptions{PathPrefix: "/api/v1", InProduction: true})
Types ¶
type APIMiddlewareOptions ¶ added in v0.25.0
type APIMiddlewareOptions struct {
// SecureHeaders adjusts the SecureHeaders config after Vukem's defaults
// are set, e.g. to add CSPSources or a ReportURI. The result is
// validated at boot.
SecureHeaders func(cfg *middleware.SecureHeadersConfig)
// I18n configures the locale resolver installed when WithTranslations
// was given. Nil resolves from the query and Accept-Language only (API
// clients send no cookies or sessions). Validated at boot; needs
// WithTranslations.
I18n *i18n.MiddlewareConfig
}
APIMiddlewareOptions customizes APIMiddlewareWith. The zero value installs the same stack as APIMiddleware.
type App ¶
type App struct {
// Config is the core configuration.
Config Config
// Source gives access to every configuration value (for module configs).
Source *config.Source
// Key is the decoded APP_KEY; derive purpose-specific keys from it
// (DeriveKey returns an error instead of panicking when it is unset).
// It is zero only outside production when APP_KEY is unset.
Key config.Key
// Crypt encrypts with a key derived from APP_KEY and still decrypts
// values sealed with APP_PREVIOUS_KEYS. DB() hands it to orm.Encrypted.
// It is nil when Key is zero; its methods then fail with crypt.ErrNoKey.
Crypt *crypt.Encrypter
// Log is the application logger.
Log *slog.Logger
// Kind is the runtime kind (web, console, test).
Kind Kind
// Router holds the HTTP routes; /up is registered by default.
Router *router.Router
// Views renders templates when WithViews was given (nil otherwise).
Views *view.Engine
// Vite renders asset tags when WithVite was given (nil otherwise).
Vite *vite.Vite
// Inertia is the Inertia adapter when WithInertia was given (nil
// otherwise).
Inertia *inertia.Inertia
// I18n holds the translations when WithTranslations was given (nil
// otherwise); DefaultMiddleware then resolves the request locale.
I18n *i18n.Bundle
// Telegram is the Telegram log handler; use it for Contact and Send.
// It is never nil: without TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID it
// is disabled (Configured reports false, Send drops, SendSync returns
// telegram.ErrDisabled).
Telegram *telegram.Handler
// Events is the application event bus (WithListeners registers listeners).
Events *events.Bus
// Devtools is the development dashboard when WithDevtools or
// DEVTOOLS=true enabled it (nil otherwise).
Devtools *devtools.Devtools
// contains filtered or unexported fields
}
App is a Vukem application: configuration, logger, providers and the long-running runners (HTTP server, workers). Services added by providers live in typed fields of the structs that embed or reference the App.
func New ¶
New builds an App: it loads and validates configuration and registers providers. It refuses insecure configuration (see docs/security.md, A02).
func (*App) AddCommand ¶
AddCommand registers commands. Call it from a provider's Register.
func (*App) AddHealthCheck ¶
func (a *App) AddHealthCheck(name string, check HealthCheck)
AddHealthCheck registers a readiness check, reported by ReadyHandler and GET /up?checks=1 and bounded by DefaultHealthCheckTimeout. A check with the same name replaces the previous one. WithHealthCheck is the same thing at build time.
Make the check prove the dependency does what this app needs, not that something answers. The database check the database provider registers is a Ping, and a pool hands out a connection to a database whose migrations never ran, so a Ping passes while no query the app makes can succeed: add a check that reads a table the app owns. For a disk, use [storage.Check], because an existence check passes with credentials that cannot write.
func (*App) AddHealthCheckTimeout ¶ added in v0.24.0
func (a *App) AddHealthCheckTimeout(name string, timeout time.Duration, check HealthCheck)
AddHealthCheckTimeout is AddHealthCheck with its own timeout (<= 0 means DefaultHealthCheckTimeout).
func (*App) Addr ¶
Addr returns the HTTP listen address from HOST and PORT. IPv6 hosts ("::", "fd12::1") are bracketed; a host already in brackets is accepted.
func (*App) Boot ¶
Boot runs every provider's Boot hook once, in order, then the WithRoutesAfterBoot functions. Boot is not retryable: later calls return the first call's error (nil after a successful boot). A KindWarmup app never boots: Boot returns ErrWarmupRun.
func (*App) Cache ¶ added in v0.11.0
Cache returns the application cache, built from CACHE_* on first use. The database store creates its table if needed.
func (*App) Call ¶ added in v0.16.0
Call runs the app command name with args inside the current process (the app is already booted), e.g. from a scheduled task.
func (*App) CheckHealth ¶ added in v0.24.0
CheckHealth runs every registered check in parallel, each bounded by its timeout, and returns each check's error by name (nil when healthy). The errors may hold hosts or credentials: log them, never send them to a client (ReadyHandler only reports "ok" or "fail").
func (*App) DB ¶
DB opens the default database from DATABASE_URL on first use, with app.Crypt as its encrypter (orm.Encrypted columns), adds a "db" health check and closes it on shutdown.
func (*App) DeriveKey ¶ added in v0.24.0
DeriveKey returns an n-byte key for purpose derived from APP_KEY (see config.Key.Derive). It fails with config.ErrKeyMissing when the app booted without APP_KEY, so a key-derived feature fails closed.
func (*App) DerivePreviousKeys ¶ added in v0.26.0
DerivePreviousKeys returns the n-byte keys for purpose derived from each APP_PREVIOUS_KEYS entry, in order, or nil when none is set. Pass them to the verify-only side of a key-derived feature (signed.Signer and social.Social WithPreviousKeys) so values written before an APP_KEY rotation keep working until the old key is removed. They must never sign or encrypt.
func (*App) Flags ¶ added in v0.27.0
Flags returns the feature flags registered with WithFlags, built on first use: stored values live in the feature_flags table of DB() (vukem install:flags), cached in Cache() for flags.DefaultTTL. Without registered flags it touches neither the database nor the cache.
func (*App) Handler ¶
Handler returns the compiled HTTP handler. It fails with ErrNotBooted while routes from WithRoutesAfterBoot wait for Boot.
func (*App) Instrument ¶ added in v0.25.0
func (a *App) Instrument(in Instrumentation) error
Instrument attaches in's hooks to the database, queue and mailer. Call it from a provider's Register, before those services are first used; a hook for a service already built fails with ErrInstrumentTooLate and no hook of in is attached. Several calls add up.
func (*App) MailLater ¶ added in v0.16.0
MailLater renders mailable now and sends it from a queue job: see mail.Mailer.SendLater (the app's mailer encrypts the job payload).
func (*App) MailLaterTx ¶ added in v0.25.0
func (a *App) MailLaterTx(ctx context.Context, ex database.Executor, mailable mail.Mailable, opts ...queue.Option, ) error
MailLaterTx is MailLater inside the transaction of ex: the mail is queued only if the transaction commits (see mail.Mailer.SendLaterTx and queue.Queue.DispatchTx).
func (*App) Mailer ¶ added in v0.7.0
Mailer returns the app mailer, built from MAIL_* on first use. Views come from the app's view engine (WithViews).
func (*App) OnShutdown ¶
OnShutdown registers fn to run during Shutdown, in reverse registration order, after runners stop and before providers shut down.
func (*App) OpenAPI ¶ added in v0.15.0
OpenAPI returns the OpenAPI 3.1 document of the app's routes (title APP_NAME, version API_VERSION, filtered by OPENAPI_PREFIX, with the description and security schemes of WithOpenAPI).
func (*App) Queue ¶ added in v0.16.0
Queue returns the application queue, built from QUEUE_* on first use.
func (*App) RateLimiters ¶ added in v0.23.0
RateLimiters returns a factory for the limiters routes use, chosen by RATE_LIMIT_STORE, so switching to a shared store needs no code change:
limiters, err := app.RateLimiters()
login := ratelimit.Middleware(limiters("login", 5, time.Minute), account.LoginThrottleKey)
An unknown store is an error, not a silent fallback to memory, and so is RATE_LIMIT_STORE=cache over a store cache.Shared does not report as shared (the per-process memory cache, or a custom store or wrapper that does not declare cache.Sharer). WithRateLimiters replaces the factory.
func (*App) ReadyHandler ¶ added in v0.24.0
ReadyHandler is a readiness probe: it runs every health check (see CheckHealth) and answers JSON {"status":"ok"|"fail","checks":{name: "ok"|"fail"}} with 200, or 503 when a check fails. Failures are logged; their details never reach the client. Mount it where the platform expects it, e.g. r.Handle("GET /ready", app.ReadyHandler()); GET /up stays the liveness probe.
func (*App) RequireMailer ¶ added in v0.30.0
RequireMailer reports the production MAIL_MAILER requirement as an error when the app has already built a mailer that cannot send. Call it at the start of a long-running process that sends mail as part of its normal work (queue:work, schedule:work): Mailer() defers the refusal to the send for a console process, so that `about` and route:list run (A81), and a worker should still fail at deploy time rather than job by job. An app that never asks for a mailer is never refused.
func (*App) Run ¶
Run boots the app, starts every runner and blocks until ctx is cancelled or a runner fails; then it shuts everything down gracefully within SHUTDOWN_TIMEOUT. A cancelled ctx is a clean exit (nil error). Runners that have not returned SHUTDOWN_TIMEOUT (plus three seconds) after the cancel are logged and left behind: shutdown hooks and providers run anyway, so one runner ignoring ctx cannot hold the process forever.
func (*App) Schedule ¶ added in v0.16.0
Schedule returns the application schedule, built on first use. Locks for WithoutOverlapping and OnOneServer use App.Cache (set CACHE_STORE=database or Redis when several servers run the schedule; OnOneServer tasks make the in-process cache an error).
func (*App) Seed ¶ added in v0.4.0
Seed runs every registered seeder in order, with their flag defaults; the first error stops it. db:seed boots the app first, so seeders see services providers open in Boot; call Boot before Seed when calling it directly.
func (*App) Sessions ¶
Sessions returns the session manager, building it from SESSION_* on first use. Use SetSessionStore before the first call to change the store.
func (*App) SetSessionStore ¶
SetSessionStore makes sessions server-side with store (call it from a provider's Register, before Sessions is first used).
func (*App) Shutdown ¶
Shutdown runs shutdown hooks and providers' Shutdown in reverse order. It is idempotent.
func (*App) Stderr ¶ added in v0.24.0
Stderr is where commands print diagnostics (WithStderr, os.Stderr by default).
type AppSeeder ¶ added in v0.25.0
AppSeeder is a seeder that needs the booted App: configuration (app.Source), the logger, storage disks or services added by providers. It opens the database itself with app.DB() when it needs it.
func NamedAppSeeder ¶ added in v0.25.0
func NamedAppSeeder(name string, fn AppSeederFunc) AppSeeder
NamedAppSeeder returns an AppSeeder printed and reported as name (by db:seed and in errors) instead of its Go type, which is the same for every AppSeederFunc.
type AppSeederFunc ¶ added in v0.25.0
AppSeederFunc adapts a function to AppSeeder.
type Booter ¶
Booter is implemented by providers that need a boot phase. Boot runs after every provider registered.
type CacheConfig ¶ added in v0.11.0
type CacheConfig struct {
// Store is "memory" (default) or "database" (table CACHE_TABLE).
Store string `env:"CACHE_STORE" default:"memory"`
Prefix string `env:"CACHE_PREFIX" default:"vukem:"`
Table string `env:"CACHE_TABLE" default:"cache"`
// MaxEntries bounds the memory store.
MaxEntries int `env:"CACHE_MAX_ENTRIES" default:"10000"`
}
CacheConfig is read from CACHE_* variables.
type Command ¶
type Command interface {
// Name is "namespace:action", e.g. "reports:send".
Name() string
// Description is shown by "list".
Description() string
// Flags defines flags on fs; it may be a no-op.
Flags(fs *flag.FlagSet)
// Run executes the command with the remaining positional args.
Run(ctx context.Context, app *App, args []string) error
}
Command is an application command compiled into the app binary (migrate, queue:work, user commands). The global `vukem` CLI delegates unknown commands to the app binary.
type CommandFunc ¶
type CommandFunc struct {
CommandName string
Desc string
DefineFlags func(fs *flag.FlagSet)
Fn func(ctx context.Context, app *App, args []string) error
}
CommandFunc builds a Command from a function.
func (CommandFunc) Description ¶
func (c CommandFunc) Description() string
Description implements Command.
type Config ¶
type Config struct {
Name string `env:"APP_NAME" default:"Vukem"`
Env config.Env `env:"APP_ENV" default:"development"`
Debug bool `env:"APP_DEBUG"`
URL string `env:"APP_URL" default:"http://localhost:3000"`
Key config.Secret `env:"APP_KEY"`
// PreviousKeys are former APP_KEY values (comma-separated, same format)
// that still open what they sealed or signed after a key rotation:
// app.Crypt tokens (encrypted cookies and columns, queued mail), cookie
// sessions, the maintenance bypass and, through DerivePreviousKeys,
// signed URLs and social login state. New values always use APP_KEY.
PreviousKeys config.Secret `env:"APP_PREVIOUS_KEYS"`
Host string `env:"HOST" default:"localhost"`
Port int `env:"PORT" default:"3000"`
ShutdownTimeout time.Duration `env:"SHUTDOWN_TIMEOUT" default:"15s"`
// H2C serves HTTP/2 without TLS (prior knowledge, for gRPC clients) in
// production too, behind a proxy that speaks h2c. It is always on
// outside production.
H2C bool `env:"HTTP_H2C"`
// MaxHeaderBytes caps the request line plus headers the HTTP server
// reads (4096 to 1048576 bytes, Go's own default; outside is a boot
// error). Larger requests get 431.
MaxHeaderBytes int `env:"HTTP_MAX_HEADER_BYTES" default:"65536"`
LogLevel string `env:"LOG_LEVEL" default:"info"`
LogFormat string `env:"LOG_FORMAT"`
// LogDedupWindow collapses repeated identical ERROR records of the
// default logger into one summary per window (a Go duration; "0"
// disables it). Empty means 1m in production and off elsewhere.
LogDedupWindow string `env:"LOG_DEDUP_WINDOW"`
// APIVersion is info.version of the OpenAPI document.
APIVersion string `env:"API_VERSION" default:"1.0.0"`
// OpenAPIPrefix keeps only routes under it in the document (e.g. /api).
OpenAPIPrefix string `env:"OPENAPI_PREFIX"`
// OpenAPIInProduction serves /openapi.json in production too.
OpenAPIInProduction bool `env:"OPENAPI_IN_PRODUCTION"`
// Devtools enables the /_vukem dashboard (refused in production).
Devtools bool `env:"DEVTOOLS"`
// DevtoolsInsecureAllowRemote serves the dashboard to non-loopback
// clients (containers, remote VMs). Anyone who reaches the app then
// sees its requests, queries, logs and mail.
DevtoolsInsecureAllowRemote bool `env:"DEVTOOLS_INSECURE_ALLOW_REMOTE"`
// TrustedProxies lists the proxies (comma-separated CIDRs or IPs) whose
// X-Forwarded-For is honoured by c.IP(). Empty trusts nobody; an
// invalid entry is a boot error.
TrustedProxies string `env:"TRUSTED_PROXIES"`
// TrustForwardedProto treats X-Forwarded-Proto: https from a trusted
// proxy as HTTPS (HSTS). It requires TRUSTED_PROXIES.
TrustForwardedProto bool `env:"TRUST_FORWARDED_PROTO"`
// HSTSForce sends HSTS on every response in production, for TLS
// proxies whose range cannot be put in TRUSTED_PROXIES. It requires an
// https APP_URL in every environment but is applied only in production
// (docs/security.md, A02).
HSTSForce bool `env:"HSTS_FORCE"`
// ClientIPHeader names a single-IP header set by the trusted proxy
// (such as X-Real-IP) that c.IP() reads instead of X-Forwarded-For.
// It requires TRUSTED_PROXIES.
ClientIPHeader string `env:"CLIENT_IP_HEADER"`
// RateLimitIPv6Prefix is the prefix length ratelimit.ByIP groups IPv6
// clients by (1-128): one client usually owns a whole /64 and can
// rotate addresses inside it. IPv4 keys stay per address.
RateLimitIPv6Prefix int `env:"RATE_LIMIT_IPV6_PREFIX" default:"64"`
}
Config is the core application configuration, loaded from the environment.
func (*Config) IsProduction ¶
IsProduction reports whether APP_ENV is production.
type DatabaseConfig ¶
type DatabaseConfig struct {
URL string `env:"DATABASE_URL" default:"file:storage/database.sqlite"`
MaxOpen int `env:"DB_MAX_OPEN" default:"10"`
MaxIdle int `env:"DB_MAX_IDLE" default:"5"`
}
DatabaseConfig is loaded from DB_* variables.
type FlagSeeder ¶ added in v0.27.0
FlagSeeder is a Seeder or AppSeeder that takes command-line flags from db:seed. SeedFlags defines them on fs before each run. Implement it on a pointer receiver, bind the flags to its fields and register the pointer (&DemoCatalog{}); a seeder registered as a value that defines flags is an error, since the flags would bind to a copy:
func (s *DemoCatalog) SeedFlags(fs *flag.FlagSet) {
fs.BoolVar(&s.fresh, "fresh", false, "delete the demo catalog first")
}
vukem db:seed DemoCatalog --fresh
The flags of every seeder that runs are accepted; seeders defining the same flag all receive its value. "force", "class", "help" and "h" belong to db:seed and cannot be redefined, and the words --force and --class are always read as db:seed's flags, so they cannot be values of a seeder flag. App.Seed applies the defaults.
type HTTPServer ¶
type HTTPServer struct {
Server *http.Server
// ShutdownTimeout bounds the graceful drain of in-flight requests.
ShutdownTimeout time.Duration
// Listener, when set, is used instead of listening on Server.Addr.
Listener net.Listener
// Ready, when set, receives the bound address once listening.
Ready chan<- string
}
HTTPServer runs an *http.Server as a Runner with graceful shutdown.
func NewHTTPServer ¶
func NewHTTPServer(addr string, handler http.Handler) *HTTPServer
NewHTTPServer returns an HTTPServer for handler on addr with secure timeouts.
type HealthCheck ¶
HealthCheck reports whether a dependency (database, cache...) is healthy.
type Instrumentation ¶ added in v0.25.0
type Instrumentation struct {
// Queries is notified of every statement run through DB() and its
// transactions, after devtools and WithQueryObservers.
Queries database.QueryObserver
// Jobs is notified of every job step of Queue(), next to devtools.
Jobs queue.Observer
// Mail wraps the transport of Mailer(). Wrappers apply in registration
// order around the devtools-wrapped transport, so the last one
// registered is the outermost.
Mail func(mail.Transport) mail.Transport
}
Instrumentation is a set of neutral hook points that a provider (tracing, metrics) attaches to with App.Instrument. Core only reports to them and never imports an instrumentation library; drivers/otel turns the reports into spans and metrics. Nil fields are skipped.
type Kind ¶
type Kind string
Kind is the runtime in which the application is running. Providers can be limited to some kinds (see KindFilter).
const ( KindWeb Kind = "web" KindConsole Kind = "console" KindTest Kind = "test" // KindWarmup builds the app (config, Register, routes) but never runs // providers' Boot or Shutdown, so no connection is opened. Commands that // only read the route table (codegen:routes, codegen:client, // codegen:types, openapi:export) run in it, and it tolerates a missing // APP_KEY. Boot and Run return ErrWarmupRun; never expose its Router. KindWarmup Kind = "warmup" )
Runtime kinds.
type KindFilter ¶
type KindFilter interface {
Kinds() []Kind
}
KindFilter limits a provider to some runtime kinds. Providers that do not implement it run everywhere.
type MailConfig ¶ added in v0.7.0
type MailConfig struct {
// Mailer is "smtp", "resend", "mailtrap", "log" or "array" (tests). Production
// must choose explicitly; elsewhere it defaults to "log".
Mailer string `env:"MAIL_MAILER"`
From string `env:"MAIL_FROM" default:"Vukem <hello@example.com>"`
SMTPHost string `env:"SMTP_HOST" default:"localhost"`
SMTPPort int `env:"SMTP_PORT" default:"587"`
SMTPUsername string `env:"SMTP_USERNAME"`
SMTPPassword config.Secret `env:"SMTP_PASSWORD"`
// SMTPInsecure allows servers without STARTTLS (local catchers only).
SMTPInsecure bool `env:"SMTP_INSECURE"`
ResendAPIKey config.Secret `env:"RESEND_API_KEY"`
MailtrapAPIToken config.Secret `env:"MAILTRAP_API_TOKEN"`
// MailtrapInboxID > 0 sends to that Email Sandbox inbox instead of delivering.
MailtrapInboxID int64 `env:"MAILTRAP_INBOX_ID"`
}
MailConfig is read from MAIL_* (and SMTP_* / RESEND_* / MAILTRAP_*) variables.
type MiddlewareOptions ¶ added in v0.24.0
type MiddlewareOptions struct {
// SecureHeaders adjusts the SecureHeaders config after Vukem's defaults
// (the Vite dev server) are set, e.g. to add CSPSources or a ReportURI.
// The result is validated at boot.
SecureHeaders func(cfg *middleware.SecureHeadersConfig)
// Stateless installs no session middleware (cookieless apps). CSRF
// protection stays on as Origin/Sec-Fetch-Site checks only: there is no
// session token, so csrfToken and csrfField render empty, and session
// features (Flash, session auth) fail with an error.
Stateless bool
// SessionBypass lists ServeMux patterns served without sessions. As in
// ServeMux, a pattern ending in "/" ("/webhooks/") covers its whole
// subtree; "POST /hooks/{id}" matches one method and path. It cannot be
// combined with Stateless.
SessionBypass []string
// CSRFBypass lists ServeMux patterns (same matching as SessionBypass)
// exempt from CSRF checks, e.g. signed webhooks. Each bypass is logged
// at boot.
CSRFBypass []string
// CSRFTokenFallback also requires the session's CSRF token (the
// csrfField template function, or the X-CSRF-Token header) on unsafe
// requests that carry the session cookie but neither Sec-Fetch-Site nor
// Origin, i.e. browsers too old for CrossOriginProtection (see
// csrf.Config.TokenFallback). It needs sessions: combined with
// Stateless it is a boot error.
CSRFTokenFallback bool
// I18n configures the locale resolver installed when WithTranslations
// was given (nil: i18n.MiddlewareConfig defaults), e.g. its sources and
// their order, or the selectable locales. It is validated at boot and
// needs WithTranslations.
I18n *i18n.MiddlewareConfig
}
MiddlewareOptions customizes DefaultMiddlewareWith. The zero value installs the same stack as DefaultMiddleware.
type OpenAPIOptions ¶ added in v0.25.0
type OpenAPIOptions struct {
// PathPrefix keeps only routes under it, e.g. "/api/v1" (default
// Config.OpenAPIPrefix, OPENAPI_PREFIX).
PathPrefix string
// InProduction serves this document in production too. Set it for a
// public API; OPENAPI_IN_PRODUCTION=true does the same for every
// document and API reference page.
InProduction bool
}
OpenAPIOptions tunes ServeOpenAPIWith.
type Option ¶
type Option func(*options) error
Option configures New.
func WithAppSeeders ¶ added in v0.25.0
WithAppSeeders registers seeders that receive the App instead of only the database. They run with the WithSeeders ones, in option order.
func WithCacheStore ¶ added in v0.17.0
WithCacheStore replaces the store chosen by CACHE_STORE (e.g. Redis: vukem.WithCacheStore(redisClient.CacheStore("cache:"))). The last of WithCacheStore and WithCacheStoreFunc wins.
func WithCacheStoreFunc ¶ added in v0.24.0
WithCacheStoreFunc replaces the store chosen by CACHE_STORE with one built by fn on the first app.Cache() call, after configuration loaded, so fn can read app.Source (e.g. REDIS_URL from .env or vukemtest). fn returns ErrUseDefault to keep the CACHE_STORE store. Any other error from fn, or a nil store, is returned by app.Cache(). fn may register cleanup with app.OnShutdown, but must not call app.Cache() (or anything that does, such as app.RateLimiters()): fn runs inside Cache's sync.Once and that call would deadlock.
func WithClock ¶
WithClock sets the clock used by sessions and other time-based features (tests use vukemtest.FakeClock).
func WithDatabaseDrivers ¶
WithDatabaseDrivers makes extra drivers (e.g. postgres.Driver()) available to DB().
func WithDevtools ¶ added in v0.19.0
func WithDevtools() Option
WithDevtools enables the development dashboard at /_vukem (same as DEVTOOLS=true). New fails when APP_ENV=production.
func WithEventsFake ¶ added in v0.28.0
WithEventsFake makes App.Events record every dispatched event in f instead of running listeners (tests): assert with events.Dispatched. See events.Bus.RecordTo.
func WithFlags ¶ added in v0.27.0
WithFlags registers the application's feature flags (app.Flags). Invalid or duplicate names, bad FLAG_<NAME> values, FLAG_<NAME> for a locked flag and FLAG_ keys in .env files that name no registered flag stop New, with or without WithFlags. Process environment variables cannot be listed, so a FLAG_ typo there goes unnoticed.
func WithHealthCheck ¶ added in v0.30.0
func WithHealthCheck(name string, check HealthCheck) Option
WithHealthCheck registers a readiness check at build time, the declarative form of App.AddHealthCheck: a dependency the instance needs in order to serve, reported by App.ReadyHandler and GET /up?checks=1 and bounded by DefaultHealthCheckTimeout. Use App.AddHealthCheckTimeout from a provider for a check that needs its own timeout.
Point the platform's liveness probe at GET /up, which touches nothing, and readiness at a path serving ReadyHandler. Pointing liveness at the checks turns one slow dependency into a restart loop, and pointing a load balancer at them turns it into zero healthy instances.
func WithInertia ¶ added in v0.18.0
WithInertia enables the Inertia adapter (app.Inertia); add app.Inertia.Middleware to the routes that render pages. Errors on Inertia requests render inertia.Config.ErrorComponent (default "Error", see inertia.Inertia.ErrorPage): New installs it as app.Router.Services().ErrorPage when no hook is set yet, before the routes run, so routes may replace it or set it to nil to keep the HTML error page. Outside production a web app warns once at boot when the project (the WithEnv file system, else the working directory) has resources/js/pages but no page for that component. Version and the SSR dev endpoint default to app.Vite when WithVite is given.
func WithJobs ¶ added in v0.16.0
func WithJobs(regs ...queue.Registration) Option
WithJobs registers job handlers on the application queue. Handlers that need the app (database, clock, mailer) use WithJobsFunc.
func WithJobsFunc ¶ added in v0.24.0
func WithJobsFunc(fn func(app *App) []queue.Registration) Option
WithJobsFunc registers job handlers built from the app, so they reach its database, clock, mailer and other services without globals (generated apps pass jobs.All). fn runs once, inside the first app.Queue call; a panic in fn becomes that call's error. fn may get app.DB, app.Mailer, app.Crypt and app.Now; it must not dispatch: no app.Queue, app.Events.Dispatch (queued listeners), Mailer.SendLater / app.MailLater or running scheduled jobs, which wait for the queue being built and deadlock. Handlers may do all of that when they run.
func WithListeners ¶ added in v0.11.0
func WithListeners(regs ...events.Registration) Option
WithListeners registers event listeners on App.Events (generated apps pass listeners.All(); vukem make:listener appends there).
func WithLogOutput ¶
WithLogOutput sets where the default logger writes (stderr by default).
func WithLogger ¶
WithLogger sets the logger instead of building one from configuration.
func WithMailTransport ¶ added in v0.7.0
WithMailTransport replaces the transport chosen by MAIL_MAILER.
func WithMigrations ¶
WithMigrations registers the application's migrations used by the migrate commands (migrate, migrate:rollback, migrate:reset, migrate:fresh, migrate:status).
func WithOpenAPI ¶ added in v0.29.0
WithOpenAPI makes App.OpenAPI (and so /openapi.json, openapi:export, codegen:types, codegen:client and vukemtest.AssertMatchesOpenAPI) build the document from info, which is where an app declares its description and its security schemes: Build rejects any scheme an operation names (api.Op.Security) that the document does not declare, and only "bearer" is predefined.
vukem.WithOpenAPI(openapi.Info{
Description: "Shop API",
SecuritySchemes: map[string]openapi.Schema{
"apiKey": {"type": "apiKey", "in": "header", "name": "X-API-Key"},
},
})
An empty Title, Version or Servers keeps the configured value (APP_NAME, API_VERSION, APP_URL); an empty PathPrefix keeps OPENAPI_PREFIX, and the prefix of ServeOpenAPIWith wins over both.
func WithProviders ¶
WithProviders appends providers, registered and booted in order.
func WithQueryObservers ¶ added in v0.25.0
func WithQueryObservers(obs ...database.QueryObserver) Option
WithQueryObservers reports every statement run through DB() (and its transactions) to each observer, e.g. a tracing exporter. They run after devtools' observer when devtools is on, so neither replaces the other.
func WithQueueDriver ¶ added in v0.16.0
WithQueueDriver replaces the driver chosen by QUEUE_CONNECTION (tests pass a queue.Fake). The last of WithQueueDriver and WithQueueDriverFunc wins.
func WithQueueDriverFunc ¶ added in v0.24.0
WithQueueDriverFunc replaces the driver chosen by QUEUE_CONNECTION with one built by fn on the first app.Queue() call, after configuration loaded, so fn can read app.Source. fn returns ErrUseDefault to keep the QUEUE_CONNECTION driver. Any other error from fn, or a nil driver, is returned by app.Queue(). With QUEUE_CONNECTION=database, Boot runs fn instead (once either way) and creates the queue tables only when fn falls back to them; its error then fails the boot. fn must not call app.Queue() (or dispatch a job): it runs inside Queue's sync.Once and that call would deadlock.
func WithRateLimiters ¶ added in v0.28.0
WithRateLimiters makes App.RateLimiters return f instead of the factory RATE_LIMIT_STORE chooses: a shared limiter of your own in production, or a fake in tests (an always-allow limiter, or ratelimit.NewMemory with tight limits). f must not be nil.
func WithRoutes ¶
WithRoutes registers routes (and router middleware) after providers registered, so handlers can use the services they added. An error stops New.
func WithRoutesAfterBoot ¶ added in v0.25.0
WithRoutesAfterBoot registers routes at the end of Boot, after every provider's Boot, so handlers can use clients a provider opens in Boot (a database or object-store client). The routes exist only once the app booted: Run, commands and vukemtest boot first; Handler fails with ErrNotBooted before that, and Boot fails with ErrRoutesCompiled when the router was compiled earlier. Global router middleware applies to them; groups made in WithRoutes do not. In KindWarmup, which never boots, they run at the end of New so the route table is complete; booted services are then still unset (check app.Kind) and a panic is returned as an error.
func WithRunner ¶
WithRunner adds a long-running component started by Run.
func WithSchedule ¶ added in v0.16.0
WithSchedule declares scheduled tasks (generated apps pass routes.Schedule).
func WithSeeders ¶ added in v0.4.0
WithSeeders registers the seeders run by `db:seed` (generated apps pass seeders.All()).
func WithSessionStore ¶ added in v0.17.0
WithSessionStore replaces the store chosen by SESSION_DRIVER (e.g. Redis: vukem.WithSessionStore(redisClient.SessionStore(""))). The last of WithSessionStore and WithSessionStoreFunc wins.
func WithSessionStoreFunc ¶ added in v0.24.0
WithSessionStoreFunc replaces the store chosen by SESSION_DRIVER with one built by fn on the first app.Sessions() call (DefaultMiddleware makes it at boot), after configuration loaded. fn returns ErrUseDefault to keep the SESSION_DRIVER store (cookie sessions by default). Any other error from fn, or a nil store, is returned by app.Sessions(). SetSessionStore still takes precedence. fn must not call app.Sessions() (or DefaultMiddleware): it runs inside Sessions' sync.Once and that call would deadlock.
func WithSource ¶
WithSource uses an already resolved configuration source.
func WithStderr ¶ added in v0.24.0
WithStderr sets where commands print diagnostics (os.Stderr by default).
func WithStdout ¶
WithStdout sets where commands print (os.Stdout by default).
func WithTelegram ¶ added in v0.25.0
WithTelegram adjusts the Telegram handler config after it is read from TELEGRAM_* (for example Filter, OnError, ChatIDs or Silent). It has no effect unless TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID are set.
func WithTranslations ¶ added in v0.18.0
WithTranslations loads the JSON catalogs at the root of fsys (os.DirFS("resources/lang") or an embed.FS) into App.I18n, with APP_LOCALE (default "en") as the fallback locale, plus the framework's built-in messages for keys the catalogs do not define (i18n.Bundle.AddBuiltin). Views get the "t" and "locale" functions, and DefaultMiddleware installs i18n.Middleware.
func WithViewFuncs ¶ added in v0.28.0
WithViewFuncs adds template functions to the WithViews engine (view.Config.Funcs), for helpers such as money or date formatting. Calls accumulate. A name given twice, a nil function, a built-in's name ("route", "csrfField", "cspNonce"...) or a missing WithViews is a boot error.
func WithViews ¶
WithViews renders html/template views from fsys (os.DirFS("resources/views") or an embed.FS). Views reload on every request outside production.
func WithVite ¶ added in v0.18.0
WithVite enables the "vite" and "viteReactRefresh" view functions over the public directory fsys (os.DirFS("public") or an embed.FS). The dev server "hot" file is honoured only outside production.
func WithoutHTTP ¶
func WithoutHTTP() Option
WithoutHTTP disables the HTTP server runner (workers-only processes).
type Provider ¶
Provider registers services into the App. Register must not perform I/O; use Boot for work that needs other services.
type ProviderFunc ¶
ProviderFunc adapts a function to a Provider with only a Register phase.
type QueueConfig ¶ added in v0.16.0
type QueueConfig struct {
// Connection is "sync" (default: jobs run during Dispatch), "memory"
// (in process, run by a worker inside serve) or "database".
Connection string `env:"QUEUE_CONNECTION" default:"sync"`
// Queues lists the queues workers poll, in priority order.
Queues string `env:"QUEUE_NAMES" default:"default"`
// Concurrency is the number of parallel workers.
Concurrency int `env:"QUEUE_CONCURRENCY" default:"4"`
// Table and FailedTable are the database driver's tables.
Table string `env:"QUEUE_TABLE" default:"jobs"`
FailedTable string `env:"QUEUE_FAILED_TABLE" default:"failed_jobs"`
// InServe runs a queue worker inside serve for the database driver
// and custom drivers (Redis). Set it to false when dedicated
// queue:work processes run the jobs instead; leaving it on next to
// them is safe (reservations are atomic), it only adds workers. The
// memory queue is always worked inside serve: no other process sees
// its jobs.
InServe bool `env:"QUEUE_IN_SERVE" default:"true"`
// AllowLegacyMail sends clear-text mail jobs queued before v0.24.0
// (an upgrade window: drain the queue before upgrading, or set it
// until the old jobs are gone). Off, they fail.
AllowLegacyMail bool `env:"QUEUE_ALLOW_LEGACY_MAIL"`
}
QueueConfig is read from QUEUE_* variables.
func (QueueConfig) QueueNames ¶ added in v0.16.0
func (c QueueConfig) QueueNames() []string
QueueNames returns Queues as a list.
type RateLimitConfig ¶ added in v0.23.0
type RateLimitConfig struct {
// Store is "memory" (default: GCRA per process) or "cache" (fixed
// windows counted in App.Cache, which must be shared by every server:
// CACHE_STORE=database or a Redis store set with WithCacheStore).
Store string `env:"RATE_LIMIT_STORE" default:"memory"`
}
RateLimitConfig is read from RATE_LIMIT_* variables.
type Runner ¶
Runner is a long-running component (HTTP server, queue worker, scheduler). Run blocks until ctx is done or the runner fails, and must return after releasing its resources.
type RunnerFunc ¶
RunnerFunc adapts a function to a Runner.
type ScheduleConfig ¶ added in v0.16.0
type ScheduleConfig struct {
// Timezone of the expressions (IANA name, default UTC).
Timezone string `env:"SCHEDULE_TIMEZONE" default:"UTC"`
// InServe runs the scheduler inside serve. Set it to false when a
// dedicated schedule:work process (or a cron calling schedule:run)
// runs the schedule instead.
InServe bool `env:"SCHEDULE_IN_SERVE" default:"true"`
}
ScheduleConfig is read from SCHEDULE_* variables.
type Seeder ¶ added in v0.4.0
Seeder fills the database with data (demo content, reference tables). `vukem db:seed` runs every seeder given to WithSeeders, in order.
type SeederFunc ¶ added in v0.4.0
SeederFunc adapts a function to Seeder.
type SessionConfig ¶
type SessionConfig struct {
// Driver is "cookie" (encrypted cookie, default) or "memory"; the
// database driver is installed by the database provider.
Driver string `env:"SESSION_DRIVER" default:"cookie"`
Lifetime time.Duration `env:"SESSION_LIFETIME" default:"24h"`
IdleTimeout time.Duration `env:"SESSION_IDLE_TIMEOUT" default:"2h"`
CookieName string `env:"SESSION_COOKIE" default:"vukem_session"`
// Secure defaults to true in production or when APP_URL is https.
Secure string `env:"SESSION_SECURE" default:"auto"`
// RecordClient keeps the user agent and client network with each
// logged-in server-side session (session.Config.RecordClient).
RecordClient bool `env:"SESSION_RECORD_CLIENT" default:"false"`
}
SessionConfig is loaded from SESSION_* variables.
type Shutdowner ¶
Shutdowner is implemented by providers that release resources. Shutdown runs in reverse provider order.
type TelegramConfig ¶ added in v0.12.0
type TelegramConfig struct {
BotToken config.Secret `env:"TELEGRAM_BOT_TOKEN"`
ChatID string `env:"TELEGRAM_CHAT_ID"`
// MinLevel is debug, info, warn or error (default error).
MinLevel string `env:"TELEGRAM_MIN_LEVEL" default:"error"`
ThreadID int `env:"TELEGRAM_THREAD_ID"`
// Log forwards log records at MinLevel and above to the chat (default
// true); false keeps app.Telegram for explicit Send and Contact only.
Log bool `env:"TELEGRAM_LOG" default:"true"`
// RateLimit caps forwarded log records per minute (0: no cap).
RateLimit int `env:"TELEGRAM_RATE_LIMIT"`
// APIURL is the Bot API base URL, for a self-hosted Bot API server or
// a fake in tests. It must be https outside development and test.
APIURL string `env:"TELEGRAM_API_URL" default:"https://api.telegram.org"`
}
TelegramConfig is read from TELEGRAM_* variables.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package admin is an admin panel generated from ORM models: paginated lists with search, sort and filters, detail pages, create/edit forms built from z schemas, deletes with confirmation, and an audit log of every change.
|
Package admin is an admin panel generated from ORM models: paginated lists with search, sort and filters, detail pages, create/edit forms built from z schemas, deletes with confirmation, and an audit log of every change. |
|
Package api holds REST helpers on top of web and orm: resource transformers, pagination envelopes, allowlisted query conventions (filter, sort, fields, include, pagination) and deprecation headers.
|
Package api holds REST helpers on top of web and orm: resource transformers, pagination envelopes, allowlisted query conventions (filter, sort, fields, include, pagination) and deprecation headers. |
|
Package apidocs serves an API reference for the app's OpenAPI document with Scalar (https://scalar.com).
|
Package apidocs serves an API reference for the app's OpenAPI document with Scalar (https://scalar.com). |
|
Package archive reads a user-supplied archive under a policy, and hands back entries whose names are already safe keys.
|
Package archive reads a user-supplied archive under a policy, and hands back entries whose names are already safe keys. |
|
Package auth authenticates requests: guards (session for browsers, access tokens for APIs), user providers, middleware and password hashing (auth/hash).
|
Package auth authenticates requests: guards (session for browsers, access tokens for APIs), user providers, middleware and password hashing (auth/hash). |
|
hash
Package hash hashes and verifies passwords: argon2id by default (PHC string format), bcrypt for compatibility.
|
Package hash hashes and verifies passwords: argon2id by default (PHC string format), bcrypt for compatibility. |
|
jwt
Package jwt issues and verifies JSON Web Tokens (RFC 7519) with the standard library: HS256, RS256, ES256 and EdDSA, an algorithm allowlist tied to each key (no algorithm confusion, never "none"), key ids with static or remote (JWKS) key sets that rotate, strict claim checks, a Guard for package auth, and rotating refresh tokens with reuse detection.
|
Package jwt issues and verifies JSON Web Tokens (RFC 7519) with the standard library: HS256, RS256, ES256 and EdDSA, an algorithm allowlist tied to each key (no algorithm confusion, never "none"), key ids with static or remote (JWKS) key sets that rotate, strict claim checks, a Guard for package auth, and rotating refresh tokens with reuse detection. |
|
totp
Package totp implements time-based one-time passwords (RFC 6238, the codes of authenticator apps) for two-factor authentication: secrets, otpauth:// enrolment URIs with an SVG QR code, validation with one step of clock skew and replay protection, and hashed single-use recovery codes.
|
Package totp implements time-based one-time passwords (RFC 6238, the codes of authenticator apps) for two-factor authentication: secrets, otpauth:// enrolment URIs with an SVG QR code, validation with one step of clock skew and replay protection, and hashed single-use recovery codes. |
|
Package authz decides what an authenticated user may do: gates (named abilities) and policies (abilities on a model type).
|
Package authz decides what an authenticated user may do: gates (named abilities) and policies (abilities on a model type). |
|
Package broadcast pushes server events to browsers over named channels, in the spirit of Laravel Broadcasting: public, private ("private-") and presence ("presence-") channels, each authorized by a rule registered for its name pattern, fanned out in memory or through a Broker (drivers/redis) so every app instance delivers every event.
|
Package broadcast pushes server events to browsers over named channels, in the spirit of Laravel Broadcasting: public, private ("private-") and presence ("presence-") channels, each authorized by a rule registered for its name pattern, fanned out in memory or through a Broker (drivers/redis) so every app instance delivers every event. |
|
Package cache stores values with a TTL behind a Store (memory, database; Redis in drivers/redis).
|
Package cache stores values with a TTL behind a Store (memory, database; Redis in drivers/redis). |
|
cachetest
Package cachetest is the conformance suite every cache.Store passes (memory and database in core, Redis in drivers/redis).
|
Package cachetest is the conformance suite every cache.Store passes (memory and database in core, Redis in drivers/redis). |
|
cmd
|
|
|
vukem
command
Command vukem is the Vukem CLI: generators, the dev server, builds and delegation of app commands to the project's binary.
|
Command vukem is the Vukem CLI: generators, the dev server, builds and delegation of app commands to the project's binary. |
|
Package config loads typed application configuration from environment variables and .env files.
|
Package config loads typed application configuration from environment variables and .env files. |
|
contrib
|
|
|
graphql
module
|
|
|
grpc
module
|
|
|
imaging
module
|
|
|
stripe
module
|
|
|
Package cpu reports the CPU quota the container this process runs in was given, for sizing work the Go runtime does not size for you.
|
Package cpu reports the CPU quota the container this process runs in was given, for sizing work the Go runtime does not size for you. |
|
Package crypt encrypts and authenticates values with AES-256-GCM and supports key rotation: one current key encrypts, previous keys only decrypt.
|
Package crypt encrypts and authenticates values with AES-256-GCM and supports key rotation: one current key encrypts, previous keys only decrypt. |
|
Package csrf protects state-changing requests from cross-site request forgery using Go's http.CrossOriginProtection (Fetch Metadata and Origin checks, no tokens needed in modern browsers), with an optional session-token fallback for clients that send neither header.
|
Package csrf protects state-changing requests from cross-site request forgery using Go's http.CrossOriginProtection (Fetch Metadata and Origin checks, no tokens needed in modern browsers), with an optional session-token fallback for clients that send neither header. |
|
Package database manages SQL connections for Vukem: dialects, SQLite defaults (WAL, busy timeout, foreign keys, a single-connection writer pool beside a reader pool), PostgreSQL and MySQL through driver modules, and transactions that roll back on error or panic.
|
Package database manages SQL connections for Vukem: dialects, SQLite defaults (WAL, busy timeout, foreign keys, a single-connection writer pool beside a reader pool), PostgreSQL and MySQL through driver modules, and transactions that roll back on error or panic. |
|
dbtest
Package dbtest is the database conformance suite: the same schema, ORM, transaction and migration checks run against every dialect (SQLite in core, Postgres and others in their driver modules).
|
Package dbtest is the database conformance suite: the same schema, ORM, transaction and migration checks run against every dialect (SQLite in core, Postgres and others in their driver modules). |
|
Package devtools is Vukem's development dashboard, served at /_vukem: recent requests with timings, SQL queries (with N+1 detection), jobs, events, sent mail previews, log records, per-request traces and the route table.
|
Package devtools is Vukem's development dashboard, served at /_vukem: recent requests with timings, SQL queries (with N+1 detection), jobs, events, sent mail previews, log records, per-request traces and the route table. |
|
drivers
|
|
|
mongo
module
|
|
|
mysql
module
|
|
|
otel
module
|
|
|
postgres
module
|
|
|
redis
module
|
|
|
s3
module
|
|
|
websocket
module
|
|
|
Package errs defines the error values handlers return.
|
Package errs defines the error values handlers return. |
|
Package events dispatches typed events to listeners registered by type.
|
Package events dispatches typed events to listeners registered by type. |
|
Package export streams CSV, JSON and NDJSON downloads row by row, so a large export never sits in memory:
|
Package export streams CSV, JSON and NDJSON downloads row by row, so a large export never sits in memory: |
|
Package factory builds models with fake data for tests and seeders:
|
Package factory builds models with fake data for tests and seeders: |
|
Package flags provides feature flags: typed values declared in code with a default, overridden by FLAG_<NAME> configuration and by values stored in the database (globally or for one scope, such as a user or a tenant).
|
Package flags provides feature flags: typed values declared in code with a default, overridden by FLAG_<NAME> configuration and by values stored in the database (globally or for one scope, such as a user or a tenant). |
|
Package htmx provides helpers for htmx 4: reading htmx request headers, building response headers, and rendering <hx-partial> and out-of-band fragments.
|
Package htmx provides helpers for htmx 4: reading htmx request headers, building response headers, and rendering <hx-partial> and out-of-band fragments. |
|
Package httpclient builds outbound HTTP clients for URLs that users control (webhooks, link previews, avatars by URL).
|
Package httpclient builds outbound HTTP clients for URLs that users control (webhooks, link previews, avatars by URL). |
|
Package i18n translates messages from JSON catalogs: one file per locale (resources/lang/en.json, pt-BR.json...), nested or dotted keys, {name} placeholders, CLDR plural categories, a locale fallback chain (pt-BR → pt → default), a locale-resolving middleware and translated validation messages.
|
Package i18n translates messages from JSON catalogs: one file per locale (resources/lang/en.json, pt-BR.json...), nested or dotted keys, {name} placeholders, CLDR plural categories, a locale fallback chain (pt-BR → pt → default), a locale-resolving middleware and translated validation messages. |
|
Package inertia is a server-side adapter for the Inertia.js v3 protocol (https://inertiajs.com/docs/v3/core-concepts/the-protocol).
|
Package inertia is a server-side adapter for the Inertia.js v3 protocol (https://inertiajs.com/docs/v3/core-concepts/the-protocol). |
|
internal
|
|
|
cli
Package cli implements the global `vukem` command: project scaffolding, generators, the dev server and delegation of app commands to the project's own binary (cmd/app).
|
Package cli implements the global `vukem` command: project scaffolding, generators, the dev server and delegation of app commands to the project's own binary (cmd/app). |
|
cli/starter/app/controllers
Package controllers holds the HTTP handlers.
|
Package controllers holds the HTTP handlers. |
|
cli/starter/app/jobs
Package jobs holds the queued jobs.
|
Package jobs holds the queued jobs. |
|
cli/starter/app/models
Package models holds the database models.
|
Package models holds the database models. |
|
cli/starter/app/requests
Package requests holds validated request inputs.
|
Package requests holds validated request inputs. |
|
cli/starter/database/factories
Package factories builds models with fake data for tests and seeders.
|
Package factories builds models with fake data for tests and seeders. |
|
cli/starter/routes
Package routes declares the HTTP routes.
|
Package routes declares the HTTP routes. |
|
codemod
Package codemod edits Go source the way a developer would: it appends an element to the slice literal returned by a known function and adds the imports it needs.
|
Package codemod edits Go source the way a developer would: it appends an element to the slice literal returned by a known function and adds the imports it needs. |
|
disposition
Package disposition builds safe Content-Disposition attachment headers for client-facing download names (export and web.Ctx.Download).
|
Package disposition builds safe Content-Disposition attachment headers for client-facing download names (export and web.Ctx.Download). |
|
naming
Package naming converts identifiers between Go and database/file conventions.
|
Package naming converts identifiers between Go and database/file conventions. |
|
principal
Package principal carries the identity a guard authenticated for one request.
|
Package principal carries the identity a guard authenticated for one request. |
|
sqlscan
Package sqlscan finds the "?" bind placeholders of a SQL statement, skipping string literals, quoted identifiers and comments the way the target dialect lexes them.
|
Package sqlscan finds the "?" bind placeholders of a SQL statement, skipping string literals, quoted identifiers and comments the way the target dialect lexes them. |
|
testutil
Package testutil holds helpers shared by the core packages' tests.
|
Package testutil holds helpers shared by the core packages' tests. |
|
tools/apicheck
command
Command apicheck compares the exported API of the core module and of every published module (drivers/*, contrib/*) with its latest stable release tag, using golang.org/x/exp/cmd/apidiff:
|
Command apicheck compares the exported API of the core module and of every published module (drivers/*, contrib/*) with its latest stable release tag, using golang.org/x/exp/cmd/apidiff: |
|
tools/commitlint
command
Command commitlint validates a commit message file against Conventional Commits 1.0.
|
Command commitlint validates a commit message file against Conventional Commits 1.0. |
|
tools/covergate
command
Command covergate fails when a core package is below its coverage gate (docs/testing.md): 80% by default, 90% for the strict packages.
|
Command covergate fails when a core package is below its coverage gate (docs/testing.md): 80% by default, 90% for the strict packages. |
|
tools/fixinline
command
Command fixinline checks that go fix inlines every //go:fix inline wrapper of the repository correctly:
|
Command fixinline checks that go fix inlines every //go:fix inline wrapper of the repository correctly: |
|
tools/modcheck
command
Command modcheck gates the nested modules on being resolvable the way a consumer resolves them: from their own go.mod, with no workspace.
|
Command modcheck gates the nested modules on being resolvable the way a consumer resolves them: from their own go.mod, with no workspace. |
|
tools/release
command
Command release cuts a release at the end of a PLAN.md task group:
|
Command release cuts a release at the end of a PLAN.md task group: |
|
Package logging holds log/slog handlers shared by Vukem apps.
|
Package logging holds log/slog handlers shared by Vukem apps. |
|
telegram
Package telegram is a log/slog handler that sends records to Telegram chats, ported from the maintainer's TypeScript telegram-logger.
|
Package telegram is a log/slog handler that sends records to Telegram chats, ported from the maintainer's TypeScript telegram-logger. |
|
Package mail sends email.
|
Package mail sends email. |
|
Package mediatype names the types an application **produces**: the canonical file extension and a human label for a file it generates and hands back as a download.
|
Package mediatype names the types an application **produces**: the canonical file extension and a human label for a file it generates and hands back as a download. |
|
Package middleware provides Vukem's built-in net/http middleware.
|
Package middleware provides Vukem's built-in net/http middleware. |
|
Package openapi generates an OpenAPI 3.1 document from the router: typed routes (api.Register) are described from their input and output types and `validate` tags; plain routes get a generic entry.
|
Package openapi generates an OpenAPI 3.1 document from the router: typed routes (api.Register) are described from their input and output types and `validate` tags; plain routes get a generic entry. |
|
Package orm is a generic query builder and model mapper over database/sql.
|
Package orm is a generic query builder and model mapper over database/sql. |
|
Package queue runs typed background jobs.
|
Package queue runs typed background jobs. |
|
queuetest
Package queuetest is the conformance suite for queue drivers:
|
Package queuetest is the conformance suite for queue drivers: |
|
Package ratelimit limits requests per key (token, user, IP).
|
Package ratelimit limits requests per key (token, user, IP). |
|
Package router is a thin layer over net/http.ServeMux (Go 1.22+ patterns) adding route groups, named routes with URL generation, resource controllers and three middleware stacks:
|
Package router is a thin layer over net/http.ServeMux (Go 1.22+ patterns) adding route groups, named routes with URL generation, resource controllers and three middleware stacks: |
|
Package schedule runs recurring tasks: functions, app commands and queue jobs, on cron expressions or helpers such as DailyAt.
|
Package schedule runs recurring tasks: functions, app commands and queue jobs, on cron expressions or helpers such as DailyAt. |
|
Package schema builds DDL with a fluent, dialect-aware API (SQLite, PostgreSQL and MySQL/MariaDB) and runs versioned migrations with batches and checksums.
|
Package schema builds DDL with a fluent, dialect-aware API (SQLite, PostgreSQL and MySQL/MariaDB) and runs versioned migrations with batches and checksums. |
|
stores
Package stores ships the migrations for the tables vukem's own database-backed stores use: the queue's jobs and failed jobs tables, the sessions table, the cache table and the webhook delivery log.
|
Package stores ships the migrations for the tables vukem's own database-backed stores use: the queue's jobs and failed jobs tables, the sessions table, the cache table and the webhook delivery log. |
|
Package security defines the security event dispatched for every security-relevant decision (failed logins, denied authorization, CSRF rejections, rate limits, invalid signatures).
|
Package security defines the security event dispatched for every security-relevant decision (failed logins, denied authorization, CSRF rejections, rate limits, invalid signatures). |
|
Package session provides server-side and cookie sessions with flash messages, old input, regeneration (anti-fixation) and idle/absolute timeouts.
|
Package session provides server-side and cookie sessions with flash messages, old input, regeneration (anti-fixation) and idle/absolute timeouts. |
|
sessiontest
Package sessiontest is the conformance suite every session.ListStore passes (memory and database in core, Redis in drivers/redis).
|
Package sessiontest is the conformance suite every session.ListStore passes (memory and database in core, Redis in drivers/redis). |
|
Package signed creates and verifies signed URLs: links that carry an expiry and an HMAC-SHA256 signature over their path and query, for email verification, unsubscribe links or temporary downloads.
|
Package signed creates and verifies signed URLs: links that carry an expiry and an HMAC-SHA256 signature over their path and query, for email verification, unsubscribe links or temporary downloads. |
|
Package social signs users in with OAuth2 / OpenID Connect providers (GitHub, Google, GitLab, Discord, Microsoft and any compliant provider), in the spirit of AdonisJS Ally, using only the standard library.
|
Package social signs users in with OAuth2 / OpenID Connect providers (GitHub, Google, GitLab, Discord, Microsoft and any compliant provider), in the spirit of AdonisJS Ally, using only the standard library. |
|
Package sse streams server-sent events (text/event-stream).
|
Package sse streams server-sent events (text/event-stream). |
|
Package storage reads and writes files on disks: the local filesystem (through os.Root, so paths can never escape the disk) and memory (tests); S3-compatible storage lives in the drivers/s3 module.
|
Package storage reads and writes files on disks: the local filesystem (through os.Root, so paths can never escape the disk) and memory (tests); S3-compatible storage lives in the drivers/s3 module. |
|
storagetest
Package storagetest is the conformance suite every storage.Disk passes (local and memory in core, S3 in drivers/s3).
|
Package storagetest is the conformance suite every storage.Disk passes (local and memory in core, S3 in drivers/s3). |
|
Package ulid generates and validates ULIDs with the standard library only: 128-bit identifiers whose 26-character text form sorts by creation time.
|
Package ulid generates and validates ULIDs with the standard library only: 128-bit identifiers whose 26-character text form sorts by creation time. |
|
Package upload receives files without buffering them: streaming multipart parsing with per-route limits, content sniffing that must agree with the file extension, random stored names, an optional virus-scan step (ClamAV) before files become visible, resumable uploads (tus 1.0) and presigned direct-to-storage uploads.
|
Package upload receives files without buffering them: streaming multipart parsing with per-route limits, content sniffing that must agree with the file extension, random stored names, an optional virus-scan step (ClamAV) before files become visible, resumable uploads (tus 1.0) and presigned direct-to-storage uploads. |
|
Package uuid generates and parses RFC 9562 UUIDs with the standard library only: random version 4 and time-ordered version 7, both from crypto/rand.
|
Package uuid generates and parses RFC 9562 UUIDs with the standard library only: random version 4 and time-ordered version 7, both from crypto/rand. |
|
Package view renders html/template views with layouts, partials and fragments (single blocks, for htmx), plus request-aware helpers: route, asset, csrfField, old, errors, flash, cspNonce, t and locale.
|
Package view renders html/template views with layouts, partials and fragments (single blocks, for htmx), plus request-aware helpers: route, asset, csrfField, old, errors, flash, cspNonce, t and locale. |
|
Package vite renders the tags that load a Vite frontend, following the Laravel "hot file" model.
|
Package vite renders the tags that load a Vite frontend, following the Laravel "hot file" model. |
|
Package vukemtest is Vukem's testing toolkit: boot an app for a test with an isolated in-memory database, send in-process HTTP requests with a fluent client, and assert on responses, sessions and the database.
|
Package vukemtest is Vukem's testing toolkit: boot an app for a test with an isolated in-memory database, send in-process HTTP requests with a fluent client, and assert on responses, sessions and the database. |
|
Package web provides the request context (*Ctx) used by Vukem handlers: request helpers, typed input, responses, and the central error handler.
|
Package web provides the request context (*Ctx) used by Vukem handlers: request helpers, typed input, responses, and the central error handler. |
|
Package webhook verifies signed incoming webhooks: Middleware checks an HMAC-SHA256 of the raw body (GitHub, Shopify, your own integrations), Standard checks Standard Webhooks signatures (Svix, Resend, Clerk) with a timestamp replay window, and Dedupe drops at-least-once redeliveries.
|
Package webhook verifies signed incoming webhooks: Middleware checks an HMAC-SHA256 of the raw body (GitHub, Shopify, your own integrations), Standard checks Standard Webhooks signatures (Svix, Resend, Clerk) with a timestamp replay window, and Dedupe drops at-least-once redeliveries. |
|
Package z parses untrusted input into typed Go values with Zod-inspired schemas, and checks outgoing values against the same contracts.
|
Package z parses untrusted input into typed Go values with Zod-inspired schemas, and checks outgoing values against the same contracts. |