Skip to content

Latest commit

 

History

History
573 lines (428 loc) · 14.5 KB

File metadata and controls

573 lines (428 loc) · 14.5 KB

Architecture Overview

This document explains the architectural decisions and structure of the TimelyOne application.

Design Philosophy

Single-User First

This application is designed as a self-hosted, single-user calendar platform, not a multi-tenant SaaS. This fundamental decision influences every aspect of the architecture:

  • No complex authentication: Optional password auth, assumes trusted environment
  • Simplified queries: No need to filter by user in every query
  • Direct database access: getCurrentUser() simply returns the first (and only) user
  • Reduced overhead: No multi-tenancy abstractions or row-level security
  • Better performance: Fewer joins, simpler queries, smaller codebase

Privacy-Focused

  • Self-hosted: All data stays on the user's server
  • Minimal external calls: Only to Google/Microsoft APIs for calendar sync
  • No telemetry: Optional Next.js telemetry can be disabled
  • Local storage: All events cached in local PostgreSQL database

Technical Stack

Frontend

Next.js 14+ (App Router)

  • Server Components by default for better performance
  • Client Components only where needed (forms, interactive UI)
  • Built-in API routes for backend logic
  • Automatic code splitting and optimization

React 19

  • Latest features and performance improvements
  • Server Components support
  • Concurrent rendering

shadcn/ui + Tailwind CSS 4

  • Accessible components built on Radix UI
  • Customizable and themeable
  • Modern, responsive design
  • No runtime CSS-in-JS overhead

Backend

Next.js API Routes

  • Serverless-ready architecture
  • Simple REST endpoints
  • Integrated with Next.js middleware

Prisma ORM

  • Type-safe database queries
  • Automatic migrations
  • Connection pooling via PostgreSQL adapter
  • Schema versioning

Database

PostgreSQL 14+

  • Reliable, proven RDBMS
  • JSONB support for flexible event metadata
  • Full-text search capabilities (future)
  • Excellent performance for single-user scale

Application Flow

1. First Launch - Setup Wizard

User visits app
  ↓
Middleware checks: User exists?
  ↓ No
Redirect to /setup
  ↓
User fills setup form
  ↓
POST /api/setup
  ↓
Create user in database
  ↓
Redirect to main app

2. Normal Operation

User visits app
  ↓
Middleware checks: User exists?
  ↓ Yes
Continue to requested page
  ↓
Page loads (Server Component)
  ↓
getCurrentUser() fetches user
  ↓
Fetch user's events/data
  ↓
Render page

3. Calendar Sync (Future)

User connects Google Calendar
  ↓
OAuth flow → Store tokens
  ↓
Background job: Fetch events
  ↓
Parse and store in database
  ↓
Display in unified view

Key Components

1. Middleware (src/middleware.ts)

Purpose: Enforce setup completion before allowing app access

Logic:

if (path is public) return next()
if (user exists) return next()
else redirect to /setup

Why needed: Ensures single-user is created before any app functionality

2. getCurrentUser() (src/lib/user.ts)

Purpose: Get the current (and only) user from database

Implementation:

export async function getCurrentUser() {
  return await prisma.user.findFirst()
}

Why simple: Single-user mode means first user IS the current user

3. Setup Wizard (src/app/setup/)

Components:

  • page.tsx: Setup page layout
  • setup-form.tsx: Form with validation
  • /api/setup/route.ts: API endpoint to create user

Flow:

  1. Display form
  2. Validate input
  3. Create user in database
  4. Redirect to main app

4. Calendar View (src/components/calendar-view.tsx)

Current State: Week view with day/week/month switcher

Future Enhancement:

  • Fetch events from database
  • Display events in timeline
  • Handle click to create/edit events
  • Show conflicts visually

Database Schema

Single-User Considerations

Even though this is single-user, we maintain user relationships in the schema:

Why?

  1. Flexibility: Easy to expand to multi-user if needed
  2. Clarity: Explicit relationships make code more maintainable
  3. Standard practice: Familiar pattern for developers

Tradeoff: Slight performance overhead vs. massive maintainability gain

Tables

users

  • Single row in production
  • Stores name, email
  • Created by setup wizard

calendar_connections

  • One row per connected calendar
  • Stores OAuth tokens (encrypted in production)
  • Links to user (always same user)

events

  • Cached calendar events
  • Stores all event metadata
  • Links to calendar_connection and user

Security Considerations

Single-User Security Model

Assumptions:

  • Application runs in trusted environment
  • Only owner has network access (VPS, home server, etc.)
  • Optional: Reverse proxy handles authentication (nginx, Caddy)

Security Measures:

  1. Database: Not exposed to internet
  2. API Routes: Server-side only, no client-side access to tokens
  3. OAuth Tokens: Stored server-side, never sent to client
  4. Environment Variables: Sensitive config in .env (not committed)

Optional Password Auth

User can set password during setup:

  • Used for future multi-user expansion
  • Currently not enforced
  • Recommended if exposing to internet without reverse proxy

Production Recommendation: Use reverse proxy (nginx, Caddy) with HTTP Basic Auth or OAuth proxy


Deployment Architecture

Docker Compose (Recommended)

[Internet] → [Reverse Proxy] → [Next.js App] → [PostgreSQL]
                (optional)        (port 3000)     (port 5432)

Benefits:

  • Automatic container networking
  • Volume persistence for database
  • Easy backup/restore
  • One-command deployment

Manual Deployment

[Internet] → [nginx/Caddy] → [PM2 → Next.js] → [PostgreSQL]
            (SSL, port 443)   (port 3000)      (localhost:5432)

Benefits:

  • More control over configuration
  • Traditional VPS setup
  • Can use system services

Performance Considerations

Single-User Optimizations

  1. No user filtering overhead: Queries don't need complex WHERE clauses
  2. Simplified caching: Cache entire dataset, not per-user
  3. Direct database access: No connection pooling complexity
  4. Smaller codebase: Less code = faster execution

Database Performance

  • Indexes: On user_id, start_time, end_time for fast event queries
  • Connection pooling: Via Prisma PostgreSQL adapter
  • Query optimization: Server Components allow efficient data fetching

Expected Performance

For typical single-user load (< 10,000 events):

  • Page load: < 500ms
  • Event sync: < 5s
  • Database queries: < 50ms

Future Enhancements

Phase 2: OAuth Integration

Google Calendar:

src/app/api/auth/google/callback
src/lib/calendar/google.ts

Microsoft Teams:

src/app/api/auth/microsoft/callback
src/lib/calendar/microsoft.ts

Phase 3: Event Sync

Background Jobs:

  • Cron job or Next.js API route with timeout
  • Incremental sync (only recent changes)
  • Webhook support (Google push notifications)

Conflict Detection:

  • Query for overlapping time ranges
  • Check across all calendar connections
  • Display warnings in UI

Phase 4: Booking Link

Implementation:

src/app/booking/[username]/page.tsx → Public booking page
src/api/booking/available → Check availability
src/api/booking/create → Create appointment

Availability Calculation:

  1. Fetch all events for date range
  2. Calculate free slots based on working hours
  3. Apply buffer times
  4. Return available slots

Development Guidelines

Adding New Features

  1. Server Components first: Use Client Components only when needed
  2. Type safety: Leverage Prisma types throughout
  3. Single-user aware: Don't add multi-user complexity
  4. Test self-hosted: Use Docker Compose for testing

Code Organization

src/
├── app/              # Pages and API routes
│   ├── (pages)/     # Page routes
│   └── api/         # API endpoints
├── components/       # React components
│   ├── ui/          # shadcn components
│   └── ...          # Custom components
└── lib/             # Utilities and business logic
    ├── prisma.ts    # Database client
    ├── user.ts      # User utilities
    └── calendar/    # Calendar integrations (future)

Best Practices

  • Server-first: Fetch data in Server Components
  • Type everything: Use TypeScript strictly
  • Error handling: Always handle database errors gracefully
  • Logging: Use console.error for server-side errors
  • Comments: Document complex logic, not obvious code

Troubleshooting

Common Issues

Setup wizard loops:

  • Check database connection
  • Verify migrations ran successfully
  • Check API route errors in console

Middleware not working:

  • Verify matcher config in middleware.ts
  • Check environment variables loaded
  • Test with direct /setup navigation

Database connection fails:

  • Verify PostgreSQL is running
  • Check DATABASE_URL format
  • Ensure migrations are applied

Theme System

Architecture

TimelyOne implements a comprehensive dark/light theme system using next-themes:

Components:

  • ThemeProvider (src/components/theme-provider.tsx): Wraps application in root layout
  • ThemeToggle (src/components/theme-toggle.tsx): UI control for theme switching
  • CSS Variables in globals.css: Define colors for both themes

Configuration:

<ThemeProvider
  attribute="class"           // Uses class-based theme switching
  defaultTheme="system"       // Respects OS preference by default
  enableSystem                // Allow system theme detection
  disableTransitionOnChange   // Prevent flash during theme change
>

Color System

Light Mode: Uses bg-{color}-100 backgrounds with darker text Dark Mode: Uses dark:bg-{color}-900/40 with lighter text

Calendar event colors adapt automatically:

// From calendar-utils.ts
export const CALENDAR_COLORS = {
  blue: {
    bg: "bg-blue-100 dark:bg-blue-900/40",
    border: "border-blue-500 dark:border-blue-400",
    text: "text-blue-700 dark:text-blue-300",
    hover: "hover:bg-blue-200 dark:hover:bg-blue-800/50",
  },
  // ... other colors
}

Benefits:

  • System-aware: Automatically follows OS dark mode preference
  • Persistent: Theme choice saved in localStorage
  • Smooth transitions: No flash of unstyled content
  • Full coverage: All components styled for both themes

Recent Feature Additions (2024-2025)

Dark Theme Support

  • Complete theme system with light/dark/system modes
  • Adaptive calendar event colors
  • Theme toggle in navigation bar
  • Persistent theme preference

Enhanced Calendar Views

  • Event colors adapt to theme (light/dark mode)
  • Improved contrast and readability
  • Visual conflict warnings
  • Drag-and-drop event rescheduling

Booking Link System

  • Personal scheduling pages (/book/[slug])
  • Availability calculation with conflict checking
  • Timezone-aware bookings
  • Google Meet auto-creation
  • Guest information collection

Authentication Improvements

  • Optional password-based login
  • Bcrypt password hashing
  • Session management with httpOnly cookies
  • Public routes for booking pages

User Experience

  • Timezone auto-detection
  • Loading states and feedback
  • Error handling and validation
  • Responsive design improvements

Technology Updates

Updated Stack (Current)

Frontend:

  • Next.js 16.0.3 (was 14+) - Latest App Router features
  • React 19.2.0 (was 19) - Concurrent rendering improvements
  • Tailwind CSS 4 - Latest version with new features
  • next-themes 0.4.6 - Theme management system

Backend:

  • Prisma 7.0.0 (was 5.x) - Latest ORM version
  • Composio SDK @composio/core - Managed OAuth for calendars
  • bcryptjs - Password hashing utility

UI Components:

  • shadcn/ui - Based on Radix UI primitives
  • @dnd-kit - Drag and drop functionality
  • lucide-react - Icon library
  • sonner - Toast notifications
  • react-hook-form + zod - Form validation

Key Integrations

Composio Platform:

  • Eliminates need for Google Cloud Console setup
  • Automatic OAuth token management
  • Multi-provider calendar support (Google, Microsoft)
  • Simplified authentication flow

Calendar Sync:

  • Incremental sync using sync tokens
  • Only fetches changed events (efficient)
  • Bi-directional synchronization
  • Automatic calendar discovery

Component Architecture Updates

New Components

Theme Components:

  • components/theme-provider.tsx - Next-themes integration
  • components/theme-toggle.tsx - UI toggle button

Calendar Components:

  • components/calendar/DayView.tsx - Hourly timeline view
  • components/calendar/WeekView.tsx - 7-day grid view
  • components/calendar/MonthView.tsx - Traditional month grid
  • components/calendar/EventCard.tsx - Event display component
  • components/calendar/DraggableEventCard.tsx - Drag-and-drop events
  • components/calendar/AllDayEventsRow.tsx - All-day event display

Connection Management:

  • components/connections/account-accordion.tsx - Calendar accounts UI
  • components/connections/calendar-list.tsx - Calendar selection
  • components/connections/connection-header.tsx - Header with sync controls

Service Layer

Calendar Sync Service (lib/services/calendar-sync-service.ts):

  • Handles incremental event syncing
  • Uses Google sync tokens for efficiency
  • Manages event creation/update/deletion
  • Processes calendar-level sync operations

Best Practices (Updated)

  • Server-first: Fetch data in Server Components
  • Type everything: Use TypeScript strictly with latest version
  • Error handling: Always handle database errors gracefully
  • Logging: Use console.error for server-side errors
  • Comments: Document complex logic, not obvious code
  • Theme-aware: Use Tailwind dark: variants for all UI
  • Performance: Use React 19 concurrent features wisely
  • Security: Never expose OAuth tokens to client

Troubleshooting

When contributing, remember:

  • This is a single-user application
  • Keep it simple and maintainable
  • Prioritize privacy and self-hosting
  • Test with Docker Compose
  • Document deployment-critical changes

Resources