A self-hostable web application for cataloging personal building block collections (LEGO, Block Tech, and generic clone brands). Built with Go, SQLite, and server-rendered HTML templates.
- 🧱 Collection Management: Track sets, brands, and collection items
- 📱 Mobile-First: Responsive design optimized for desktop and mobile
- 🖼️ Collection Photos: Upload multiple photos per collection item
- 🔍 External Data: Optional metadata enrichment from Brickset & Rebrickable
- 💰 Valuation (TBD): Resale valuation (provider not finalized)
- 🏠 Self-Hosted: Single binary deployment with SQLite database
- 🔒 Secure: JWT authentication with secure cookie sessions
- 📊 Dashboard: Statistics and overview of your collection
# Clone and build
git clone https://github.com/matthewgall/blocks.git
cd blocks
go mod tidy
go build ./cmd/blocks
# Create data directory
mkdir -p data/uploads
# Run
./blocksOn first launch, visit /setup to create the initial admin account.
Create a config.yaml file:
server:
address: ":8080"
read_timeout: 10s
write_timeout: 10s
idle_timeout: 60s
database:
path: "data/blocks.db"
auth:
session_secret: "your-secret-key-here"
bcrypt_cost: 12
uploads:
method: "local" # local or s3
max_size: 10485760 # 10MB
local:
directory: "data/uploads"
s3:
bucket: ""
region: ""
endpoint: "" # Optional for S3-compatible services
public_url: "" # Optional hostname/base URL for serving uploads
access_key_id: ""
secret_access_key: ""
session_token: ""
prefix: ""
path_style: false
app:
name: "Blocks"
version: "1.0.0"
default_currency: "GBP"
embed_assets: true
# Optional external providers
providers:
brickset:
api_key: "your-brickset-api-key"
daily_limit: 100
rebrickable:
api_key: "your-rebrickable-api-key"
cache:
provider: "sqlite" # sqlite or redis
ttl:
default: 24h
remote: 720h # 30 days
redis:
url: "" # Optional: redis://user:pass@host:6379/0 or rediss://host:6379/0
addr: "" # e.g. "localhost:6379"
password: ""
db: 0
tls: falsePOST /api/auth/login- Login with username/passwordPOST /api/auth/logout- Logout
GET /api/brands- List all brandsPOST /api/brands- Create new brandGET /api/brands/{id}- Get brand detailsPUT /api/brands/{id}- Update brandDELETE /api/brands/{id}- Delete brand
GET /api/sets- List all sets (with search/filter)POST /api/sets- Create new setGET /api/sets/{id}- Get set detailsPUT /api/sets/{id}- Update setDELETE /api/sets/{id}- Delete set
GET /api/collection- List collection itemsPOST /api/collection- Add collection itemPUT /api/collection/{id}- Update collection itemDELETE /api/collection/{id}- Delete collection item
- Valuation provider is not finalized yet.
GET /api/providers/sets/{setNum}- Fetch set metadata (Brickset → Rebrickable)
- Purpose: Set metadata and images
- Rate Limit: ~100 calls/day (configurable)
- Cache TTL: 30 days
- Required: API key from brickset.com
- Purpose: Alternative metadata source
- Rate Limit: 1 rps, burst 3
- Cache TTL: 30 days
- Required: API key from rebrickable.com
- Provider selection is in progress.
The application uses SQLite with the following main tables:
brands- LEGO/clone brand informationsets- Building set catalogcollection_items- Personal collection trackingvaluations- Price valuation historyexternal_cache- API response cachingusers- Authentication (single-user)
go build ./cmd/blocksgo test ./...golangci-lint runcmd/blocks/ # Main application entry point
internal/
config/ # Configuration management
db/ # Database layer and migrations
models/ # Data models and types
auth/ # Authentication and sessions
cache/ # External API caching
providers/ # External API clients
brickset/ # Brickset API client
rebrickable/ # Rebrickable API client
http/ # HTTP server and handlers
templates/ # HTML templates
static/ # Static assets (CSS, JS, images)
data/ # Runtime data directory
- Authentication: JWT tokens with secure HttpOnly cookies
- CSRF Protection: Built into POST routes
- Input Validation: All user input validated and sanitized
- SQL Injection: Prepared statements used throughout
- Rate Limiting: Configurable limits on auth endpoints
- File Uploads: Size-limited and validated uploads
- Import your collection via
/importusing Brickset or Rebrickable CSV exports. - Export your collection via
/exportin Brickset CSV, Rebrickable CSV, or Blocks formats. - For a full backup, use the Blocks SQLite export (
/export/blocks?format=sqlite) or copydata/blocks.db.
# Backup database
cp data/blocks.db backup/blocks-$(date +%Y%m%d).db
# Backup uploads
tar -czf backup/uploads-$(date +%Y%m%d).tar.gz data/uploads/# Stop the application
pkill blocks
# Restore database
cp backup/blocks-20231201.db data/blocks.db
# Restore uploads
tar -xzf backup/uploads-20231201.tar.gz
# Start the application
./blocks# Build image
docker build -t blocks .
# Run container
docker run -d \
--name blocks \
-p 8080:8080 \
-v $(pwd)/data:/data \
-v $(pwd)/config.yaml:/app/config.yaml:ro \
blocksCreate /etc/systemd/system/blocks.service:
[Unit]
Description=Blocks Building Block Collection Manager
After=network.target
[Service]
Type=simple
User=blocks
WorkingDirectory=/opt/blocks
ExecStart=/opt/blocks/blocks
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl enable blocks
sudo systemctl start blocks- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
- Issues: Report bugs and feature requests on GitHub
- Documentation: See the
/docsdirectory for detailed guides - Community: Join discussions in GitHub Issues
- Initial release
- Complete collection management
- External API integration
- Mobile-responsive UI
- Docker deployment support