diff --git a/CLAUDE.md b/CLAUDE.md index 8fb5ad1..0d768a8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,200 +5,117 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview Filamenteka is a 3D printing filament inventory management system for tracking Bambu Lab filaments. It consists of: -- **Frontend**: Next.js app with React, TypeScript, and Tailwind CSS (static export) -- **Backend**: Node.js API server with PostgreSQL database +- **Frontend**: Next.js app with React, TypeScript, and Tailwind CSS (static export to `/out`) +- **Backend**: Node.js Express API server (`/api/server.js`) with PostgreSQL database - **Infrastructure**: AWS (CloudFront + S3 for frontend, EC2 for API, RDS for database) +- **Repository**: `git.demirix.dev/dax/Filamenteka` ## Critical Rules -- NEVER mention ANY author in commits. No author tags or attribution -- NEVER mention AI/assistant names anywhere -- Keep commit messages clean and professional with NO attribution +- NEVER mention ANY author in commits. No author tags or attribution of any kind - Build for AMD64 Linux when deploying (development is on ARM macOS) -- Always run security checks before commits +- Always run security checks before commits (Husky pre-commit hook does this automatically) ## Common Commands ```bash -# Frontend Development -npm run dev # Start Next.js development server (port 3000) -npm run build # Build static export to /out directory -npm run lint # Run ESLint -npm run typecheck # Run TypeScript type checking -npm test # Run Jest tests -npm run test:watch # Run Jest in watch mode +# Development +npm run dev # Start Next.js dev server (port 3000) +npm run build # Build static export to /out +npm run lint # ESLint +npm test # Jest tests +npm run test:watch # Jest watch mode +npm test -- --testPathPattern=__tests__/components/ColorCell # Run single test file -# API Development (in /api directory) -cd api && npm run dev # Start API server with nodemon (port 4000) +# API Development +cd api && npm run dev # Start API with nodemon (port 4000) -# Security & Quality -npm run security:check # Check for credential leaks (scripts/security/security-check.js) -npm run test:build # Test if build succeeds without creating files -./scripts/pre-commit.sh # Runs security, build, and test checks (triggered by husky) +# Quality Gates +npm run security:check # Credential leak detection +npm run test:build # Verify build succeeds without deploying +npx tsc --noEmit # TypeScript type checking -# Database Migrations +# Database npm run migrate # Run pending migrations locally -npm run migrate:clear # Clear migration history (development only) scripts/update-db-via-aws.sh # Run migrations on production RDS via EC2 -# Deployment -scripts/deploy-api-update.sh # Deploy API to EC2 via AWS SSM (recommended) -scripts/deploy-frontend.sh # Manual frontend deployment helper +# Deployment (auto via CI/CD on push to main, or manual) +scripts/deploy-api-update.sh # Deploy API to EC2 via AWS SSM +scripts/deploy-frontend.sh # Manual frontend deploy to S3/CloudFront ``` ## Architecture -### Frontend Structure (Next.js App Router) -- `/app` - Next.js 13+ app directory structure - - `/page.tsx` - Public filament inventory table - - `/upadaj` - Admin panel (password protected) - - `/page.tsx` - Admin login - - `/dashboard/page.tsx` - Filament CRUD operations - - `/colors/page.tsx` - Color management - - `/requests/page.tsx` - Customer color requests -- `/src` - Source files - - `/components` - React components - - `/services/api.ts` - Axios instance with auth interceptors - - `/data` - Color definitions (bambuLabColors.ts, bambuLabColorsComplete.ts) - - `/types` - TypeScript type definitions +### Frontend (Next.js App Router) +- `/app/page.tsx` — Public filament inventory table (home page) +- `/app/upadaj/` — Admin panel: login, `/dashboard` (filament CRUD), `/colors` (color management), `/requests` (customer color requests) +- `/src/services/api.ts` — Axios instance with auth interceptors (auto token injection, 401/403 redirect) +- `/src/data/bambuLabColors.ts` — Primary color-to-hex mapping with gradient support (used by `ColorCell` component) +- `/src/data/bambuLabColorsComplete.ts` — Extended color database grouped by finish type (used by filters) -### API Structure -- `/api` - Node.js Express server (runs on EC2, port 80) - - `/server.js` - Main Express server with all routes inline - - Database: PostgreSQL on AWS RDS - - Endpoints: `/api/login`, `/api/filaments`, `/api/colors`, `/api/sale/bulk`, `/api/color-requests` +### API (`/api/server.js`) +Single-file Express server running on EC2 (port 80). All routes inline. +- Endpoints: `/api/login`, `/api/filaments`, `/api/colors`, `/api/sale/bulk`, `/api/color-requests` +- Auth: JWT tokens with 24h expiry, single admin user (password from `ADMIN_PASSWORD` env var) -### Key Components -- `FilamentTableV2` - Main inventory display with sorting/filtering -- `SaleManager` - Bulk sale management interface -- `BulkFilamentPriceEditor` - Bulk price modification interface -- `ColorCell` - Smart color rendering with gradient support -- `EnhancedFilters` - Advanced filtering system -- `ColorRequestForm` - Customer color request form -- `ColorRequestModal` - Modal for color requests -- `SaleCountdown` - Sale timer display -- `MatomoAnalytics` - Analytics tracking -- `BackToTop` - Scroll-to-top button -- `MaterialBadge`, `ColorSwatch`, `InventoryBadge` - Display components +### Database (PostgreSQL on RDS) +Key schemas and constraints: -### Data Models - -#### Filament Schema (PostgreSQL) -```sql -filaments: { - id: UUID, - tip: VARCHAR(50), # Material type (PLA, PETG, ABS) - finish: VARCHAR(50), # Finish type (Basic, Matte, Silk) - boja: VARCHAR(100), # Color name - refill: INTEGER, # Refill spool count - spulna: INTEGER, # Regular spool count - kolicina: INTEGER, # Total quantity (refill + spulna) - cena: VARCHAR(50), # Price - sale_active: BOOLEAN, # Sale status - sale_percentage: INTEGER,# Sale discount - sale_end_date: TIMESTAMP # Sale expiry -} +``` +filaments: id, tip (material), finish, boja (color), refill, spulna, kolicina, cena, sale_* +colors: id, name, hex, cena_refill (default 3499), cena_spulna (default 3999) +color_requests: id, color_name, message, contact_name, contact_phone, status ``` -#### Color Schema -```sql -colors: { - id: UUID, - name: VARCHAR(100), # Color name (must match filament.boja) - hex: VARCHAR(7), # Hex color code - cena_refill: INTEGER, # Refill price (default: 3499) - cena_spulna: INTEGER # Regular price (default: 3999) -} -``` +**Critical constraints:** +- `filaments.boja` → FK to `colors.name` (ON UPDATE CASCADE) — colors must exist before filaments reference them +- `kolicina = refill + spulna` — enforced by check constraint +- Migrations in `/database/migrations/` with sequential numbering (currently up to `020_`) -#### Color Requests Schema -```sql -color_requests: { - id: UUID, - color_name: VARCHAR(255), # Requested color name - message: TEXT, # Customer message - contact_name: VARCHAR(255), # Customer name (required) - contact_phone: VARCHAR(50), # Customer phone (required) - status: VARCHAR(20), # Status: pending, reviewed, fulfilled - created_at: TIMESTAMP -} -``` +## Key Patterns -## Deployment +### Color Management (two-layer system) +1. **Database `colors` table**: Defines valid color names + pricing. Must be populated first due to FK constraint. +2. **Frontend `bambuLabColors.ts`**: Maps color names → hex codes for UI display. Supports solid (`hex: '#FF0000'`) and gradient (`hex: ['#HEX1', '#HEX2'], isGradient: true`). +3. Color names must match exactly between `colors.name`, `filaments.boja`, and `bambuLabColors.ts` keys. +4. The `ColorCell` component renders table row backgrounds from these mappings. -### Frontend (AWS CloudFront + S3) -- **Primary Method**: CloudFront distribution with S3 origin -- S3 bucket: `filamenteka-frontend` -- CloudFront Function: `index-rewrite` for directory-to-index.html routing -- Build output: Static files in `/out` directory (Next.js static export) -- Config: `next.config.js` (output: 'export'), `terraform/cloudfront-frontend.tf` -- Cache: 24h TTL for static assets, GET/HEAD methods cached -- OAC (Origin Access Control): S3 public access blocked, CloudFront-only access -- **CI/CD**: Gitea Actions workflow (`.gitea/workflows/deploy.yml`) auto-deploys on push to main - -### API Server (EC2) -- Deployment via `scripts/deploy-api-update.sh` (uses AWS SSM to push updates) -- Instance ID: `i-03956ecf32292d7d9` -- Server IP: `3.71.161.51` -- Domain: `api.filamenteka.rs` -- Service: `node-api` (systemd) -- Deploy script pulls from Gitea main branch and restarts service -- IMPORTANT: When deploying API, remember to build for AMD64 Linux (not ARM macOS) - -### Database (RDS PostgreSQL) -- Host: `filamenteka.ci7fsdlbzmag.eu-central-1.rds.amazonaws.com` -- User: `filamenteka_admin` -- Database: `filamenteka` -- Migrations in `/database/migrations/` -- Schema in `/database/schema.sql` -- Use `scripts/update-db-via-aws.sh` for running migrations on production - -## Important Patterns +### Adding New Colors +1. Add to database `colors` table (via admin panel at `/upadaj/colors` or migration) +2. Add hex mapping to `src/data/bambuLabColors.ts` +3. Optionally add to `src/data/bambuLabColorsComplete.ts` finish groups ### API Communication -- All API calls organized in service modules (`src/services/api.ts`) -- Services: `authService`, `colorService`, `filamentService`, `colorRequestService` -- Axios instance with interceptors for automatic auth token injection +- Service modules in `src/services/api.ts`: `authService`, `colorService`, `filamentService`, `colorRequestService` - Auth token stored in localStorage with 24h expiry -- Automatic redirect on 401/403 in admin routes (via response interceptor) -- Cache busting on filament fetches with timestamp query param +- Cache busting on filament fetches via timestamp query param -### Color Management -- Frontend color mappings in `src/data/bambuLabColors.ts` and `bambuLabColorsComplete.ts` -- Database color definitions in `colors` table with pricing (cena_refill, cena_spulna) -- ColorMapping interface supports both solid colors (hex string) and gradients (hex array) -- Automatic row coloring via ColorCell component based on filament.boja -- Special handling for gradient filaments (e.g., Cotton Candy Cloud) -- Foreign key constraint: filaments.boja → colors.name (ON UPDATE CASCADE) +## CI/CD (Gitea Actions) -### State Management -- React hooks for local component state -- No global state management library -- Data fetching in components with useEffect -- Service layer handles all API interactions +`.gitea/workflows/deploy.yml` triggers on push to `main`. Auto-detects changes via `git diff HEAD~1`: +- **Frontend changes** (anything outside `api/`): security check → tests → build → S3 deploy (3-tier cache: HTML no-cache, `_next/` immutable, rest 24h) → CloudFront invalidation +- **API changes** (`api/` files): SSM command to EC2 to pull `server.js` from Gitea and restart `node-api` service +- Both run if both changed in a single push -### Testing -- Jest + React Testing Library -- Tests in `__tests__/` directory -- Config: `jest.config.js`, `jest.setup.js` -- Coverage goal: >80% -- Run with `npm test` or `npm run test:watch` -- Test files include: - - `ui-features.test.ts` - UI functionality - - `api-integration.test.ts` - API interactions - - `no-mock-data.test.ts` - Real data validation (ensures no fake/mock data) - - `color-management.test.ts` - Color CRUD operations - - `data-consistency.test.ts` - Data validation - - `components/*.test.tsx` - Component unit tests - - `data/*.test.ts` - Data structure validation +## Deployment Details + +| Component | Target | Method | +|-----------|--------|--------| +| Frontend | S3 `filamenteka-frontend` → CloudFront | Static export, OAC-protected | +| API | EC2 `i-03956ecf32292d7d9` | SSM, systemd `node-api` service | +| Database | RDS `filamenteka.ci7fsdlbzmag.eu-central-1.rds.amazonaws.com` | Migrations via `scripts/update-db-via-aws.sh` | +| DNS | Cloudflare | `api.filamenteka.rs` → EC2 | +| IaC | `/terraform/` | VPC, EC2, RDS, ALB, CloudFront, Cloudflare | ## Environment Variables ```bash # Frontend (.env.local) NEXT_PUBLIC_API_URL=https://api.filamenteka.rs/api +NEXT_PUBLIC_MATOMO_URL=https://analytics.demirix.dev +NEXT_PUBLIC_MATOMO_SITE_ID=7 -# API Server (.env in /api directory) +# API Server (.env in /api) DATABASE_URL=postgresql://filamenteka_admin:PASSWORD@filamenteka.ci7fsdlbzmag.eu-central-1.rds.amazonaws.com:5432/filamenteka JWT_SECRET=... ADMIN_PASSWORD=... @@ -206,75 +123,10 @@ NODE_ENV=production PORT=80 ``` -## Security Considerations +## Pre-commit Hooks (Husky) -- **Authentication**: JWT tokens with 24h expiry (hardcoded admin user for now) -- **Password**: Stored in environment variable ADMIN_PASSWORD (bcrypt ready for multi-user) -- **SQL Injection**: Prevented via parameterized queries in all database operations -- **Credential Leak Detection**: Pre-commit hook runs `scripts/security/security-check.js` -- **CORS**: Currently allows all origins (origin: true) - consider hardening for production -- **Auth Interceptors**: Automatic 401/403 handling with redirect to login -- **Pre-commit Checks**: Husky runs `scripts/pre-commit.sh` which executes: - 1. Author mention check (blocks commits with attribution) - 2. Security check (credential leaks) - 3. Build test (ensures code compiles) - 4. Unit tests (Jest with --passWithNoTests) - -## Development Workflows - -### Adding New Colors -1. Add color to database via admin panel (`/upadaj/colors`) OR via migration -2. Update frontend color mappings in `src/data/bambuLabColors.ts`: - ```typescript - 'Color Name': { hex: '#HEXCODE' } // Solid color - 'Gradient Name': { hex: ['#HEX1', '#HEX2'], isGradient: true } // Gradient - ``` -3. Color names must match exactly between database and filament.boja - -### Database Migrations -When modifying the database: -1. Create migration file in `/database/migrations/` with sequential numbering (e.g., `019_add_new_feature.sql`) -2. Test locally first with `npm run migrate` -3. Run migration on production: - - Use `scripts/update-db-via-aws.sh` for remote execution via EC2 - - Or SSH to EC2 and run migration directly -4. Update corresponding TypeScript types in `/src/types/` - -Important database constraints: -- `filaments.boja` has foreign key to `colors.name` (ON UPDATE CASCADE) -- `filaments.kolicina` has check constraint: `kolicina = refill + spulna` -- Always update `colors` table first before adding filaments with new colors - -### Deployment Workflow (Gitea Actions CI/CD) -Pushing to `main` triggers `.gitea/workflows/deploy.yml` which auto-detects what changed: -- **Frontend changes**: Runs security check, tests, builds Next.js, deploys to S3 with cache headers, invalidates CloudFront -- **API changes**: Sends SSM command to EC2 to download `server.js` from Gitea and restart the service -- **Both**: If a push touches frontend and API files, both deploy steps run - -Manual deployment is still available: -1. Frontend: `./scripts/deploy-frontend.sh` -2. API: `./scripts/deploy-api-update.sh` -3. Verify API: `curl https://api.filamenteka.rs/` should return `{"status":"ok"}` - -## Infrastructure - -### Terraform (IaC) -Infrastructure as Code in `/terraform/`: -- **VPC**: `vpc.tf` - Network setup with subnets and routing -- **EC2**: `ec2-api.tf` - API server instance (i-03956ecf32292d7d9) -- **RDS**: `rds.tf` - PostgreSQL database -- **ALB**: `alb.tf` - Application Load Balancer -- **ECR**: `ecr.tf` - Docker image registry -- **CloudFront**: `cloudfront-frontend.tf` - Frontend CDN distribution -- **Cloudflare**: `cloudflare-api.tf` - DNS integration for api.filamenteka.rs -- **Configuration**: `variables.tf`, `outputs.tf`, `main.tf` - -### Current Stack -- **Frontend**: AWS CloudFront + S3 (static Next.js export) -- **API**: EC2 instance running Node.js/Express with systemd service -- **Database**: AWS RDS PostgreSQL (eu-central-1) -- **DNS**: Cloudflare for api.filamenteka.rs -- **CI/CD**: Gitea Actions (`.gitea/workflows/deploy.yml`) -- **Deployment**: CloudFront + S3 for frontend, SSM for API updates -- **Repository**: `git.demirix.dev/dax/Filamenteka` -- **CDN**: CloudFront PriceClass_100 (US, Canada, Europe) \ No newline at end of file +`scripts/pre-commit.sh` runs automatically and blocks commits that fail: +1. Author mention check (blocks attribution) +2. Security check (credential leaks) +3. Build test (ensures compilation) +4. Unit tests (`jest --passWithNoTests`)