Documentation
¶
Index ¶
- Variables
- func CSRFToken(session *scs.SessionManager, r *http.Request) string
- func GetHTTPStatus(err error) int
- func IsCode(err error, code ErrorCode) bool
- func RotateCSRFToken(session *scs.SessionManager, r *http.Request)
- func ValidateEncryptionKey(key []byte) error
- type BackgroundService
- func (b *BackgroundService) CronStatus() []CronRun
- func (b *BackgroundService) GetCronEntry(name string) (cron.EntryID, bool)
- func (b *BackgroundService) ListCronJobs() []string
- func (b *BackgroundService) ScheduleCron(name, expr string, fn func()) (cron.EntryID, error)
- func (b *BackgroundService) UnscheduleCron(name string) bool
- type CronRun
- type DataService
- type Database
- type Encryption
- type ErrorCode
- type FileSystemRegistry
- type HTTPService
- type LoggingService
- type Module
- type ModuleRegistry
- func (r *ModuleRegistry) Count() int
- func (r *ModuleRegistry) Get(name string) Module
- func (r *ModuleRegistry) Has(name string) bool
- func (r *ModuleRegistry) InitializeAll(g *Tjo) error
- func (r *ModuleRegistry) Names() []string
- func (r *ModuleRegistry) Register(m Module) error
- func (r *ModuleRegistry) ShutdownAll(ctx context.Context) error
- type Server
- type Tjo
- func (g *Tjo) AddMonitoringRoutes(mux *chi.Mux)
- func (g *Tjo) BuildDSN() string
- func (g *Tjo) CSRF(next http.Handler) http.Handler
- func (g Tjo) CreateDirIfNotExists(path string) error
- func (g Tjo) CreateFileIfNotExists(path string) error
- func (g *Tjo) CrossOriginProtection(next http.Handler) http.Handler
- func (g *Tjo) DownloadFile(w http.ResponseWriter, r *http.Request, pathToFile, filename string) error
- func (g *Tjo) Error404(w http.ResponseWriter, r *http.Request)
- func (g *Tjo) Error500(w http.ResponseWriter, r *http.Request)
- func (g *Tjo) ErrorForbidden(w http.ResponseWriter, r *http.Request)
- func (g *Tjo) ErrorStatus(w http.ResponseWriter, status int)
- func (g *Tjo) ErrorUnauthorized(w http.ResponseWriter, r *http.Request)
- func (g *Tjo) GetModule(name string) Module
- func (g *Tjo) HasModule(name string) bool
- func (g *Tjo) Init(p initPaths) error
- func (g *Tjo) ListenAndServe() error
- func (g *Tjo) LoadTime(start time.Time)
- func (g *Tjo) MigrateDownAll(dsn string) error
- func (g *Tjo) MigrateForce(dsn string) error
- func (g *Tjo) MigrateUp(dsn string) error
- func (g *Tjo) New(rootPath string, modules ...Module) error
- func (g *Tjo) NoSurf(next http.Handler) http.Handlerdeprecated
- func (g *Tjo) OpenDB(dbType, dsn string) (*sql.DB, error)
- func (g Tjo) RandomString(length int) string
- func (g *Tjo) ReadJson(w http.ResponseWriter, r *http.Request, data interface{}) error
- func (g *Tjo) SessionLoad(next http.Handler) http.Handler
- func (g *Tjo) Shutdown(ctx context.Context) error
- func (g *Tjo) Steps(steps int, dsn string) error
- func (g *Tjo) Validator(data url.Values) *Validation
- func (g *Tjo) ValidatorFor(ctx context.Context, data url.Values) *Validation
- func (g *Tjo) WriteJson(w http.ResponseWriter, status int, data interface{}, headers ...http.Header) error
- func (g *Tjo) WriteXML(w http.ResponseWriter, status int, data interface{}, headers ...http.Header) error
- type TjoError
- type Validation
- func (v *Validation) AddError(key, message string)
- func (v *Validation) Check(ok bool, key, message string)
- func (v *Validation) Equals(eq bool, field, verified string)
- func (v *Validation) EscapeHTML(value string) string
- func (v *Validation) Has(field string, r *http.Request) bool
- func (v *Validation) IsAlphanumeric(field, value string)
- func (v *Validation) IsDateISO(field, value string)
- func (v *Validation) IsEmail(field, value string)
- func (v *Validation) IsFloat(field, value string)
- func (v *Validation) IsInt(field, value string)
- func (v *Validation) IsString(field, value string)
- func (v *Validation) IsURL(field, value string)
- func (v *Validation) MaxLength(field, value string, maxLength int)
- func (v *Validation) MinLength(field, value string, minLength int)
- func (v *Validation) NoSpaces(field, value string)
- func (v *Validation) Required(r *http.Request, fields ...string)
- func (v *Validation) SanitizeHTML(value string) string
- func (v *Validation) SanitizeRichText(value string) string
- func (v *Validation) Valid() bool
- type ValidationError
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNotFoundError is returned when a resource is not found ErrNotFoundError = NewError("", "resource not found", ErrNotFound) ErrUnauthorizedError = NewError("", "unauthorized", ErrUnauthorized) // ErrForbiddenError is returned when access is denied ErrForbiddenError = NewError("", "access denied", ErrForbidden) // ErrInternalError is returned for internal server errors ErrInternalError = NewError("", "internal server error", ErrInternal) // ErrValidationError is returned for validation failures ErrValidationError = NewError("", "validation failed", ErrValidation) )
Common pre-defined errors for convenience
var ErrDuplicateJSONKey = jsonstrict.ErrDuplicateKey
ErrDuplicateJSONKey is returned when a request body names the same object member twice. See internal/jsonstrict for why encoding/json does not.
Functions ¶
func CSRFToken ¶ added in v0.9.0
func CSRFToken(session *scs.SessionManager, r *http.Request) string
CSRFToken returns the token for this request's session, creating one if needed. Templates render it; AJAX clients read it from the response header.
func GetHTTPStatus ¶
GetHTTPStatus returns the appropriate HTTP status for an error
func RotateCSRFToken ¶ added in v0.9.0
func RotateCSRFToken(session *scs.SessionManager, r *http.Request)
RotateCSRFToken issues a fresh token, discarding the previous one.
Call it whenever the session's identity changes. `tjo make auth` renews the session ID on login and after 2FA verification; a token that survived that rotation would outlive the anonymous session it was minted for.
func ValidateEncryptionKey ¶
ValidateEncryptionKey checks that the key meets AES requirements. AES requires keys of exactly 16, 24, or 32 bytes for AES-128, AES-192, or AES-256.
Types ¶
type BackgroundService ¶
type BackgroundService struct {
Jobs *jobs.JobManager
Scheduler *cron.Cron
Mail email.Mail
SMS sms.SMSProvider
// contains filtered or unexported fields
}
BackgroundService handles background jobs, scheduling, mail, and SMS
func (*BackgroundService) CronStatus ¶ added in v0.11.0
func (b *BackgroundService) CronStatus() []CronRun
CronStatus reports the last run of every scheduled job, sorted by name.
func (*BackgroundService) GetCronEntry ¶
func (b *BackgroundService) GetCronEntry(name string) (cron.EntryID, bool)
GetCronEntry returns the entry ID for a named cron job.
func (*BackgroundService) ListCronJobs ¶
func (b *BackgroundService) ListCronJobs() []string
ListCronJobs returns all named cron job names.
func (*BackgroundService) ScheduleCron ¶
func (b *BackgroundService) ScheduleCron(name, expr string, fn func()) (cron.EntryID, error)
ScheduleCron adds a named cron job that can be unscheduled later. Returns the entry ID and any error from adding the job.
func (*BackgroundService) UnscheduleCron ¶
func (b *BackgroundService) UnscheduleCron(name string) bool
UnscheduleCron removes a named cron job. Returns true if the job was found and removed.
type CronRun ¶ added in v0.11.0
type CronRun struct {
// Name is the scheduled job.
Name string
// LastRun is when it last started. Zero means it has not run since the
// process started -- which for a nightly job is normal for most of the day,
// and is why the dashboard shows the schedule next to it.
LastRun time.Time
// Duration is how long the last run took.
Duration time.Duration
// Runs and Failures count since the process started.
Runs int
Failures int
// LastError is the panic recovered from the last failed run.
LastError string
}
CronRun is what happened the last time a scheduled job ran.
Nothing recorded this before, which is why a cron entry that had silently stopped firing was invisible: the scheduler knew the job existed and nothing knew whether it had ever done anything.
type DataService ¶
type DataService struct {
DB Database
Cache cache.Cache
Files *FileSystemRegistry
// contains filtered or unexported fields
}
DataService handles database, caching, and file storage
type Encryption ¶
type Encryption struct {
Key []byte
}
func NewEncryption ¶
func NewEncryption(key []byte) (*Encryption, error)
NewEncryption creates a validated encryption instance. Returns an error if the key doesn't meet AES requirements.
func (Encryption) Decrypt ¶
func (e Encryption) Decrypt(cryptoText string) (string, error)
Decrypt reverses Encrypt. It returns an error if the ciphertext was modified in any way, rather than silently returning altered plaintext.
func (Encryption) Encrypt ¶
func (e Encryption) Encrypt(data string) (string, error)
Encrypt returns an authenticated ciphertext (AES-GCM) as a URL-safe base64 string. The nonce is prepended to the sealed output.
GCM rather than an unauthenticated mode: without a MAC an attacker can flip bits in the ciphertext and have them land as controlled changes in the plaintext, and Decrypt has no way to notice. Anything treating a decrypted value as trustworthy is then forgeable.
type ErrorCode ¶
type ErrorCode int
ErrorCode represents categories of errors for classification and handling
const ( // ErrInternal represents internal server errors ErrInternal ErrorCode = iota // ErrValidation represents input validation failures ErrValidation // ErrNotFound represents resource not found errors ErrNotFound ErrUnauthorized // ErrForbidden represents access denied errors ErrForbidden // ErrDatabase represents database operation failures ErrDatabase // ErrExternal represents external service failures ErrExternal // ErrConfiguration represents configuration errors ErrConfiguration // ErrTimeout represents operation timeout errors ErrTimeout )
func (ErrorCode) HTTPStatus ¶
HTTPStatus returns the appropriate HTTP status code for an ErrorCode
type FileSystemRegistry ¶
type FileSystemRegistry struct {
// contains filtered or unexported fields
}
FileSystemRegistry provides thread-safe access to registered file systems. It uses the filesystems.FS interface for type safety instead of map[string]interface{}.
func NewFileSystemRegistry ¶
func NewFileSystemRegistry() *FileSystemRegistry
NewFileSystemRegistry creates a new file system registry
func (*FileSystemRegistry) Get ¶
func (r *FileSystemRegistry) Get(name string) (filesystems.FS, bool)
Get retrieves a file system by name
func (*FileSystemRegistry) Has ¶
func (r *FileSystemRegistry) Has(name string) bool
Has checks if a file system is registered
func (*FileSystemRegistry) Names ¶
func (r *FileSystemRegistry) Names() []string
Names returns all registered file system names
func (*FileSystemRegistry) Register ¶
func (r *FileSystemRegistry) Register(name string, fs filesystems.FS)
Register adds a file system to the registry
type HTTPService ¶
type HTTPService struct {
Router *chi.Mux
Session *scs.SessionManager
Render *render.Render
JetViews *jet.Set
}
HTTPService handles HTTP routing, sessions, and rendering
type LoggingService ¶
type LoggingService struct {
Error *log.Logger
Info *log.Logger
Logger *logging.Logger
Metrics *logging.MetricRegistry
Health *logging.HealthMonitor
App *logging.ApplicationMetrics
OTel *otel.Provider // OpenTelemetry provider for distributed tracing
}
LoggingService handles all logging, metrics, and health monitoring
type Module ¶
type Module interface {
// Name returns a unique identifier for this module (e.g., "sms", "email", "websocket")
Name() string
// Initialize sets up the module with access to the framework.
// Called during app.New() after core services are ready.
//
// app is the *Tjo instance. The parameter is any rather than *Tjo because
// the shipped modules (email, sms, otel, websocket) are separate Go
// modules that tjo imports, so they cannot import tjo back to name the
// concrete type. A module that needs something from the framework should
// declare the narrow interface it wants and assert against it:
//
// type mailer interface{ SendEmail(to, subject, body string) error }
// if m, ok := app.(mailer); ok { ... }
Initialize(app any) error
// Shutdown gracefully stops the module.
// Called during graceful shutdown with a context for timeout control.
Shutdown(ctx context.Context) error
}
Module defines the interface for optional framework components. Modules are initialized after core services and can depend on them. Use this for opt-in features like SMS, Email, WebSockets, OpenTelemetry, etc.
type ModuleRegistry ¶
type ModuleRegistry struct {
// contains filtered or unexported fields
}
ModuleRegistry manages module registration and lifecycle. It ensures modules are initialized in registration order and shut down in reverse order.
func NewModuleRegistry ¶
func NewModuleRegistry() *ModuleRegistry
NewModuleRegistry creates an empty module registry.
func (*ModuleRegistry) Count ¶
func (r *ModuleRegistry) Count() int
Count returns the number of registered modules.
func (*ModuleRegistry) Get ¶
func (r *ModuleRegistry) Get(name string) Module
Get returns a module by name, or nil if not found.
func (*ModuleRegistry) Has ¶
func (r *ModuleRegistry) Has(name string) bool
Has checks if a module is registered.
func (*ModuleRegistry) InitializeAll ¶
func (r *ModuleRegistry) InitializeAll(g *Tjo) error
InitializeAll calls Initialize on all registered modules in order. Stops on first error and returns it.
func (*ModuleRegistry) Names ¶
func (r *ModuleRegistry) Names() []string
Names returns all registered module names in registration order.
func (*ModuleRegistry) Register ¶
func (r *ModuleRegistry) Register(m Module) error
Register adds a module to the registry. Modules are initialized in the order they are registered. Returns an error if a module with the same name already exists.
func (*ModuleRegistry) ShutdownAll ¶
func (r *ModuleRegistry) ShutdownAll(ctx context.Context) error
ShutdownAll calls Shutdown on all registered modules in reverse order. Collects all errors and returns them combined.
type Tjo ¶
type Tjo struct {
// Core configuration
AppName string
Debug bool
Version string
RootPath string
EncryptionKey string
Server Server
Config *config.Config
// Services (composed)
Logging *LoggingService
HTTP *HTTPService
Data *DataService
Background *BackgroundService
// Modules (opt-in components)
Modules *ModuleRegistry
}
Tjo is the main framework struct that orchestrates all components. It uses composition to organize functionality into focused services: - Logging: structured logging, metrics, and health monitoring - HTTP: routing, sessions, and template rendering - Data: database, caching, and file storage - Background: job processing, scheduling, mail, and SMS - Modules: optional components (SMS, Email, WebSocket, OTel, etc.)
func (*Tjo) AddMonitoringRoutes ¶
AddMonitoringRoutes adds health and metrics endpoints. Call this in your routes() function AFTER adding your middleware.
func (*Tjo) BuildDSN ¶
BuildDSN returns the database connection string. Deprecated: Use g.Config.Database.DSN(g.RootPath) instead.
func (*Tjo) CSRF ¶ added in v0.9.0
CSRF verifies the submitted token against the session's.
It publishes the token as a response header from inside the handler chain rather than around it. That distinction was a real bug: reading the token from a wrapper outside the CSRF middleware always yielded "", which left AJAX and SPA clients with no way to obtain one.
func (Tjo) CreateDirIfNotExists ¶
func (Tjo) CreateFileIfNotExists ¶
func (*Tjo) CrossOriginProtection ¶ added in v0.8.0
CrossOriginProtection rejects non-safe cross-origin browser requests using Sec-Fetch-Site, falling back to comparing Origin against Host.
This sits in front of NoSurf rather than replacing it, and the distinction matters. Token CSRF only protects a form the application remembered to put a token in; this protects every state-changing request whether the template author got it right or not. Conversely this deliberately allows requests carrying neither Sec-Fetch-Site nor Origin, because they are either same-origin or not from a browser at all -- so it is not a replacement for tokens either. The two cover different halves.
Trusted origins come from CORS_ALLOWED_ORIGINS, the variable the security package already documents for the same purpose, rather than inventing a second list that can disagree with the first.
func (*Tjo) DownloadFile ¶
func (*Tjo) ErrorForbidden ¶
func (g *Tjo) ErrorForbidden(w http.ResponseWriter, r *http.Request)
func (*Tjo) ErrorStatus ¶
func (g *Tjo) ErrorStatus(w http.ResponseWriter, status int)
func (*Tjo) ErrorUnauthorized ¶
func (g *Tjo) ErrorUnauthorized(w http.ResponseWriter, r *http.Request)
func (*Tjo) GetModule ¶
GetModule returns a module by name, or nil if not registered. Use type assertion to get the concrete module type.
Example:
if m := app.GetModule("sms"); m != nil {
smsModule := m.(*sms.Module)
smsModule.Send("+1234567890", "Hello!", false)
}
func (*Tjo) ListenAndServe ¶
ListenAndServe starts the web server with graceful shutdown support. It handles SIGINT and SIGTERM signals to gracefully stop the server, waiting for in-flight requests to complete before shutting down. Returns an error if the server fails to start or encounters a fatal error.
func (*Tjo) MigrateDownAll ¶
func (*Tjo) MigrateForce ¶
func (*Tjo) New ¶
New initializes the Tjo framework with optional modules. Modules are opt-in components for features like SMS, Email, WebSocket, etc. Example:
app := tjo.Tjo{}
app.New(rootPath,
sms.NewModule(),
email.NewModule(),
)
func (Tjo) RandomString ¶
func (*Tjo) Shutdown ¶
Shutdown gracefully shuts down the application and all its components. It stops background services, modules (in reverse order), and closes connections.
func (*Tjo) ValidatorFor ¶ added in v0.13.0
ValidatorFor is Validator with the request's language attached, so the framework's own validation messages come back in it.
Prefer it in a handler. Validator stays for the places with no request -- a job, a CLI command -- where the fallback language is the only sensible answer anyway.
type TjoError ¶
type TjoError struct {
// Op is the operation that failed (e.g., "database.query", "auth.validate")
Op string
// Err is the underlying error
Err error
// Code categorizes the error for handling
Code ErrorCode
// Context provides additional error context as key-value pairs
Context map[string]interface{}
}
TjoError is the standard error type for the framework. It provides structured error information including operation context, error classification, and optional additional context.
func WrapError ¶
WrapError wraps an error with operation context and classification. Use this when catching errors from lower-level operations.
func (*TjoError) WithContext ¶
WithContext adds context to the error and returns it for chaining
type Validation ¶
type Validation struct {
Data url.Values
Errors map[string]string
// Printer renders the framework's own messages in the request's language.
// Nil means the fallback language, which is what a background job gets.
Printer *i18n.Printer
}
func (*Validation) AddError ¶
func (v *Validation) AddError(key, message string)
func (*Validation) Check ¶
func (v *Validation) Check(ok bool, key, message string)
func (*Validation) Equals ¶
func (v *Validation) Equals(eq bool, field, verified string)
func (*Validation) EscapeHTML ¶
func (v *Validation) EscapeHTML(value string) string
EscapeHTML completely escapes all HTML special characters. Use this when you want to display user input as literal text, preserving characters like < > & as visible text rather than HTML entities.
func (*Validation) IsAlphanumeric ¶
func (v *Validation) IsAlphanumeric(field, value string)
IsAlphanumeric validates that a field contains only letters and numbers
func (*Validation) IsDateISO ¶
func (v *Validation) IsDateISO(field, value string)
func (*Validation) IsEmail ¶
func (v *Validation) IsEmail(field, value string)
func (*Validation) IsFloat ¶
func (v *Validation) IsFloat(field, value string)
func (*Validation) IsInt ¶
func (v *Validation) IsInt(field, value string)
func (*Validation) IsString ¶
func (v *Validation) IsString(field, value string)
func (*Validation) IsURL ¶
func (v *Validation) IsURL(field, value string)
IsURL validates that a field contains a valid URL
func (*Validation) MaxLength ¶
func (v *Validation) MaxLength(field, value string, maxLength int)
MaxLength validates that a field doesn't exceed a maximum length
func (*Validation) MinLength ¶
func (v *Validation) MinLength(field, value string, minLength int)
MinLength validates that a field meets a minimum length requirement
func (*Validation) NoSpaces ¶
func (v *Validation) NoSpaces(field, value string)
func (*Validation) SanitizeHTML ¶
func (v *Validation) SanitizeHTML(value string) string
SanitizeHTML removes ALL HTML tags from input using bluemonday's strict policy. Use this for input that should be displayed as plain text.
func (*Validation) SanitizeRichText ¶
func (v *Validation) SanitizeRichText(value string) string
SanitizeRichText allows safe HTML formatting (bold, italic, links, etc.) while removing dangerous elements like scripts, iframes, and event handlers. Use this for user-generated content like blog posts, comments, or rich text editors.
func (*Validation) Valid ¶
func (v *Validation) Valid() bool
type ValidationError ¶
ValidationError represents a validation error with field-specific messages
func NewValidationError ¶
func NewValidationError(fields map[string]string) *ValidationError
NewValidationError creates a new validation error with field messages
func (*ValidationError) AddField ¶
func (e *ValidationError) AddField(field, message string) *ValidationError
AddField adds a field error and returns the error for chaining
func (*ValidationError) HasErrors ¶
func (e *ValidationError) HasErrors() bool
HasErrors returns true if there are any validation errors
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package admin is a model-driven CRUD panel: register a struct, get a working list and edit screen.
|
Package admin is a model-driven CRUD panel: register a struct, get a working list and edit screen. |
|
Package auth is authentication as a library: verbs and interfaces, no server, no tables of its own.
|
Package auth is authentication as a library: verbs and interfaces, no server, no tables of its own. |
|
cmd
|
|
|
tjo
command
|
|
|
Package config provides structured configuration with validation for Tjo applications.
|
Package config provides structured configuration with validation for Tjo applications. |
|
Package core provides minimal utilities for CLI tools and code generation.
|
Package core provides minimal utilities for CLI tools and code generation. |
|
Package docs embeds this framework's documentation so a tool can serve the version that is actually installed.
|
Package docs embeds this framework's documentation so a tool can serve the version that is actually installed. |
|
email
module
|
|
|
Command evals measures whether a coding agent produces a working Tjo application, using the compiler as the grader.
|
Command evals measures whether a coding agent produces a working Tjo application, using the compiler as the grader. |
|
Package i18n makes an application translatable, and this framework with it.
|
Package i18n makes an application translatable, and this framework with it. |
|
internal
|
|
|
jsonstrict
Package jsonstrict adds the strictness encoding/json does not provide.
|
Package jsonstrict adds the strictness encoding/json does not provide. |
|
llm
module
|
|
|
Package ops is a self-hosted operations dashboard: errors, slow requests, slow queries, the job queue, cron and health, on a page you run yourself.
|
Package ops is a self-hosted operations dashboard: errors, slow requests, slow queries, the job queue, cron and health, on a page you run yourself. |
|
otel
module
|
|
|
sms
module
|
|
|
Package sse writes Server-Sent Events.
|
Package sse writes Server-Sent Events. |
|
websocket
module
|
