Back to Documentation

Developer Guide

Technical documentation for developers

Architecture Overview

Ratio Tuta is built with modern web technologies for performance, scalability, and developer experience.

Tech Stack

Frontend

  • Next.js 15.5.0 - React framework with App Router
  • TypeScript - Type safety
  • Tailwind CSS - Utility-first styling
  • next-intl - Internationalization
  • React Hot Toast - Notifications

Backend

  • Next.js API Routes - Serverless endpoints
  • Prisma - ORM and database toolkit
  • PostgreSQL - Relational database
  • bcryptjs - Password hashing
  • iron-session - Secure sessions

Infrastructure

  • AWS S3 - File storage (avatars, images)
  • Upstash Redis - Rate limiting & caching
  • Resend - Transactional emails
  • Vercel - Deployment platform

Security

  • CSRF Protection - Stateless HMAC tokens
  • CSP Headers - Content Security Policy
  • Rate Limiting - DDoS protection
  • AES-256-GCM - Email encryption

Project Structure

ratio-tuta/
                          ├── src/
                          │   ├── app/                  # Next.js 15 App Router
                          │   │   ├── api/             # API endpoints
                          │   │   ├── auth/            # Authentication pages
                          │   │   ├── dashboard/       # Admin dashboard
                          │   │   ├── cash-register/   # POS interface
                          │   │   └── docs/            # Documentation
                          │   ├── components/          # React components
                          │   │   ├── admin-zone/      # Admin UI components
                          │   │   ├── cash-register/   # POS components
                          │   │   └── ui/              # Reusable UI elements
                          │   ├── lib/                 # Utility libraries
                          │   │   ├── api-client.ts    # CSRF-enabled API wrapper
                          │   │   ├── auth.ts          # Authentication helpers
                          │   │   ├── csrf.ts          # CSRF token generation
                          │   │   ├── session.ts       # Session management
                          │   │   └── prisma.ts        # Database client
                          │   └── hooks/               # Custom React hooks
                          ├── prisma/
                          │   └── schema.prisma        # Database schema
                          ├── lib/                     # Shared backend utilities
                          ├── public/                  # Static assets
                          └── messages/                # i18n translations

Design Patterns

  • Server Components: Default for data fetching and SSR
  • Client Components: Only when needed for interactivity
  • API Route Handlers: RESTful endpoints in /app/api
  • Middleware: Session validation, CSRF, rate limiting
  • Custom Hooks: Reusable stateful logic (e.g., useCsrfToken)
  • Repository Pattern: Database operations abstracted via Prisma

💡 Architecture Principle: Architecture Principle: We follow a pragmatic approach - use server components for data fetching, client components for interactivity, and API routes for mutations.

Database Schema

PostgreSQL database managed with Prisma ORM for type-safe queries and migrations.

Core Models

User & Authentication
model User {
                        id              String    @id @default(cuid())
                        email           String    @unique
                        emailHmac       String    @unique  // HMAC for lookups
                        emailEncrypted  String            // AES-256-GCM encrypted
                        passwordHash    String
                        firstName       String
                        lastName        String
                        role            Role      @default(USER)
                        emailVerified   Boolean   @default(false)
                        avatarUrl       String?
                        createdAt       DateTime  @default(now())
                      }

Security: Emails are encrypted (AES-256-GCM) and HMAC-indexed for secure lookups.

Team & Members
model Team {
                        id          String         @id @default(cuid())
                        name        String
                        ownerId     String
                        members     TeamMember[]
                        places      Place[]
                        items       Item[]
                        plan        SubscriptionPlan @default(FREE)
                      }

                      model TeamMember {
                        id       String   @id @default(cuid())
                        teamId   String
                        userId   String
                        role     TeamRole @default(MEMBER)
                      }

Multi-tenancy: Team-based isolation for security and data separation.

Place (Location)
model Place {
                      id          String    @id @default(cuid())
                      teamId      String
                      name        String
                      address1    String?
                      city        String?
                      country     String?
                      timezone    String?
                      currency    String    @default("EUR")
                      isActive    Boolean   @default(true)
                      receipts    Receipt[]
                      createdAt   DateTime  @default(now())
                    }
Item (Product)
model Item {
                      id              String          @id @default(cuid())
                      teamId          String
                      name            String
                      sku             String?
                      price           Float
                      pricePaid       Float?
                      taxRateBps      Int             // Basis points (100 = 1%)
                      imageUrl        String
                      measurementType MeasurementType @default(PCS)
                      stockQuantity   Float           @default(0)
                      categoryId      String?
                      isActive        Boolean         @default(true)
                    }

Measurement Types: PCS, WEIGHT, LENGTH, VOLUME, AREA

Receipt & Line Items
model Receipt {
                      id            String        @id @default(cuid())
                      placeId       String
                      userId        String
                      totalAmount   Float
                      taxAmount     Float
                      amountGiven   Float
                      changeAmount  Float
                      paymentOption PaymentOption
                      lineItems     ReceiptLineItem[]
                      createdAt     DateTime      @default(now())
                    }

                    model ReceiptLineItem {
                      id          String  @id @default(cuid())
                      receiptId   String
                      itemId      String
                      quantity    Float
                      unitPrice   Float
                      totalPrice  Float
                      taxRateBps  Int
                    }

Database Operations

// Run migrations
                  npm run prisma:migrate

                  // Generate Prisma Client
                  npm run prisma:generate

                  // Open Prisma Studio (GUI)
                  npm run prisma:studio

                  // Reset database (development only!)
                  npm run prisma:reset

⚠️ Security: All database queries use parameterized statements via Prisma. Email addresses are encrypted at rest and HMAC-indexed for secure lookups.

API Routes

RESTful API built with Next.js 15 API Routes. All endpoints require authentication unless marked as public.

Authentication Endpoints

POST /api/login

Login with email & password. Returns user data and sets session cookie.

// Request
{ "email": "user@example.com", "password": "***", "remember": true }

// Response
{ "id": "...", "email": "...", "role": "USER" }
POST /api/register/self

Register new user account. Sends verification email.

// Request
{ "name": "John Doe", "email": "...", "password": "...", "teamName": "..." }

// Response
{ "message": "Account created. Please verify your email." }
POST /api/logout

End session and clear cookies. Requires CSRF token.

Resource Endpoints

EndpointMethodDescriptionCSRF
/api/users/meGETGet current user
/api/users/mePATCHUpdate profile
/api/users/meDELETEDelete account
/api/placesGETList places
/api/placesPOSTCreate place
/api/places/[id]PATCHUpdate place
/api/places/[id]DELETEDelete place
/api/itemsGETList items
/api/itemsPOSTCreate item
/api/receiptsPOSTCreate receipt

Using the API Client

Frontend uses a CSRF-enabled API wrapper:

import { api, ApiError } from '@/lib/api-client'

                  // GET request (no CSRF needed)
                  const data = await api.get('/api/places')

                  // POST request (CSRF auto-included)
                  const newPlace = await api.post('/api/places', {
                    name: 'New Store',
                    currency: 'EUR'
                  })

                  // PATCH request
                  const updated = await api.patch('/api/places/123', {
                    name: 'Updated Name'
                  })

                  // DELETE request
                  await api.delete('/api/places/123')

                  // Error handling
                  try {
                    await api.post('/api/places', data)
                  } catch (err) {
                    if (err instanceof ApiError) {
                      console.error(err.message, err.status)
                    }
                  }

✅ CSRF Protection: All POST/PATCH/PUT/DELETE requests automatically include a CSRF token via the X-CSRF-Token header.

Security Practices

Comprehensive security implementation following OWASP guidelines and industry best practices.

CSRF Protection

Implementation: Stateless HMAC-based tokens tied to user sessions

  • Token format: randomValue.hmac(randomValue:userId)
  • No server-side storage required
  • Timing-safe token comparison (prevents timing attacks)
  • Auto-included in all state-changing requests
  • 9 critical endpoints protected
// Backend validation
                  import { requireCsrfToken } from '@lib/csrf'

                  export async function POST(req: Request) {
                    const session = await getSession()
                    requireCsrfToken(req, session) // Throws if invalid
                    // ... handle request
                  }

Authentication & Sessions

  • Password Hashing: bcrypt with 12 rounds
  • Password Requirements: 8-128 characters, complexity checks
  • Pwned Password Check: k-Anonymity API integration
  • Session Cookies: HttpOnly, Secure, SameSite=Strict, __Host- prefix
  • Session Storage: iron-session with AES-256-GCM encryption
  • Email Verification: Required for account activation

Data Protection

Email Encryption

  • • AES-256-GCM at rest
  • • HMAC-SHA256 for lookups
  • • Prevents data breach exposure

File Storage

  • • AWS S3 with signed URLs
  • • 5MB max file size
  • • MIME type validation

Security Headers

// next.config.ts
                  headers: [
                    { key: 'X-Frame-Options', value: 'SAMEORIGIN' },
                    { key: 'X-Content-Type-Options', value: 'nosniff' },
                    { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
                    { key: 'Permissions-Policy', value: 'geolocation=(), microphone=()' },
                    {
                      key: 'Content-Security-Policy',
                      value: "default-src 'self'; img-src 'self' data: https://*.amazonaws.com;"
                    },
                    {
                      key: 'Strict-Transport-Security',
                      value: 'max-age=63072000; includeSubDomains; preload'
                    }
                  ]

Rate Limiting

  • Authentication: 5 attempts per 15 minutes per IP
  • API Endpoints: Sliding window algorithm
  • Storage: Upstash Redis (distributed)
  • Response Headers: X-RateLimit-* for debugging

Audit Logging

All security-sensitive actions are logged:

import { logAudit } from '@lib/logger'

                await logAudit({
                  action: 'user.login.success',
                  status: 'SUCCESS',
                  userId: user.id,
                  metadata: { ip: req.ip }
                })

🔒 Security Checklist: Never commit secrets (.env), always use HTTPS in production, rotate JWT secrets regularly, monitor audit logs for suspicious activity.

Deployment Guide

Deploy to production with Vercel, PostgreSQL, and AWS services.

Environment Variables

Required environment variables (see .env.example):

# Database
                      DATABASE_URL="postgresql://..."

                      # Sessions
                      SESSION_SECRET="strong-random-secret-min-32-chars"

                      # AWS S3 (file uploads)
                      AWS_ACCESS_KEY_ID="..."
                      AWS_SECRET_ACCESS_KEY="..."
                      AWS_REGION="us-east-1"
                      AWS_S3_BUCKET="ratio-tuta-files"

                      # Email (Resend)
                      RESEND_API_KEY="re_..."
                      RESEND_FROM_EMAIL="noreply@yourdomain.com"

                      # Redis (rate limiting)
                      UPSTASH_REDIS_REST_URL="https://..."
                      UPSTASH_REDIS_REST_TOKEN="..."

                      # App
                      NEXT_PUBLIC_APP_URL="https://yourdomain.com"
                      NODE_ENV="production"

Deployment Steps

  1. Set up PostgreSQL database
    # Use Vercel Postgres, Supabase, or any PostgreSQL provider
                          # Run migrations
                          npx prisma migrate deploy
  2. Configure AWS S3 bucket
    • Create bucket with private access
    • Configure CORS for your domain
    • Create IAM user with S3 permissions
  3. Set up Redis (Upstash)
    # Create Redis database at upstash.com
                            # Copy REST URL and token to env vars
  4. Deploy to Vercel
    # Install Vercel CLI
                            npm i -g vercel
    
                            # Deploy
                            vercel --prod
    
                            # Or connect GitHub repo for auto-deploys
  5. Set environment variables in Vercel dashboard
  6. Configure custom domain

Production Checklist

  • Database migrations applied
  • All environment variables set
  • SESSION_SECRET is cryptographically strong (32+ chars)
  • HTTPS enabled (automatic with Vercel)
  • Email sending configured and tested
  • S3 bucket CORS configured for your domain
  • Rate limiting enabled (Upstash Redis connected)
  • Audit logging reviewed
  • Error monitoring configured (Sentry, etc.)

Monitoring

  • Vercel Analytics: Performance and usage metrics
  • Database: Monitor connection pool, query performance
  • Redis: Check rate limit hits, cache performance
  • S3: Monitor storage usage and costs
  • Logs: Review audit logs for security events

🚀 Performance: Next.js automatically optimizes your app with code splitting, image optimization, and edge caching. Vercel provides global CDN distribution.

Ratio Tuta - Modern Inventory & Financial Management System