$ git clone https://github.com/angvp/tango $ cd tango/examples/board $ go run .
Board
The bulletin board built in the tutorial, at its final (part 10) state: JSON API with bearer tokens and rate limits, server-rendered pages with accounts and CSRF-safe forms, the HTML admin, a live front page over WebSockets, a session-pruning job, structured logs, and graceful shutdown.
The code follows the tutorial text; when one changes, change the other.
Admin changelist filters
The HTML admin shows the boolean and foreign key filters from the admin guide's changelist filters section: Accounts filter by Active, Posts by their author, and Comments by their post. TestAdminChangelistFilters in admin_filters_test.go exercises them over HTTP on whichever database TANGO_TEST_DSN selects.
Run locally
echo"BOARD_JWT_SECRET=$(openssl rand -hex 32)" >> .env
go run . -migrate
go run . -tango-admin-create=admin
go run .
go run ./shell -c 'Models()'go test ./...
TANGO_DB_DSN picks the database (sqlite://app.db by default, or postgres://…) and TANGO_ADDR the listen address (:8000). See part 10 for the full configuration table.
The Dockerfile is the one from part 10. In this repository the module resolves github.com/angvp/tango through a local replace => ../.., so build the image from a copy of the project that depends on a published tanGO version.
examples/board/main.go
packagemainimport("context""database/sql""fmt""log/slog""os""os/signal""syscall""time""github.com/angvp/tango""github.com/angvp/tango/admin""github.com/angvp/tango/db""board/migrations""board/project"_"github.com/jackc/pgx/v5/stdlib"_"modernc.org/sqlite")funcmain(){iferr:=run();err!=nil{fmt.Fprintln(os.Stderr,err)os.Exit(1)}}funcrun()error{// One structured JSON logger for the whole app. slog.SetDefault makes// it the logger for tanGO's middleware and for slog calls in your code.logger:=slog.New(slog.NewJSONHandler(os.Stdout,nil))slog.SetDefault(logger)iferr:=tango.LoadEnvFile(".env");err!=nil{returnerr}dsn,err:=tango.LoadDBConfigFromEnv()iferr!=nil{returnerr}sqlDB,err:=sql.Open(dsn.Driver,dsn.Source)iferr!=nil{returnerr}defersqlDB.Close()store:=db.NewStore(sqlDB,dsn.Dialect)config:=project.Config(store)ifhandled,err:=admin.HandleCLI(context.Background(),store,os.Args[1:],os.Stdin,os.Stdout,os.Stderr);handled||err!=nil{returnerr}handled,err:=tango.DispatchFlags(config,sqlDB,dsn.Dialect,migrations.Migrations)ifhandled||err!=nil{returnerr}// Ctrl-C or SIGTERM (what `docker stop` and most process managers send)// cancels ctx, and ServeContext shuts down gracefully.ctx,stop:=signal.NotifyContext(context.Background(),os.Interrupt,syscall.SIGTERM)deferstop()logger.Info("listening","addr",config.Addr)err=tango.ServeContext(ctx,config,sqlDB,dsn.Dialect,tango.WithLogger(logger),tango.WithShutdownTimeout(10*time.Second),)logger.Info("stopped","clean",err==nil)returnerr}