41 KiB
HarborSmith Platform Implementation Plan
Complete Technical Guide for Production Development
Version: 1.0
Date: September 2025
Status: Ready for Development
📋 Executive Summary
HarborSmith is a comprehensive marine services platform bridging yacht owners and maritime service providers through two primary verticals: Maintenance Services and Charter Services. This document provides the complete technical implementation plan for migrating from HTML mockups to a production-ready platform using modern, scalable architecture without cutting corners.
Key Objectives
- Build a robust, scalable platform from day one
- Implement microservices architecture for future growth
- Ensure enterprise-grade security with SSO
- Enable real-time capabilities for enhanced UX
- Maintain SEO optimization through SSR
- Deploy rapidly without compromising quality
🏗️ Technology Stack
Core Technologies
# Frontend
Framework: Next.js 14 (App Router)
Language: TypeScript
Styling: TailwindCSS + Shadcn/UI
State Management: Zustand + TanStack Query
Forms: React Hook Form + Zod
SEO: Server-Side Rendering (SSR)
# Backend
Architecture: Microservices
Runtime: Node.js
Framework: Fastify
API Design: RESTful
Validation: Zod
Documentation: OpenAPI/Swagger
# Database
Primary: Supabase (PostgreSQL)
Cache: Redis
Real-time: Supabase Realtime
File Storage: MinIO S3
# Authentication
SSO Provider: Keycloak
Session Management: JWT + Refresh Tokens
Authorization: RBAC with Casbin
# Infrastructure
Containerization: Docker
Reverse Proxy: Nginx
Orchestration: Docker Compose → Kubernetes (future)
Monitoring: Umami Analytics
# Third-Party Services
Payments: Stripe
Invoicing: InvoiceNinja API
Email: Poste.io (SMTP)
Marketing: Listmonk
Maps: Mapbox
Weather: OpenWeatherMap
CMS: Directus
📁 Project Structure
harborsmith-platform/
├── docker-compose.yml # Production orchestration
├── docker-compose.dev.yml # Development overrides
├── .env.example # Environment variables template
├── Makefile # Common commands and shortcuts
├── README.md # Project overview
│
├── docs/ # Documentation
│ ├── HARBORSMITH_IMPLEMENTATION_PLAN.md (this file)
│ ├── API_DOCUMENTATION.md
│ ├── DEPLOYMENT_GUIDE.md
│ └── TROUBLESHOOTING.md
│
├── nginx/ # Reverse proxy configuration
│ ├── nginx.conf # Main Nginx config
│ ├── ssl/ # SSL certificates
│ └── sites/
│ ├── app.conf # Frontend routing
│ ├── api.conf # API gateway routing
│ └── services.conf # Microservices routing
│
├── apps/
│ ├── web/ # Next.js Frontend Application
│ │ ├── src/
│ │ │ ├── app/ # App Router pages
│ │ │ │ ├── (public)/ # Public routes
│ │ │ │ │ ├── page.tsx # Homepage
│ │ │ │ │ ├── charter/
│ │ │ │ │ │ ├── page.tsx
│ │ │ │ │ │ └── booking/
│ │ │ │ │ │ ├── [step]/page.tsx
│ │ │ │ │ │ └── components/
│ │ │ │ │ ├── maintenance/
│ │ │ │ │ │ ├── page.tsx
│ │ │ │ │ │ └── booking/
│ │ │ │ │ ├── about/
│ │ │ │ │ ├── contact/
│ │ │ │ │ └── faq/
│ │ │ │ ├── (auth)/ # Protected routes
│ │ │ │ │ ├── layout.tsx
│ │ │ │ │ ├── dashboard/
│ │ │ │ │ │ ├── page.tsx
│ │ │ │ │ │ ├── charter/
│ │ │ │ │ │ └── maintenance/
│ │ │ │ │ ├── vessels/
│ │ │ │ │ ├── bookings/
│ │ │ │ │ ├── documents/
│ │ │ │ │ ├── invoices/
│ │ │ │ │ └── profile/
│ │ │ │ └── api/ # API route handlers
│ │ │ │ ├── auth/[...nextauth]/route.ts
│ │ │ │ └── webhooks/
│ │ │ ├── components/
│ │ │ │ ├── ui/ # Shadcn components
│ │ │ │ ├── layout/
│ │ │ │ │ ├── Navigation.tsx
│ │ │ │ │ ├── Footer.tsx
│ │ │ │ │ └── ThemeSwitcher.tsx
│ │ │ │ ├── home/
│ │ │ │ ├── charter/
│ │ │ │ ├── maintenance/
│ │ │ │ └── shared/
│ │ │ ├── lib/
│ │ │ │ ├── auth/ # Keycloak integration
│ │ │ │ ├── db/ # Supabase client
│ │ │ │ ├── api/ # API client
│ │ │ │ ├── stripe/ # Payment processing
│ │ │ │ └── utils/
│ │ │ ├── hooks/
│ │ │ ├── styles/
│ │ │ │ └── globals.css
│ │ │ └── types/
│ │ ├── public/
│ │ │ ├── images/
│ │ │ └── fonts/
│ │ ├── Dockerfile
│ │ ├── next.config.js
│ │ ├── tailwind.config.js
│ │ ├── tsconfig.json
│ │ └── package.json
│ │
│ └── api/ # API Gateway
│ ├── src/
│ │ ├── server.ts # Main server file
│ │ ├── routes/
│ │ │ ├── index.ts
│ │ │ ├── charter.routes.ts
│ │ │ ├── maintenance.routes.ts
│ │ │ ├── payment.routes.ts
│ │ │ └── user.routes.ts
│ │ ├── middleware/
│ │ │ ├── auth.middleware.ts
│ │ │ ├── cors.middleware.ts
│ │ │ ├── rate-limit.middleware.ts
│ │ │ └── validation.middleware.ts
│ │ ├── services/
│ │ │ ├── service.discovery.ts
│ │ │ └── circuit-breaker.ts
│ │ └── utils/
│ ├── Dockerfile
│ ├── tsconfig.json
│ └── package.json
│
├── services/ # Microservices
│ ├── charter/
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── controllers/
│ │ │ ├── models/
│ │ │ ├── repositories/
│ │ │ └── services/
│ │ ├── tests/
│ │ ├── Dockerfile
│ │ └── package.json
│ │
│ ├── maintenance/
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── controllers/
│ │ │ ├── models/
│ │ │ ├── repositories/
│ │ │ └── services/
│ │ ├── tests/
│ │ ├── Dockerfile
│ │ └── package.json
│ │
│ ├── payments/
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── stripe/
│ │ │ ├── invoice-ninja/
│ │ │ └── webhooks/
│ │ ├── Dockerfile
│ │ └── package.json
│ │
│ └── notifications/
│ ├── src/
│ │ ├── index.ts
│ │ ├── email/
│ │ ├── sms/
│ │ └── templates/
│ ├── Dockerfile
│ └── package.json
│
├── packages/ # Shared packages (monorepo)
│ ├── types/ # TypeScript type definitions
│ │ ├── src/
│ │ │ ├── models.ts
│ │ │ ├── api.ts
│ │ │ └── enums.ts
│ │ └── package.json
│ │
│ ├── utils/ # Shared utilities
│ │ ├── src/
│ │ │ ├── dates.ts
│ │ │ ├── validation.ts
│ │ │ └── formatters.ts
│ │ └── package.json
│ │
│ └── config/ # Shared configurations
│ ├── src/
│ │ ├── theme.ts
│ │ ├── constants.ts
│ │ └── api-routes.ts
│ └── package.json
│
├── migrations/ # Database migrations
│ ├── 001_initial_schema.sql
│ ├── 002_create_organizations.sql
│ ├── 003_create_users.sql
│ ├── 004_create_vessels.sql
│ ├── 005_create_bookings.sql
│ ├── 006_create_services.sql
│ ├── 007_create_invoices.sql
│ └── seed_data.sql
│
└── scripts/ # Utility scripts
├── setup.sh # Initial project setup
├── migrate-mockups.js # Convert HTML mockups to React
├── seed-database.js # Populate with demo data
└── deploy.sh # Deployment script
🗄️ Database Architecture
Multi-Tenant Strategy: Row-Level Security with Shared Database
-- Core schema design
CREATE SCHEMA IF NOT EXISTS public;
CREATE SCHEMA IF NOT EXISTS maintenance;
CREATE SCHEMA IF NOT EXISTS charter;
-- Organizations table (tenants)
CREATE TABLE public.organizations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
type VARCHAR(20) CHECK (type IN ('maintenance', 'charter', 'both')),
subscription_tier VARCHAR(50) DEFAULT 'basic',
settings JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Users table with organization relationship
CREATE TABLE public.users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID REFERENCES public.organizations(id),
email VARCHAR(255) UNIQUE NOT NULL,
keycloak_id VARCHAR(255) UNIQUE,
role VARCHAR(50) NOT NULL,
profile JSONB DEFAULT '{}',
preferences JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Vessels table
CREATE TABLE public.vessels (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID REFERENCES public.organizations(id),
name VARCHAR(255) NOT NULL,
type VARCHAR(50),
make VARCHAR(100),
model VARCHAR(100),
year INTEGER,
length_ft DECIMAL(5,2),
capacity INTEGER,
specifications JSONB DEFAULT '{}',
health_score INTEGER DEFAULT 100,
status VARCHAR(50) DEFAULT 'active',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Charter bookings
CREATE TABLE charter.bookings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID REFERENCES public.organizations(id),
vessel_id UUID REFERENCES public.vessels(id),
user_id UUID REFERENCES public.users(id),
booking_date DATE NOT NULL,
start_time TIME NOT NULL,
duration_hours INTEGER NOT NULL,
guest_count INTEGER NOT NULL,
total_price DECIMAL(10,2),
status VARCHAR(50) DEFAULT 'pending',
payment_intent_id VARCHAR(255),
special_requests TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Maintenance work orders
CREATE TABLE maintenance.work_orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID REFERENCES public.organizations(id),
vessel_id UUID REFERENCES public.vessels(id),
user_id UUID REFERENCES public.users(id),
service_type VARCHAR(100) NOT NULL,
priority VARCHAR(20) CHECK (priority IN ('emergency', 'urgent', 'routine')),
scheduled_date DATE,
description TEXT,
status VARCHAR(50) DEFAULT 'pending',
estimated_cost DECIMAL(10,2),
actual_cost DECIMAL(10,2),
technician_notes TEXT,
completed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Documents table
CREATE TABLE public.documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID REFERENCES public.organizations(id),
entity_type VARCHAR(50) NOT NULL,
entity_id UUID NOT NULL,
name VARCHAR(255) NOT NULL,
type VARCHAR(100),
s3_key VARCHAR(500) NOT NULL,
size_bytes BIGINT,
metadata JSONB DEFAULT '{}',
uploaded_by UUID REFERENCES public.users(id),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Invoices table
CREATE TABLE public.invoices (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID REFERENCES public.organizations(id),
invoice_number VARCHAR(50) UNIQUE NOT NULL,
type VARCHAR(20) CHECK (type IN ('charter', 'maintenance')),
booking_id UUID,
work_order_id UUID,
amount DECIMAL(10,2) NOT NULL,
status VARCHAR(50) DEFAULT 'pending',
invoice_ninja_id VARCHAR(255),
due_date DATE,
paid_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Enable Row Level Security
ALTER TABLE public.organizations ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.users ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.vessels ENABLE ROW LEVEL SECURITY;
ALTER TABLE charter.bookings ENABLE ROW LEVEL SECURITY;
ALTER TABLE maintenance.work_orders ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.documents ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.invoices ENABLE ROW LEVEL SECURITY;
-- Create RLS policies
CREATE POLICY tenant_isolation_organizations ON public.organizations
USING (id = current_setting('app.current_organization')::UUID);
CREATE POLICY tenant_isolation_users ON public.users
USING (organization_id = current_setting('app.current_organization')::UUID);
CREATE POLICY tenant_isolation_vessels ON public.vessels
USING (organization_id = current_setting('app.current_organization')::UUID);
-- Indexes for performance
CREATE INDEX idx_vessels_org_id ON public.vessels(organization_id);
CREATE INDEX idx_bookings_date ON charter.bookings(booking_date);
CREATE INDEX idx_work_orders_status ON maintenance.work_orders(status);
CREATE INDEX idx_invoices_status ON public.invoices(status);
CREATE INDEX idx_documents_entity ON public.documents(entity_type, entity_id);
🔌 API Design
RESTful API Endpoints
// API Routes Structure
const API_ROUTES = {
// Authentication
'POST /api/auth/login': 'User login with Keycloak',
'POST /api/auth/logout': 'User logout',
'POST /api/auth/refresh': 'Refresh JWT token',
'GET /api/auth/me': 'Get current user',
// Organizations
'GET /api/organizations/:id': 'Get organization details',
'PUT /api/organizations/:id': 'Update organization',
'GET /api/organizations/:id/users': 'List organization users',
// Vessels
'GET /api/vessels': 'List vessels',
'POST /api/vessels': 'Create vessel',
'GET /api/vessels/:id': 'Get vessel details',
'PUT /api/vessels/:id': 'Update vessel',
'DELETE /api/vessels/:id': 'Delete vessel',
// Charter Service
'GET /api/charter/availability': 'Check vessel availability',
'GET /api/charter/bookings': 'List bookings',
'POST /api/charter/bookings': 'Create booking',
'GET /api/charter/bookings/:id': 'Get booking details',
'PUT /api/charter/bookings/:id': 'Update booking',
'POST /api/charter/bookings/:id/cancel': 'Cancel booking',
'GET /api/charter/packages': 'List available packages',
'POST /api/charter/calculate-price': 'Calculate booking price',
// Maintenance Service
'GET /api/maintenance/services': 'List available services',
'GET /api/maintenance/work-orders': 'List work orders',
'POST /api/maintenance/work-orders': 'Create work order',
'GET /api/maintenance/work-orders/:id': 'Get work order details',
'PUT /api/maintenance/work-orders/:id': 'Update work order',
'GET /api/maintenance/schedule': 'Get maintenance schedule',
'POST /api/maintenance/schedule': 'Schedule maintenance',
// Payments
'POST /api/payments/create-intent': 'Create Stripe payment intent',
'POST /api/payments/confirm': 'Confirm payment',
'GET /api/payments/history': 'Get payment history',
'POST /api/payments/refund': 'Process refund',
// Invoices
'GET /api/invoices': 'List invoices',
'GET /api/invoices/:id': 'Get invoice details',
'POST /api/invoices/:id/pay': 'Pay invoice',
'GET /api/invoices/:id/download': 'Download invoice PDF',
// Documents
'GET /api/documents': 'List documents',
'POST /api/documents/upload': 'Upload document',
'GET /api/documents/:id': 'Get document',
'DELETE /api/documents/:id': 'Delete document',
// Notifications
'POST /api/notifications/email': 'Send email notification',
'GET /api/notifications/preferences': 'Get notification preferences',
'PUT /api/notifications/preferences': 'Update preferences',
// Reports & Analytics
'GET /api/analytics/dashboard': 'Dashboard metrics',
'GET /api/analytics/revenue': 'Revenue reports',
'GET /api/analytics/utilization': 'Vessel utilization'
};
🐳 Docker Configuration
Complete docker-compose.yml
version: '3.9'
networks:
harborsmith:
driver: bridge
volumes:
postgres_data:
minio_data:
keycloak_data:
directus_uploads:
redis_data:
services:
# ===== FRONTEND =====
web:
build:
context: ./apps/web
dockerfile: Dockerfile
args:
- NODE_ENV=production
container_name: harborsmith-web
restart: unless-stopped
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- NEXT_PUBLIC_API_URL=http://api:4000
- NEXT_PUBLIC_SUPABASE_URL=${SUPABASE_URL}
- NEXT_PUBLIC_SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}
- NEXT_PUBLIC_STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY}
- NEXT_PUBLIC_KEYCLOAK_URL=http://keycloak:8080
- NEXT_PUBLIC_KEYCLOAK_REALM=harborsmith
- NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=harborsmith-web
depends_on:
- api
- keycloak
networks:
- harborsmith
# ===== API GATEWAY =====
api:
build:
context: ./apps/api
dockerfile: Dockerfile
container_name: harborsmith-api
restart: unless-stopped
ports:
- "4000:4000"
environment:
- NODE_ENV=production
- PORT=4000
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/harborsmith
- REDIS_URL=redis://redis:6379
- KEYCLOAK_URL=http://keycloak:8080
- KEYCLOAK_REALM=harborsmith
- KEYCLOAK_CLIENT_ID=harborsmith-api
- KEYCLOAK_CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET}
- JWT_SECRET=${JWT_SECRET}
- CHARTER_SERVICE_URL=http://charter-service:5001
- MAINTENANCE_SERVICE_URL=http://maintenance-service:5002
- PAYMENT_SERVICE_URL=http://payment-service:5003
- NOTIFICATION_SERVICE_URL=http://notification-service:5004
depends_on:
- postgres
- redis
- keycloak
networks:
- harborsmith
# ===== MICROSERVICES =====
charter-service:
build:
context: ./services/charter
dockerfile: Dockerfile
container_name: harborsmith-charter
restart: unless-stopped
ports:
- "5001:5001"
environment:
- NODE_ENV=production
- PORT=5001
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/harborsmith
- REDIS_URL=redis://redis:6379
depends_on:
- postgres
- redis
networks:
- harborsmith
maintenance-service:
build:
context: ./services/maintenance
dockerfile: Dockerfile
container_name: harborsmith-maintenance
restart: unless-stopped
ports:
- "5002:5002"
environment:
- NODE_ENV=production
- PORT=5002
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/harborsmith
- REDIS_URL=redis://redis:6379
depends_on:
- postgres
- redis
networks:
- harborsmith
payment-service:
build:
context: ./services/payments
dockerfile: Dockerfile
container_name: harborsmith-payments
restart: unless-stopped
ports:
- "5003:5003"
environment:
- NODE_ENV=production
- PORT=5003
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
- STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET}
- INVOICE_NINJA_URL=${INVOICE_NINJA_URL}
- INVOICE_NINJA_TOKEN=${INVOICE_NINJA_TOKEN}
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/harborsmith
depends_on:
- postgres
networks:
- harborsmith
notification-service:
build:
context: ./services/notifications
dockerfile: Dockerfile
container_name: harborsmith-notifications
restart: unless-stopped
ports:
- "5004:5004"
environment:
- NODE_ENV=production
- PORT=5004
- SMTP_HOST=${POSTE_HOST}
- SMTP_PORT=${POSTE_PORT}
- SMTP_SECURE=${POSTE_SECURE}
- SMTP_USER=${POSTE_USER}
- SMTP_PASS=${POSTE_PASS}
- LISTMONK_URL=${LISTMONK_URL}
- LISTMONK_USER=${LISTMONK_USER}
- LISTMONK_PASS=${LISTMONK_PASS}
networks:
- harborsmith
# ===== DATABASES & STORAGE =====
postgres:
image: supabase/postgres:15.1.0.117
container_name: harborsmith-db
restart: unless-stopped
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=harborsmith
volumes:
- postgres_data:/var/lib/postgresql/data
- ./migrations:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
networks:
- harborsmith
redis:
image: redis:7-alpine
container_name: harborsmith-redis
restart: unless-stopped
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- harborsmith
# ===== AUTHENTICATION =====
keycloak:
image: quay.io/keycloak/keycloak:23.0
container_name: harborsmith-keycloak
restart: unless-stopped
ports:
- "8080:8080"
environment:
- KEYCLOAK_ADMIN=${KEYCLOAK_ADMIN}
- KEYCLOAK_ADMIN_PASSWORD=${KEYCLOAK_ADMIN_PASSWORD}
- KC_DB=postgres
- KC_DB_URL=jdbc:postgresql://postgres:5432/keycloak
- KC_DB_USERNAME=postgres
- KC_DB_PASSWORD=${POSTGRES_PASSWORD}
- KC_HOSTNAME_STRICT=false
- KC_HTTP_ENABLED=true
- KC_HEALTH_ENABLED=true
command: start-dev
volumes:
- keycloak_data:/opt/keycloak/data
depends_on:
- postgres
networks:
- harborsmith
# ===== FILE STORAGE =====
minio:
image: minio/minio:latest
container_name: harborsmith-minio
restart: unless-stopped
ports:
- "9000:9000"
- "9001:9001"
environment:
- MINIO_ROOT_USER=${MINIO_ROOT_USER}
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
- MINIO_BROWSER_REDIRECT_URL=http://localhost:9001
volumes:
- minio_data:/data
command: server /data --console-address ":9001"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
networks:
- harborsmith
createbuckets:
image: minio/mc:latest
container_name: harborsmith-minio-setup
depends_on:
- minio
entrypoint: >
/bin/sh -c "
/usr/bin/mc config host add harborsmith http://minio:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD};
/usr/bin/mc mb harborsmith/documents;
/usr/bin/mc mb harborsmith/images;
/usr/bin/mc mb harborsmith/directus;
/usr/bin/mc policy set public harborsmith/images;
exit 0;
"
networks:
- harborsmith
# ===== CMS =====
directus:
image: directus/directus:10.8
container_name: harborsmith-cms
restart: unless-stopped
ports:
- "8055:8055"
environment:
- KEY=${DIRECTUS_KEY}
- SECRET=${DIRECTUS_SECRET}
- DB_CLIENT=pg
- DB_HOST=postgres
- DB_PORT=5432
- DB_DATABASE=directus
- DB_USER=postgres
- DB_PASSWORD=${POSTGRES_PASSWORD}
- ADMIN_EMAIL=${DIRECTUS_ADMIN_EMAIL}
- ADMIN_PASSWORD=${DIRECTUS_ADMIN_PASSWORD}
- PUBLIC_URL=http://localhost:8055
- STORAGE_LOCATIONS=s3
- STORAGE_S3_DRIVER=s3
- STORAGE_S3_ENDPOINT=http://minio:9000
- STORAGE_S3_BUCKET=directus
- STORAGE_S3_REGION=us-east-1
- STORAGE_S3_KEY=${MINIO_ROOT_USER}
- STORAGE_S3_SECRET=${MINIO_ROOT_PASSWORD}
volumes:
- directus_uploads:/directus/uploads
depends_on:
- postgres
- minio
networks:
- harborsmith
# ===== ANALYTICS =====
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
container_name: harborsmith-analytics
restart: unless-stopped
ports:
- "3001:3000"
environment:
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/umami
- DATABASE_TYPE=postgresql
- HASH_SALT=${UMAMI_HASH_SALT}
- TRACKER_SCRIPT_NAME=script.js
- DISABLE_UPDATES=true
depends_on:
- postgres
networks:
- harborsmith
🔄 Migration Strategy: Mockups to Production
Phase 1: Component Extraction (Days 1-3)
1. Extract Design System
// scripts/migrate-mockups.js
const fs = require('fs');
const path = require('path');
const cheerio = require('cheerio');
// Extract colors, fonts, and styles from existing CSS
const extractDesignTokens = () => {
const cssFiles = [
'website-mockups/css/styles.css',
'website-mockups/css/themes.css',
'website-mockups/css/voyage-layout.css'
];
// Parse CSS and generate Tailwind config
// Extract color variables
// Extract font definitions
// Generate component classes
};
// Convert HTML components to React
const convertToReact = (htmlFile) => {
const html = fs.readFileSync(htmlFile, 'utf8');
const $ = cheerio.load(html);
// Extract components
const navigation = $('.voyage-nav').html();
const hero = $('.hero-voyage').html();
const footer = $('.voyage-footer').html();
// Generate React components
// Handle event listeners
// Convert class names to Tailwind
// Extract inline styles
};
2. Component Mapping
// Component extraction map
interface ComponentMap {
source: string; // HTML file location
target: string; // React component location
dependencies: string[]; // Required components
data: string[]; // Required data/props
}
const componentMappings: ComponentMap[] = [
{
source: 'website-mockups/index.html',
target: 'apps/web/src/components/home/Hero.tsx',
dependencies: ['Button', 'Container'],
data: ['heroTitle', 'heroSubtitle', 'ctaText']
},
{
source: 'website-mockups/charter.html',
target: 'apps/web/src/components/charter/FleetGrid.tsx',
dependencies: ['Card', 'Badge', 'Image'],
data: ['vessels', 'pricing', 'availability']
},
// ... more mappings
];
Phase 2: React Component Generation (Days 4-7)
Example: Hero Component
// apps/web/src/components/home/Hero.tsx
'use client';
import { motion } from 'framer-motion';
import { ArrowRight, Anchor } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { useTheme } from '@/hooks/useTheme';
import Link from 'next/link';
export const Hero = () => {
const { theme } = useTheme();
return (
<section className="relative min-h-screen flex items-center">
{/* Background gradient from mockup */}
<div className="absolute inset-0 bg-gradient-to-br from-[#001f3f] via-[#003366] to-[#001f3f]" />
{/* Wave animation from original */}
<div className="absolute bottom-0 left-0 right-0">
<svg viewBox="0 0 1440 320" className="w-full">
<motion.path
fill="rgba(255,255,255,0.1)"
d="M0,96L48,112C96,128,192,160,288,160C384,160,480,128,576,112C672,96,768,96,864,112C960,128,1056,160,1152,165.3C1248,171,1344,149,1392,138.7L1440,128L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"
animate={{
d: [
"M0,96L48,112C96,128,192,160,288,160C384,160,480,128,576,112C672,96,768,96,864,112C960,128,1056,160,1152,165.3C1248,171,1344,149,1392,138.7L1440,128L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z",
"M0,128L48,138.7C96,149,192,171,288,165.3C384,160,480,128,576,112C672,96,768,96,864,112C960,128,1056,160,1152,160C1248,160,1344,128,1392,112L1440,96L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z"
]
}}
transition={{
repeat: Infinity,
repeatType: "reverse",
duration: 10,
ease: "easeInOut"
}}
/>
</svg>
</div>
{/* Content */}
<div className="container mx-auto px-4 relative z-10">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
className="max-w-4xl mx-auto text-center text-white"
>
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ delay: 0.2, type: "spring" }}
className="inline-flex items-center gap-2 mb-6 px-4 py-2 bg-white/10 backdrop-blur rounded-full"
>
<Anchor className="w-5 h-5" />
<span className="text-sm font-medium">San Francisco Bay's Premier Yacht Services</span>
</motion.div>
<h1 className="font-display text-5xl md:text-7xl font-bold mb-6">
Your Journey Begins at{' '}
<span className="bg-gradient-to-r from-[#dc143c] to-[#ef4444] bg-clip-text text-transparent">
HarborSmith
</span>
</h1>
<p className="text-xl md:text-2xl mb-8 text-white/90">
Experience luxury yacht charters and professional maintenance services
with San Francisco's most trusted maritime partner
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Button
asChild
size="lg"
className="bg-gradient-to-r from-[#dc143c] to-[#ef4444] hover:from-[#b91c3c] hover:to-[#dc143c]"
>
<Link href="/charter">
Explore Charters
<ArrowRight className="ml-2 w-5 h-5" />
</Link>
</Button>
<Button
asChild
size="lg"
variant="outline"
className="border-white text-white hover:bg-white hover:text-[#001f3f]"
>
<Link href="/maintenance">
Maintenance Services
</Link>
</Button>
</div>
</motion.div>
</div>
</section>
);
};
📅 Implementation Timeline
Week 1: Infrastructure & Foundation
Day 1-2: Project Setup
- Initialize Git repository
- Set up monorepo with pnpm workspaces
- Configure Docker environments (dev/prod)
- Set up environment variables
- Initialize Next.js with TypeScript
- Configure ESLint and Prettier
Day 3-4: Database & Auth
- Deploy PostgreSQL with Supabase
- Run initial migrations
- Configure Keycloak realm
- Set up SSO clients
- Test authentication flow
- Configure Row Level Security
Day 5: Services Setup
- Create API Gateway structure
- Initialize microservices
- Set up service discovery
- Configure Redis caching
- Set up MinIO buckets
- Deploy Directus CMS
Week 2: Core Application
Day 6-7: Frontend Foundation
- Convert mockup styles to Tailwind
- Create component library
- Implement theme system
- Set up routing structure
- Create layout components
- Implement navigation
Day 8-9: Public Pages
- Homepage with all sections
- Charter service pages
- Maintenance service pages
- About page
- Contact page
- FAQ page
Day 10: Authentication Integration
- Keycloak provider setup
- Login/Register flows
- Protected routes
- Role-based access
- Session management
- Logout functionality
Week 3: Portal Development
Day 11-12: User Dashboard
- Dashboard layout
- Charter dashboard widgets
- Maintenance dashboard widgets
- Activity timeline
- Quick actions
- Notifications panel
Day 13-14: Booking Systems
- Charter booking wizard
- Maintenance scheduling
- Calendar integration
- Availability checking
- Price calculation
- Booking confirmation
Day 15: Document Management
- File upload to MinIO
- Document viewer
- Document categorization
- Download functionality
- Document sharing
- Version control
Week 4: Integration & Launch
Day 16-17: Payment & Invoicing
- Stripe integration
- Payment intents
- InvoiceNinja API
- Invoice generation
- Payment history
- Refund processing
Day 18-19: Testing & Optimization
- End-to-end testing
- Performance optimization
- Security audit
- Load testing
- Bug fixes
- Mobile optimization
Day 20: Deployment
- Production configuration
- SSL certificates
- DNS configuration
- Monitoring setup
- Backup configuration
- Go-live checklist
🚀 Quick Start Guide
Prerequisites
# Required software
- Node.js 20+
- Docker & Docker Compose
- pnpm (npm install -g pnpm)
- Git
Initial Setup
# 1. Clone the repository
git clone [repository-url]
cd harborsmith-platform
# 2. Install dependencies
pnpm install
# 3. Set up environment variables
cp .env.example .env
# Edit .env with your configuration
# 4. Start Docker services
docker-compose up -d
# 5. Run database migrations
docker exec harborsmith-db psql -U postgres -d harborsmith -f /docker-entrypoint-initdb.d/001_initial_schema.sql
docker exec harborsmith-db psql -U postgres -d harborsmith -f /docker-entrypoint-initdb.d/002_create_organizations.sql
# ... run all migrations
# 6. Seed demo data
pnpm run seed
# 7. Start development servers
pnpm run dev
# 8. Convert mockups to React components
pnpm run migrate:mockups
Access Points
Frontend: http://localhost:3000
API Gateway: http://localhost:4000
Keycloak: http://localhost:8080
Directus CMS: http://localhost:8055
MinIO Console: http://localhost:9001
Umami Analytics: http://localhost:3001
Development Commands
# Start all services
pnpm run dev
# Run tests
pnpm run test
# Build for production
pnpm run build
# Deploy to production
pnpm run deploy
# Generate API documentation
pnpm run docs:api
# Run database migrations
pnpm run db:migrate
# Seed database
pnpm run db:seed
# Format code
pnpm run format
# Lint code
pnpm run lint
🎯 Critical Path to MVP
Must-Have Features (Week 1-2)
-
User Authentication
- Login/Register with Keycloak
- Role-based access (Customer, Admin, Service Provider)
- Session management
-
Core Booking Flow
- Browse available services
- Select yacht/service
- Choose date/time
- Complete payment
- Receive confirmation
-
Basic Dashboard
- View bookings
- Manage vessels
- View invoices
- Upload documents
Should-Have Features (Week 3)
-
Communication
- Email notifications
- Booking confirmations
- Reminder emails
-
Payment Processing
- Stripe integration
- Invoice generation
- Payment history
-
Admin Panel
- Manage users
- View all bookings
- Generate reports
Nice-to-Have Features (Week 4+)
-
Advanced Features
- Real-time availability
- Dynamic pricing
- Weather integration
- Route planning
-
Analytics
- Revenue tracking
- Utilization reports
- Customer insights
🔒 Security Considerations
Authentication & Authorization
- Keycloak SSO with MFA support
- JWT tokens with refresh mechanism
- Role-based access control (RBAC)
- API key management for services
Data Protection
- TLS/SSL for all communications
- Encryption at rest (database)
- Encryption in transit
- PII data isolation
- GDPR compliance measures
Infrastructure Security
- Container security scanning
- Regular dependency updates
- Rate limiting on APIs
- DDoS protection
- Regular security audits
📊 Performance Targets
Frontend Performance
Metrics:
First Contentful Paint: < 1.5s
Time to Interactive: < 3.5s
Cumulative Layout Shift: < 0.1
Largest Contentful Paint: < 2.5s
Optimization:
- Next.js ISR for static content
- Image optimization with next/image
- Code splitting per route
- Lazy loading components
- CDN for static assets
Backend Performance
API Response Times:
GET endpoints: < 200ms (p95)
POST endpoints: < 300ms (p95)
Database queries: < 50ms (p95)
Optimization:
- Redis caching layer
- Database query optimization
- Connection pooling
- Horizontal scaling ready
🚨 Monitoring & Alerts
Application Monitoring
- Umami Analytics for user behavior
- Custom dashboards for business metrics
- Error tracking with Sentry (optional)
- Performance monitoring
Infrastructure Monitoring
- Container health checks
- Database performance metrics
- API response times
- Resource utilization
Alerting Rules
- Downtime > 1 minute
- Error rate > 1%
- Response time > 1s
- Database connection failures
- Payment processing failures
📝 Deployment Checklist
Pre-Deployment
- All tests passing
- Security audit complete
- Performance benchmarks met
- Documentation updated
- Environment variables configured
- SSL certificates ready
Deployment Steps
- Backup existing data
- Deploy database migrations
- Deploy backend services
- Deploy frontend application
- Verify all services running
- Run smoke tests
Post-Deployment
- Monitor error rates
- Check performance metrics
- Verify email delivery
- Test payment processing
- Update DNS records
- Announce go-live
🤝 Team Collaboration
Git Workflow
main
├── develop
│ ├── feature/user-authentication
│ ├── feature/booking-system
│ └── feature/payment-integration
├── staging
└── production
Code Review Process
- Create feature branch from develop
- Implement feature with tests
- Create pull request
- Automated tests run
- Code review by team
- Merge to develop
- Deploy to staging
- Test in staging
- Merge to main
- Deploy to production
📚 Additional Resources
Documentation
- API Documentation:
/docs/API_DOCUMENTATION.md - Deployment Guide:
/docs/DEPLOYMENT_GUIDE.md - Contributing Guide:
/docs/CONTRIBUTING.md - Security Policy:
/docs/SECURITY.md
External Resources
- Next.js Documentation
- Supabase Documentation
- Keycloak Documentation
- Docker Documentation
- Stripe Documentation
Support
- Technical Issues: Create GitHub issue
- Security Issues: security@harborsmith.com
- Business Inquiries: business@harborsmith.com
🎯 Success Metrics
Technical Metrics
- 99.9% uptime
- < 3s page load time
- < 300ms API response time
- Zero critical security vulnerabilities
Business Metrics
- 500+ vessels onboarded (Year 1)
- 100+ service providers
- 1000+ monthly active users
- 15% month-over-month growth
✅ Conclusion
This implementation plan provides everything needed to build HarborSmith from the ground up:
- Modern Architecture: Microservices, SSR, real-time capabilities
- Enterprise Security: Keycloak SSO, RLS, encryption
- Scalable Infrastructure: Docker, Redis, horizontal scaling ready
- Developer Experience: TypeScript, hot reloading, automated testing
- Production Ready: Monitoring, analytics, backup strategies
With this plan, you can start development immediately and have a production-ready MVP within 4 weeks.
Next Steps:
- Set up development environment
- Begin with Week 1 infrastructure tasks
- Convert mockups to React components
- Implement core booking flows
- Deploy to staging for testing
- Launch MVP to production
This document is a living guide and should be updated as the project evolves.