diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..636fe4b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,46 @@ +# Dependencies +node_modules +**/node_modules +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Build outputs +.next +.turbo +dist +build +out +*.log + +# Environment files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Git +.git +.gitignore +.gitattributes + +# IDE +.vscode +.idea +*.swp +*.swo +*~ +.DS_Store + +# Testing +coverage +.nyc_output + +# Misc +*.md +!README.md +.editorconfig +.prettierrc +.eslintrc* diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5f5df93 --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +# Node Environment +NODE_ENV=development + +# PostgreSQL Configuration +POSTGRES_USER=lightsaver +POSTGRES_PASSWORD=lightsaver_dev +POSTGRES_DB=lightsaver +DATABASE_URL=postgresql://lightsaver:lightsaver_dev@localhost:5432/lightsaver + +# RabbitMQ Configuration +RABBITMQ_USER=lightsaver +RABBITMQ_PASSWORD=lightsaver_dev +RABBITMQ_URL=amqp://lightsaver:lightsaver_dev@localhost:5672 + +# API Configuration +API_PORT=4000 + +# Frontend Configuration +NEXT_PUBLIC_API_URL=http://localhost:4000/graphql diff --git a/Dockerfile b/Dockerfile index 0e8040f..ec0117c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,33 +1,50 @@ +# Production Build - Podman Compatible # 1. Build stage -FROM node:22-slim AS builder +FROM docker.io/library/node:22-slim AS builder ENV NVM_DIR=/root/.nvm ENV NODE_VERSION=22 # Install dependencies for Node -RUN apt-get update && apt-get dist-upgrade -y && apt-get install -y ca-certificates build-essential +RUN apt-get update && \ + apt-get dist-upgrade -y && \ + apt-get install -y ca-certificates build-essential && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* # Enable corepack and prepare pnpm RUN corepack enable && corepack prepare pnpm@latest --activate WORKDIR /app -COPY . . +# Copy package files first for better layer caching +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json ./ +COPY apps/lightsaver_user_client/package.json ./apps/lightsaver_user_client/ +COPY packages ./packages -RUN yes | pnpm install +# Install dependencies +RUN pnpm install --frozen-lockfile +# Copy source code +COPY apps/lightsaver_user_client ./apps/lightsaver_user_client + +# Build the application RUN pnpm turbo run build --filter=lightsaver_user_client... -#debugging -RUN ls -al /app/apps/lightsaver_user_client - # 2. Production stage -FROM node:22-slim +FROM docker.io/library/node:22-slim WORKDIR /app -COPY --from=builder /app/apps/lightsaver_user_client ./ +# Copy built application and dependencies +COPY --from=builder /app/apps/lightsaver_user_client/.next ./apps/lightsaver_user_client/.next +COPY --from=builder /app/apps/lightsaver_user_client/public ./apps/lightsaver_user_client/public +COPY --from=builder /app/apps/lightsaver_user_client/package.json ./apps/lightsaver_user_client/ COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/packages ./packages +COPY --from=builder /app/package.json ./ -EXPOSE 3000 \ No newline at end of file +EXPOSE 3000 + +# Run the production server +CMD ["node_modules/.bin/next", "start", "apps/lightsaver_user_client"] \ No newline at end of file diff --git a/Dockerfile.api b/Dockerfile.api new file mode 100644 index 0000000..2c69f05 --- /dev/null +++ b/Dockerfile.api @@ -0,0 +1,35 @@ +# Backend API Dockerfile for Podman +FROM docker.io/library/node:22-slim + +# Install required dependencies +RUN apt-get update && \ + apt-get dist-upgrade -y && \ + apt-get install -y ca-certificates build-essential && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Enable corepack and prepare pnpm +RUN corepack enable && corepack prepare pnpm@latest --activate + +# Set working directory +WORKDIR /app + +# Copy package files +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY turbo.json ./ + +# Copy service and package directories +COPY services/package.json ./services/ +COPY packages ./packages + +# Install dependencies +RUN pnpm install --frozen-lockfile + +# Copy service source code +COPY services ./services + +# Expose API port +EXPOSE 4000 + +# Default command (can be overridden in docker-compose) +CMD ["pnpm", "--filter", "api", "dev"] diff --git a/Dockerfile.frontend b/Dockerfile.frontend new file mode 100644 index 0000000..5405ea4 --- /dev/null +++ b/Dockerfile.frontend @@ -0,0 +1,35 @@ +# Frontend Dockerfile for Podman +FROM docker.io/library/node:22-slim + +# Install required dependencies +RUN apt-get update && \ + apt-get dist-upgrade -y && \ + apt-get install -y ca-certificates build-essential && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Enable corepack and prepare pnpm +RUN corepack enable && corepack prepare pnpm@latest --activate + +# Set working directory +WORKDIR /app + +# Copy package files +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY turbo.json ./ + +# Copy app and package directories +COPY apps/lightsaver_user_client/package.json ./apps/lightsaver_user_client/ +COPY packages ./packages + +# Install dependencies +RUN pnpm install --frozen-lockfile + +# Copy app source code +COPY apps/lightsaver_user_client ./apps/lightsaver_user_client + +# Expose Next.js port +EXPOSE 3000 + +# Default command (can be overridden in docker-compose) +CMD ["pnpm", "--filter", "lightsaver_user_client", "dev", "--", "--turbopack"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..793ae24 --- /dev/null +++ b/Makefile @@ -0,0 +1,93 @@ +.PHONY: help dev up down build rebuild logs clean install test + +# Default target +help: + @echo "LightSaver - Podman Development Commands" + @echo "" + @echo "Available targets:" + @echo " make dev - Start all services in development mode with hot-reload" + @echo " make up - Start all services in detached mode" + @echo " make down - Stop all services" + @echo " make build - Build all service images" + @echo " make rebuild - Rebuild and restart all services" + @echo " make logs - Follow logs from all services" + @echo " make logs-api - Follow logs from API service" + @echo " make logs-fe - Follow logs from frontend service" + @echo " make clean - Stop services and remove volumes" + @echo " make install - Install dependencies in running containers" + @echo " make shell-api - Open shell in API container" + @echo " make shell-fe - Open shell in frontend container" + @echo " make db-shell - Open PostgreSQL shell" + @echo " make setup - Initial setup (copy .env, install deps)" + +# Start services in development mode with hot-reload +dev: + podman-compose -f docker-compose.yml -f docker-compose.dev.yml up + +# Start services in detached mode +up: + podman-compose up -d + +# Stop all services +down: + podman-compose down + +# Build all images +build: + podman-compose build + +# Rebuild and restart +rebuild: + podman-compose down + podman-compose build + podman-compose up -d + +# View logs +logs: + podman-compose logs -f + +logs-api: + podman-compose logs -f api + +logs-fe: + podman-compose logs -f frontend + +logs-db: + podman-compose logs -f postgres + +logs-mq: + podman-compose logs -f rabbitmq + +# Stop and remove everything including volumes +clean: + podman-compose down -v + @echo "Cleaned up all containers and volumes" + +# Install dependencies +install: + podman-compose exec frontend pnpm install + podman-compose exec api pnpm install + +# Open shells +shell-api: + podman-compose exec api sh + +shell-fe: + podman-compose exec frontend sh + +# Database shell +db-shell: + podman-compose exec postgres psql -U lightsaver -d lightsaver + +# Initial setup +setup: + @if [ ! -f .env ]; then cp .env.example .env; echo "Created .env file"; fi + @echo "Setup complete! Run 'make dev' to start development" + +# Production build +prod-build: + podman build -t lightsaver-app . + +# Run production container +prod-run: + podman run -p 3000:3000 lightsaver-app diff --git a/README.md b/README.md index 686c078..e6f15d5 100644 --- a/README.md +++ b/README.md @@ -6,20 +6,122 @@ - **GraphQL**: API query language for flexible and efficient data fetching. - **Express.js**: Fast, unopinionated web framework for Node.js, powering the backend API. - **PostgreSQL**: Relational database for persistent storage. -- **Docker**: Containerization for consistent deployment. +- **Podman & Podman Compose**: Container orchestration for local development and deployment. - **Terraform CDKTF**: Infrastructure as Code (IaC) using the Cloud Development Kit for Terraform. - **RabbitMQ**: Message broker for asynchronous communication and background processing. -## Docker Commands +## Prerequisites -To build the Docker image: +- **Podman**: Install from [podman.io](https://podman.io/getting-started/installation) +- **Podman Compose**: Install via `pip install podman-compose` +- **Node.js**: Version 22.17.0 or higher +- **pnpm**: Package manager (installed via corepack) +## Local Development with Podman + +### Quick Start + +1. **Clone the repository** + ```sh + git clone + cd lightsaver + ``` + +2. **Set up environment variables** + ```sh + cp .env.example .env + # Edit .env with your configuration if needed + ``` + +3. **Start all services with Podman Compose** + ```sh + # Development mode with hot-reload + podman-compose -f docker-compose.yml -f docker-compose.dev.yml up + + # Or use the base configuration only + podman-compose up + ``` + +4. **Access the applications** + - Frontend: http://localhost:3000 + - Backend API: http://localhost:4000 + - RabbitMQ Management: http://localhost:15672 (user: lightsaver, password: lightsaver_dev) + - PostgreSQL: localhost:5432 + +### Available Podman Commands + +**Start services in detached mode:** ```sh -docker build -t lightsaver-app . +podman-compose up -d ``` -To run the container: - +**Stop services:** ```sh -docker run -p 3000:3000 lightsaver-app -``` \ No newline at end of file +podman-compose down +``` + +**Stop services and remove volumes:** +```sh +podman-compose down -v +``` + +**View logs:** +```sh +# All services +podman-compose logs -f + +# Specific service +podman-compose logs -f frontend +podman-compose logs -f api +``` + +**Rebuild services:** +```sh +podman-compose build +podman-compose up --build +``` + +**Run commands in a service container:** +```sh +# Frontend +podman-compose exec frontend pnpm install +podman-compose exec frontend pnpm build + +# API +podman-compose exec api pnpm install +``` + +### Production Build + +Build the production image: +```sh +podman build -t lightsaver-app . +``` + +Run the production container: +```sh +podman run -p 3000:3000 lightsaver-app +``` + +### Service Architecture + +The project uses the following services: + +- **frontend**: Next.js application (port 3000) +- **api**: Express + GraphQL API (port 4000) +- **postgres**: PostgreSQL database (port 5432) +- **rabbitmq**: RabbitMQ message broker (ports 5672, 15672) + +### Troubleshooting + +**Port conflicts:** +If ports are already in use, modify the port mappings in `docker-compose.yml`. + +**Permission issues with volumes:** +Podman runs rootless by default. Add `:z` or `:Z` to volume mounts if you encounter permission issues. + +**SELinux issues:** +The dev compose file includes `:z` flags for volume mounts to handle SELinux contexts. + +**Container doesn't start:** +Check logs with `podman-compose logs ` to diagnose issues. \ No newline at end of file diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..b825357 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,61 @@ +version: '3.8' + +# Development-specific configuration +# Usage: podman-compose -f docker-compose.yml -f docker-compose.dev.yml up + +services: + postgres: + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + # Add local SQL scripts for development + - ./scripts/db:/docker-entrypoint-initdb.d + + rabbitmq: + ports: + - "5672:5672" + - "15672:15672" + volumes: + - rabbitmq_data:/var/lib/rabbitmq + + api: + build: + target: builder + environment: + NODE_ENV: development + DEBUG: "api:*" + volumes: + # Enable hot-reload for development + - ./services:/app/services:z + - ./packages:/app/packages:z + # Prevent overwriting node_modules + - /app/node_modules + - /app/services/node_modules + stdin_open: true + tty: true + + frontend: + build: + target: builder + environment: + NODE_ENV: development + NEXT_TELEMETRY_DISABLED: 1 + volumes: + # Enable hot-reload for development + - ./apps/lightsaver_user_client:/app/apps/lightsaver_user_client:z + - ./packages:/app/packages:z + # Prevent overwriting node_modules and build artifacts + - /app/node_modules + - /app/apps/lightsaver_user_client/node_modules + - /app/apps/lightsaver_user_client/.next + stdin_open: true + tty: true + +volumes: + postgres_data: + rabbitmq_data: + +networks: + lightsaver-network: + driver: bridge diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2aed657 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,100 @@ +version: '3.8' + +services: + # PostgreSQL Database + postgres: + image: docker.io/library/postgres:16-alpine + container_name: lightsaver-postgres + environment: + POSTGRES_USER: ${POSTGRES_USER:-lightsaver} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-lightsaver_dev} + POSTGRES_DB: ${POSTGRES_DB:-lightsaver} + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-lightsaver}"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - lightsaver-network + + # RabbitMQ Message Broker + rabbitmq: + image: docker.io/library/rabbitmq:3-management-alpine + container_name: lightsaver-rabbitmq + environment: + RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER:-lightsaver} + RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD:-lightsaver_dev} + ports: + - "5672:5672" # AMQP port + - "15672:15672" # Management UI + volumes: + - rabbitmq_data:/var/lib/rabbitmq + healthcheck: + test: ["CMD", "rabbitmq-diagnostics", "ping"] + interval: 30s + timeout: 10s + retries: 5 + networks: + - lightsaver-network + + # Backend API Service + api: + build: + context: . + dockerfile: Dockerfile.api + container_name: lightsaver-api + environment: + NODE_ENV: ${NODE_ENV:-development} + PORT: ${API_PORT:-4000} + DATABASE_URL: postgresql://${POSTGRES_USER:-lightsaver}:${POSTGRES_PASSWORD:-lightsaver_dev}@postgres:5432/${POSTGRES_DB:-lightsaver} + RABBITMQ_URL: amqp://${RABBITMQ_USER:-lightsaver}:${RABBITMQ_PASSWORD:-lightsaver_dev}@rabbitmq:5672 + ports: + - "4000:4000" + volumes: + - ./services:/app/services + - ./packages:/app/packages + - /app/node_modules + - /app/services/node_modules + depends_on: + postgres: + condition: service_healthy + rabbitmq: + condition: service_healthy + networks: + - lightsaver-network + command: sh -c "cd /app && pnpm install && pnpm --filter api dev" + + # Frontend Next.js Application + frontend: + build: + context: . + dockerfile: Dockerfile.frontend + container_name: lightsaver-frontend + environment: + NODE_ENV: ${NODE_ENV:-development} + NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:4000/graphql} + ports: + - "3000:3000" + volumes: + - ./apps/lightsaver_user_client:/app/apps/lightsaver_user_client + - ./packages:/app/packages + - /app/node_modules + - /app/apps/lightsaver_user_client/node_modules + - /app/apps/lightsaver_user_client/.next + depends_on: + - api + networks: + - lightsaver-network + command: sh -c "cd /app && pnpm install && pnpm --filter lightsaver_user_client dev" + +networks: + lightsaver-network: + driver: bridge + +volumes: + postgres_data: + rabbitmq_data: diff --git a/scripts/db/01-init.sql b/scripts/db/01-init.sql new file mode 100644 index 0000000..0d43f11 --- /dev/null +++ b/scripts/db/01-init.sql @@ -0,0 +1,16 @@ +-- LightSaver Database Initialization Script +-- This script runs automatically when the PostgreSQL container starts for the first time + +-- Create extensions if needed +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +-- Add your table definitions here +-- Example: +-- CREATE TABLE IF NOT EXISTS users ( +-- id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), +-- email VARCHAR(255) UNIQUE NOT NULL, +-- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, +-- updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +-- ); + +-- Add initial seed data here if needed diff --git a/scripts/db/README.md b/scripts/db/README.md new file mode 100644 index 0000000..6a2e211 --- /dev/null +++ b/scripts/db/README.md @@ -0,0 +1,21 @@ +# Database Initialization Scripts + +SQL scripts in this directory are automatically executed when the PostgreSQL container is first initialized. + +## Usage + +- Scripts are executed in alphabetical order +- Use numbered prefixes (01-, 02-, etc.) to control execution order +- Place schema definitions in early scripts +- Place seed data in later scripts + +## Example + +- `01-init.sql` - Create extensions and initial schema +- `02-seed.sql` - Insert initial data +- `03-indexes.sql` - Create indexes + +## Notes + +- These scripts only run on first container creation +- To re-run, you must remove the postgres volume: `make clean` or `podman-compose down -v`