2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00
2026-07-24 11:34:00 -03:00

ByRachel — Clothing Business Management

A full-featured web application for managing a small clothing business. Built with Next.js, optimized for deployment on a single VPS with Docker.

Features

  • Product Catalog — Products with variants (size/color), images, traffic light inventory system
  • Sales Management — Full sales workflow: create, confirm, deliver, cancel, returns
  • Purchase Orders — Supplier purchases with receive/confirm/cancel workflow
  • Inventory — Movement-based stock tracking, restock suggestions, adjustments
  • Customers — Customer management with tags and sale history
  • Suppliers — Supplier management with product associations
  • Categories — Hierarchical categories with color coding
  • Analytics — Sales trends, revenue/COGS/profit KPIs, best sellers, seasonality, cost evolution, inventory health
  • Reports — PDF generation for sales, purchases, inventory, customers, brand, analytics
  • Public Catalog — Customer-facing product catalog at /catalogo
  • Settings — Brand configuration, pricing rules, password management, TOTP 2FA
  • Audit Logging — All critical operations are logged with before/after data

Tech Stack

Layer Technology
Framework Next.js 16 (App Router)
Language TypeScript (strict mode)
Styling Tailwind CSS 4
UI Components shadcn/ui (Radix UI primitives)
Database SQLite (better-sqlite3)
ORM Drizzle ORM
Authentication JWT (jose) + TOTP (otplib) + Argon2 password hashing
Charts Recharts
PDF Generation pdf-lib (client-side)
Image Processing Sharp
Testing Vitest
Drag & Drop @dnd-kit
Validation Zod
Container Docker (multi-stage build)

Prerequisites

  • Node.js 22+ and npm (for development)
  • Docker & Docker Compose (for container deployment)
  • A domain name (for production HTTPS, optional for local use)

Quick Start (Docker)

The fastest way to get ByRachel running:

# 1. Clone the repository
git clone <your-repo-url> byrachel
cd byrachel

# 2. Create environment file
cp .env.example .env
# Edit .env and change JWT_SECRET to a random secure value:
# openssl rand -base64 32

# 3. Start the application
docker compose up -d

# 4. Open in browser
# http://localhost:3000
# You'll be redirected to /setup to create your admin account

Development Setup

# 1. Clone and install
git clone <your-repo-url> byrachel
cd byrachel
npm install

# 2. Set up environment
cp .env.example .env

# 3. Initialize the database
npm run db:push

# 4. (Optional) Seed sample data
npm run seed

# 5. Start development server
npm run dev
# Open http://localhost:3000

Development Commands

Command Description
npm run dev Start development server with hot reload
npm run build Create production build
npm run start Start production server
npm run test Run all tests
npm run test:watch Run tests in watch mode
npm run test:coverage Run tests with coverage report
npm run db:push Push schema changes to database
npm run db:generate Generate migration files
npm run db:migrate Run pending migrations
npm run db:studio Open Drizzle Studio (database GUI)
npm run seed Seed database with sample data

Environment Variables

Variable Description Default Required
DATABASE_URL SQLite database file path. Dev: ./data/db.sqlite, Docker: /app/data/db.sqlite ./data/db.sqlite Yes
JWT_SECRET Secret key for JWT signing. MUST be changed in production. Generate with: openssl rand -base64 32 change-me-to-a-256-bit-secret Yes
UPLOAD_DIR Directory for uploaded images. Dev: ./public/uploads, Docker: /app/public/uploads ./public/uploads Yes
STORAGE_ADAPTER Storage backend. Currently only local (filesystem) is supported local Yes
NODE_ENV Node environment: development or production production Yes
NEXT_TELEMETRY_DISABLED Disable Next.js telemetry. Set to 1 to disable 1 No

API Routes Reference

All API routes are under /api/. Admin routes require a valid JWT cookie (except /api/health and /api/auth/*).

Authentication

Method Route Description
POST /api/auth/setup Initial admin account creation (first run only)
POST /api/auth/login Login with email/password
POST /api/auth/verify-totp Verify TOTP code
GET /api/auth/me Get current user info
POST /api/auth/logout Logout (clear session)
PUT /api/auth/change-password Change password
POST /api/auth/regenerate-recovery-codes Generate new recovery codes

Products

Method Route Description
GET/POST /api/products List / Create products
GET/PUT/DELETE /api/products/[id] Get / Update / Delete product
POST /api/products/[id]/duplicate Duplicate a product
GET/POST /api/products/[id]/images List / Upload product images
DELETE /api/products/[id]/images/[imageId] Delete product image
PUT /api/products/[id]/images/reorder Reorder product images
GET/POST /api/products/[id]/variants List / Create variants
PUT/DELETE /api/products/[id]/variants/[variantId] Update / Delete variant
GET /api/products/[id]/variants/[variantId]/movements Get variant movements

Categories

Method Route Description
GET/POST /api/categories List / Create categories
PUT/DELETE /api/categories/[id] Update / Delete category

Sales

Method Route Description
GET/POST /api/sales List / Create sales
GET/PUT/DELETE /api/sales/[id] Get / Update / Delete sale
POST /api/sales/[id]/confirm Confirm sale
POST /api/sales/[id]/deliver Mark sale as delivered
POST /api/sales/[id]/cancel Cancel sale
POST /api/sales/[id]/return Process sale return

Purchases

Method Route Description
GET/POST /api/purchases List / Create purchases
GET/PUT/DELETE /api/purchases/[id] Get / Update / Delete purchase
POST /api/purchases/[id]/receive Receive purchase items
POST /api/purchases/[id]/confirm Confirm purchase
POST /api/purchases/[id]/cancel Cancel purchase

Inventory

Method Route Description
GET /api/inventory Get inventory status
POST /api/inventory/restock Restock variants
POST /api/inventory/adjust Manual stock adjustment
GET /api/inventory/movements List stock movements

Customers

Method Route Description
GET/POST /api/customers List / Create customers
GET/PUT/DELETE /api/customers/[id] Get / Update / Delete customer
GET/POST /api/customers/[id]/tags List / Add customer tags
DELETE /api/customers/[id]/tags/[tagId] Remove customer tag
GET /api/customer-tags List all customer tags

Suppliers

Method Route Description
GET/POST /api/suppliers List / Create suppliers
GET/PUT/DELETE /api/suppliers/[id] Get / Update / Delete supplier
GET /api/suppliers/[id]/products Get supplier's products

Analytics

Method Route Description
GET /api/analytics/summary Sales summary KPIs
GET /api/analytics/sales-trend Revenue/units over time
GET /api/analytics/categories Sales by category
GET /api/analytics/products Product performance
GET /api/analytics/customers Customer analytics
GET /api/analytics/cost-evolution Cost price evolution
GET /api/analytics/inventory-health Inventory health metrics
GET /api/analytics/seasonality Seasonal sales patterns

Reports (PDF Generation)

Method Route Description
GET /api/reports/sales Sales report data
GET /api/reports/sales/[id] Single sale report
GET /api/reports/sales/profits Profit report
GET /api/reports/purchases Purchases report data
GET /api/reports/purchases/[id] Single purchase report
GET /api/reports/inventory Inventory report
GET /api/reports/customers Customers report
GET /api/reports/customers/[id] Single customer report
GET /api/reports/brand Brand/catalog report
GET /api/reports/analytics/best-sellers Best sellers report
GET /api/reports/analytics/low-rotation Low rotation report

Settings

Method Route Description
GET/PUT /api/settings/brand Get / Update brand settings
GET/PUT /api/settings/pricing Get / Update pricing settings

Admin

Method Route Description
GET /api/admin/backup Download full backup (tar.gz)

Other

Method Route Description
GET /api/health Health check (public, no auth)
POST /api/upload Upload image file

Backup & Restore

Automated Backup (Admin Endpoint)

Admin users can download a full backup from the app:

GET /api/admin/backup

This returns a tar.gz archive containing:

  • data/ — SQLite database files (main DB, WAL, SHM)
  • public/uploads/ — All uploaded images

Requirements: Must be authenticated as admin. Download over HTTPS only.

Manual SQLite Backup

SQLite with WAL mode requires a safe backup procedure to avoid corruption:

# Option 1: Using SQLite .backup command (safest)
docker compose exec app sh -c 'sqlite3 /app/data/db.sqlite ".backup /app/data/backup.db"'
# Then copy backup.db from the container

# Option 2: Copy WAL-safe (stop writes first)
# 1. Temporarily stop the application
docker compose stop app

# 2. Copy the database files
docker compose cp app:/app/data/db.sqlite ./backup/db.sqlite
docker compose cp app:/app/data/db.sqlite-wal ./backup/db.sqlite-wal
docker compose cp app:/app/data/db.sqlite-shm ./backup/db.sqlite-shm

# 3. Restart the application
docker compose start app

# Option 3: Using docker volume backup
docker run --rm -v byrachel_db-data:/data -v $(pwd)/backup:/backup alpine \
  tar czf /backup/byrachel-db-$(date +%Y%m%d).tar.gz -C /data .

Image Volume Backup

docker run --rm -v byrachel_uploads:/uploads -v $(pwd)/backup:/backup alpine \
  tar czf /backup/byrachel-uploads-$(date +%Y%m%d).tar.gz -C /uploads .

Restore Procedure

# 1. Stop the application
docker compose stop app

# 2. Restore database
docker run --rm -v byrachel_db-data:/data -v $(pwd)/backup:/backup alpine \
  sh -c 'rm -rf /data/* && tar xzf /backup/byrachel-db-YYYYMMDD.tar.gz -C /data'

# 3. Restore uploads
docker run --rm -v byrachel_uploads:/uploads -v $(pwd)/backup:/backup alpine \
  sh -c 'rm -rf /uploads/* && tar xzf /backup/byrachel-uploads-YYYYMMDD.tar.gz -C /uploads'

# 4. Start the application
docker compose start app

# 5. Verify
docker compose ps  # Check health status
curl http://localhost:3000/api/health  # Should return {"status":"ok"}

Production Deployment

Docker Deployment

# 1. Clone on your server
git clone <your-repo-url> byrachel
cd byrachel

# 2. Configure environment
cp .env.example .env
nano .env
# Set JWT_SECRET to a random value: openssl rand -base64 32

# 3. Build and start
docker compose up -d --build

# 4. Verify
docker compose ps
docker compose logs -f app

Nginx Proxy Manager Setup

Nginx Proxy Manager is the recommended reverse proxy for HTTPS and domain routing.

1. Install Nginx Proxy Manager

# Add to your docker-compose.yml or run separately
docker run -d \
  --name nginx-proxy-manager \
  -p 80:80 -p 81:81 -p 443:443 \
  -v ./npm-data:/data \
  -v ./npm-le:/etc/letsencrypt \
  jc21/nginx-proxy-manager:latest

2. Configure Proxy Host

  1. Open NPM admin: http://your-server-ip:81
  2. Default login: admin@example.com / changeme
  3. Go to Hosts → Proxy Hosts → Add Proxy Host
  4. Configure:
    • Domain Names: yourdomain.com
    • Scheme: http
    • Hostname: byrachel-app (Docker container name) or your server's Docker IP
    • Port: 3000
    • Common Settings:
      • Enable: Block Common Exploits
      • Enable: Websockets Support
    • SSL Tab:
      • SSL Certificate: Request a new Let's Encrypt certificate
      • Enable: Force SSL
      • Enable: HTTP/2 Support

If both NPM and ByRachel are in the same Docker network:

# docker-compose.yml
services:
  app:
    # ... existing config ...
    networks:
      - proxy-net

networks:
  proxy-net:
    external: true

Then in NPM, use the container name byrachel-app as the hostname.

HTTPS Configuration

HTTPS is configured through Nginx Proxy Manager:

  1. Request Certificate: In the Proxy Host SSL tab, select "Request a new SSL Certificate"
  2. Let's Encrypt: NPM handles Let's Encrypt automatically
  3. Force SSL: Enable to redirect HTTP → HTTPS
  4. Auto-renewal: Certificates renew automatically

Domain Configuration

  1. DNS Record: Create an A record pointing your domain to your server's IP

    Type: A
    Name: yourdomain.com (or @)
    Value: YOUR_SERVER_IP
    TTL: 300
    
  2. Wait for DNS propagation (usually minutes, up to 48h)

  3. Configure in NPM: Add the domain in the Proxy Host settings (see above)

Auto-restart & Log Management

Auto-restart

The Docker Compose configuration includes restart: unless-stopped, which means:

  • Container restarts automatically on crash
  • Container starts on Docker daemon startup (server reboot)
  • Container only stays stopped if you explicitly docker compose stop

For additional server-level auto-start, create a systemd service:

# /etc/systemd/system/byrachel.service
[Unit]
Description=ByRachel Docker Compose
Requires=docker.service
After=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/path/to/byrachel
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target
sudo systemctl enable byrachel.service
sudo systemctl start byrachel.service

Log Management

# View live logs
docker compose logs -f app

# View last 100 lines
docker compose logs --tail=100 app

# Configure log rotation (in docker-compose.yml, add to app service):
# logging:
#   driver: json-file
#   options:
#     max-size: "10m"
#     max-file: "3"

SQLite → PostgreSQL Migration Plan

ByRachel uses Drizzle ORM, which supports multiple database dialects. Migration to PostgreSQL is possible when needed:

Steps

  1. Install PostgreSQL driver:

    npm install pg
    npm install @types/pg
    
  2. Update Drizzle config (drizzle.config.ts):

    export default defineConfig({
      schema: './src/lib/db/schema.ts',
      out: './src/lib/db/migrations',
      dialect: 'postgresql',  // Change from 'sqlite'
      dbCredentials: {
        host: process.env.PGHOST,
        port: Number(process.env.PGPORT),
        user: process.env.PGUSER,
        password: process.env.PGPASSWORD,
        database: process.env.PGDATABASE,
      },
    });
    
  3. Update database connection (src/lib/db/index.ts):

    • Replace drizzle(betterSqlite3(...)) with drizzle(pgPool) or drizzle(nodePostgres(...))
  4. Update environment variables:

    DATABASE_URL=postgresql://user:password@localhost:5432/byrachel
    
  5. Generate and run migrations:

    npm run db:generate
    npm run db:migrate
    
  6. Data migration: Export data from SQLite and import into PostgreSQL using a migration script.

Considerations

  • SQLite uses integer primary keys with auto-increment — PostgreSQL uses serial or identity
  • SQLite boolean (integer 0/1) maps directly to PostgreSQL boolean
  • SQLite text timestamps work in both, but PostgreSQL has native timestamp types
  • Test thoroughly in a staging environment before migrating production data

First-Run Setup

1. Initial Admin Account

  1. Open the app in your browser (e.g., http://localhost:3000)
  2. You'll be redirected to /setup (only available when no users exist)
  3. Fill in:
    • Email: Admin email address
    • Password: Strong password (min 8 characters)
    • Name: Admin display name (optional)
  4. Click "Create Account"

2. TOTP Configuration

After creating the account, TOTP (Time-based One-Time Password) setup begins automatically:

  1. A QR code is displayed on screen
  2. Open your authenticator app (Google Authenticator, Authy, 1Password, etc.)
  3. Scan the QR code (or enter the secret key manually)
  4. Enter the 6-digit code from your authenticator app to verify
  5. Save your recovery codes in a secure location — these are your backup if you lose access to your authenticator

3. Brand Configuration

After logging in for the first time:

  1. Navigate to Settings (gear icon in sidebar)
  2. Configure your brand:
    • Store Name: Your business name
    • Logo: Upload your logo image
    • Tagline: Short description
    • Colors: Primary and secondary brand colors
    • Contact Info: Phone, email, WhatsApp
    • Address: Business address
    • Footer Text: Custom footer for catalog pages
  3. Click "Save Settings"
  4. To publish your public catalog, check "Publish Catalog" in settings

Known Limitations

  • SQLite write concurrency: SQLite handles concurrent reads well but writes are serialized. For a single-admin small business, this is not an issue. If you need multi-writer support, migrate to PostgreSQL (see migration plan above).
  • No built-in email: The app does not send emails. Password recovery uses local recovery codes instead of email-based reset.
  • Single admin: The app supports admin and operator roles, but the initial setup creates only one admin user. Additional users must be created manually via the database.
  • No automated backups: The backup endpoint provides on-demand backups. Set up cron jobs or external tools for scheduled backups.
  • Local file storage only: Images are stored on the local filesystem. For multi-instance deployments, a shared storage solution (S3, etc.) would be needed.
  • No real-time updates: The app uses standard HTTP requests. Changes made in one browser tab are not reflected in other tabs until refresh.
  • Catalog is read-only for customers: The public catalog at /catalogo is for browsing only. Customers cannot place orders through the app.

License

Private — All rights reserved.

S
Description
cosa de vizcachas
Readme 541 KiB
Languages
TypeScript 98.6%
CSS 1.2%
Dockerfile 0.1%