tjo

package module
v0.13.0 Latest Latest
Warning

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

Go to latest
Published: Aug 7, 2026 License: MIT Imports: 61 Imported by: 0

README

Tjo

CI OpenSSF Scorecard

alt tjo

Tjo is a modern, full-featured web application framework for Go that provides everything you need to build scalable web applications quickly and securely.

Installation

Download a prebuilt CLI for macOS, Linux or Windows from the latest release, or build from source:

go install github.com/jimmitjoo/tjo/cmd/tjo@latest

macOS binaries are unsigned, so Gatekeeper will ask before the first run.

Requirements

  • Go 1.25+ (to build applications; not needed to run the CLI)

Features

  • Chi Router - Fast and lightweight HTTP router
  • Multi-Database Support - PostgreSQL, MySQL, MariaDB, SQLite (PostgreSQL needs WithDialect, see query builder docs)
  • Internationalisation - CLDR plurals, locale negotiation, right-to-left, and the framework's own strings translatable (docs)
  • Admin Panel - Model-driven CRUD over your own structs, server-rendered, no build step (docs)
  • Ops Dashboard - Self-hosted errors, slow queries, job queue, cron and health (docs)
  • Authentication - Passwords, 2FA with recovery codes, remember-me, passkeys, organizations and roles, as a package rather than generated code
  • Security First - CSRF protection, rate limiting, input validation, XSS prevention, 2FA
  • Email System - Multiple provider support with templates
  • Caching - Redis and Badger cache implementations
  • Background Jobs - Database-backed queue with cron scheduler and durable, checkpointed workflows
  • WebSocket Support - Real-time communication with hub pattern
  • Server-Sent Events - Streaming, and broadcast of rendered fragments to subscribed clients
  • File Storage - S3 and MinIO filesystem integrations
  • SMS Integration - Multiple SMS provider support
  • Template Engine - Jet template engine for dynamic views
  • Logging & Metrics - Structured logging with health monitoring
  • OpenTelemetry - Distributed tracing and observability
  • LLM Integration - Chat, tools, structured output and embeddings over the first-party SDKs (docs)
  • Vector Search - pgvector and sqlite-vec in the query builder
  • Session Management - Secure session handling with multiple stores
  • CLI Tools - Project scaffolding and code generation
  • AI-Native Development - MCP server for AI assistants
Building the CLI from a checkout
git clone https://github.com/jimmitjoo/tjo
cd tjo
make build

This creates the tjo executable in dist/tjo. Add it to your PATH for global access.

Go Install
go install github.com/jimmitjoo/tjo/cmd/tjo@latest

Quick Start

Create a New Project
tjo new myapp
cd myapp
tjo run
Starter Templates

Tjo includes starter templates for common use cases:

tjo new myapp                      # Default template
tjo new myapp -t blog              # Blog starter
tjo new myapp -t api               # API-only starter
tjo new myapp -t saas              # SaaS starter with billing
Template Description
default Basic web application with authentication
blog Blog with posts, categories, and comments
api REST API with versioning and JWT auth
saas SaaS with Stripe billing and subscriptions
Running Your Application
tjo run              # Start the application
tjo run --watch      # Hot-reload during development (requires air)
tjo run -w           # Short form
Project Structure
myapp/
├── .env                 # Environment configuration
├── Makefile             # Build and development commands
├── handlers/            # HTTP handlers
├── migrations/          # Database migrations
├── views/               # Template files
├── email/               # Email templates
├── data/                # Models and database logic
├── public/              # Static assets
├── middleware/          # Custom middleware
└── logs/                # Application logs

CLI Commands

tjo new <name>           # Create new project
tjo new <name> -t <tpl>  # Create with starter template
tjo run                  # Run application
tjo run -w               # Run with hot-reload
tjo migrate              # Run migrations up
tjo migrate down         # Rollback last migration
tjo migrate reset        # Reset all migrations
tjo make model <name>    # Create model
tjo make handler <name>  # Create handler
tjo make migration <name># Create migration
tjo make mail <name>     # Create email template
tjo make auth            # Setup authentication
tjo make session         # Create session tables
tjo mcp                  # Start MCP server

AI-Native Development (MCP)

Tjo includes an MCP server for AI assistants like Claude Code and Cursor.

Setup

Add to your MCP config:

{
  "mcpServers": {
    "tjo": {
      "command": "tjo",
      "args": ["mcp"]
    }
  }
}
Available Tools
Tool Description
tjo_create_project Create a new Tjo project
tjo_create_model Create a database model
tjo_create_handler Create an HTTP handler
tjo_create_migration Create a database migration
tjo_create_middleware Create middleware
tjo_create_mail Create email template
tjo_run_migrations Run pending migrations
tjo_rollback Rollback migrations
tjo_setup_auth Setup auth with 2FA
tjo_create_session_table Create session table
tjo_setup_docker Generate Docker config
tjo_module_info Get module setup instructions
Usage

Just ask your AI assistant:

  • "Create a User model with name and email"
  • "Add a migration to create a posts table"
  • "Create a handler for managing products"
  • "Setup authentication for my app"

Opt-in Modules

Import only what you need:

import (
    "github.com/jimmitjoo/tjo"
    "github.com/jimmitjoo/tjo/sms"
    "github.com/jimmitjoo/tjo/email"
    "github.com/jimmitjoo/tjo/websocket"
    "github.com/jimmitjoo/tjo/otel"
)

func main() {
    app := tjo.Tjo{}
    app.New(rootPath,
        sms.NewModule(),
        email.NewModule(),
        websocket.NewModule(),
        otel.NewModule(
            otel.WithServiceName("my-app"),
            otel.WithOTLPExporter("localhost:4317", true),
        ),
    )
}
Module Configuration
// SMS with Twilio
sms.NewModule(sms.WithTwilio(accountSid, apiKey, apiSecret, fromNumber))

// Email with SMTP
email.NewModule(
    email.WithSMTP("smtp.example.com", 587, "user", "pass", "tls"),
    email.WithFrom("noreply@example.com", "My App"),
)

// WebSocket with auth
websocket.NewModule(
    websocket.WithAllowedOrigins([]string{"https://example.com"}),
    websocket.WithAuthenticateConnection(myAuthFunc),
)

Configuration

Configuration via .env file:

# Application
APP_NAME=MyApp
DEBUG=true
PORT=4000

# Database
DATABASE_TYPE=postgres
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_NAME=myapp
DATABASE_USER=postgres
DATABASE_PASS=password

# Cache
CACHE=redis
REDIS_HOST=localhost:6379

# Session
SESSION_TYPE=redis
SESSION_LIFETIME=24

# OpenTelemetry (optional)
OTEL_ENABLED=false
OTEL_SERVICE_NAME=my-app
OTEL_ENDPOINT=localhost:4317

Security

  • Cross-origin request protection (net/http.CrossOriginProtection) and CSRF tokens
  • Rate limiting and throttling, with trusted-proxy-aware client IP resolution
  • Input validation and sanitization
  • SQL injection prevention
  • XSS protection
  • Secure password hashing (bcrypt)
  • Two-Factor Authentication (TOTP)

To report a vulnerability, see SECURITY.md. Reports go through GitHub private vulnerability reporting, not public issues.

Four advisories have been published and fixed, all explained in the changelog with what was measured rather than only what changed. Generated code is in scope: a flaw in what tjo make auth produces is a flaw in the framework.

govulncheck gates every build across all five modules, weekly as well as on every change. Run it yourself:

make vuln

Authentication

The auth package works with net/http and your own storage. It declares interfaces and provides verbs; it never owns a table, so an application that needs to join on users has one users table rather than two.

// Login. The lookup and the comparison happen unconditionally, in that order,
// so an unknown address costs the same as a known one.
account, err := auth.Authenticate(ctx, store, email, password)

// Password reset. Single-use, database-persisted, bound to a user, and
// consumed atomically.
token, _ := auth.NewResetToken(userID, auth.PurposePasswordReset, time.Hour)
resetStore.Save(ctx, token)          // mail token.PlainText; only the hash is stored

userID, hash, err := auth.ResetPassword(ctx, resetStore, policy, submitted, newPassword)

// Organizations, which is where multi-tenancy lives.
err = auth.Authorize(ctx, orgs, perms, orgID, accountID, auth.PermManageMembers)
qb, err := auth.ScopeTo(ctx, database.NewQueryBuilder(db).Table("invoices"), "organization_id")

SQLResetStore ships for PostgreSQL, MySQL and SQLite because token consumption has to be atomic, and a SELECT-then-UPDATE implementation loses that race with somebody else's account as the prize.

Agent evaluation

Can a coding agent build a working Tjo application? evals/ measures it with the compiler as the grader — a task passes if the project builds, vets and passes its own tests.

make build
go run ./evals -cli $(pwd)/dist/tjo              # deterministic baseline
go run ./evals -cli $(pwd)/dist/tjo -agent '...' # with a model in the loop

The deterministic baseline is 5/5. No generative number has been published yet; see evals/README.md for why one without a named model, prompt and date is worse than none.

Testing

make test              # Run all tests
make vuln              # Known vulnerabilities reachable from our code
./run-tests -p ./pkg   # Test specific package
./run-tests -c         # With coverage
./run-tests -s         # Skip Docker tests
make cover             # Coverage report

Some tests need a service and skip without one:

# PostgreSQL integration tests (database/postgres_integration_test.go)
docker run -d --name tjo-pg -e POSTGRES_PASSWORD=secret -e POSTGRES_USER=tjo \
  -e POSTGRES_DB=tjotest -p 5432:5432 postgres:16-alpine
TJO_TEST_POSTGRES_DSN='postgres://tjo:secret@localhost:5432/tjotest?sslmode=disable' go test ./database/...

Documentation

Contributing

Pull requests welcome at github.com/jimmitjoo/tjo.

License

MIT License

For coding agents

  • AGENTS.md — how to work in this repository, and the traps that have shipped defects here

  • llms.txt — a short orientation, leading with what models get wrong about this framework

  • llms-full.txt — the same with signatures and worked examples

  • skills/tjo — an Agent Skills bundle, installable as a Claude Code plugin:

    /plugin marketplace add jimmitjoo/tjo
    /plugin install tjo@tjo
    
  • tjo mcp — a Model Context Protocol server over stdio: the generators, plus introspection over your application's routes, schema and configuration, plus the documentation of the version you have installed.

Whether any of this changes what an agent produces is a measurable question and evals/README.md says how it is measured, including that the number has not been recorded yet.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotFoundError is returned when a resource is not found
	ErrNotFoundError = NewError("", "resource not found", ErrNotFound)
	// ErrUnauthorizedError is returned when authentication fails
	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

View Source
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

func GetHTTPStatus(err error) int

GetHTTPStatus returns the appropriate HTTP status for an error

func IsCode

func IsCode(err error, code ErrorCode) bool

IsCode checks if an error is a TjoError with a specific code

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

func ValidateEncryptionKey(key []byte) error

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

func NewDataService

func NewDataService() *DataService

NewDataService creates a new data service

type Database

type Database struct {
	DataType    string
	Pool        *sql.DB
	TablePrefix string
}

Database represents a database connection with metadata

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 represents authentication/authorization failures
	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 GetCode

func GetCode(err error) ErrorCode

GetCode returns the ErrorCode from an error, or ErrInternal if not a TjoError

func (ErrorCode) HTTPStatus

func (c ErrorCode) HTTPStatus() int

HTTPStatus returns the appropriate HTTP status code for an ErrorCode

func (ErrorCode) String

func (c ErrorCode) String() string

String returns the string representation of 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 Server

type Server struct {
	ServerName string
	Port       string
	Secure     bool
	URL        string
}

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

func (g *Tjo) AddMonitoringRoutes(mux *chi.Mux)

AddMonitoringRoutes adds health and metrics endpoints. Call this in your routes() function AFTER adding your middleware.

func (*Tjo) BuildDSN

func (g *Tjo) BuildDSN() string

BuildDSN returns the database connection string. Deprecated: Use g.Config.Database.DSN(g.RootPath) instead.

func (*Tjo) CSRF added in v0.9.0

func (g *Tjo) CSRF(next http.Handler) http.Handler

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 (g Tjo) CreateDirIfNotExists(path string) error

func (Tjo) CreateFileIfNotExists

func (g Tjo) CreateFileIfNotExists(path string) error

func (*Tjo) CrossOriginProtection added in v0.8.0

func (g *Tjo) CrossOriginProtection(next http.Handler) http.Handler

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 (g *Tjo) DownloadFile(w http.ResponseWriter, r *http.Request, pathToFile, filename string) error

func (*Tjo) Error404

func (g *Tjo) Error404(w http.ResponseWriter, r *http.Request)

func (*Tjo) Error500

func (g *Tjo) Error500(w http.ResponseWriter, r *http.Request)

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

func (g *Tjo) GetModule(name string) Module

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) HasModule

func (g *Tjo) HasModule(name string) bool

HasModule checks if a module is registered.

func (*Tjo) Init

func (g *Tjo) Init(p initPaths) error

func (*Tjo) ListenAndServe

func (g *Tjo) ListenAndServe() error

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) LoadTime

func (g *Tjo) LoadTime(start time.Time)

func (*Tjo) MigrateDownAll

func (g *Tjo) MigrateDownAll(dsn string) error

func (*Tjo) MigrateForce

func (g *Tjo) MigrateForce(dsn string) error

func (*Tjo) MigrateUp

func (g *Tjo) MigrateUp(dsn string) error

func (*Tjo) New

func (g *Tjo) New(rootPath string, modules ...Module) error

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) NoSurf deprecated

func (g *Tjo) NoSurf(next http.Handler) http.Handler

NoSurf is retained as an alias for CSRF so applications that mounted it directly keep working. It no longer uses nosurf; see csrf.go.

Deprecated: use CSRF.

func (*Tjo) OpenDB

func (g *Tjo) OpenDB(dbType, dsn string) (*sql.DB, error)

func (Tjo) RandomString

func (g Tjo) RandomString(length int) string

func (*Tjo) ReadJson

func (g *Tjo) ReadJson(w http.ResponseWriter, r *http.Request, data interface{}) error

func (*Tjo) SessionLoad

func (g *Tjo) SessionLoad(next http.Handler) http.Handler

func (*Tjo) Shutdown

func (g *Tjo) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the application and all its components. It stops background services, modules (in reverse order), and closes connections.

func (*Tjo) Steps

func (g *Tjo) Steps(steps int, dsn string) error

func (*Tjo) Validator

func (g *Tjo) Validator(data url.Values) *Validation

func (*Tjo) ValidatorFor added in v0.13.0

func (g *Tjo) ValidatorFor(ctx context.Context, data url.Values) *Validation

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.

func (*Tjo) WriteJson

func (g *Tjo) WriteJson(w http.ResponseWriter, status int, data interface{}, headers ...http.Header) error

func (*Tjo) WriteXML

func (g *Tjo) WriteXML(w http.ResponseWriter, status int, data interface{}, headers ...http.Header) error

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 NewError

func NewError(op string, message string, code ErrorCode) *TjoError

NewError creates a new TjoError with a message

func WrapError

func WrapError(op string, err error, code ErrorCode) *TjoError

WrapError wraps an error with operation context and classification. Use this when catching errors from lower-level operations.

func (*TjoError) Error

func (e *TjoError) Error() string

Error implements the error interface

func (*TjoError) Unwrap

func (e *TjoError) Unwrap() error

Unwrap returns the underlying error for errors.Is and errors.As

func (*TjoError) WithContext

func (e *TjoError) WithContext(key string, value interface{}) *TjoError

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) Has

func (v *Validation) Has(field string, r *http.Request) bool

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) Required

func (v *Validation) Required(r *http.Request, fields ...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

type ValidationError struct {
	TjoError
	Fields map[string]string
}

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

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

Jump to

Keyboard shortcuts

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