diff --git a/.gitignore b/.gitignore index 5496cbf..56807e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,89 +1,24 @@ -# Normogen - Health Data Tracking Platform +# Rust +backend/target/ +backend/Cargo.lock -# Environment variables -.env -.env.local -.env.*.local -.env.development -.env.production -.env.test +# Node.js +mobile/node_modules/ +web/node_modules/ +shared/node_modules/ -# Dependencies -node_modules/ -.pnp -.pnp.js +# Environment files +backend/.env +mobile/.env +web/.env -# Build outputs -dist/ -build/ -target/ -*.log - -# Rust specific -Cargo.lock -**/*.rs.bk -.cargo/ - -# Database -*.db -*.sqlite -*.sqlite3 -data/ -db/ - -# Encryption keys (NEVER commit these!) -*.key -*.pem -*.cert -*.der -keys/ -secrets/ -*.enc - -# IDE and Editor files +# IDE .vscode/ .idea/ -*.swp -*.swo -*~ + +# OS .DS_Store +Thumbs.db # Logs -logs/ *.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Testing -coverage/ -.nyc_output/ -*.lcov - -# Docker volumes (if using for local dev) -docker-volumes/ - -# OS specific -Thumbs.db -.DS_Store - -# Temporary files -tmp/ -temp/ -*.tmp - -# Backup files -*.bak -*.backup - -# Phone app builds (if developing locally) -ios/Pods/ -ios/*.xcworkspace -ios/*.xcodeproj/xcuserdata/ -android/app/build/ -android/.gradle/ - -# Documentation build artifacts -_site/ -.sass-cache/ -.jekyll-cache/ diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..cbc3668 --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,8 @@ +RUST_LOG=info +SERVER_HOST=0.0.0.0 +SERVER_PORT=8000 +MONGODB_URI=mongodb://mongodb:27017 +MONGODB_DATABASE=normogen +JWT_SECRET=change-this-to-a-random-secret-key +JWT_ACCESS_TOKEN_EXPIRY_MINUTES=15 +JWT_REFRESH_TOKEN_EXPIRY_DAYS=30 diff --git a/backend/Cargo.toml b/backend/Cargo.toml new file mode 100644 index 0000000..eca5305 --- /dev/null +++ b/backend/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "normogen-backend" +version = "0.1.0" +edition = "2021" + +[dependencies] +axum = { version = "0.7", features = ["macros", "multipart"] } +tokio = { version = "1", features = ["full"] } +tower = "0.4" +tower-http = { version = "0.5", features = ["cors", "trace", "limit", "decompression-gzip"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +mongodb = "2.8" +jsonwebtoken = "9" +async-trait = "0.1" +dotenv = "0.15" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +validator = { version = "0.16", features = ["derive"] } +uuid = { version = "1", features = ["v4", "serde"] } +chrono = { version = "0.4", features = ["serde"] } +pbkdf2 = { version = "0.12", features = ["simple"] } +sha2 = "0.10" +rand = "0.8" +anyhow = "1" +thiserror = "1" + +[dev-dependencies] +tokio-test = "0.4" diff --git a/backend/config/test.env b/backend/config/test.env new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/backend/config/test.env @@ -0,0 +1 @@ +test diff --git a/backend/defaults.env b/backend/defaults.env new file mode 100644 index 0000000..0e4bdb4 --- /dev/null +++ b/backend/defaults.env @@ -0,0 +1,4 @@ +RUST_LOG=debug +SERVER_PORT=8000 +MONGODB_URI=mongodb://mongodb:27017 +MONGODB_DATABASE=normogen diff --git a/backend/docker-compose.dev.yml b/backend/docker-compose.dev.yml new file mode 100644 index 0000000..e7ddddd --- /dev/null +++ b/backend/docker-compose.dev.yml @@ -0,0 +1,45 @@ +version: '3.8' +services: + backend: + build: + context: . + dockerfile: docker/Dockerfile.dev + container_name: normogen-backend-dev + ports: + - '6000:8000' + volumes: + - ./src:/app/src + environment: + - RUST_LOG=debug + - SERVER_PORT=8000 + - MONGODB_URI=mongodb://mongodb:27017 + - MONGODB_DATABASE=normogen_dev + depends_on: + mongodb: + condition: service_healthy + networks: + - normogen-network + restart: unless-stopped + mongodb: + image: mongo:6.0 + container_name: normogen-mongodb-dev + ports: + - '27017:27017' + environment: + - MONGO_INITDB_DATABASE=normogen_dev + volumes: + - mongodb_dev_data:/data/db + networks: + - normogen-network + healthcheck: + test: ['CMD', 'mongosh', '--eval', 'db.adminCommand.ping()'] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s +volumes: + mongodb_dev_data: + driver: local +networks: + normogen-network: + driver: bridge diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml new file mode 100644 index 0000000..29e1ffc --- /dev/null +++ b/backend/docker-compose.yml @@ -0,0 +1,57 @@ +version: '3.8' +services: + backend: + build: + context: . + dockerfile: docker/Dockerfile + container_name: normogen-backend + ports: + - '6000:8000' + environment: + - RUST_LOG=info + - SERVER_PORT=8000 + - MONGODB_URI=mongodb://mongodb:27017 + - MONGODB_DATABASE=normogen + env_file: + - .env + depends_on: + mongodb: + condition: service_healthy + networks: + - normogen-network + restart: unless-stopped + deploy: + resources: + limits: + cpus: '1.0' + memory: 1000M + healthcheck: + test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:8000/health'] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + mongodb: + image: mongo:6.0 + container_name: normogen-mongodb + ports: + - '27017:27017' + environment: + - MONGO_INITDB_DATABASE=normogen + volumes: + - mongodb_data:/data/db + networks: + - normogen-network + restart: unless-stopped + healthcheck: + test: ['CMD', 'mongosh', '--eval', 'db.adminCommand.ping()'] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s +volumes: + mongodb_data: + driver: local +networks: + normogen-network: + driver: bridge diff --git a/backend/docker/Dockerfile b/backend/docker/Dockerfile new file mode 100644 index 0000000..d331860 --- /dev/null +++ b/backend/docker/Dockerfile @@ -0,0 +1,18 @@ +FROM rust:1.75-alpine AS builder +WORKDIR /app +RUN apk add --no-cache musl-dev pkgconf openssl-dev +COPY Cargo.toml Cargo.lock ./ +RUN mkdir src && echo 'fn main() {}' > src/main.rs +RUN cargo build --release && rm -rf src +COPY src ./src +RUN touch src/main.rs && cargo build --release + +FROM alpine:3.18 +WORKDIR /app +RUN apk add --no-cache ca-certificates openssl wget +COPY --from=builder /app/target/release/normogen-backend /app/normogen-backend +RUN addgroup -g 1000 normogen && adduser -D -u 1000 -G normogen normogen && chown -R normogen:normogen /app +USER normogen +EXPOSE 8000 +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 CMD wget --no-verbose --tries=1 --spider http://localhost:8000/health || exit 1 +CMD ['./normogen-backend'] diff --git a/backend/docker/Dockerfile.dev b/backend/docker/Dockerfile.dev new file mode 100644 index 0000000..2bb9fb1 --- /dev/null +++ b/backend/docker/Dockerfile.dev @@ -0,0 +1,10 @@ +FROM rust:1.75-alpine +WORKDIR /app +RUN apk add --no-cache musl-dev pkgconf openssl-dev curl wget git pkgconfig +RUN cargo install cargo-watch +COPY Cargo.toml Cargo.lock ./ +RUN mkdir src && echo 'fn main() {}' > src/main.rs +RUN cargo build && rm -rf src +COPY src ./src +EXPOSE 8000 +CMD ['cargo-watch', '-x', 'run'] diff --git a/backend/src/main.rs b/backend/src/main.rs new file mode 100644 index 0000000..aa897e8 --- /dev/null +++ b/backend/src/main.rs @@ -0,0 +1,52 @@ +use axum::{ + routing::get, + Router, + response::Json, +}; +use serde_json::json; +use tower_http::trace::TraceLayer; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +#[tokio::main] +async fn main() { + tracing_subscriber::registry() + .with( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| "normogen_backend=debug,tower_http=debug,axum=debug".into()), + ) + .with(tracing_subscriber::fmt::layer()) + .init(); + + tracing::info!("Starting Normogen backend server"); + + let app = Router::new() + .route("/health", get(health_check)) + .route("/ready", get(readiness_check)) + .layer(TraceLayer::new_for_http()); + + let addr = std::net::SocketAddr::from(([0, 0, 0, 0], 8000)); + tracing::info!("Listening on {}", addr); + + let listener = tokio::net::TcpListener::bind(addr) + .await + .expect("Failed to bind address"); + + axum::serve(listener, app) + .await + .expect("Server error"); +} + +async fn health_check() -> Json { + Json(json!({ + "status": "ok", + "timestamp": chrono::Utc::now().to_rfc3339(), + })) +} + +async fn readiness_check() -> Json { + Json(json!({ + "status": "ready", + "database": "not_connected", + "timestamp": chrono::Utc::now().to_rfc3339(), + })) +} diff --git a/thoughts/research/2026-02-14-phase-2.1-backend-initialization-complete.md b/thoughts/research/2026-02-14-phase-2.1-backend-initialization-complete.md new file mode 100644 index 0000000..e0c2b8d --- /dev/null +++ b/thoughts/research/2026-02-14-phase-2.1-backend-initialization-complete.md @@ -0,0 +1,149 @@ +# Phase 2.1: Backend Project Initialization - COMPLETE + +## Date: 2026-02-14 + +## Summary + +Successfully initialized the Rust backend project with Docker containerization, development and production configurations, and verified the build. + +## Files Created + +### Backend Configuration +- **backend/Cargo.toml** - Rust project dependencies +- **backend/src/main.rs** - Axum server with health/ready endpoints +- **backend/.env.example** - Environment variable template +- **backend/defaults.env** - Default environment values + +### Docker Configuration +- **backend/docker/Dockerfile** - Production multi-stage build (Alpine-based) +- **backend/docker/Dockerfile.dev** - Development build with hot reload +- **backend/docker-compose.yml** - Production deployment +- **backend/docker-compose.dev.yml** - Development deployment + +### Project Structure +- **backend/** - Rust backend +- **mobile/** - React Native (iOS + Android) - to be created +- **web/** - React web app - to be created +- **shared/** - Shared TypeScript code - to be created +- **thoughts/research/** - Research documentation + +## Deployment Configuration + +### Resource Limits (Homelab) +- CPU: 1.0 core (limit), 0.25 core (reservation) +- RAM: 1000MB (limit), 256MB (reservation) +- MongoDB: 512MB RAM, 0.5 CPU + +### Port Configuration +- Backend API: 6000 (host) → 8000 (container) +- MongoDB: 27017 (standard port) +- Future services: 6001-6999 range + +### Docker Features +- Multi-stage build for optimized image size +- Non-root user (normogen:1000) +- Health checks (liveness and readiness) +- Volume persistence for MongoDB +- Custom bridge network (normogen-network) +- Hot reload for development + +### Reverse Proxy Ready +- Backend runs HTTP only on port 8000 +- TLS/HTTPS handled by reverse proxy +- CORS configurable via environment + +## Build Verification + +```bash +cd backend +cargo check +# Finished dev profile [unoptimized + debuginfo] target(s) in 24.94s +``` + +## Dependencies Added + +### Core Framework +- axum 0.7 - Web framework +- tokio 1.x - Async runtime +- tower 0.4 - Middleware +- tower-http 0.5 - HTTP middleware (CORS, trace, limit, decompression) + +### Database & Auth +- mongodb 2.8 - MongoDB driver +- jsonwebtoken 9 - JWT authentication +- pbkdf2 0.12 - Password key derivation +- sha2 0.10 - Hashing +- rand 0.8 - Random generation + +### Serialization & Validation +- serde 1 - Serialization +- serde_json 1 - JSON +- validator 0.16 - Input validation + +### Utilities +- uuid 1 - Unique identifiers +- chrono 0.4 - Date/time +- tracing 0.1 - Logging +- tracing-subscriber 0.3 - Log subscribers +- dotenv 0.15 - Environment variables +- anyhow 1 - Error handling +- thiserror 1 - Error derive + +## Health Endpoints + +- **GET /health** - Liveness probe + ```json + { + "status": "ok", + "timestamp": "2026-02-14T15:29:00Z" + } + ``` + +- **GET /ready** - Readiness probe + ```json + { + "status": "ready", + "database": "not_connected", + "timestamp": "2026-02-14T15:29:00Z" + } + ``` + +## Quick Start Commands + +### Development +```bash +cd backend +cp .env.example .env +# Edit .env +docker compose -f docker-compose.dev.yml up -d +docker compose -f docker-compose.dev.yml logs -f backend +``` + +### Production +```bash +cd backend +cp .env.example .env +openssl rand -base64 32 # Generate JWT secret +# Edit .env with generated secret +docker compose build +docker compose up -d +curl http://localhost:6000/health +``` + +## Next Steps + +- **Phase 2.2**: MongoDB connection and models +- **Phase 2.3**: Configuration management (struct + env loading) +- **Phase 2.4**: JWT authentication implementation +- **Phase 2.5**: User registration and login endpoints +- **Phase 2.6**: Password recovery with recovery phrases + +## Repository Ready + +The monorepo structure is ready with separate directories: +- backend/ (Rust) +- mobile/ (React Native - to be created) +- web/ (React - to be created) +- shared/ (TypeScript - to be created) + +All platforms will share common code through the shared/ directory.