You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1377 lines
32 KiB
Markdown
1377 lines
32 KiB
Markdown
# 1.1 Development Environment Setup - Detailed Implementation Plan
|
|
|
|
## Overview
|
|
|
|
| Attribute | Value |
|
|
|-----------|-------|
|
|
| **Priority** | CRITICAL |
|
|
| **Duration** | 2-3 days |
|
|
| **Dependencies** | None (first step) |
|
|
| **Team Size** | 1 developer |
|
|
|
|
## Objectives
|
|
|
|
1. Establish a reproducible development environment for all team members
|
|
2. Configure Go multi-module monorepo structure
|
|
3. Set up frontend workspace with proper tooling
|
|
4. Create containerized local infrastructure (PostgreSQL, Redis, Zitadel)
|
|
5. Implement consistent linting and formatting standards
|
|
6. Document onboarding process
|
|
|
|
---
|
|
|
|
## Task Breakdown
|
|
|
|
### Task 1: Initialize Go Workspace (go.work)
|
|
|
|
**Duration:** 2-3 hours
|
|
|
|
#### 1.1.1 Create Root go.work File
|
|
|
|
Create the Go workspace file at repository root to manage multiple modules.
|
|
|
|
**File:** `/go.work`
|
|
```go
|
|
go 1.25
|
|
|
|
use (
|
|
./backend/services/game-session-service
|
|
./backend/services/question-bank-service
|
|
./backend/services/user-service
|
|
./backend/services/leaderboard-service
|
|
./backend/services/admin-service
|
|
./backend/services/gateway-service
|
|
./backend/shared
|
|
)
|
|
```
|
|
|
|
#### 1.1.2 Initialize Each Service Module
|
|
|
|
For each service, create the go.mod file:
|
|
|
|
**Directory Structure to Create:**
|
|
```
|
|
backend/
|
|
├── go.work
|
|
├── services/
|
|
│ ├── game-session-service/
|
|
│ │ ├── go.mod
|
|
│ │ ├── go.sum
|
|
│ │ └── cmd/
|
|
│ │ └── main.go
|
|
│ ├── question-bank-service/
|
|
│ │ ├── go.mod
|
|
│ │ ├── go.sum
|
|
│ │ └── cmd/
|
|
│ │ └── main.go
|
|
│ ├── user-service/
|
|
│ │ ├── go.mod
|
|
│ │ ├── go.sum
|
|
│ │ └── cmd/
|
|
│ │ └── main.go
|
|
│ ├── leaderboard-service/
|
|
│ │ ├── go.mod
|
|
│ │ ├── go.sum
|
|
│ │ └── cmd/
|
|
│ │ └── main.go
|
|
│ ├── admin-service/
|
|
│ │ ├── go.mod
|
|
│ │ ├── go.sum
|
|
│ │ └── cmd/
|
|
│ │ └── main.go
|
|
│ └── gateway-service/
|
|
│ ├── go.mod
|
|
│ ├── go.sum
|
|
│ └── cmd/
|
|
│ └── main.go
|
|
└── shared/
|
|
├── go.mod
|
|
├── go.sum
|
|
└── .gitkeep
|
|
```
|
|
|
|
**Example go.mod for game-session-service:**
|
|
```go
|
|
module knowfoolery/backend/services/game-session-service
|
|
|
|
go 1.25
|
|
|
|
require (
|
|
github.com/gofiber/fiber/v3 v3.0.0
|
|
knowfoolery/backend/shared v0.0.0
|
|
)
|
|
|
|
replace knowfoolery/backend/shared => ../../shared
|
|
```
|
|
|
|
**Example go.mod for shared package:**
|
|
```go
|
|
module knowfoolery/backend/shared
|
|
|
|
go 1.25
|
|
|
|
require (
|
|
github.com/rs/zerolog v1.31.0
|
|
github.com/go-playground/validator/v10 v10.16.0
|
|
github.com/prometheus/client_golang v1.17.0
|
|
go.opentelemetry.io/otel v1.21.0
|
|
)
|
|
```
|
|
|
|
#### 1.1.3 Create Stub Main Files
|
|
|
|
**File:** `backend/services/game-session-service/cmd/main.go`
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"log"
|
|
"github.com/gofiber/fiber/v3"
|
|
)
|
|
|
|
func main() {
|
|
app := fiber.New(fiber.Config{
|
|
AppName: "Know Foolery - Game Session Service",
|
|
})
|
|
|
|
app.Get("/health", func(c fiber.Ctx) error {
|
|
return c.JSON(fiber.Map{"status": "healthy", "service": "game-session"})
|
|
})
|
|
|
|
log.Fatal(app.Listen(":8080"))
|
|
}
|
|
```
|
|
|
|
**Repeat for each service with appropriate port:**
|
|
| Service | Port |
|
|
|---------|------|
|
|
| game-session-service | 8080 |
|
|
| question-bank-service | 8081 |
|
|
| user-service | 8082 |
|
|
| leaderboard-service | 8083 |
|
|
| admin-service | 8085 |
|
|
| gateway-service | 8086 |
|
|
|
|
#### Verification Steps:
|
|
```bash
|
|
cd backend
|
|
go work sync
|
|
go build ./...
|
|
```
|
|
|
|
---
|
|
|
|
### Task 2: Set Up Shared Packages Structure
|
|
|
|
**Duration:** 2-3 hours
|
|
|
|
#### 1.2.1 Create Shared Package Directory Structure
|
|
|
|
```
|
|
backend/shared/
|
|
├── go.mod
|
|
├── go.sum
|
|
├── domain/
|
|
│ ├── errors/
|
|
│ │ ├── errors.go # Base error types
|
|
│ │ └── codes.go # Error code constants
|
|
│ ├── events/
|
|
│ │ ├── event.go # Event interface
|
|
│ │ └── contracts.go # Event type definitions
|
|
│ ├── types/
|
|
│ │ ├── id.go # ID type wrappers
|
|
│ │ ├── pagination.go # Pagination types
|
|
│ │ └── enums.go # Common enums
|
|
│ └── valueobjects/
|
|
│ ├── player_name.go # PlayerName value object
|
|
│ └── score.go # Score value object
|
|
└── infra/
|
|
├── auth/
|
|
│ ├── zitadel/
|
|
│ │ ├── client.go # Zitadel client
|
|
│ │ └── middleware.go # JWT middleware
|
|
│ └── rbac/
|
|
│ └── roles.go # Role definitions
|
|
├── database/
|
|
│ ├── postgres/
|
|
│ │ └── client.go # PostgreSQL connection factory
|
|
│ └── redis/
|
|
│ └── client.go # Redis connection factory
|
|
├── observability/
|
|
│ ├── logging/
|
|
│ │ └── logger.go # Zerolog setup
|
|
│ ├── tracing/
|
|
│ │ └── tracer.go # OpenTelemetry setup
|
|
│ └── metrics/
|
|
│ └── prometheus.go # Prometheus registration
|
|
├── security/
|
|
│ └── sanitize.go # Input sanitization
|
|
└── utils/
|
|
├── httputil/
|
|
│ ├── errors.go # HTTP error responses
|
|
│ ├── response.go # Standard response format
|
|
│ └── pagination.go # Pagination helpers
|
|
└── validation/
|
|
└── validator.go # Validation helpers
|
|
```
|
|
|
|
#### 1.2.2 Create Placeholder Files
|
|
|
|
Create minimal placeholder implementations for each file to establish the structure.
|
|
|
|
**File:** `backend/shared/domain/errors/errors.go`
|
|
```go
|
|
package errors
|
|
|
|
import "fmt"
|
|
|
|
// DomainError represents a domain-level error
|
|
type DomainError struct {
|
|
Code string
|
|
Message string
|
|
Err error
|
|
}
|
|
|
|
func (e *DomainError) Error() string {
|
|
if e.Err != nil {
|
|
return fmt.Sprintf("[%s] %s: %v", e.Code, e.Message, e.Err)
|
|
}
|
|
return fmt.Sprintf("[%s] %s", e.Code, e.Message)
|
|
}
|
|
|
|
func (e *DomainError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// Common domain errors
|
|
var (
|
|
ErrNotFound = &DomainError{Code: "NOT_FOUND", Message: "Resource not found"}
|
|
ErrValidation = &DomainError{Code: "VALIDATION", Message: "Validation failed"}
|
|
ErrUnauthorized = &DomainError{Code: "UNAUTHORIZED", Message: "Unauthorized access"}
|
|
ErrConflict = &DomainError{Code: "CONFLICT", Message: "Resource conflict"}
|
|
ErrInternalServer = &DomainError{Code: "INTERNAL", Message: "Internal server error"}
|
|
)
|
|
```
|
|
|
|
**File:** `backend/shared/infra/observability/logging/logger.go`
|
|
```go
|
|
package logging
|
|
|
|
import (
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog"
|
|
)
|
|
|
|
// NewLogger creates a new zerolog logger
|
|
func NewLogger(serviceName, environment string) zerolog.Logger {
|
|
zerolog.TimeFieldFormat = time.RFC3339Nano
|
|
|
|
var logger zerolog.Logger
|
|
|
|
if environment == "development" {
|
|
logger = zerolog.New(zerolog.ConsoleWriter{
|
|
Out: os.Stdout,
|
|
TimeFormat: "15:04:05",
|
|
}).With().Timestamp().Logger()
|
|
} else {
|
|
logger = zerolog.New(os.Stdout).With().Timestamp().Logger()
|
|
}
|
|
|
|
return logger.With().
|
|
Str("service", serviceName).
|
|
Str("environment", environment).
|
|
Logger()
|
|
}
|
|
```
|
|
|
|
#### Verification Steps:
|
|
```bash
|
|
cd backend/shared
|
|
go mod tidy
|
|
go build ./...
|
|
```
|
|
|
|
---
|
|
|
|
### Task 3: Configure Linting (golangci-lint, ESLint)
|
|
|
|
**Duration:** 2-3 hours
|
|
|
|
#### 1.3.1 Backend Linting (golangci-lint)
|
|
|
|
**File:** `backend/.golangci.yml`
|
|
```yaml
|
|
run:
|
|
timeout: 5m
|
|
tests: true
|
|
go: "1.25"
|
|
|
|
linters:
|
|
enable:
|
|
- gofmt
|
|
- goimports
|
|
- govet
|
|
- errcheck
|
|
- staticcheck
|
|
- unused
|
|
- gosimple
|
|
- ineffassign
|
|
- typecheck
|
|
- gosec
|
|
- misspell
|
|
- lll
|
|
- unconvert
|
|
- dupl
|
|
- goconst
|
|
- gocyclo
|
|
- prealloc
|
|
- bodyclose
|
|
- noctx
|
|
- exhaustive
|
|
|
|
linters-settings:
|
|
lll:
|
|
line-length: 120
|
|
gocyclo:
|
|
min-complexity: 15
|
|
dupl:
|
|
threshold: 150
|
|
goconst:
|
|
min-len: 3
|
|
min-occurrences: 3
|
|
misspell:
|
|
locale: US
|
|
gosec:
|
|
excludes:
|
|
- G104 # Unhandled errors (we use errcheck)
|
|
|
|
issues:
|
|
exclude-rules:
|
|
- path: _test\.go
|
|
linters:
|
|
- gocyclo
|
|
- errcheck
|
|
- dupl
|
|
- gosec
|
|
- path: cmd/main\.go
|
|
linters:
|
|
- gocyclo
|
|
max-issues-per-linter: 50
|
|
max-same-issues: 10
|
|
```
|
|
|
|
#### 1.3.2 Frontend Linting (ESLint)
|
|
|
|
**File:** `frontend/.eslintrc.json`
|
|
```json
|
|
{
|
|
"root": true,
|
|
"env": {
|
|
"browser": true,
|
|
"es2022": true,
|
|
"node": true
|
|
},
|
|
"extends": [
|
|
"eslint:recommended",
|
|
"plugin:@typescript-eslint/recommended",
|
|
"plugin:solid/recommended",
|
|
"prettier"
|
|
],
|
|
"parser": "@typescript-eslint/parser",
|
|
"parserOptions": {
|
|
"ecmaVersion": "latest",
|
|
"sourceType": "module"
|
|
},
|
|
"plugins": [
|
|
"@typescript-eslint",
|
|
"solid"
|
|
],
|
|
"rules": {
|
|
"@typescript-eslint/explicit-function-return-type": "warn",
|
|
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
|
|
"@typescript-eslint/no-explicit-any": "warn",
|
|
"prefer-const": "error",
|
|
"no-var": "error",
|
|
"object-shorthand": "error",
|
|
"prefer-template": "error",
|
|
"no-console": ["warn", { "allow": ["warn", "error"] }]
|
|
},
|
|
"ignorePatterns": [
|
|
"dist",
|
|
"node_modules",
|
|
"*.config.js",
|
|
"*.config.ts"
|
|
]
|
|
}
|
|
```
|
|
|
|
**File:** `frontend/.prettierrc`
|
|
```json
|
|
{
|
|
"semi": false,
|
|
"singleQuote": true,
|
|
"tabWidth": 2,
|
|
"trailingComma": "es5",
|
|
"printWidth": 100,
|
|
"bracketSpacing": true
|
|
}
|
|
```
|
|
|
|
#### 1.3.3 Editor Configuration
|
|
|
|
**File:** `.editorconfig` (repository root)
|
|
```ini
|
|
root = true
|
|
|
|
[*]
|
|
indent_style = space
|
|
indent_size = 2
|
|
end_of_line = lf
|
|
charset = utf-8
|
|
trim_trailing_whitespace = true
|
|
insert_final_newline = true
|
|
|
|
[*.go]
|
|
indent_style = tab
|
|
indent_size = 4
|
|
|
|
[*.md]
|
|
trim_trailing_whitespace = false
|
|
|
|
[Makefile]
|
|
indent_style = tab
|
|
```
|
|
|
|
#### Verification Steps:
|
|
```bash
|
|
# Backend
|
|
cd backend
|
|
golangci-lint run ./...
|
|
|
|
# Frontend
|
|
cd frontend
|
|
npx eslint . --ext .ts,.tsx
|
|
npx prettier --check .
|
|
```
|
|
|
|
---
|
|
|
|
### Task 4: Create Docker Compose for Local Development
|
|
|
|
**Duration:** 3-4 hours
|
|
|
|
#### 1.4.1 Development Docker Compose
|
|
|
|
**File:** `infrastructure/dev/docker-compose.yml`
|
|
```yaml
|
|
version: "3.9"
|
|
|
|
services:
|
|
# PostgreSQL Database
|
|
postgres:
|
|
image: postgres:15-alpine
|
|
container_name: knowfoolery-postgres
|
|
environment:
|
|
POSTGRES_USER: knowfoolery
|
|
POSTGRES_PASSWORD: devpassword
|
|
POSTGRES_DB: knowfoolery
|
|
ports:
|
|
- "5432:5432"
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
- ./init-scripts:/docker-entrypoint-initdb.d
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U knowfoolery -d knowfoolery"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
networks:
|
|
- knowfoolery-network
|
|
|
|
# Redis Cache
|
|
redis:
|
|
image: redis:7-alpine
|
|
container_name: knowfoolery-redis
|
|
ports:
|
|
- "6379:6379"
|
|
volumes:
|
|
- redis_data:/data
|
|
command: redis-server --appendonly yes
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "ping"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
networks:
|
|
- knowfoolery-network
|
|
|
|
# Zitadel Authentication (optional for initial setup)
|
|
zitadel:
|
|
image: ghcr.io/zitadel/zitadel:latest
|
|
container_name: knowfoolery-zitadel
|
|
command: start-from-init --masterkeyFromEnv --tlsMode disabled
|
|
environment:
|
|
ZITADEL_MASTERKEY: "MasterkeyNeedsToHave32Characters"
|
|
ZITADEL_DATABASE_COCKROACH_HOST: crdb
|
|
ZITADEL_EXTERNALSECURE: false
|
|
ZITADEL_FIRSTINSTANCE_ORG_NAME: "KnowFoolery"
|
|
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: "admin"
|
|
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: "AdminPassword123!"
|
|
ports:
|
|
- "8080:8080"
|
|
depends_on:
|
|
crdb:
|
|
condition: service_healthy
|
|
networks:
|
|
- knowfoolery-network
|
|
profiles:
|
|
- auth
|
|
|
|
# CockroachDB for Zitadel
|
|
crdb:
|
|
image: cockroachdb/cockroach:latest
|
|
container_name: knowfoolery-crdb
|
|
command: start-single-node --insecure --http-addr :9090
|
|
ports:
|
|
- "26257:26257"
|
|
- "9090:9090"
|
|
volumes:
|
|
- crdb_data:/cockroach/cockroach-data
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:9090/health?ready=1"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 10
|
|
networks:
|
|
- knowfoolery-network
|
|
profiles:
|
|
- auth
|
|
|
|
# Prometheus Metrics
|
|
prometheus:
|
|
image: prom/prometheus:latest
|
|
container_name: knowfoolery-prometheus
|
|
ports:
|
|
- "9091:9090"
|
|
volumes:
|
|
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
|
- prometheus_data:/prometheus
|
|
command:
|
|
- '--config.file=/etc/prometheus/prometheus.yml'
|
|
- '--storage.tsdb.path=/prometheus'
|
|
- '--web.enable-lifecycle'
|
|
networks:
|
|
- knowfoolery-network
|
|
profiles:
|
|
- observability
|
|
|
|
# Grafana Dashboards
|
|
grafana:
|
|
image: grafana/grafana:latest
|
|
container_name: knowfoolery-grafana
|
|
ports:
|
|
- "3001:3000"
|
|
environment:
|
|
GF_SECURITY_ADMIN_USER: admin
|
|
GF_SECURITY_ADMIN_PASSWORD: admin
|
|
GF_USERS_ALLOW_SIGN_UP: false
|
|
volumes:
|
|
- grafana_data:/var/lib/grafana
|
|
- ./grafana/provisioning:/etc/grafana/provisioning
|
|
depends_on:
|
|
- prometheus
|
|
networks:
|
|
- knowfoolery-network
|
|
profiles:
|
|
- observability
|
|
|
|
# Jaeger Tracing
|
|
jaeger:
|
|
image: jaegertracing/all-in-one:latest
|
|
container_name: knowfoolery-jaeger
|
|
ports:
|
|
- "16686:16686" # UI
|
|
- "14268:14268" # Collector HTTP
|
|
- "6831:6831/udp" # Agent
|
|
environment:
|
|
COLLECTOR_ZIPKIN_HOST_PORT: ":9411"
|
|
networks:
|
|
- knowfoolery-network
|
|
profiles:
|
|
- observability
|
|
|
|
volumes:
|
|
postgres_data:
|
|
redis_data:
|
|
crdb_data:
|
|
prometheus_data:
|
|
grafana_data:
|
|
|
|
networks:
|
|
knowfoolery-network:
|
|
driver: bridge
|
|
```
|
|
|
|
#### 1.4.2 Database Initialization Script
|
|
|
|
**File:** `infrastructure/dev/init-scripts/01-create-databases.sql`
|
|
```sql
|
|
-- Create separate databases for each service
|
|
CREATE DATABASE IF NOT EXISTS game_sessions;
|
|
CREATE DATABASE IF NOT EXISTS questions;
|
|
CREATE DATABASE IF NOT EXISTS users;
|
|
CREATE DATABASE IF NOT EXISTS leaderboards;
|
|
CREATE DATABASE IF NOT EXISTS admin;
|
|
|
|
-- Grant privileges
|
|
GRANT ALL PRIVILEGES ON DATABASE game_sessions TO knowfoolery;
|
|
GRANT ALL PRIVILEGES ON DATABASE questions TO knowfoolery;
|
|
GRANT ALL PRIVILEGES ON DATABASE users TO knowfoolery;
|
|
GRANT ALL PRIVILEGES ON DATABASE leaderboards TO knowfoolery;
|
|
GRANT ALL PRIVILEGES ON DATABASE admin TO knowfoolery;
|
|
```
|
|
|
|
#### 1.4.3 Prometheus Configuration
|
|
|
|
**File:** `infrastructure/dev/prometheus/prometheus.yml`
|
|
```yaml
|
|
global:
|
|
scrape_interval: 15s
|
|
evaluation_interval: 15s
|
|
|
|
scrape_configs:
|
|
- job_name: 'prometheus'
|
|
static_configs:
|
|
- targets: ['localhost:9090']
|
|
|
|
- job_name: 'game-session-service'
|
|
static_configs:
|
|
- targets: ['host.docker.internal:8080']
|
|
metrics_path: /metrics
|
|
|
|
- job_name: 'question-bank-service'
|
|
static_configs:
|
|
- targets: ['host.docker.internal:8081']
|
|
metrics_path: /metrics
|
|
|
|
- job_name: 'user-service'
|
|
static_configs:
|
|
- targets: ['host.docker.internal:8082']
|
|
metrics_path: /metrics
|
|
|
|
- job_name: 'leaderboard-service'
|
|
static_configs:
|
|
- targets: ['host.docker.internal:8083']
|
|
metrics_path: /metrics
|
|
|
|
- job_name: 'gateway-service'
|
|
static_configs:
|
|
- targets: ['host.docker.internal:8086']
|
|
metrics_path: /metrics
|
|
```
|
|
|
|
#### 1.4.4 Environment Variables
|
|
|
|
**File:** `infrastructure/dev/.env`
|
|
```bash
|
|
# Database
|
|
POSTGRES_USER=knowfoolery
|
|
POSTGRES_PASSWORD=devpassword
|
|
POSTGRES_HOST=localhost
|
|
POSTGRES_PORT=5432
|
|
POSTGRES_DB=knowfoolery
|
|
|
|
# Redis
|
|
REDIS_HOST=localhost
|
|
REDIS_PORT=6379
|
|
|
|
# Zitadel (when auth profile is enabled)
|
|
ZITADEL_URL=http://localhost:8080
|
|
ZITADEL_PROJECT_ID=your-project-id
|
|
ZITADEL_CLIENT_ID=your-client-id
|
|
|
|
# Environment
|
|
ENVIRONMENT=development
|
|
LOG_LEVEL=debug
|
|
|
|
# Service Ports
|
|
GAME_SESSION_PORT=8080
|
|
QUESTION_BANK_PORT=8081
|
|
USER_SERVICE_PORT=8082
|
|
LEADERBOARD_PORT=8083
|
|
ADMIN_SERVICE_PORT=8085
|
|
GATEWAY_PORT=8086
|
|
```
|
|
|
|
#### Verification Steps:
|
|
```bash
|
|
cd infrastructure/dev
|
|
docker-compose up -d postgres redis
|
|
docker-compose ps
|
|
docker-compose logs postgres
|
|
```
|
|
|
|
---
|
|
|
|
### Task 5: Initialize Frontend Workspace (Yarn Workspaces)
|
|
|
|
**Duration:** 2-3 hours
|
|
|
|
#### 1.5.1 Root Package Configuration
|
|
|
|
**File:** `frontend/package.json`
|
|
```json
|
|
{
|
|
"name": "knowfoolery-frontend",
|
|
"private": true,
|
|
"workspaces": [
|
|
"shared/*",
|
|
"apps/*"
|
|
],
|
|
"scripts": {
|
|
"dev": "yarn workspace @knowfoolery/web dev",
|
|
"build": "yarn workspaces foreach -A run build",
|
|
"lint": "eslint . --ext .ts,.tsx",
|
|
"lint:fix": "eslint . --ext .ts,.tsx --fix",
|
|
"format": "prettier --write .",
|
|
"format:check": "prettier --check .",
|
|
"test": "yarn workspaces foreach -A run test",
|
|
"clean": "yarn workspaces foreach -A run clean"
|
|
},
|
|
"devDependencies": {
|
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
"@typescript-eslint/parser": "^6.0.0",
|
|
"eslint": "^8.0.0",
|
|
"eslint-config-prettier": "^9.0.0",
|
|
"eslint-plugin-solid": "^0.13.0",
|
|
"prettier": "^3.0.0",
|
|
"typescript": "^5.0.0"
|
|
},
|
|
"packageManager": "yarn@4.0.0"
|
|
}
|
|
```
|
|
|
|
#### 1.5.2 Web Application Package
|
|
|
|
**File:** `frontend/apps/web/package.json`
|
|
```json
|
|
{
|
|
"name": "@knowfoolery/web",
|
|
"private": true,
|
|
"version": "0.0.1",
|
|
"type": "module",
|
|
"scripts": {
|
|
"dev": "vite",
|
|
"build": "tsc && vite build",
|
|
"preview": "vite preview",
|
|
"test": "vitest",
|
|
"clean": "rm -rf dist node_modules/.vite"
|
|
},
|
|
"dependencies": {
|
|
"solid-js": "^1.9.0",
|
|
"@solidjs/router": "^0.10.0",
|
|
"@suid/material": "^0.16.0",
|
|
"@suid/icons-material": "^0.7.0"
|
|
},
|
|
"devDependencies": {
|
|
"vite": "^5.0.0",
|
|
"vite-plugin-solid": "^2.8.0",
|
|
"vitest": "^1.0.0",
|
|
"@solidjs/testing-library": "^0.8.0"
|
|
}
|
|
}
|
|
```
|
|
|
|
**File:** `frontend/apps/web/vite.config.ts`
|
|
```typescript
|
|
import { defineConfig } from 'vite'
|
|
import solid from 'vite-plugin-solid'
|
|
|
|
export default defineConfig({
|
|
plugins: [solid()],
|
|
server: {
|
|
port: 3000,
|
|
proxy: {
|
|
'/api': {
|
|
target: 'http://localhost:8086',
|
|
changeOrigin: true,
|
|
},
|
|
},
|
|
},
|
|
build: {
|
|
target: 'esnext',
|
|
},
|
|
})
|
|
```
|
|
|
|
**File:** `frontend/apps/web/tsconfig.json`
|
|
```json
|
|
{
|
|
"compilerOptions": {
|
|
"target": "ESNext",
|
|
"module": "ESNext",
|
|
"moduleResolution": "bundler",
|
|
"allowSyntheticDefaultImports": true,
|
|
"esModuleInterop": true,
|
|
"jsx": "preserve",
|
|
"jsxImportSource": "solid-js",
|
|
"types": ["vite/client"],
|
|
"noEmit": true,
|
|
"isolatedModules": true,
|
|
"strict": true,
|
|
"skipLibCheck": true,
|
|
"resolveJsonModule": true,
|
|
"baseUrl": ".",
|
|
"paths": {
|
|
"@/*": ["src/*"],
|
|
"@shared/*": ["../../shared/*"]
|
|
}
|
|
},
|
|
"include": ["src"]
|
|
}
|
|
```
|
|
|
|
**File:** `frontend/apps/web/index.html`
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Know Foolery - Quiz Game</title>
|
|
</head>
|
|
<body>
|
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
<div id="root"></div>
|
|
<script type="module" src="/src/main.tsx"></script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
**File:** `frontend/apps/web/src/main.tsx`
|
|
```typescript
|
|
import { render } from 'solid-js/web'
|
|
import App from './App'
|
|
import './styles/global.css'
|
|
|
|
const root = document.getElementById('root')
|
|
|
|
if (!root) {
|
|
throw new Error('Root element not found')
|
|
}
|
|
|
|
render(() => <App />, root)
|
|
```
|
|
|
|
**File:** `frontend/apps/web/src/App.tsx`
|
|
```typescript
|
|
import type { Component } from 'solid-js'
|
|
|
|
const App: Component = () => {
|
|
return (
|
|
<div>
|
|
<h1>Know Foolery</h1>
|
|
<p>Quiz game coming soon...</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default App
|
|
```
|
|
|
|
**File:** `frontend/apps/web/src/styles/global.css`
|
|
```css
|
|
:root {
|
|
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
line-height: 1.5;
|
|
font-weight: 400;
|
|
|
|
color-scheme: light dark;
|
|
color: rgba(255, 255, 255, 0.87);
|
|
background-color: #242424;
|
|
|
|
font-synthesis: none;
|
|
text-rendering: optimizeLegibility;
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
}
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
#root {
|
|
max-width: 1280px;
|
|
margin: 0 auto;
|
|
padding: 2rem;
|
|
}
|
|
```
|
|
|
|
#### 1.5.3 Shared UI Components Package
|
|
|
|
**File:** `frontend/shared/ui-components/package.json`
|
|
```json
|
|
{
|
|
"name": "@knowfoolery/ui-components",
|
|
"private": true,
|
|
"version": "0.0.1",
|
|
"type": "module",
|
|
"main": "src/index.ts",
|
|
"scripts": {
|
|
"test": "vitest",
|
|
"build": "echo 'No build needed for shared package'",
|
|
"clean": "echo 'Nothing to clean'"
|
|
},
|
|
"dependencies": {
|
|
"solid-js": "^1.9.0",
|
|
"@suid/material": "^0.16.0"
|
|
},
|
|
"devDependencies": {
|
|
"vitest": "^1.0.0"
|
|
},
|
|
"peerDependencies": {
|
|
"solid-js": "^1.9.0"
|
|
}
|
|
}
|
|
```
|
|
|
|
**File:** `frontend/shared/ui-components/src/index.ts`
|
|
```typescript
|
|
// UI Components barrel export
|
|
// Components will be added as they are developed
|
|
export {}
|
|
```
|
|
|
|
#### Verification Steps:
|
|
```bash
|
|
cd frontend
|
|
yarn install
|
|
yarn dev
|
|
# Open http://localhost:3000
|
|
```
|
|
|
|
---
|
|
|
|
### Task 6: Create Makefile for Development Commands
|
|
|
|
**Duration:** 1-2 hours
|
|
|
|
**File:** `Makefile` (repository root)
|
|
```makefile
|
|
.PHONY: help dev dev-full dev-auth dev-obs stop clean \
|
|
backend-lint backend-test backend-build \
|
|
frontend-lint frontend-test frontend-build \
|
|
db-up db-down db-logs db-shell redis-shell \
|
|
lint test build
|
|
|
|
# Default target
|
|
help:
|
|
@echo "KnowFoolery Development Commands"
|
|
@echo ""
|
|
@echo "Development Environment:"
|
|
@echo " make dev - Start core infrastructure (PostgreSQL, Redis)"
|
|
@echo " make dev-full - Start all infrastructure including observability"
|
|
@echo " make dev-auth - Start infrastructure with Zitadel auth"
|
|
@echo " make stop - Stop all containers"
|
|
@echo " make clean - Stop containers and remove volumes"
|
|
@echo ""
|
|
@echo "Backend:"
|
|
@echo " make backend-lint - Run Go linters"
|
|
@echo " make backend-test - Run Go tests"
|
|
@echo " make backend-build - Build all services"
|
|
@echo ""
|
|
@echo "Frontend:"
|
|
@echo " make frontend-dev - Start frontend dev server"
|
|
@echo " make frontend-lint - Run ESLint and Prettier"
|
|
@echo " make frontend-test - Run frontend tests"
|
|
@echo " make frontend-build - Build frontend for production"
|
|
@echo ""
|
|
@echo "Database:"
|
|
@echo " make db-logs - Show PostgreSQL logs"
|
|
@echo " make db-shell - Open PostgreSQL shell"
|
|
@echo " make redis-shell - Open Redis CLI"
|
|
@echo ""
|
|
@echo "All:"
|
|
@echo " make lint - Run all linters"
|
|
@echo " make test - Run all tests"
|
|
@echo " make build - Build everything"
|
|
|
|
# =============================================================================
|
|
# Development Environment
|
|
# =============================================================================
|
|
|
|
dev:
|
|
@echo "Starting core infrastructure..."
|
|
cd infrastructure/dev && docker-compose up -d postgres redis
|
|
@echo "Waiting for services to be healthy..."
|
|
@sleep 5
|
|
@echo ""
|
|
@echo "Infrastructure ready:"
|
|
@echo " PostgreSQL: localhost:5432 (user: knowfoolery, password: devpassword)"
|
|
@echo " Redis: localhost:6379"
|
|
|
|
dev-full: dev
|
|
@echo "Starting observability stack..."
|
|
cd infrastructure/dev && docker-compose --profile observability up -d
|
|
@echo ""
|
|
@echo "Observability ready:"
|
|
@echo " Prometheus: http://localhost:9091"
|
|
@echo " Grafana: http://localhost:3001 (admin/admin)"
|
|
@echo " Jaeger: http://localhost:16686"
|
|
|
|
dev-auth:
|
|
@echo "Starting full stack with authentication..."
|
|
cd infrastructure/dev && docker-compose --profile auth up -d
|
|
@echo ""
|
|
@echo "Zitadel ready at http://localhost:8080"
|
|
@echo "Admin credentials: admin / AdminPassword123!"
|
|
|
|
stop:
|
|
@echo "Stopping all containers..."
|
|
cd infrastructure/dev && docker-compose --profile auth --profile observability down
|
|
|
|
clean:
|
|
@echo "Stopping containers and removing volumes..."
|
|
cd infrastructure/dev && docker-compose --profile auth --profile observability down -v
|
|
|
|
# =============================================================================
|
|
# Backend
|
|
# =============================================================================
|
|
|
|
backend-lint:
|
|
@echo "Running Go linters..."
|
|
cd backend && golangci-lint run ./...
|
|
|
|
backend-test:
|
|
@echo "Running Go tests..."
|
|
cd backend && go test -v -race -cover ./...
|
|
|
|
backend-build:
|
|
@echo "Building all services..."
|
|
cd backend && go build ./...
|
|
|
|
# =============================================================================
|
|
# Frontend
|
|
# =============================================================================
|
|
|
|
frontend-dev:
|
|
@echo "Starting frontend development server..."
|
|
cd frontend && yarn dev
|
|
|
|
frontend-lint:
|
|
@echo "Running frontend linters..."
|
|
cd frontend && yarn lint && yarn format:check
|
|
|
|
frontend-test:
|
|
@echo "Running frontend tests..."
|
|
cd frontend && yarn test
|
|
|
|
frontend-build:
|
|
@echo "Building frontend..."
|
|
cd frontend && yarn build
|
|
|
|
# =============================================================================
|
|
# Database Utilities
|
|
# =============================================================================
|
|
|
|
db-logs:
|
|
cd infrastructure/dev && docker-compose logs -f postgres
|
|
|
|
db-shell:
|
|
docker exec -it knowfoolery-postgres psql -U knowfoolery -d knowfoolery
|
|
|
|
redis-shell:
|
|
docker exec -it knowfoolery-redis redis-cli
|
|
|
|
# =============================================================================
|
|
# Combined Commands
|
|
# =============================================================================
|
|
|
|
lint: backend-lint frontend-lint
|
|
@echo "All linters passed!"
|
|
|
|
test: backend-test frontend-test
|
|
@echo "All tests passed!"
|
|
|
|
build: backend-build frontend-build
|
|
@echo "Build complete!"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 7: Create Developer Onboarding Documentation
|
|
|
|
**Duration:** 1-2 hours
|
|
|
|
**File:** `docs/DEVELOPMENT.md`
|
|
```markdown
|
|
# KnowFoolery Development Guide
|
|
|
|
## Prerequisites
|
|
|
|
Before you begin, ensure you have the following installed:
|
|
|
|
| Tool | Version | Installation |
|
|
|------|---------|--------------|
|
|
| Go | 1.25+ | https://go.dev/dl/ |
|
|
| Node.js | 20+ | https://nodejs.org/ or use nvm |
|
|
| Yarn | 4.x | `corepack enable` |
|
|
| Docker | 24+ | https://docs.docker.com/get-docker/ |
|
|
| Docker Compose | 2.x | Included with Docker Desktop |
|
|
| golangci-lint | latest | `go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest` |
|
|
|
|
## Quick Start
|
|
|
|
### 1. Clone the Repository
|
|
|
|
```bash
|
|
git clone https://gitea.paas.celticinfo.fr/oabrivard/knowfoolery.git
|
|
cd knowfoolery
|
|
```
|
|
|
|
### 2. Start Infrastructure
|
|
|
|
```bash
|
|
# Start PostgreSQL and Redis
|
|
make dev
|
|
|
|
# Or start everything including observability
|
|
make dev-full
|
|
```
|
|
|
|
### 3. Install Dependencies
|
|
|
|
```bash
|
|
# Backend
|
|
cd backend
|
|
go mod download
|
|
|
|
# Frontend
|
|
cd frontend
|
|
yarn install
|
|
```
|
|
|
|
### 4. Start Development Servers
|
|
|
|
**Backend Services** (run each in a separate terminal):
|
|
```bash
|
|
cd backend/services/game-session-service && go run cmd/main.go
|
|
cd backend/services/question-bank-service && go run cmd/main.go
|
|
# ... repeat for other services
|
|
```
|
|
|
|
**Frontend**:
|
|
```bash
|
|
cd frontend
|
|
yarn dev
|
|
```
|
|
|
|
### 5. Access the Application
|
|
|
|
- **Web App**: http://localhost:3000
|
|
- **API Gateway**: http://localhost:8086
|
|
- **Prometheus** (if running): http://localhost:9091
|
|
- **Grafana** (if running): http://localhost:3001
|
|
- **Jaeger** (if running): http://localhost:16686
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
knowfoolery/
|
|
├── backend/
|
|
│ ├── go.work # Go workspace file
|
|
│ ├── shared/ # Shared packages
|
|
│ └── services/ # Microservices
|
|
├── frontend/
|
|
│ ├── package.json # Yarn workspace root
|
|
│ ├── apps/ # Applications (web, cross-platform)
|
|
│ └── shared/ # Shared components
|
|
├── infrastructure/
|
|
│ └── dev/ # Docker Compose for local dev
|
|
├── docs/ # Documentation
|
|
└── Makefile # Development commands
|
|
```
|
|
|
|
## Common Commands
|
|
|
|
| Command | Description |
|
|
|---------|-------------|
|
|
| `make help` | Show all available commands |
|
|
| `make dev` | Start core infrastructure |
|
|
| `make dev-full` | Start infrastructure + observability |
|
|
| `make lint` | Run all linters |
|
|
| `make test` | Run all tests |
|
|
| `make stop` | Stop all containers |
|
|
| `make clean` | Stop containers and remove data |
|
|
|
|
## Environment Variables
|
|
|
|
Copy the example environment file:
|
|
```bash
|
|
cp infrastructure/dev/.env.example infrastructure/dev/.env
|
|
```
|
|
|
|
## Database Access
|
|
|
|
### PostgreSQL
|
|
|
|
```bash
|
|
# Via make command
|
|
make db-shell
|
|
|
|
# Or directly
|
|
docker exec -it knowfoolery-postgres psql -U knowfoolery -d knowfoolery
|
|
```
|
|
|
|
### Redis
|
|
|
|
```bash
|
|
# Via make command
|
|
make redis-shell
|
|
|
|
# Or directly
|
|
docker exec -it knowfoolery-redis redis-cli
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Port Conflicts
|
|
|
|
If you get port conflict errors:
|
|
1. Check what's using the port: `lsof -i :5432`
|
|
2. Stop the conflicting service or change ports in docker-compose.yml
|
|
|
|
### Docker Memory Issues
|
|
|
|
If containers are slow or crashing:
|
|
1. Increase Docker Desktop memory allocation (Settings > Resources)
|
|
2. Recommended: 4GB+ RAM
|
|
|
|
### Go Module Issues
|
|
|
|
```bash
|
|
cd backend
|
|
go work sync
|
|
go mod tidy
|
|
```
|
|
|
|
### Frontend Build Issues
|
|
|
|
```bash
|
|
cd frontend
|
|
rm -rf node_modules
|
|
yarn cache clean
|
|
yarn install
|
|
```
|
|
|
|
## IDE Setup
|
|
|
|
### VS Code
|
|
|
|
Recommended extensions:
|
|
- Go (golang.go)
|
|
- ESLint
|
|
- Prettier
|
|
- Docker
|
|
- YAML
|
|
|
|
### GoLand/IntelliJ
|
|
|
|
1. Open the `backend` folder as your Go project
|
|
2. Enable "Go Modules integration"
|
|
3. Set GOROOT to your Go 1.25+ installation
|
|
|
|
## Contributing
|
|
|
|
1. Create a feature branch: `git checkout -b feature/my-feature`
|
|
2. Make your changes
|
|
3. Run linters: `make lint`
|
|
4. Run tests: `make test`
|
|
5. Commit with conventional commits: `git commit -m "feat: add new feature"`
|
|
6. Push and create a PR
|
|
```
|
|
|
|
---
|
|
|
|
## Deliverables Checklist
|
|
|
|
| Deliverable | Status | Notes |
|
|
|-------------|--------|-------|
|
|
| Go workspace (go.work) | [ ] | Multi-module monorepo |
|
|
| Service module stubs | [ ] | 6 services with health endpoints |
|
|
| Shared packages structure | [ ] | domain/ and infra/ directories |
|
|
| golangci-lint config | [ ] | .golangci.yml |
|
|
| ESLint + Prettier config | [ ] | .eslintrc.json, .prettierrc |
|
|
| EditorConfig | [ ] | .editorconfig |
|
|
| Docker Compose (dev) | [ ] | PostgreSQL, Redis, optional stacks |
|
|
| Makefile | [ ] | Development commands |
|
|
| Developer documentation | [ ] | DEVELOPMENT.md |
|
|
|
|
---
|
|
|
|
## Verification Checklist
|
|
|
|
### Backend Verification
|
|
```bash
|
|
# 1. Go workspace builds
|
|
cd backend && go build ./...
|
|
|
|
# 2. Linter runs without config errors
|
|
cd backend && golangci-lint run ./...
|
|
|
|
# 3. All modules resolve correctly
|
|
cd backend && go work sync
|
|
```
|
|
|
|
### Frontend Verification
|
|
```bash
|
|
# 1. Dependencies install
|
|
cd frontend && yarn install
|
|
|
|
# 2. Dev server starts
|
|
cd frontend && yarn dev
|
|
|
|
# 3. Linters pass
|
|
cd frontend && yarn lint
|
|
```
|
|
|
|
### Infrastructure Verification
|
|
```bash
|
|
# 1. Docker Compose starts
|
|
cd infrastructure/dev && docker-compose up -d postgres redis
|
|
|
|
# 2. PostgreSQL is accessible
|
|
docker exec knowfoolery-postgres pg_isready -U knowfoolery
|
|
|
|
# 3. Redis is accessible
|
|
docker exec knowfoolery-redis redis-cli ping
|
|
```
|
|
|
|
---
|
|
|
|
## Estimated Time Breakdown
|
|
|
|
| Task | Duration | Cumulative |
|
|
|------|----------|------------|
|
|
| Task 1: Go Workspace | 2-3 hours | 3 hours |
|
|
| Task 2: Shared Packages | 2-3 hours | 6 hours |
|
|
| Task 3: Linting Config | 2-3 hours | 9 hours |
|
|
| Task 4: Docker Compose | 3-4 hours | 13 hours |
|
|
| Task 5: Frontend Workspace | 2-3 hours | 16 hours |
|
|
| Task 6: Makefile | 1-2 hours | 18 hours |
|
|
| Task 7: Documentation | 1-2 hours | 20 hours |
|
|
| **Buffer & Testing** | 4 hours | **24 hours (3 days)** |
|
|
|
|
---
|
|
|
|
## Next Steps After Completion
|
|
|
|
Once this phase is complete, proceed to:
|
|
- **Phase 1.2**: Implement shared backend packages (errors, logging, metrics, tracing)
|
|
- Begin development of Question Bank Service (Phase 2.1)
|