blueprint

package module
v0.5.2 Latest Latest
Warning

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

Go to latest
Published: Jul 14, 2025 License: Apache-2.0 Imports: 10 Imported by: 0

README

blueprint

go-version Build Status Go Report Card


Documentation: https://oddbit-project.github.io/blueprint/


Go application framework for building web applications and microservices with built-in support for:

  • Container-based application lifecycle management
  • Configuration management
  • Structured logging with file rotation
  • HTTP server with comprehensive security features:
    • TLS support with configurable cipher suites
    • CSRF protection with session-based tokens
    • Security headers (CSP, HSTS, XSS protection, etc.)
    • Rate limiting with per-IP and per-endpoint controls
    • Device fingerprinting for security monitoring
    • Cookie-based session management with multiple backends
  • Authentication providers:
    • JWT authentication with symmetric/asymmetric key support
    • Token revocation system
    • HTPasswd file-based authentication
  • Database connectivity (PostgreSQL, ClickHouse)
  • Message queue integration (Kafka, MQTT)
  • Metrics endpoint for Prometheus monitoring
  • Middleware system with request helpers and utilities

Application example

Applications are executed in a runtime context, instantiated from blueprint.Container. This container holds the main run cycle, and manages the startup/shutdown cycle of the application.

It is possible to register global destructor functions that will be called back in sequence when the application is terminated. To achieve this, any ordered application exit must use blueprint.Shutdown():

(...)
if err := doSomething(); err != nil {
	// perform application shutdown
	blueprint.Shutdown(err)
	os.exit(-1)
}

Simple API application example using blueprint.Container with JWT authentication and security middleware:

package main

import (
 "flag"
 "fmt"
 "github.com/gin-gonic/gin"
 "github.com/oddbit-project/blueprint"
 "github.com/oddbit-project/blueprint/config/provider"
 "github.com/oddbit-project/blueprint/log"
 "github.com/oddbit-project/blueprint/provider/httpserver"
 "github.com/oddbit-project/blueprint/provider/httpserver/auth"
 "github.com/oddbit-project/blueprint/provider/jwtprovider"
 "os"
)

const (
 VERSION = "9.9.0"
)

// CliArgs Command-line options
type CliArgs struct {
 ConfigFile  *string
 ShowVersion *bool
}

// Application sample application container
type Application struct {
 container   *blueprint.Container      // runnable application container
 args        *CliArgs                  // cli args
 httpServer  *httpserver.Server        // our api server
 logger      *log.Logger
 jwtProvider jwtprovider.JWTProvider   // JWT authentication provider
}

// command-line args
var cliArgs = &CliArgs{
 ConfigFile:  flag.String("c", "config/sample.json", "Config file"),
 ShowVersion: flag.Bool("version", false, "Show version"),
}

// NewApplication Sample application factory
func NewApplication(args *CliArgs, logger *log.Logger) (*Application, error) {
 cfg, err := provider.NewJsonProvider(*args.ConfigFile)
 if err != nil {
  return nil, err
 }
 if logger == nil {
  logger = log.New("application")
 }
 return &Application{
  container:   blueprint.NewContainer(cfg),
  args:        args,
  httpServer:  nil,
  logger:      logger,
  jwtProvider: nil,
 }, nil
}

func (a *Application) Build() {
 // assemble internal dependencies of application
 // if some error occurs, generate fatal error & abort execution

 // initialize http server
 a.logger.Info("Building Sample Application...")

 // initialize JWT provider
 jwtConfig := jwtprovider.NewJWTConfig()
 if err := a.container.Config.GetKey("jwt", jwtConfig); err != nil {
  a.container.AbortFatal(err)
 }
 var err error
 a.jwtProvider, err = jwtprovider.NewProvider(jwtConfig)
 a.container.AbortFatal(err)

 // initialize http server config
 httpConfig := httpserver.NewServerConfig()
 // fill parameters from config provider
 if err := a.container.Config.GetKey("server", httpConfig); err != nil {
  a.container.AbortFatal(err)
 }
 // Create http server from config
 a.httpServer, err = httpConfig.NewServer(a.logger)
 a.container.AbortFatal(err)

 // Apply security middleware
 a.httpServer.UseDefaultSecurityHeaders()
 a.httpServer.UseCSRFProtection()
 a.httpServer.UseRateLimiting(60) // 60 requests per minute

 // Create JWT auth middleware
 jwtAuth := auth.NewAuthJWT(a.jwtProvider)

 // Add protected routes with JWT authentication
 v1 := a.httpServer.Route().Group("/v1")
 v1.Use(auth.AuthMiddleware(jwtAuth))
 
 // endpoint: /v1/hello (protected)
 v1.GET("/hello", func(ctx *gin.Context) {
  claims, _ := auth.GetClaims(ctx)
  ctx.JSON(200, gin.H{
   "message": "Hello World",
   "user":    claims.Subject,
   "data":    claims.Data,
  })
 })

 // Public login endpoint
 a.httpServer.Route().POST("/login", func(ctx *gin.Context) {
  // Basic authentication logic (replace with your auth system)
  var loginData struct {
   Username string `json:"username"`
   Password string `json:"password"`
  }
  
  if err := ctx.ShouldBindJSON(&loginData); err != nil {
   ctx.JSON(400, gin.H{"error": "Invalid request"})
   return
  }
  
  // Validate credentials (implement your validation logic)
  if loginData.Username == "demo" && loginData.Password == "password" {
   token, err := a.jwtProvider.GenerateToken("demo", map[string]interface{}{
    "role": "user",
   })
   if err != nil {
    ctx.JSON(500, gin.H{"error": "Token generation failed"})
    return
   }
   ctx.JSON(200, gin.H{"token": token})
  } else {
   ctx.JSON(401, gin.H{"error": "Invalid credentials"})
  }
 })
}

func (a *Application) Run() {
 // register http destructor callback
 blueprint.RegisterDestructor(func() error {
  return a.httpServer.Shutdown(a.container.GetContext())
 })

 // Start  application - http server
 a.container.Run(func(app interface{}) error {
  go func() {
   a.logger.Infof("Running Sample Application API at https://%s:%d", a.httpServer.Config.Host, a.httpServer.Config.Port)
   a.logger.Infof("Login: POST /login (username: demo, password: password)")
   a.logger.Infof("Protected: GET /v1/hello (requires JWT token)")
   a.container.AbortFatal(a.httpServer.Start())
  }()
  return nil
 })
}

func main() {
 // config logger
 log.Configure(log.NewDefaultConfig())
 logger := log.New("sample-application")

 flag.Parse()

 if *cliArgs.ShowVersion {
  fmt.Printf("Version: %s\n", VERSION)
  os.Exit(0)
 }

 app, err := NewApplication(cliArgs, logger)
 if err != nil {
  logger.Error(err, "Initialization failed")
  os.Exit(-1)
 }

 // build application
 app.Build()
 // execute application
 app.Run()
}

Example config file sample.json:

{
  "server": {
    "host": "localhost",
    "port": 8080,
    "readTimeout": 30,
    "writeTimeout": 30,
    "debug": true,
    "tlsEnable": false,
    "tlsCert": "",
    "tlsKey": "",
    "options": {
      "authTokenSecret": "your-api-secret-key-here"
    }
  },
  "jwt": {
    "signingKey": {
      "password": "your-jwt-secret-key-change-in-production"
    },
    "signingAlgorithm": "HS256",
    "expirationSeconds": 3600,
    "issuer": "sample-application",
    "audience": "api-users",
    "keyID": "default",
    "requireIssuer": true,
    "requireAudience": true,
    "trackUserTokens": false,
    "maxUserSessions": 0,
    "maxTokenSize": 4096
  }
}

License information

All the custom code is licensed under Apache2 license. Some code pieces were copied or imported from different sources, and have their own licensing info. All the adapted files contain a full copy of their respective license, and all the adaptations are licensed under the original license of the source.

The repository includes code adapted from the following sources:

Argon2Id password hashing (c) Alex Edwards, MIT License

Telegraf TLS plugin (c) InfluxData Inc, MIT License

Threadsafe package (c) Ravishanker Kusuma, MIT License

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetDestructorManager

func GetDestructorManager() *callstack.CallStack

GetDestructorManager Retrieve callback manager

func RegisterDestructor

func RegisterDestructor(fn callstack.CallableFn)

RegisterDestructor Register a function to perform shutdown procedures

func Shutdown

func Shutdown(arg error)

Shutdown Shuts down the whole application

Types

type Container

type Container struct {
	Config    config.ConfigInterface
	Context   context.Context
	CancelCtx context.CancelFunc
	// contains filtered or unexported fields
}

func NewContainer

func NewContainer(config config.ConfigInterface) *Container

NewContainer create new container runtime with the specified config provider and a new application context

func (*Container) AbortFatal

func (c *Container) AbortFatal(err error)

AbortFatal aborts execution in case of fatal error

func (*Container) Exists added in v0.3.0

func (c *Container) Exists(name string) bool

Exists check if a service is registered

func (*Container) Get added in v0.3.0

func (c *Container) Get(name string) (interface{}, bool)

Get retrieve a service by name

func (*Container) GetContext

func (c *Container) GetContext() context.Context

GetContext helper function to retrieve context

func (*Container) Register added in v0.3.0

func (c *Container) Register(name string, service interface{})

Register a service by name

func (*Container) Run

func (c *Container) Run(mainFn ...RuntimeFn)

Run runs application container mainFn is a collection of non-blocking functions; they will be executed in order. each one will receive the Container object as the parameter: Example:

object.Run(func(app interface{}) error{
		app := a.(*Container)
		app.AbortFatal(nil) // won't abort because arg is nil
		return nil
})

the main loop will wait for an os signal on the 'monitor' channel; when signal is received, the application is terminated in an orderly fashion by invoking Terminate()

func (*Container) Terminate

func (c *Container) Terminate(err error)

Terminate application execution and exit to operating system

type RuntimeFn

type RuntimeFn func(app interface{}) error

Directories

Path Synopsis
crypt
db
qb
log
provider
clickhouse
The code on this file is mostly imported from the clickhouse-go project in https://github.com/ClickHouse/clickhouse-go/blob/main/struct_map.go; The original license is maintained
The code on this file is mostly imported from the clickhouse-go project in https://github.com/ClickHouse/clickhouse-go/blob/main/struct_map.go; The original license is maintained
kv
pgsql
AdvisoryLock implements primitives to use PostgreSQL's advisory locks.
AdvisoryLock implements primitives to use PostgreSQL's advisory locks.
tls
etcd module
franz module
s3 module
smtp module
samples
application command
ch-migrations command
clickhouse command
console command
dbgrid command
filelogging command
htpasswd command
http-csrf command
httpserver command
kafka command
metrics command
mqtt command
nats command
nextjs-api-demo command
pgsql command
session command
types
threadsafe
* imported from https://github.com/hayageek/threadsafe * (c) Copyright (c) 2024 Ravishanker Kusuma - MIT License * See LICENSE for more details
* imported from https://github.com/hayageek/threadsafe * (c) Copyright (c) 2024 Ravishanker Kusuma - MIT License * See LICENSE for more details
env
fs
str

Jump to

Keyboard shortcuts

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