Skip to content

Repository files navigation

Cetus

English | 中文 | 日本語 | 한국어

A Go toolkit for building HTTP services with Gin. Provides configuration management, database access (MySQL/PostgreSQL), JWT authentication, structured logging, middleware, and standardized API responses out of the box.

Looking for a working example? Check out cetus-demo — a complete REST API with user registration, JWT auth, and CRUD, built step by step.

Features

  • Configuration - Environment-based config with .env files, singleton pattern
  • Database - MySQL & PostgreSQL via GORM, auto driver selection
  • JWT Authentication - RSA-signed tokens with Redis-backed session management
  • Logging - Structured logging with Zap (JSON/console, file/stdout)
  • Middleware - Request ID tracking, rate limiting
  • API Responses - Standardized JSON response helpers for Gin
  • Utilities - Bcrypt hashing, ID obfuscation (Optimus), RSA crypto, i18n, file operations

Installation

go get github.com/JackDPro/cetus

Quick Start

1. Create .env file

Copy the example and modify values for your environment:

cp .env.example .env

Key environment variables:

APP_NAME=my-appAPP_ENV=devLOG_CONSOLE_OUT=trueLOG_FILE_OUT=falseLOG_LEVEL=debugLOG_FORMAT=jsonDB_TYPE=mysqlDB_HOST=127.0.0.1DB_PORT=3306DB_DATABASE=mydbDB_USERNAME=rootDB_PASSWORD=passwordREDIS_HOST=127.0.0.1REDIS_PORT=6379REDIS_DATABASE=0REDIS_PASSWORD=passwordSERVER_HTTP_PORT=80SERVER_GRPC_PORT=50051

See .env.example for all available options.

2. Create your server

package main
import (
"fmt""github.com/JackDPro/cetus/config""github.com/JackDPro/cetus/controller""github.com/JackDPro/cetus/middleware""github.com/JackDPro/cetus/provider""github.com/gin-contrib/cors""github.com/gin-gonic/gin"
)
funcmain() {
router:=gin.New()
router.Use(gin.Recovery())
// CORScorsConf:=cors.DefaultConfig()
corsConf.AllowAllOrigins=truecorsConf.AllowHeaders= []string{"Authorization", "Accept-Language"}
router.Use(cors.New(corsConf))
// Request ID middlewarerouter.Use(middleware.RequestId())
// Health check endpointprobeCtr:=controller.NewProbeController()
router.GET("/probe", probeCtr.Show)
// Your routes here// router.GET("/users", userController.Index)conf:=config.GetApiConfig()
addr:=fmt.Sprintf("0.0.0.0:%d", conf.HttpPort)
provider.GetLogger().Info("server starting", "address", addr)
iferr:=router.Run(addr); err!=nil {
panic(err)
}
}

Package Guide

config - Configuration Management

All configuration is loaded from environment variables (via .env file). Each config struct uses the singleton pattern.

import"github.com/JackDPro/cetus/config"appConf:=config.GetAppConfig() // App name, env, data root, public URLdbConf:=config.GetDatabaseConfig() // DB type, host, port, credentialsapiConf:=config.GetApiConfig() // HTTP and gRPC portsauthConf:=config.GetAuthConf() // JWT cert/key paths, expirationredisConf:=config.GetRedisConfig() // Redis connection infologConf:=config.GetLogConfig() // Log level, format, output targetshashConf:=config.GetHashIdConfig() // Optimus ID hashing paramsnatsConf:=config.GetNatsConf() // NATS messaging config

Use APP_ENV to control behavior:

ifconfig.GetAppConfig().Env=="prod" {
gin.SetMode(gin.ReleaseMode)
}

provider - Core Services

Thread-safe singleton providers for common infrastructure.

Logger

Structured logging powered by Zap. Supports console/JSON format, file/stdout output.

import"github.com/JackDPro/cetus/provider"logger:=provider.GetLogger()
logger.Info("server started")
logger.Infow("user created", "userId", 123)
logger.Errorw("request failed", "error", err)

Database (ORM)

GORM wrapper with automatic MySQL/PostgreSQL driver selection based on DB_TYPE.

import"github.com/JackDPro/cetus/provider"db:=provider.GetOrm().Db// Use standard GORM operationsvarusers []Userdb.Where("active = ?", true).Find(&users)
// Auto-migratedb.AutoMigrate(&User{}, &Post{})

Redis

import"github.com/JackDPro/cetus/provider"rdb:=provider.GetRedisClient()
rdb.Set(ctx, "key", "value", time.Hour)
val, err:=rdb.Get(ctx, "key").Result()

Password Hashing (Bcrypt)

import"github.com/JackDPro/cetus/provider"// Hash a passwordhashed, err:=provider.HashMake("my-password")
// Verifyerr=provider.HashCheck("my-password", hashed)
// Check if re-hash is neededifprovider.HashNeedRefresh(hashed) {
// re-hash with current cost
}

ID Obfuscation (Optimus)

Reversible integer ID encoding to hide sequential database IDs in APIs.

import"github.com/JackDPro/cetus/provider"encoded:=provider.Hash().Encode(42) // e.g. 1580030173decoded:=provider.Hash().Decode(1580030173) // 42

Requires OPTIMUS_PRIME, OPTIMUS_INVERSE, OPTIMUS_RANDOM in .env.

How to get these values:

  1. Visit http://primes.utm.edu/lists/small/millions/, download any .txt file, open it and randomly pick a prime number less than 2,147,483,647 — this will be your OPTIMUS_PRIME.
  2. Use the generator in cetus-demo to calculate the other two values:
go run storage/optimus_gen.go 104393867
# Output:# OPTIMUS_PRIME=104393867# OPTIMUS_INVERSE=1990279033# OPTIMUS_RANDOM=1333095938

Important: Once deployed to production, never change these values — all existing encoded IDs will become invalid.

RSA Cryptography

import"github.com/JackDPro/cetus/provider"// Load from filersaKey, err:=provider.NewRsaByKeyFile("path/to/private.pem")
// Sign datasignature, err:=rsaKey.Signature(data)
// Verify signatureerr=rsaKey.VerifySignature(data, signature)

String & Array Utilities

import"github.com/JackDPro/cetus/provider"provider.RandomString(16) // "aB3kF9mNpQ2xR7wL"provider.RandomInt(6) // "483921"provider.StringInSlice("a", []string{"a", "b"}) // true

File Operations

import"github.com/JackDPro/cetus/provider"provider.CopyFile("src.txt", "dst.txt")
provider.CopyDir("src_dir", "dst_dir") // recursive copy

i18n Translation

import"github.com/JackDPro/cetus/provider"t:=provider.GetTranslate()
t.SetLanguage("zh")
msg:=t.Tr("hello_world")
// Or use the global shorthandmsg:=provider.Tr("hello_world")

model - Data Models & Response Structures

BaseModel

Embed BaseModel in your models to get serialization helpers:

import"github.com/JackDPro/cetus/model"typeUserstruct {
model.BaseModelIduint64`json:"id" gorm:"primaryKey"`Nicknamestring`json:"nickname"`Emailstring`json:"email"`
}
// Implement IModel interfacefunc (u*User) ToMap() (map[string]interface{}, error) {
returnu.BaseModel.ToMap(u)
}

BaseModel provides:

  • ToMap(model) - Convert struct to map using json tags
  • ToJson(model) - Convert struct to JSON string
  • Include(model, relations, db) - Preload GORM relations from a comma-separated string

IModel Interface

Any model implementing IModel can be used with the response helpers:

typeIModelinterface {
ToMap() (map[string]interface{}, error)
}

Response Structures

// API responses are wrapped in DataWrappertypeDataWrapperstruct {
Datainterface{} `json:"data"`Metainterface{} `json:"meta,omitempty"`
}
// Error responsetypeErrorstruct {
Codeint`json:"code"`Messagestring`json:"message"`Detailstring`json:"detail,omitempty"`
}
// Pagination metadatatypePaginationstruct {
Countint`json:"count"`CurrentPageint`json:"current_page"`PerPageint`json:"per_page"`Totalint`json:"total"`TotalPagesint`json:"total_pages"`
}

controller - Response Helpers

Standardized HTTP response functions for Gin handlers:

import"github.com/JackDPro/cetus/controller"func (ctr*UserController) Store(c*gin.Context) {
// Validate inputiferr:=c.ShouldBindJSON(&req); err!=nil {
controller.ResponseUnprocessable(c, 1, "invalid params", err)
return
}
// Create resourceuser, err:=createUser(req)
iferr!=nil {
controller.ResponseInternalError(c, 2, "create failed", err)
return
}
// Return created resourcecontroller.ResponseCreated(c, user.Id)
}
func (ctr*UserController) Show(c*gin.Context) {
user, err:=findUser(id)
iferr!=nil {
controller.ResponseNotFound(c, "user not found")
return
}
controller.ResponseItem(c, user) // user must implement IModel
}
func (ctr*UserController) Index(c*gin.Context) {
users, meta:=listUsers(page, perPage)
controller.ResponseCollection(c, users, meta)
}

Available response helpers:

FunctionHTTP StatusUse Case
ResponseSuccess(c)200Action succeeded
ResponseItem(c, item)200Return single resource
ResponseCollection(c, items, meta)200Return list with optional pagination
ResponseCreated(c, id)201Resource created (sets Location header)
ResponseAccepted(c)202Async action accepted
ResponseBadRequest(c, code, msg)400Invalid request
ResponseUnauthorized(c)401Authentication required
ResponseForbidden(c)403Permission denied
ResponseNotFound(c, msg)404Resource not found
ResponseUnprocessable(c, code, msg, err)422Validation failed
ResponseInternalError(c, code, msg, err)500Server error (auto-logged)

jwt - JWT Authentication

RSA-signed JWT tokens with Redis-backed session storage. Supports access tokens and refresh tokens.

Prerequisites:

  • RSA key pair (PKCS#8 private key + PEM public certificate)
  • Redis server
  • JWT config in .env
JWT_EXPIRES_IN=72JWT_REDIS_PREFIX=authJWT_CERT_PATH=storage/jwt.crtJWT_KEY_PATH=storage/jwt.keyJWT_ISSUE=example.com

Generate RSA keys:

The private key must be in PKCS#8 DER format, the public key in PEM format:

# 1. Generate RSA private key (PKCS#1 PEM)
openssl genrsa -out jwt1.pem 2048
# 2. Convert to PKCS#8 DER format (required by cetus)
openssl pkcs8 -topk8 -inform PEM -outform DER \
-in jwt1.pem -out storage/jwt.key -nocrypt
# 3. Extract public key (PEM)
openssl rsa -in jwt1.pem -pubout -out storage/jwt.crt
# 4. Clean up
rm jwt1.pem

Usage:

import"github.com/JackDPro/cetus/jwt"guard, err:=jwt.GetJwtGuard()
// Create access token + refresh tokenaccessToken, err:=guard.CreateToken(userId, true) // true = revoke old tokens// accessToken.AccessToken - JWT string// accessToken.RefreshToken - Refresh JWT string// accessToken.Type - "bearer"// accessToken.ExpiresIn - Expiration in seconds// Create access token only (e.g. for API keys)accessToken, err:=guard.CreateAccessToken(userId)
// Validate tokenvalidToken, err:=guard.Attempt(tokenString)
// validToken.UserId - Authenticated user ID// validToken.Type - "token", "refresh", or "access_key"// Revoke token (logout)err=guard.DeleteCredential(tokenString)

middleware - HTTP Middleware

Request ID

Adds a unique request ID to every request. Checks incoming headers first (X-Request-Id, HTTP_X_REQUEST_ID, HTTP_REQUEST_ID), generates a UUID if none found.

import"github.com/JackDPro/cetus/middleware"router.Use(middleware.RequestId())

Rate Limiting

Powered by tollbooth.

import (
"github.com/JackDPro/cetus/middleware""github.com/didip/tollbooth/v7/limiter"
)
lmt:=limiter.New(nil).SetMax(100) // 100 requests per secondrouter.Use(middleware.LimitRate(lmt))

GORM Model with Preloading

Use BaseModel.Include() to dynamically preload relations from query parameters:

// GET /users?include=posts,commentsfunc (ctr*UserController) Index(c*gin.Context) {
includeStr:=c.Query("include")
varusers []Userdb:=provider.GetOrm().Dbdb= (&model.BaseModel{}).Include(&User{}, includeStr, db)
db.Find(&users)
controller.ResponseCollection(c, users, nil)
}

Full Example

A complete REST API setup:

package main
import (
"fmt""github.com/JackDPro/cetus/config""github.com/JackDPro/cetus/controller""github.com/JackDPro/cetus/middleware""github.com/JackDPro/cetus/provider""github.com/gin-contrib/cors""github.com/gin-gonic/gin"
)
funcmain() {
appConf:=config.GetAppConfig()
ifappConf.Env=="prod" {
gin.SetMode(gin.ReleaseMode)
}
router:=gin.New()
router.Use(gin.Recovery())
// CORScorsConf:=cors.DefaultConfig()
corsConf.AllowAllOrigins=truecorsConf.AllowHeaders= []string{"Authorization", "Accept-Language"}
router.Use(cors.New(corsConf))
// Middlewarerouter.Use(middleware.RequestId())
// Public routesprobeCtr:=controller.NewProbeController()
router.GET("/probe", probeCtr.Show)
router.POST("/auth/login", authLogin)
router.POST("/users", createUser)
// Protected routes (add your auth middleware)authorized:=router.Group("/")
// authorized.Use(yourAuthMiddleware())authorized.GET("/users/me", getMe)
authorized.POST("/auth/logout", logout)
// StartapiConf:=config.GetApiConfig()
addr:=fmt.Sprintf("0.0.0.0:%d", apiConf.HttpPort)
provider.GetLogger().Info("server starting", "address", addr)
iferr:=router.Run(addr); err!=nil {
panic(err)
}
}

Password Hashing with GORM Hooks

Use bcrypt hashing in GORM model hooks for automatic password encryption:

import (
"github.com/JackDPro/cetus/model""github.com/JackDPro/cetus/provider""gorm.io/gorm"
)
typeUserstruct {
model.BaseModelIduint64`json:"id" gorm:"primaryKey"`Emailstring`json:"email"`Passwordstring`json:"password,omitempty"`
}
func (u*User) BeforeSave(tx*gorm.DB) (errerror) {
ifu.Password!="" {
u.Password, err=provider.HashMake(u.Password)
}
return
}
func (u*User) ToMap() (map[string]interface{}, error) {
returnu.BaseModel.ToMap(u)
}

Environment Variables Reference

VariableDescriptionDefault/Example
APP_NAMEApplication namemy-app
APP_ENVEnvironment (dev/prod)dev
APP_DATA_ROOTData storage root path/usr/app
APP_PUBLIC_RES_URLPublic static resource URLhttp://example.com/static
LOG_CONSOLE_OUTOutput logs to consoletrue
LOG_FILE_OUTOutput logs to filefalse
LOG_FILE_PATHLog file path/var/log/app.log
LOG_LEVELLog level (debug/info/warn/error)debug
LOG_FORMATLog format (json/console)json
DB_TYPEDatabase type (mysql/postgres)mysql
DB_HOSTDatabase host127.0.0.1
DB_PORTDatabase port3306
DB_DATABASEDatabase namemydb
DB_USERNAMEDatabase usernameroot
DB_PASSWORDDatabase password-
DB_SSLMODEPostgreSQL SSL modedisable
DB_MIGRATE_SELF_ONLYLimit migration scopefalse
JWT_EXPIRES_INToken expiration (hours)72
JWT_REDIS_PREFIXRedis key prefix for tokensauth
JWT_CERT_PATHRSA public key pathstorage/jwt.crt
JWT_KEY_PATHRSA private key pathstorage/jwt.key
JWT_ISSUEJWT issuer claimexample.com
OPTIMUS_PRIMEOptimus prime number-
OPTIMUS_INVERSEOptimus inverse-
OPTIMUS_RANDOMOptimus random seed-
REDIS_HOSTRedis host127.0.0.1
REDIS_PORTRedis port6379
REDIS_DATABASERedis database index0
REDIS_PASSWORDRedis password-
NATS_HOSTNATS server host127.0.0.1
NATS_USERNAMENATS username-
NATS_PASSWORDNATS password-
SERVER_HTTP_PORTHTTP server port80
SERVER_GRPC_PORTgRPC server port50051

Demo Project

See cetus-demo for a complete working example with user registration, JWT authentication, and CRUD operations.

License

MIT

About

A Go toolkit for building HTTP services with Gin — config, database (MySQL/PostgreSQL), JWT auth, logging, middleware, and standardized API responses out of the box.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages