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
7 changes: 7 additions & 0 deletions backend-api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ DATABASE_URL=postgres://civiclens:password@localhost:5432/civiclens_dev?sslmode=
# ─── Auth ─────────────────────────────────────────────────────────────────────
# Generate with: openssl rand -hex 32
JWT_SECRET=change-me-in-production-use-a-long-random-secret

# ─── Initial Accounts & Auto-Seeding ──────────────────────────────────────────
INITIAL_ADMIN_EMAIL=admin@civiclens.org
INITIAL_ADMIN_PASSWORD=AdminSecurePass123!
INITIAL_USER_EMAIL=user@civiclens.org
INITIAL_USER_PASSWORD=UserSecurePass123!

27 changes: 14 additions & 13 deletions backend-api/.golangci.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
version: "2"

# golangci-lint configuration for CivicLens backend API
# Documentation: https://golangci-lint.run/usage/configuration/

run:
timeout: 5m
go: "1.22"

output:
formats:
- format: colored-line-number
print-issued-lines: true
print-linter-name: true
# output:
# format: colored-line-number
# print-issued-lines: true
# print-linter-name: true

linters:
enable:
Expand All @@ -23,11 +24,11 @@ linters:
- staticcheck # Comprehensive static analysis
- unused # Finds unused code
- ineffassign # Detects ineffectual assignments
- deadcode # Finds unreachable code
# - deadcode # Finds unreachable code

# Style & conventions
- gofmt # Enforces gofmt formatting
- goimports # Enforces import grouping
# - gofmt # Enforces gofmt formatting
# - goimports # Enforces import grouping
- misspell # Catches common misspellings
- godot # Checks that comments end with a period
- revive # Fast, configurable, extensible linter
Expand All @@ -49,11 +50,11 @@ linters:
- gochecknoglobals # Allows global vars for now

linters-settings:
gofmt:
simplify: true

goimports:
local-prefixes: github.com/civiclens
# gofmt:
# simplify: true
#
# goimports:
# local-prefixes: github.com/civiclens

errcheck:
check-type-assertions: true
Expand Down
55 changes: 43 additions & 12 deletions backend-api/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,62 @@
# Usage:
# make run – start the development server
# make test – run all tests with race detection
# make test-v – run tests with verbose output
# make lint – run golangci-lint
# make fmt – format all Go files
# make vet – run go vet
# make tidy – tidy go module dependencies
# make sqlc-gen – regenerate type-safe DB code from SQL files
# make build – produce a production binary
# make clean – remove build artifacts
# ────────────────────────────────────────────────────────────────────────────

.PHONY: all run build test lint fmt sqlc-gen clean tidy help
.PHONY: all run build test test-v lint fmt vet tidy sqlc-gen clean help

# Default goal
all: fmt lint test build

BINARY_NAME := civiclens-api
BUILD_DIR := ./bin

# Prevent Go from auto-downloading a newer toolchain triggered by transitive deps
export GOTOOLCHAIN := local

# OS detection for cross-platform support
ifeq ($(OS),Windows_NT)
BINARY_EXT := .exe
else
BINARY_EXT :=
endif

BINARY_NAME := civiclens-api$(BINARY_EXT)
BUILD_DIR := bin
BINARY_PATH := $(BUILD_DIR)/$(BINARY_NAME)

# ── Development ──────────────────────────────────────────────────────────────

## run: Start the API server using environment variables from .env
run:
go run ./cmd/server

## build: Compile a production binary to ./bin/
## build: Compile a production binary to bin/
build: export CGO_ENABLED := 0
build:
ifeq ($(OS),Windows_NT)
@if not exist $(BUILD_DIR) mkdir $(BUILD_DIR)
else
@mkdir -p $(BUILD_DIR)
CGO_ENABLED=0 go build -ldflags="-s -w" -o $(BUILD_DIR)/$(BINARY_NAME) ./cmd/server
@echo "Binary written to $(BUILD_DIR)/$(BINARY_NAME)"
endif
go build -ldflags="-s -w" -o $(BINARY_PATH) ./cmd/server
@echo Binary written to $(BINARY_PATH)

# ── Testing ───────────────────────────────────────────────────────────────────

## test: Run all tests with race detection and coverage report
## test: Run all tests with coverage report
test:
go test -race -coverprofile=coverage.out -covermode=atomic ./...
go test -coverprofile=coverage.out -covermode=atomic ./...
go tool cover -func=coverage.out

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

# ── Code Quality ─────────────────────────────────────────────────────────────

Expand Down Expand Up @@ -74,10 +90,25 @@ sqlc-gen:

## clean: Remove build artifacts and coverage reports
clean:
rm -rf $(BUILD_DIR) coverage.out
ifeq ($(OS),Windows_NT)
@if exist $(BUILD_DIR) rmdir /s /q $(BUILD_DIR)
@if exist coverage.out del /q coverage.out
else
@rm -rf $(BUILD_DIR) coverage.out
endif

# ── Help ─────────────────────────────────────────────────────────────────────

## help: Display this help message
help:
@grep -E '^## ' $(MAKEFILE_LIST) | sed 's/^## //'
@echo Usage:
@echo make run - start the development server
@echo make test - run all tests with race detection
@echo make test-v - run tests with verbose output
@echo make lint - run golangci-lint
@echo make fmt - format all Go files
@echo make vet - run go vet
@echo make tidy - tidy go module dependencies
@echo make sqlc-gen - regenerate type-safe DB code from SQL files
@echo make build - produce a production binary
@echo make clean - remove build artifacts
16 changes: 15 additions & 1 deletion backend-api/cmd/server/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Package main is the entrypoint for the CivicLens backend API.
package main

import (
"context"
"errors"
"fmt"
"log"
"net/http"
Expand All @@ -12,6 +14,7 @@ import (

"github.com/civiclens/backend-api/internal/config"
"github.com/civiclens/backend-api/internal/db"
"github.com/civiclens/backend-api/internal/db/sqlcdb"
"github.com/civiclens/backend-api/internal/router"
)

Expand All @@ -29,14 +32,25 @@ func main() {
}
defer pool.Close()

// Run database migrations
if err := db.RunMigrations(cfg.DatabaseURL); err != nil {
log.Fatalf("failed to run database migrations: %v", err)
}

// Seed initial data (e.g. Super Admin)
querier := sqlcdb.New(pool)
if err := db.SeedInitialData(context.Background(), querier, cfg); err != nil {
log.Fatalf("failed to seed initial data: %v", err)
}

// Build Echo router
e := router.New(pool, cfg)

// Start server with graceful shutdown
serverAddr := fmt.Sprintf(":%s", cfg.Port)
go func() {
log.Printf("starting server on %s", serverAddr)
if err := e.Start(serverAddr); err != nil && err != http.ErrServerClosed {
if err := e.Start(serverAddr); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("server error: %v", err)
}
}()
Expand Down
1 change: 1 addition & 0 deletions backend-api/coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mode: atomic
30 changes: 17 additions & 13 deletions backend-api/go.mod
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
module github.com/civiclens/backend-api

go 1.22
go 1.25.0

require (
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/golang-migrate/migrate/v4 v4.19.1
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v5 v5.6.0
github.com/joho/godotenv v1.5.1
github.com/labstack/echo/v4 v4.12.0
github.com/stretchr/testify v1.9.0
github.com/labstack/echo-jwt/v4 v4.4.0
github.com/labstack/echo/v4 v4.13.4
github.com/stretchr/testify v1.11.1
golang.org/x/crypto v0.54.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/net v0.56.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.40.0 // indirect
golang.org/x/time v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading