Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions stacks/go-react/go-backend/.air.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# AngelaMos | 2026
# .air.toml - Hot reload configuration

root = "."
tmp_dir = "tmp"

[build]
cmd = "go build -o ./tmp/main ./cmd/api"
bin = "tmp/main"
full_bin = "./tmp/main"
include_ext = ["go", "yaml", "yml"]
exclude_dir = ["tmp", "vendor", "bin", "keys", "migrations"]
exclude_regex = ["_test\\.go"]
delay = 1000
stop_on_error = true
send_interrupt = true
kill_delay = 500

[log]
time = false

[color]
main = "cyan"
watcher = "magenta"
build = "yellow"
runner = "green"

[misc]
clean_on_exit = true
33 changes: 33 additions & 0 deletions stacks/go-react/go-backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# AngelaMos | 2026
# .env.example

# Environment: development, staging, production
ENVIRONMENT=development

# Server
HOST=0.0.0.0
PORT=8080

# Database
DATABASE_URL=postgres://postgres:postgres@localhost:5432/app?sslmode=disable

# Redis
REDIS_URL=redis://localhost:6379/0

# JWT
JWT_PRIVATE_KEY_PATH=keys/private.pem
JWT_PUBLIC_KEY_PATH=keys/public.pem
ACCESS_TOKEN_EXPIRE_MINUTES=15
REFRESH_TOKEN_EXPIRE_DAYS=7

# Rate Limiting
RATE_LIMIT_REQUESTS=100
RATE_LIMIT_WINDOW=1m

# OpenTelemetry
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
OTEL_SERVICE_NAME=go-backend

# Logging
LOG_LEVEL=debug
LOG_FORMAT=text
44 changes: 44 additions & 0 deletions stacks/go-react/go-backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# AngelaMos | 2026
# .gitignore

# Binaries
bin/
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test
*.test
coverage.out
coverage.html

# Build
tmp/

# IDE
.idea/
.vscode/
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db

# Environment
.env
.env.local
.env.*.local

# Keys (sensitive)
keys/*.pem
keys/*.key

# Vendor (if using)
vendor/

# Debug
__debug_bin*
113 changes: 113 additions & 0 deletions stacks/go-react/go-backend/.golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# AngelaMos | 2026
# .golangci.yml

version: "2"

linters:
default: none
enable:
- errcheck
- govet
- gosec
- bodyclose
- nilerr
- errorlint
- exhaustive
- gocritic
- funlen
- gocognit
- dupl
- goconst
- ineffassign
- unused
- unconvert
- unparam
- testifylint
- fatcontext

settings:
errcheck:
check-type-assertions: true
check-blank: true

funlen:
lines: 100
statements: 50

gocognit:
min-complexity: 20

govet:
enable-all: true
disable:
- fieldalignment

revive:
rules:
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
- name: increment-decrement
- name: var-declaration
- name: package-comments
disabled: true
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf
- name: empty-block
- name: superfluous-else
- name: unreachable-code

staticcheck:
checks:
- all

gosec:
excludes:
- G104

sloglint:
no-mixed-args: true
kv-only: true
context: all

issues:
max-same-issues: 50
exclude-dirs:
- vendor
- testdata
exclude-rules:
- path: _test\.go
linters:
- funlen
- dupl
- goconst


formatters:
enable:
- gci # Groups imports
- gofumpt # Whitespace
- golines # Vertical wrap
settings:
golines:
max-len: 80
reformat-tags: true
goimports:
local-prefixes:
- github.com/carterperez-dev/templates/go-backend
gci:
sections:
- standard
- default
- prefix(github.com/carterperez-dev)
custom-order: true
gofumpt:
extra-rules: true
36 changes: 36 additions & 0 deletions stacks/go-react/go-backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# AngelaMos | 2026
# Dockerfile - Multi-stage distroless build

# Build stage
FROM golang:1.25-bookworm AS builder

WORKDIR /build

# Copy dependency files first for layer caching
COPY go.mod go.sum ./
RUN go mod download

# Copy source code
COPY . .

# Build binary
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app ./cmd/api

# Production stage - Distroless
FROM gcr.io/distroless/static-debian12

# Copy binary from builder
COPY --from=builder /app /app

# Copy migrations (embedded, but useful for debugging)
COPY --from=builder /build/migrations /migrations

# Copy keys if they exist (for JWT)
COPY --from=builder /build/keys /keys

# Run as non-root user
USER nonroot:nonroot

EXPOSE 8080

ENTRYPOINT ["/app"]
110 changes: 110 additions & 0 deletions stacks/go-react/go-backend/Justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# AngelaMos | 2026
# Justfile

set dotenv-load := true

# Default recipe
default:
@just --list

# Development
# -----------

# Run the API server with hot reload (requires air)
dev:
air -c .air.toml

# Run the API server without hot reload
run:
go run ./cmd/api

# Build the binary
build:
CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o bin/api ./cmd/api

# Run tests
test:
go test -v -race -coverprofile=coverage.out ./...

# Run tests with coverage report
test-coverage: test
go tool cover -html=coverage.out -o coverage.html

# Run linter
lint:
golangci-lint run --timeout=5m

# Format code
fmt:
gofumpt -w .
goimports -w .

# Tidy dependencies
tidy:
go mod tidy

# Database
# --------

# Run database migrations
migrate-up:
goose -dir migrations postgres "${DATABASE_URL}" up

# Rollback last migration
migrate-down:
goose -dir migrations postgres "${DATABASE_URL}" down

# Check migration status
migrate-status:
goose -dir migrations postgres "${DATABASE_URL}" status

# Create new migration
migrate-create name:
goose -dir migrations create {{name}} sql

# Docker
# ------

# Start development containers
up:
docker compose -f dev.compose.yml up -d

# Stop development containers
down:
docker compose -f dev.compose.yml down

# View container logs
logs:
docker compose -f dev.compose.yml logs -f

# Rebuild and restart containers
rebuild:
docker compose -f dev.compose.yml up -d --build

# Build production image
docker-build:
docker build -t go-backend:latest .

# Keys
# ----

# Generate ES256 keypair for JWT signing
generate-keys:
#!/usr/bin/env bash
set -euo pipefail
mkdir -p keys
openssl ecparam -genkey -name prime256v1 -noout -out keys/private.pem
openssl ec -in keys/private.pem -pubout -out keys/public.pem
chmod 600 keys/private.pem
echo "ES256 keypair generated in keys/"

# Clean
# -----

# Remove build artifacts
clean:
rm -rf bin/ coverage.out coverage.html tmp/

# Full clean including containers
clean-all: clean
docker compose -f dev.compose.yml down -v
Loading
Loading