Phastos utilities provide file generators, cloud storage (GCS), CSV/Excel data importers, authentication middleware, environment detection, structured logging, and a collection of helpers for JWT, encryption, Slack notifications, templating, and more.
go/generator/ — Unified file generation via the FileGenerator interface (go/generator/generator.go:20).
import "github.com/kodekoding/phastos/v2/go/generator"type FileGenerator interface {
Generate() error
FileName() string
}All generator types (PDF, CSV, Excel, QR, Banner) implement this interface, enabling polymorphic file generation:
var gen generator.FileGenerator
switch outputType {
case "pdf":
gen = pdfInstance
case "csv":
gen = csvInstance
case "excel":
gen = excelInstance
}
if err := gen.Generate(); err != nil { /* ... */ }
fmt.Println("output:", gen.FileName())Requires wkhtmltopdf binary installed on the system. Uses go-wkhtmltopdf to convert HTML templates into PDF documents.
pdf, err := generator.NewPDF(&generator.ConverterOptions{
PageSize: "A4",
MarginBottom: 10,
MarginTop: 10,
MarginLeft: 11,
MarginRight: 11,
})
if err != nil {
log.Fatal(err)
}
pdf.
AddCustomFunction("formatDate", myFormatFunc).
SetTemplate("templates/report.html", templateData).
SetFooterHTMLTemplate("templates/footer.html").
SetFileName(&outputPath).
Generate()Interface PDFs |
Description |
|---|---|
NewPDF(opts) |
Creates PDF generator. Optional *ConverterOptions for page size and margins |
SetTemplate(path, data) |
Parses an HTML template with data and renders it to string content |
SetFooterHTMLTemplate(path) |
Sets an HTML footer with configurable spacing |
SetFileName(*string) |
Sets the output filename (auto-creates tmp/pdf/ directory) |
AddCustomFunction(name, func) |
Registers a custom template function for use in HTML templates |
Generate() |
Renders HTML to PDF and writes to fileName |
FileName() |
Returns the output file path |
Error() |
Returns accumulated errors from chained calls |
csv := generator.NewCSV()
csv.
SetHeader([]string{"Name", "Email", "Role"}).
AppendDataRow([]string{"Alice", "alice@example.com", "Admin"}).
AppendDataRow([]string{"Bob", "bob@example.com", "User"}).
Generate()
path := csv.FileName() // "generated-csv.csv"Interface CSVs |
Description |
|---|---|
NewCSV() |
Creates CSV generator with default filename generated-csv.csv |
SetFileName(name) |
Sets the output filename (.csv suffix is appended automatically) |
SetHeader([]string) |
Prepends a header row; validates column count matches existing rows |
AppendDataRow([]string) |
Appends a data row; validates column count matches header |
Generate() |
Writes content to the CSV file |
FileName() |
Returns the output file path |
Error() |
Returns accumulated validation errors |
Operates in three modes: new file, open from path, and open from upload. Uses excelize for .xlsx files.
// Create a new Excel file
xls := generator.NewExcel(&generator.ExcelOptions{Source: "new"})
xls.
SetSheetName("Users").
SetHeader([]string{"Name", "Email", "Role"}).
AppendDataRow([]string{"Alice", "alice@example.com", "Admin"}).
AppendDataRow([]string{"Bob", "bob@example.com", "User"}).
SetFileName("users-report").
Generate()
// Open an existing file
xls := generator.NewExcel(&generator.ExcelOptions{
Source: "path",
File: "/path/to/file.xlsx",
})
// Open from HTTP upload
xls := generator.NewExcel(&generator.ExcelOptions{
Source: "upload",
File: uploadedMultipartFile,
})Interface Excels |
Description |
|---|---|
NewExcel(opts) |
Creates Excel handler. Source: "new", "path", or "upload" |
SetFileName(name) |
Sets output filename (.xlsx suffix appended) |
SetSheetName(name) |
Sets the active sheet name (default "Sheet1") |
SetHeader([]string) |
Prepends a header row with bold + center styling |
AppendDataRow([]string) |
Appends a data row |
Generate() |
Creates sheet, writes content, applies header styling, saves as .xlsx |
WriteToBuffer() |
Writes Excel to *bytes.Buffer instead of file (for HTTP response streaming) |
GetContents(sheetName) |
Reads all rows as []map[string]string with header keys |
ScanContentToStruct(sheetName, &dest) |
Reads sheet and JSON-unmarshals into a pointer struct |
GetMergeCell(sheetName) |
Returns merged cells in the sheet |
GetExcelFile() |
Returns underlying *excelize.File for advanced operations |
FileName() |
Returns output file path |
Error() |
Returns accumulated errors |
Uses yeqown/go-qrcode to generate QR code images. Supports embedding a logo in the center.
qr, err := generator.NewQR("https://example.com/verify/abc123")
if err != nil {
log.Fatal(err)
}
qr.
SetLogoImg("assets/logo.png").
SetFileName(&outputPath).
Generate()
path := qr.FileName() // tmp/qr/<md5-hash>.jpegInterface QRs |
Description |
|---|---|
NewQR(content) |
Creates a QR code for the given string content |
SetLogoImg(path) |
Embeds a logo image in the center of the QR code |
SetFileName(*string) |
Sets output filename (auto-creates tmp/qr/ with MD5-hashed name) |
Generate() |
Saves QR code as JPEG to disk |
FileName() |
Returns the output file path |
Uses fogleman/gg for 2D graphics. Composes banners with background color, image layers, and text labels.
banner := generator.NewBanner(
generator.WithWidth(1200),
generator.WithHeight(400),
generator.WithBackgroudColor("#1a1a2e"),
)
banner.
AddImageLayer(&generator.ImageLayer{
Image: logo,
XPos: 50,
YPos: 50,
}).
AddLabel(&generator.Label{
Text: "Certificate of Completion",
FontPath: "fonts/Roboto-Bold.ttf",
Size: 48,
Color: color.White,
XPos: 600,
YPos: 200,
}).
Generate().
Save("output/certificate.png")Interface Banners |
Description |
|---|---|
NewBanner(opts...) |
Creates banner. Options: WithWidth, WithHeight, WithBackgroudColor (hex). Default: 1200×400, white |
AddImageLayer(img) |
Adds an image layer (higher index = upper layer) |
AddLabel(label) |
Adds a text label with font, size, color, and position |
Generate() |
Renders background + layers + labels into image.Image |
Save(destPath) |
Saves to PNG, JPEG, or GIF based on file extension |
SetDestPath(path) |
Sets destination path (alternative to passing path to Save) |
Image() |
Returns the rendered image.Image |
FileName() |
Returns the destination path |
go/storage/ — Google Cloud Storage client implementing the Buckets interface.
import "github.com/kodekoding/phastos/v2/go/storage"type Buckets interface {
UploadImage(ctx context.Context, file multipart.File, fileName *string) error
UploadFile(ctx context.Context, file multipart.File, fileName *string) error
UploadImageFromLocalPath(ctx context.Context, filePath string, fileName *string, deleteAfterSuccess ...bool) error
UploadFileFromLocalPath(ctx context.Context, filePath string, fileName *string, deleteAfterSuccess ...bool) error
UploadImagePublic(ctx context.Context, file multipart.File, fileName *string) error
UploadFilePublic(ctx context.Context, file multipart.File, fileName *string) error
UploadImageFromLocalPathPublic(ctx context.Context, filePath string, fileName *string, deleteAfterSuccess ...bool) error
UploadFileFromLocalPathPublic(ctx context.Context, filePath string, fileName *string, deleteAfterSuccess ...bool) error
GetSignedURLFile(ctx context.Context, imgPath string) (signedUrl string, err error)
GetFileFS(ctx context.Context, filePath string) (fs.File, error)
SetFileExpiredTime(minutes int) Buckets
SetBucketName(fileName string) Buckets
SetContentType(contentType string) Buckets
RollbackProcess(ctx context.Context, fileName string) error
DeleteFile(ctx context.Context, fileName string) error
CopyFileToAnotherBucket(ctx context.Context, destBucket, fileName string) error
GenerateSignedURL(urlType string, path string, expires ...time.Duration) (string, error)
Close()
}Requires STORAGE_CREDENTIALS_PATH (or GOOGLE_APPLICATION_CREDENTIALS) environment variable pointing to a GCP service account JSON key file.
ctx := context.Background()
gcs, err := storage.NewGCS(ctx, "my-bucket-name")
if err != nil {
log.Fatal(err)
}
defer gcs.Close()All upload methods prepend the path with {private|public}/{img|file}/{env} (e.g., private/img/local/photo.jpg). The *fileName pointer is updated in-place with the full GCS object path.
| Method | Source | Visibility |
|---|---|---|
UploadImage(ctx, file, fileName) |
multipart.File |
private |
UploadFile(ctx, file, fileName) |
multipart.File |
private |
UploadImageFromLocalPath(ctx, path, fileName, delete?) |
local file path | private |
UploadFileFromLocalPath(ctx, path, fileName, delete?) |
local file path | private |
UploadImagePublic(ctx, file, fileName) |
multipart.File |
public (allUsers reader ACL) |
UploadFilePublic(ctx, file, fileName) |
multipart.File |
public |
UploadImageFromLocalPathPublic(ctx, path, fileName, delete?) |
local file path | public |
UploadFileFromLocalPathPublic(ctx, path, fileName, delete?) |
local file path | public |
deleteAfterSuccess (variadic ...bool): when true, the local file is removed after successful upload. Default: false.
// Simple signed URL for file download (default 60-min expiry)
url, err := gcs.GetSignedURLFile(ctx, "private/img/local/photo.jpg")
// Generate signed URL for upload with custom expiry
uploadURL, err := gcs.GenerateSignedURL(storage.UploadProcess, "uploads/file.pdf", 10*time.Minute)
// Generate signed URL for download
downloadURL, err := gcs.GenerateSignedURL(storage.DownloadProcess, "reports/report.xlsx", 30*time.Minute)| Method | Description |
|---|---|
GetSignedURLFile(ctx, path) |
Generates a GET signed URL. Expiry controlled by SetFileExpiredTime (default 60 min) |
GenerateSignedURL(urlType, path, expires...) |
Generates signed URL. urlType: storage.UploadProcess (PUT) or storage.DownloadProcess (GET). Upload default expiry: 1 min. Download default: 5 min |
SetFileExpiredTime(minutes) |
Sets expiry in minutes for GetSignedURLFile |
// Copy file to another bucket
err := gcs.CopyFileToAnotherBucket(ctx, "archive-bucket", "private/img/local/doc.pdf")
// Delete a file
err := gcs.DeleteFile(ctx, "private/img/local/doc.pdf")
// Rollback (alias for DeleteFile)
err := gcs.RollbackProcess(ctx, "private/img/local/doc.pdf")
// Read file as io/fs.File (supports io/fs interfaces)
f, err := gcs.GetFileFS(ctx, "templates/email.html")
// Set content type for subsequent uploads
gcs.SetContentType("application/pdf")
// Switch to a different bucket
gcs.SetBucketName("another-bucket")drive, err := storage.NewDrive(ctx)Uses DRIVE_CREDENTIALS_PATH (or GOOGLE_APPLICATION_CREDENTIALS) to initialise a *drive.Service for Google Drive API operations.
go/importer/ — CSV/Excel data import with configurable worker pool, transaction-per-batch processing, struct validation, progress tracking, and optional Slack notifications.
import "github.com/kodekoding/phastos/v2/go/importer"type UserImport struct {
Name string `csv:"Name"`
Email string `csv:"Email"`
Role string `csv:"Role"`
}
result := importer.New(
importer.WithCtx(ctx),
importer.WithFile(uploadedFile),
importer.WithExtFile(".xlsx"),
importer.WithStructDestination(UserImport{}),
importer.WithTransaction(db),
importer.WithWorker(10),
importer.WithProcessName("user-import"),
importer.WithProcessFn(func(ctx context.Context, singleData interface{}, trx *sqlx.Tx, workerIdx int) *api.HttpError {
user := singleData.(*UserImport)
_, err := db.Write(ctx, &database.QueryOpts{
CUDRequest: constructInsert(user),
Trx: trx,
})
if err != nil {
return api.NewErr(api.WithErrorData(user), api.WithErrorStatus(500), api.WithErrorMessage(err.Error()))
}
return nil
}),
importer.WithSentNotifToSlack(true),
).ProcessData()
fmt.Printf("Total: %d, Success: %d, Failed: %d, Time: %.2fs\n",
result.TotalData, result.TotalSuccess, result.TotalFailed, result.ExecutionTime)| Option | Description |
|---|---|
WithCtx(ctx) |
Parent context (required) |
WithFile(file) |
multipart.File from HTTP upload (required) |
WithExtFile(ext) |
File extension: ".xls", ".xlsx", or ".csv" (required) |
WithStructDestination(struct) |
Target struct for field mapping via csv/excel tags (required for regular import) |
WithTransaction(trx) |
Database transaction provider (database.Transactions) (required) |
WithWorker(n) |
Number of concurrent worker goroutines (default: 10) |
WithProcessName(name) |
Name for logging and Slack notification title |
WithProcessFn(fn) |
Processing function func(ctx, data, trx, workerIdx) *api.HttpError (required) |
WithSentNotifToSlack(bool, channel?) |
Send Slack notification on completion. Optional custom webhook channel |
WithSheetName(name) |
Sheet name for Excel files (default: first sheet) |
type ImportResult struct {
FailedList map[string][]interface{} // grouped by error message
SuccessList []interface{}
TotalData int
TotalFailed int
TotalSuccess int
ExecutionTime float64 // seconds
UniqueProcessKey string
}Each row flows through this pipeline:
- File Reader — reads headers + rows from CSV or Excel, parses each row into the destination struct via JSON marshal/unmarshal, sends through
chan rowData - Worker Pool —
ngoroutines consume from the channel. Each worker:- Validates the struct via
api.ValidateStruct() - Begins a transaction via
trx.Begin() - Calls the user's
processFn - Calls
trx.Finish(trx, err)— commits on nil error, rolls back otherwise - Sends
processedResultto the result channel
- Validates the struct via
- Aggregator — collects results, builds
FailedListandSuccessList, sends Slack notification if enabled - Slack Notification — sends summary with total data, failed count, execution time, and JWT user info
The pipeline uses sync.Pool for processedResult objects to minimise GC pressure.
For files with cross-tab/pivot layouts where column headers represent data values:
result := importer.New(
importer.WithCtx(ctx),
importer.WithFile(uploadedFile),
importer.WithExtFile(".xlsx"),
importer.WithSheetName("Sheet1"),
// Pivot configuration
importer.WithHeaderRowIndex(0), // row index of column headers
importer.WithDataStartRow(1), // first data row (after header)
importer.WithKeyColumns([]int{0, 1}), // columns that form the composite key
importer.WithKeySeparator("|"),
importer.WithValueStartCol(2), // first column containing values
importer.WithOnPivotEntry(func(key, value string) {
fmt.Printf("Processing: %s = %s\n", key, value)
}),
importer.WithTransaction(db),
importer.WithWorker(10),
importer.WithProcessName("pivot-import"),
importer.WithProcessFn(func(ctx context.Context, singleData interface{}, trx *sqlx.Tx, workerIdx int) *api.HttpError {
entry := singleData.(map[string]any)
// entry["Employee ID"], entry["Department"], entry["pivot_header"], entry["pivot_value"]
return nil
}),
importer.WithSentNotifToSlack(true),
).ProcessPivotData()Each entry in processFn is a map[string]any containing:
- All key column values (e.g.,
"Employee ID": "444201123") "pivot_header"— the value column header (e.g.,"2024-03-26")"pivot_value"— the cell value (e.g.,"HONS")
Use ReadPivotData() instead of ProcessPivotData() to get a flat map[string]string result without worker-pool processing.
// Get all rows from .xlsx file
rows, err := importer.GetDataFromXlsx(uploadedFile, "Sheet1")
// Get all rows from .xls file
rows, err := importer.GetDataFromXls(uploadedFile)go/middlewares/ — HTTP middleware for JWT authentication, static secret-based auth, and rate limiting.
import "github.com/kodekoding/phastos/v2/go/middlewares"Validates Authorization: Bearer <token> header using HS256 signing. Requires JWT_SIGNING_KEY environment variable. On success, stores claims in request context via context.SetJWT(r, claims).
// Register as route middleware
r := api.NewRoute("GET", myHandler, api.WithMiddleware(middlewares.JWTAuth))
// Access claims in handler
claims := context.GetJWT(r)
fmt.Println(claims.Data) // custom data embedded in JWT
fmt.Println(claims.Token) // raw token string| Detail | Value |
|---|---|
| Auth header | Authorization: Bearer <token> |
| Signing algorithm | HS256 |
| Env variable | JWT_SIGNING_KEY |
| Claims struct | entity.JWTClaimData (stored in context) |
| Context access | context.GetJWT(r) |
| Error codes | INVALID_KEY, INVALID_CLAIMS, TOKEN_NOT_VALID, INVALID_STRUCT_CLAIM |
Validates a secret header against SERVICE_SECRET environment variable. Useful for service-to-service communication.
r := api.NewRoute("POST", internalHandler, api.WithMiddleware(middlewares.StaticAuth))| Detail | Value |
|---|---|
| Auth header | Secret: <token> (constant common.HeaderSecret) |
| Env variable | SERVICE_SECRET |
| Error message | "Invalid Token" / ERR_INVALID_TOKEN |
Token-bucket rate limiter per IP address using golang.org/x/time/rate. Returns 429 Too Many Requests when limit exceeded.
// Default: 10 rps, burst 20
limiter := middlewares.NewRateLimiter()
// Custom rate
limiter := middlewares.NewRateLimiter(
middlewares.WithRate(100, 200), // 100 rps, burst 200
middlewares.WithSkipPaths("/ping", "/health"),
middlewares.WithMessage("Too many requests, calm down", "CUSTOM_RATE_LIMIT"),
)
// Custom key extractor (e.g., by API key instead of IP)
limiter := middlewares.NewRateLimiter(
middlewares.WithKeyExtractor(func(r *http.Request) string {
return r.Header.Get("X-API-Key")
}),
)
r := api.NewRoute("GET", handler, api.WithMiddleware(limiter))| Option | Description |
|---|---|
NewRateLimiter(opts...) |
Creates rate limiter middleware. Default: 10 rps, burst 20, per-IP |
WithRate(rps, burst) |
Sets requests-per-second limit and burst size |
WithSkipPaths(paths...) |
Disables rate limiting for specific exact paths |
WithMessage(msg, code) |
Custom error message and error code (default: "rate limit exceeded", "RATE_LIMITED") |
WithKeyExtractor(fn) |
Custom function to extract the rate-limit bucket key (default: client IP from X-Forwarded-For, X-Real-Ip, or RemoteAddr) |
go/env/ — Environment detection utilities. Reads from APPS_ENV environment variable and .env file on startup.
import "github.com/kodekoding/phastos/v2/go/env"env := env.ServiceEnv() // "local" | "development" | "staging" | "production"
if env.IsProduction() { /* ... */ }
if env.IsStaging() { /* ... */ }
if env.IsDevelopment() { /* ... */ }
if env.IsLocal() { /* ... */ }| Constant | Value |
|---|---|
LocalEnv |
"local" |
DevelopmentEnv |
"development" |
StagingEnv |
"staging" |
ProductionEnv |
"production" |
| Function | Returns |
|---|---|
ServiceEnv() |
Current env string; defaults to "development" if unset |
IsLocal() |
true when APPS_ENV=local |
IsDevelopment() |
true when APPS_ENV=development |
IsStaging() |
true when APPS_ENV=staging |
IsProduction() |
true when APPS_ENV=production |
GoVersion() |
Go runtime version string |
SetFromEnvFile(filepath) |
Reads a .env file and sets OS environment variables (called automatically on init()) |
go/log/ — Structured logging via rs/zerolog with New Relic and OpenTelemetry integration.
import "github.com/kodekoding/phastos/v2/go/log"Call log.Get() early in main(). Uses sync.Once — subsequent calls return the same logger instance.
// Basic: console output (colored, development-friendly)
logger := log.Get(log.WithAppPort(8080))
// With New Relic log correlation
logger := log.Get(
log.WithAppPort(8080),
log.WithAppVersion("v1.2.3"),
log.WithNewRelicApp(nrApp),
)
// With OpenTelemetry log forwarding
logger := log.Get(
log.WithAppPort(8080),
log.WithOTelLogEndpoint(),
)| Option | Description |
|---|---|
WithAppPort(port) |
Application port, included in every log entry |
WithAppVersion(version) |
Application version, included in every log entry |
WithNewRelicApp(app) |
Injects New Relic trace/span IDs into log entries |
WithOTelLogEndpoint() |
Forwards logs to OpenTelemetry collector via TCP (port 54526, derived from OTEL_EXPORTER_OTLP_ENDPOINT) |
| Environment | Writer Configuration |
|---|---|
| Production | JSON writer (stdout) + optional NR log correlation + optional OTel TCP writer. Log level: InfoLevel |
| Non-production | Colored console writer (stdout) + optional NR/OTel. Log level: DebugLevel |
func handler(w http.ResponseWriter, r *http.Request) {
logger := log.Ctx(r.Context())
logger.Info().
Str("user_id", "123").
Int("items", 5).
Msg("Order created")
}log.Ctx(ctx) returns *zerolog.Logger from context. The request logger middleware (registered automatically by api.App.Init()) stores the logger with trace context in each request's context.
Automatic fields on every log entry: app, env, port, app_version (if set), container_name (if CONTAINER_NAME env is set).
go/helper/ — General-purpose utilities for JWT, encryption, Slack notifications, templating, UUID generation, and more.
import "github.com/kodekoding/phastos/v2/go/helper"// Generate JWT with custom claims data
token, err := helper.GenerateJWTToken(
map[string]interface{}{"user_id": 42, "role": "admin"},
2*time.Hour, // optional: custom expiry (default: 24h)
)| Function | Description |
|---|---|
GenerateJWTToken(data, expireTime...) |
Creates HS256 JWT with entity.JWTClaimData claims. Uses JWT_SIGNING_KEY env and JWT_ISSUER env (default: "phastos") |
// From an explicit key
cm, err := helper.NewCryptoManager("my-32-byte-encryption-secret-key")
if err != nil {
log.Fatal(err)
}
// From an environment variable
cm, err := helper.NewCryptoManagerFromEnv("ENCRYPTION_KEY")
// Encrypt
ciphertext, err := cm.Encrypt("sensitive-api-key-value")
// Decrypt
plaintext, err := cm.Decrypt(ciphertext)
// Generate a public key hash (safe for client-side storage)
publicKey := helper.GeneratePublicKey(plaintext)
// Verify a key against its public hash
valid := helper.VerifyAPIKey(plaintext, publicKey)| Type/Method | Description |
|---|---|
NewCryptoManager(key) |
Creates AES-256-GCM crypto manager. Key is hashed with SHA-256 to ensure 32 bytes |
NewCryptoManagerFromEnv(envVar) |
Creates from environment variable |
Encrypt(plaintext) |
Encrypts with random nonce, returns base64-encoded ciphertext |
Decrypt(ciphertext) |
Decrypts base64-encoded ciphertext |
GeneratePublicKey(apiKey) |
SHA-256 hash of the plaintext key, base64-encoded |
VerifyAPIKey(apiKey, publicKey) |
Verifies key against stored public key hash |
ctx := r.Context()
err := helper.SendSlackNotification(ctx,
helper.NotifTitle("Import Complete"),
helper.NotifMsgType(helper.NotifInfoType),
helper.NotifData(map[string]string{
"Total Data": "1000",
"Success": "995",
"Failed": "5",
"Execution Time": "3.42 second(s)",
}),
helper.NotifChannel("https://hooks.slack.com/services/..."),
)| Type | Description |
|---|---|
NotifInfoType |
Green color (#2fe329), info icon |
NotifWarnType |
Yellow color (#f7bf31), warning icon |
NotifErrorType |
Red color (#ff0e0a), broken-heart icon |
NotifTitle(title) |
Sets notification title |
NotifMsgType(type) |
Sets type: info, warn, error |
NotifData(map[string]string) |
Key-value data fields. Keys prefixed with - disable Short field (full-width) |
NotifChannel(url) |
Custom webhook URL (overrides context's default Slack destination) |
// UUID v7 (google/uuid)
id := helper.GenerateUUID() // e.g., "0190c9a7-8f5e-7b3a-9c1d-2e4f6a8b0c3d"
// UUID v4 (satori/go.uuid)
id := helper.GenerateUUIDV4() // e.g., "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
// Random alphanumeric string
str := helper.GenerateRandomString(16) // e.g., "kL9mN2xR7vB5qW8p"
// Random string with custom charset
str := helper.GenerateRandomStringWithCharset(8, "0123456789ABCDEF")
// Fast nanoid-style ID (crypto/rand, ~15 chars, pool-allocated)
id := helper.GenerateFastID() // e.g., "aB3xK_9mN2-rV8pL"
// Ultra-fast counter-based ID (not cryptographically secure)
id := helper.GenerateFastIDCounter() // returns string
buf := helper.GenerateFastIDCounterBytes() // returns *[]byte (pool-allocated, zero heap alloc)
helper.PutFastIDCounterBytes(buf) // return buffer to pool after use| Function | Length | Source | Notes |
|---|---|---|---|
GenerateUUID() |
36 chars (standard) | google/uuid v7 |
Cryptographically random |
GenerateUUIDV4() |
36 chars (standard) | satori/go.uuid v4 |
Cryptographically random |
GenerateRandomString(n) |
n chars |
math/rand |
Alphanumeric (A-Za-z0-9) |
GenerateRandomStringWithCharset(n, charset) |
n chars |
math/rand |
Custom charset |
GenerateFastID() |
15 chars | crypto/rand |
Nanoid-style, 64-char alphabet, zero-bias mask |
GenerateFastIDCounter() |
15 chars | atomic counter | Not crypto-secure, sequential, ~15ns |
// Parse from embed.FS
var templates embed.FS
result, err := helper.ParseTemplate(templates, "slack/notif.json", data)
slackPayload := result.String()
// Parse from filesystem path
result, err := helper.ParseFileTemplate("/templates/email.html", data, "<html>", "</html>")
// Parse from path (supports local files AND HTTP URLs) with custom functions
result, err := helper.ParseTemplateFromPath(
"https://cdn.example.com/templates/report.html",
data,
template.FuncMap{"formatDate": myFormatFunc},
"<!-- additional body content -->",
)| Function | Description |
|---|---|
ParseTemplate(embedFS, file, data, additionalBodyContent...) |
Parses template from embed.FS |
ParseFileTemplate(path, data, additionalBodyContent...) |
Parses from filesystem using html/template |
ParseTemplateFromPath(path, data, optionalParams...) |
Parses from local file or HTTP URL. Supports template.FuncMap, map[string]any func maps, and string body content |
GetTemplate(file, data, destStruct) |
Parses template from file path and JSON-unmarshals into a struct |
GetTemplateFS(embedFS, file, data, destStruct) |
Same as GetTemplate but from embed.FS |
// Generate INSERT columns/values from a struct
cols, values := helper.ConstructColNameAndValue(ctx, myStruct)
// Generate UPDATE SET clause from a struct (cached template for repeated use)
updateData := helper.ConstructColNameAndValueForUpdate(ctx, updateStruct)
// updateData.Cols = ["name=?", "email=?", "updated_at=?"]
// updateData.Values = ["Alice", "alice@example.com", "2024-01-15 10:30:00"]
// updateData.ColsInsert = "name,email,updated_at"
// Generate bulk INSERT from a slice of structs
bulkData, err := helper.ConstructColNameAndValueBulk(ctx, []User{user1, user2, user3})
// Generate SELECT column list from a struct (cached)
cols := helper.GenerateSelectCols(ctx, User{})
cols := helper.GenerateSelectCols(ctx, User{}, helper.WithExcludedCols("password,salt"))
// Convert struct to map[string]interface{} using csv/excel tags
m := helper.ConvertStructToMap(myStruct)| Function | Description |
|---|---|
ConstructColNameAndValue(ctx, struct) |
Returns (columns []string, values []interface{}) using db tag mapping. Handles null types, pointers, embedded structs, skip tags |
ConstructColNameAndValueForUpdate(ctx, struct, extraValues...) |
Returns *CUDConstructData with cached template-based column/value extraction. Auto-adds updated_at=? if not present |
ConstructColNameAndValueBulk(ctx, slice, conditions...) |
Returns *CUDConstructData for bulk INSERT or bulk UPDATE with JOIN conditions |
GenerateSelectCols(ctx, struct, opts...) |
Returns []string of column names. Supports WithExcludedCols, WithIncludedCols, recursive embedded structs. Results are cached per struct type |
ConvertStructToMap(struct) |
Converts struct to map[string]interface{} using csv, excel, or field name tags |
helper.ToCamelCase("any_kind_of_string") // "AnyKindOfString"
helper.ToLowerCamelCase("any_kind_of_string") // "anyKindOfString"
helper.ToSnakeCase("AnyKindOfString") // "any_kind_of_string"
helper.TrimDuplicatedSpace("hello world") // "hello world"helper.SliceContains([]string{"a", "b", "c"}, "b") // true
helper.Remove(slice, 2) // removes element at index 2rgba, err := helper.ParseHexColor("#ff5733") // color.RGBA{R:255, G:87, B:51, A:255}
rgba, err := helper.ParseHexColor("#f53") // short form: #ff5533
uniform := helper.GetColorUniform("#1a1a2e") // *image.Uniform{R:26, G:26, B:46, A:255}// Get base file path (returns "files" in local env, "" otherwise)
base := helper.GetFilePath()
// Ensure directory exists for a file path
helper.CheckFolder("/tmp/pdf/output/report.pdf")
// Split path into folder and filename
folder, filename := helper.GetFolderAndFileName("/tmp/pdf/report.pdf")
// folder = "tmp/pdf/", filename = "report.pdf"
// Get temp folder path (creates if missing)
tmpPath, err := helper.GetTmpFolderPath() // "files/tmp" or "/tmp"// Upload to local temp folder
err := helper.UploadToTmp(ctx, &filePath, uploadedFile)
// Upload to GCS temp bucket
err := helper.UploadToTmp(ctx, &filePath, uploadedFile, &helper.CloudStorageTmpUpload{
Storage: gcsClient,
TmpBucketName: "temp-uploads",
})The *path is updated in-place to the final upload path.