Skip to content

nexlified/grout

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

123 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Grout API Guide

Grout is a small HTTP service that renders SVG avatars with user initials and rectangular placeholder images. It relies on the github.com/fogleman/gg drawing library and embeds Go fonts for crisp text output.

Quick Start

Using Docker Compose

docker compose up --build

Using Go directly

go run ./cmd/grout

The server listens on :8080 by default and exposes the routes below.

/avatar/ Endpoint

Generates a square avatar that displays the initials derived from the provided name.

  • Path: /avatar/{name}[.ext] where ext can be svg, png, jpg, jpeg, gif, or webp. You can also use the name query parameter.
  • Format: Images are served as SVG by default when no extension is specified. Use .svg, .png, .jpg, .jpeg, .gif, or .webp extension to request a specific format.
  • Size: size query parameter (default 128), applied to both width and height.
  • Background Color: background or bg query parameter accepts hex (f0e9e9) or the literal random to derive a deterministic color per name.
  • Text Color: color query parameter (hex, default auto-contrasted).
  • Rounded: rounded=true draws a circle instead of a square.
  • Bold: bold=true switches to the embedded Go Bold font.

Examples:

# Default SVG format
curl "http://localhost:8080/avatar/Jane+Doe?size=256&rounded=true&bold=true&background=random"

# SVG format (explicit)
curl "http://localhost:8080/avatar/Jane+Doe.svg?size=256&rounded=true&bold=true&background=random"

# PNG format
curl "http://localhost:8080/avatar/Jane+Doe.png?size=256&rounded=true&bold=true&background=random"

# JPG format
curl "http://localhost:8080/avatar/Jane+Doe.jpg?size=256"

# WebP format
curl "http://localhost:8080/avatar/Jane+Doe.webp?size=256"

# Using 'bg' parameter (shorthand for background)
curl "http://localhost:8080/avatar/Jane+Doe?size=256&bg=ff5733"

/placeholder/ Endpoint

Creates a rectangular placeholder image with custom dimensions and optional overlay text. Supports automatic text wrapping for long content like quotes and jokes.

  • Path Form: /placeholder/{width}x{height}[.ext] where ext can be svg, png, jpg, jpeg, gif, or webp. If extension is omitted, images are served as SVG by default.
  • Format: Images are served as SVG by default when no extension is specified. Use .svg, .png, .jpg, .jpeg, .gif, or .webp extension to request a specific format.
  • Dimensions: Can also use query parameters w and h (default 128).
  • Text: text query parameter (defaults to "{width} x {height}").
  • Quote: quote=true query parameter to use a random quote instead of custom text. Requires minimum width of 300px.
  • Joke: joke=true query parameter to use a random joke instead of custom text. Requires minimum width of 300px.
  • Category: category query parameter to filter quotes/jokes by category (optional).
  • Background Color: background or bg query parameter (hex, default cccccc). Supports gradients with comma-separated colors (e.g., ff0000,0000ff for red to blue).
  • Text Color: color query parameter (hex, default auto-contrasted).

Text Rendering Features:

  • Automatic text wrapping for quotes and jokes based on image width
  • Content is centered with 10% padding on all sides
  • Dynamic font sizing (16px-48px) based on text length and image dimensions
  • Multi-line text support with 1.5x line spacing for readability

Quote Categories

  • inspirational - Inspirational quotes to motivate and uplift
  • motivational - Motivational quotes for taking action
  • life - Quotes about life and living
  • success - Quotes about achieving success
  • wisdom - Wise sayings and philosophical thoughts
  • love - Quotes about love and relationships
  • happiness - Quotes about finding joy and happiness
  • technology - Quotes about technology and innovation

Joke Categories

  • programming - Developer and programming jokes
  • science - Scientific and chemistry jokes
  • dad - Classic dad jokes
  • puns - Wordplay and puns
  • technology - Technology and computer jokes
  • work - Work and office humor
  • animals - Animal-related jokes
  • general - General purpose jokes

Examples:

# Default SVG format
curl "http://localhost:8080/placeholder/800x400?text=Hero+Image&background=222222&color=f5f5f5"

# SVG format (explicit)
curl "http://localhost:8080/placeholder/800x400.svg?text=Hero+Image&background=222222&color=f5f5f5"

# PNG format (using 'bg' shorthand)
curl "http://localhost:8080/placeholder/800x400.png?text=Hero+Image&bg=222222&color=f5f5f5"

# JPG format
curl "http://localhost:8080/placeholder/1200x600.jpg?text=Banner"

# GIF format
curl "http://localhost:8080/placeholder/400x400.gif"

# Gradient background (red to blue, SVG)
curl "http://localhost:8080/placeholder/800x400?bg=ff0000,0000ff&text=Gradient"

# Gradient background (green to yellow, PNG)
curl "http://localhost:8080/placeholder/1200x600.png?bg=00ff00,ffff00"

# Random quote (any category) - text wraps automatically
curl "http://localhost:8080/placeholder/1200x400?quote=true"

# Random inspirational quote with custom colors
curl "http://localhost:8080/placeholder/1200x400?quote=true&category=inspirational&bg=2c3e50&color=ecf0f1"

# Random programming joke
curl "http://localhost:8080/placeholder/800x600.png?joke=true&category=programming"

# Random joke with custom colors
curl "http://localhost:8080/placeholder/1000x500?joke=true&bg=2c3e50&color=ecf0f1"

Response Characteristics

  • Images are served as SVG by default (when no extension is specified). The Content-Type header is set based on the requested format: image/svg+xml, image/webp, image/png, image/jpeg, or image/gif.
  • Successful responses include Cache-Control: public, max-age=31536000, immutable and an ETag keyed by the query parameters and format.
  • Cached entries are stored in an in-memory LRU (CacheSize = 2000) to reduce rendering overhead. Cache hits expose the header X-Cache: HIT.

Error Handling

If generation fails (for example due to invalid parameters), the server responds with HTTP 500 and Failed to generate image. Invalid dimensions fallback to safe defaults to keep the server responsive.

Configuration

  • ADDR env var or -addr flag controls the HTTP bind address (default :8080).
  • CACHE_SIZE env var or -cache-size flag sets LRU entry count (default 2000).
  • DOMAIN env var or -domain flag sets the public domain for example URLs in the home page (default localhost:8080).
  • STATIC_DIR env var or -static-dir flag sets the directory for static files like robots.txt and sitemap.xml (default ./static).
  • RATE_LIMIT_RPM env var or -rate-limit-rpm flag sets the rate limit in requests per minute per IP (default 100).
  • RATE_LIMIT_BURST env var or -rate-limit-burst flag sets the burst size for the rate limiter (default 10).

Rate Limiting

Grout implements per-IP rate limiting to prevent DoS attacks. By default:

  • /avatar/ and /placeholder/ endpoints are rate limited to 100 requests per minute per IP with a burst of 10
  • Static assets (/favicon.ico, /robots.txt, /sitemap.xml) and the health endpoint (/health) are not rate limited
  • Rate limiting is based on client IP, respecting X-Forwarded-For and X-Real-IP headers for proxy scenarios
  • When the rate limit is exceeded, the server returns HTTP 429 Too Many Requests

To adjust the rate limits, set the environment variables or use command-line flags:

# Allow 200 requests per minute with burst of 20
RATE_LIMIT_RPM=200 RATE_LIMIT_BURST=20 go run ./cmd/grout

Docker Configuration

When using Docker Compose, you can override environment variables in docker-compose.yml:

environment:
  ADDR: ":3000"
  CACHE_SIZE: "5000"
  DOMAIN: "grout.example.com"
  STATIC_DIR: "/app/static"
  RATE_LIMIT_RPM: "200"
  RATE_LIMIT_BURST: "20"

Static Files

The application serves static files (like robots.txt and sitemap.xml) from the configured STATIC_DIR directory. If files are not found in this directory, the application falls back to embedded default versions.

To customize static files:

  1. Create a static directory (or use the default location)
  2. Add your customized robots.txt and/or sitemap.xml files
  3. These files support the {{DOMAIN}} placeholder, which will be replaced with the configured domain

Docker Deployment:

For persistent static files in Docker, mount a volume:

services:
  grout:
    volumes:
      - ./static:/app/static

This ensures your customizations persist across container restarts and updates. The embedded files serve as fallbacks if custom files are not provided.

Building from Source

Build binary

go build -o grout ./cmd/grout

Build Docker image

docker build -t grout .

Run Docker container

docker run -p 8080:8080 -e ADDR=":8080" -e DOMAIN="grout.example.com" grout

CI/CD

The project includes GitHub Actions workflows that automatically:

Test Workflow (.github/workflows/test.yml)

Runs on every pull request and push to main/master:

  • Tests: Runs all unit tests with race detection and coverage reporting
  • Lint: Runs golangci-lint for code quality checks
  • Format: Verifies code is properly formatted with go fmt
  • Vet: Runs go vet to catch common issues
  • Coverage: Optionally uploads coverage to Codecov (requires CODECOV_TOKEN secret)

Setup Secrets

To enable Codecov integration (optional):

  • CODECOV_TOKEN: Your Codecov upload token

Development Tips

  • Customize the defaults by editing the constants in internal/config/config.go.
  • Extend DrawImage in internal/render/render.go if you need additional shapes, padding, or font scaling strategies.
  • Consider fronting the service with a CDN when deploying to production so the long-lived cache headers are effective.

Running Tests

The project includes comprehensive unit and integration tests:

# Run all tests (unit + integration)
go test ./...

# Run tests with race detection and coverage
go test -race -coverprofile=coverage.out ./...

# Run only unit tests (skip integration tests)
go test -short ./...

# Run only integration tests
go test ./internal/handlers -run TestIntegration

# Run tests with verbose output
go test -v ./...

# Run benchmarks
go test -bench=. ./...

Integration tests start a real HTTP server and make actual HTTP requests to verify end-to-end functionality. They are fast enough for CI (complete in ~2 seconds) and can be skipped during development with the -short flag.

Documentation

For more information about the project:

Contributing

We welcome contributions! Please read our Contributing Guidelines before submitting pull requests.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Generate Placeholder Images to use in webpages

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •