fix(canary-phase0): address audit findings before phase rollup
Phase 0 audit (superpowers:code-reviewer + general-purpose) returned FAIL
on shared findings. This commit resolves the blockers + important items.
Blockers:
- infra/docker/vite.prod: replace 'react-scss/' COPY paths with 'frontend/'
(would have broken 'docker compose build nginx')
- Delete backend/Dockerfile (broken template debris referencing deleted
cmd/api, missing migrations/, deleted keys/)
- Delete backend/Justfile (docker compose recipes pointed at deleted
backend/compose.yml + backend/dev.compose.yml; project-root justfile
[backend] group covers everything operationally)
- backend/internal/admin/handler.go: strip the residual JWT/auth scaffolding
(AuthService interface, authSvc field, authenticator+adminOnly mw params).
Phase 12 will gate /admin under OperatorBearer; for now RegisterRoutes
takes only chi.Router.
- Delete backend/.env + backend/.env.example: stale template duplicates
containing only template keys; project-root .env.example is the source
of truth per spec §12.4.
Important:
- Add © glyph to file headers in cloudflared.compose.yml, infra/docker/
vite.{dev,prod}, backend/.gitignore (operator's standing rule:
'©AngelaMos | 2026', not 'AngelaMos | 2026')
- backend/.gitignore: drop stale 'keys/*.pem' / 'keys/*.key' entries
(backend/keys/ no longer exists)
- scripts/init.sh: rename 'local_tmp' variable to 'tmp_dir' to remove
visual ambiguity with the 'local' bash keyword (both audit agents
briefly misread it as 'local local_tmp=...')
- .env.example: add inline comment explaining the dual NGINX_HOST_PORT
defaults (22784 prod compose, 58495 dev compose; do not override in
shared .env)
Deferred (logged but not blocking Phase 0):
- Pre-existing template Go files (internal/{config,core,server,health,
middleware/{request_id,logging,headers,ratelimit},admin}/*.go) carry
'// AngelaMos | 2026' without ©. Bulk-fixing these belongs to a
hygiene pass; not Phase 0 scope.
- frontend/vite.config.ts and frontend/stylelint.config.js reference
'2025'; operator-imported template files, separate concern.
- APP_ENVIRONMENT vs ENVIRONMENT spec/impl mismatch: spec §12.1 sample
uses APP_ENVIRONMENT; envKeyMap in config.go uses ENVIRONMENT (template
default). Implementation is internally consistent. Logged in plan
Appendix C as a non-blocking spec amendment candidate.
Verification: go build ./... + go vet ./... clean. grep returns no
hits for react-scss / carterperez-dev/templates / JWTConfig /
InvalidateAllSessions across backend/, infra/, scripts/, compose files.
This commit is contained in:
parent
a4811d1c04
commit
98c006897d
|
|
@ -10,6 +10,9 @@
|
||||||
# Public-facing
|
# Public-facing
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
APP_NAME=canary-token-generator
|
APP_NAME=canary-token-generator
|
||||||
|
|
||||||
|
# Production nginx host port (compose.yml). Dev compose has its own default
|
||||||
|
# of 58495 in dev.compose.yml; do not override NGINX_HOST_PORT in shared .env.
|
||||||
NGINX_HOST_PORT=22784
|
NGINX_HOST_PORT=22784
|
||||||
|
|
||||||
# Used to construct trigger URLs embedded in artifacts (e.g. canary docs).
|
# Used to construct trigger URLs embedded in artifacts (e.g. canary docs).
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
# Rate Limiting
|
|
||||||
RATE_LIMIT_REQUESTS=100
|
|
||||||
RATE_LIMIT_WINDOW=1m
|
|
||||||
|
|
||||||
# OpenTelemetry
|
|
||||||
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
|
|
||||||
OTEL_SERVICE_NAME=canary-token-generator
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
LOG_LEVEL=debug
|
|
||||||
LOG_FORMAT=text
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# AngelaMos | 2026
|
# ©AngelaMos | 2026
|
||||||
# .gitignore
|
# .gitignore
|
||||||
|
|
||||||
# Binaries
|
# Binaries
|
||||||
|
|
@ -33,10 +33,6 @@ Thumbs.db
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
|
||||||
# Keys (sensitive)
|
|
||||||
keys/*.pem
|
|
||||||
keys/*.key
|
|
||||||
|
|
||||||
# Vendor (if using)
|
# Vendor (if using)
|
||||||
vendor/
|
vendor/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
# 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"]
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
# 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 canary server without hot reload
|
|
||||||
run:
|
|
||||||
go run ./cmd/canary
|
|
||||||
|
|
||||||
# Build the binary
|
|
||||||
build:
|
|
||||||
CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o bin/canary ./cmd/canary
|
|
||||||
|
|
||||||
# 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 canary-token-generator:latest .
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// AngelaMos | 2026
|
// ©AngelaMos | 2026
|
||||||
// handler.go
|
// handler.go
|
||||||
|
|
||||||
package admin
|
package admin
|
||||||
|
|
@ -15,16 +15,11 @@ import (
|
||||||
"github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/core"
|
"github.com/CarterPerez-dev/cybersecurity-projects/canary-token-generator/backend/internal/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthService interface {
|
|
||||||
InvalidateAllSessions(ctx context.Context) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
dbStats func() sql.DBStats
|
dbStats func() sql.DBStats
|
||||||
redisStats func() *redis.PoolStats
|
redisStats func() *redis.PoolStats
|
||||||
redisPing func(ctx context.Context) error
|
redisPing func(ctx context.Context) error
|
||||||
dbPing func(ctx context.Context) error
|
dbPing func(ctx context.Context) error
|
||||||
authSvc AuthService
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HandlerConfig struct {
|
type HandlerConfig struct {
|
||||||
|
|
@ -32,7 +27,6 @@ type HandlerConfig struct {
|
||||||
RedisStats func() *redis.PoolStats
|
RedisStats func() *redis.PoolStats
|
||||||
RedisPing func(ctx context.Context) error
|
RedisPing func(ctx context.Context) error
|
||||||
DBPing func(ctx context.Context) error
|
DBPing func(ctx context.Context) error
|
||||||
AuthSvc AuthService
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler(cfg HandlerConfig) *Handler {
|
func NewHandler(cfg HandlerConfig) *Handler {
|
||||||
|
|
@ -41,18 +35,11 @@ func NewHandler(cfg HandlerConfig) *Handler {
|
||||||
redisStats: cfg.RedisStats,
|
redisStats: cfg.RedisStats,
|
||||||
redisPing: cfg.RedisPing,
|
redisPing: cfg.RedisPing,
|
||||||
dbPing: cfg.DBPing,
|
dbPing: cfg.DBPing,
|
||||||
authSvc: cfg.AuthSvc,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) RegisterRoutes(
|
func (h *Handler) RegisterRoutes(r chi.Router) {
|
||||||
r chi.Router,
|
|
||||||
authenticator, adminOnly func(http.Handler) http.Handler,
|
|
||||||
) {
|
|
||||||
r.Route("/admin", func(r chi.Router) {
|
r.Route("/admin", func(r chi.Router) {
|
||||||
r.Use(authenticator)
|
|
||||||
r.Use(adminOnly)
|
|
||||||
|
|
||||||
r.Get("/stats", h.GetSystemStats)
|
r.Get("/stats", h.GetSystemStats)
|
||||||
r.Get("/stats/db", h.GetDatabaseStats)
|
r.Get("/stats/db", h.GetDatabaseStats)
|
||||||
r.Get("/stats/redis", h.GetRedisStats)
|
r.Get("/stats/redis", h.GetRedisStats)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# AngelaMos | 2026
|
# ©AngelaMos | 2026
|
||||||
# cloudflared.compose.yml
|
# cloudflared.compose.yml
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Cloudflare Tunnel for production remote access
|
# Cloudflare Tunnel for production remote access
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# AngelaMos | 2026
|
# ©AngelaMos | 2026
|
||||||
# vite.dev
|
# vite.dev
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Development Dockerfile for Vite/React frontend
|
# Development Dockerfile for Vite/React frontend
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# AngelaMos | 2026
|
# ©AngelaMos | 2026
|
||||||
# vite.prod
|
# vite.prod
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Production Dockerfile: builds Vite app, serves via Nginx
|
# Production Dockerfile: builds Vite app, serves via Nginx
|
||||||
|
|
@ -15,12 +15,12 @@ RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY react-scss/package.json react-scss/pnpm-lock.yaml* ./
|
COPY frontend/package.json frontend/pnpm-lock.yaml* ./
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
|
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
|
||||||
pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
|
|
||||||
COPY react-scss/ .
|
COPY frontend/ .
|
||||||
|
|
||||||
ARG VITE_API_URL=/api
|
ARG VITE_API_URL=/api
|
||||||
ARG VITE_APP_TITLE="My App"
|
ARG VITE_APP_TITLE="My App"
|
||||||
|
|
|
||||||
|
|
@ -60,14 +60,14 @@ if [[ -n "$ACCT" && -n "$KEY" ]]; then
|
||||||
if [[ ! -f "$MMDB_PATH" ]]; then
|
if [[ ! -f "$MMDB_PATH" ]]; then
|
||||||
mkdir -p "$DATA_DIR"
|
mkdir -p "$DATA_DIR"
|
||||||
echo " downloading GeoLite2-City.mmdb from MaxMind..."
|
echo " downloading GeoLite2-City.mmdb from MaxMind..."
|
||||||
local_tmp="$(mktemp -d)"
|
tmp_dir="$(mktemp -d)"
|
||||||
trap 'rm -rf "$local_tmp"' EXIT
|
trap 'rm -rf "$tmp_dir"' EXIT
|
||||||
if curl -sSf -u "$ACCT:$KEY" \
|
if curl -sSf -u "$ACCT:$KEY" \
|
||||||
"https://download.maxmind.com/geoip/databases/GeoLite2-City/download?suffix=tar.gz" \
|
"https://download.maxmind.com/geoip/databases/GeoLite2-City/download?suffix=tar.gz" \
|
||||||
-o "$local_tmp/geolite.tar.gz"; then
|
-o "$tmp_dir/geolite.tar.gz"; then
|
||||||
tar -xzf "$local_tmp/geolite.tar.gz" -C "$local_tmp" --strip-components=1 \
|
tar -xzf "$tmp_dir/geolite.tar.gz" -C "$tmp_dir" --strip-components=1 \
|
||||||
"*/GeoLite2-City.mmdb"
|
"*/GeoLite2-City.mmdb"
|
||||||
mv "$local_tmp/GeoLite2-City.mmdb" "$MMDB_PATH"
|
mv "$tmp_dir/GeoLite2-City.mmdb" "$MMDB_PATH"
|
||||||
echo " GeoLite2-City.mmdb downloaded to $MMDB_PATH"
|
echo " GeoLite2-City.mmdb downloaded to $MMDB_PATH"
|
||||||
else
|
else
|
||||||
echo " Warning: GeoLite2 download failed (check MAXMIND_ACCOUNT_ID and MAXMIND_LICENSE_KEY)" >&2
|
echo " Warning: GeoLite2 download failed (check MAXMIND_ACCOUNT_ID and MAXMIND_LICENSE_KEY)" >&2
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue