Cliff Hill 52b1f452a5 Merge pull request #2 from xlorepdarkhelm/Issue-1_datetime_error
Issue-1 completed.

CICD not completely functional yet, so passing thing and merging despite the error in the nox step.
2025-10-20 15:28:02 -04:00
2025-10-20 15:19:06 -04:00
2025-10-20 15:19:06 -04:00
2025-10-06 16:38:17 -04:00
2025-08-26 23:40:47 -04:00
2025-10-06 15:57:14 -04:00
2025-10-08 23:15:11 -04:00
2025-10-20 15:19:06 -04:00
2025-10-02 23:31:17 -04:00
2025-10-02 22:37:45 -04:00
2025-10-09 12:15:46 -04:00

Online Booking Application

Cliff Hill's Coding Project

This is a full-stack application simulating an online booking system for conference rooms.

This can be run with a simple docker compose up command.

Note: For the application to completely work, you must copy the .env.example file to .env in the project root. Otherwise, the the application will fail to build.


Project Architecture Overview

Monorepo Structure

  • The project is organized as a monorepo with two main subprojects:
    • backend/: Python FastAPI application for API and business logic.
    • frontend/: React + TypeScript application for the user interface.
  • Shared configuration and orchestration files (Docker, Compose, Nox, etc.) are at the root.

Backend Architecture (FastAPI, SQLAlchemy, Pydantic)

  • Entry Point: backend/src/backend/main.py initializes the FastAPI app, configures CORS, and includes routers for users, rooms, and bookings.
  • Routers: Each resource (users, rooms, bookings) has its own router, service, and schema module, following a clear separation of concerns.
  • Models: SQLAlchemy ORM models (models.py) define User, Room, Booking, and Invitee with relationships and constraints.
  • Schemas: Pydantic models for request/response validation, separated for create/update/response.
  • Service Layer: Business logic is abstracted into service modules, keeping routers thin and focused on HTTP.
  • Database: Uses async SQLAlchemy with PostgreSQL, migrations managed via Alembic.
  • Logging: Centralized logging setup for analytics and debugging.
  • Testing: Pytest-based suite, with coverage, type checks (mypy), and pre-commit hooks for code quality.
  • Configuration: Environment variables managed via .env files and Docker Compose.

Frontend Architecture (React, TypeScript, MUI)

  • Entry Point: frontend/src/App.tsx sets up React Router and wraps the app in context providers for rooms, users, and bookings.
  • State Management: Uses React Context for global state (rooms, bookings, users), with real-time updates via SSE or polling.
  • Component Design: Modular, reusable components (e.g., BookingForm, RoomList, CalendarView) with strong typing and JSDoc.
  • UI Library: Material-UI (MUI) for consistent, accessible, and responsive design.
  • API Layer: API calls abstracted into helper modules.
  • Testing: Jest and React Testing Library, with Storybook for UI component development.
  • Styling: CSS modules and theming, with color logic based on room IDs for visual consistency.
  • Error Handling: Robust validation and user-friendly error messages throughout forms and UI.

Orchestration & DevOps

  • Docker: Multi-stage Dockerfiles for backend and frontend, optimized for production and development.
  • Docker Compose: Orchestrates backend, frontend, and Postgres services, with healthchecks and persistent volumes.
  • Nox: Unified automation for testing, linting, and building docs in both frontend and backend.
  • CI/CD Ready: Structure and scripts are ready for integration into a CI/CD pipeline.

Key Design Decisions (Summary)

  • Separation of Concerns: Clear boundaries between API, business logic, data models, and UI.
  • Type Safety: TypeScript (frontend) and Pydantic (backend) for robust data validation.
  • Modularity: Reusable components and service abstractions for maintainability and extensibility.
  • Real-Time UX: Server-Sent Events (SSE) for live updates in the frontend.
  • Accessibility & UX: Focus on error states, skeleton loaders, and accessible UI elements.
  • Centralized Logging: Consistent logging across both backend and frontend.
  • Testing & Quality: Comprehensive test suites, code coverage, and pre-commit hooks.
  • Dev Experience: Automated setup, scripts, and documentation for easy onboarding and development.

In summary: The project is a modern, full-stack web application with a strong emphasis on modularity, type safety, real-time features, and developer experience. The architecture is clean and scalable, with best practices applied throughout both backend and frontend.


AI Assistance in Development

Tools Used

  • GitHub Copilot: Provided code suggestions, boilerplate, and documentation directly in VS Code.
  • CodeGPT (VSCode extension): Helped with generating docstrings, logging messages, and code refactoring.
  • General LLMs (e.g., ChatGPT): Used for answering coding questions, debugging, and rapid prototyping, especially for frontend work.

How AI Was Utilized

  • Scaffolding and Boilerplate: AI was used to quickly generate Dockerfiles, Docker Compose files, and initial test stubs for backend and frontend.
  • Documentation: AI generated or improved docstrings, JSDoc comments, and some README content.
  • Rapid Prototyping: AI helped set up the structure for React components, backend routers, services, and schemas.
  • Refactoring and Debugging: AI suggested code improvements and helped debug failing tests.
  • Frontend Support: AI was especially helpful for CSS, UI structure, and React code, where the developer had less experience.

AI-Generated or AI-Assisted Parts of the Repo

  • Dockerfiles: Dockerfile-backend, Dockerfile-frontend
  • Compose Files: compose.yml, compose.dev.yml
  • Test Stubs: Initial tests for routers, services, and React components
  • Frontend Components: Structure, documentation, and some logic in files like BookingForm.tsx, RoomList.tsx, and CalendarView.tsx
  • Backend Routers/Services/Schemas: Initial structure, docstrings, and some logic in files like users.py, rooms.py, and bookings.py
  • Documentation: Parts of README.md and code comments throughout the repo
  • CSS and Theming: AI helped organize and clean up frontend styles

Human Oversight

  • All AI-generated code was reviewed, corrected, and adapted by the developer. No critical business logic was left to AI alone.

Documentation & Storybook

Backend API Documentation

To build and view the backend API documentation:

  1. Navigate to the backend directory:
cd backend
  1. Run the Nox docs session (live-reloading server):
nox -s docs

This will build and serve the docs with live reloading. Your browser should open automatically to the docs.

Alternative (static build):

If you want to build the static HTML docs only:

cd backend
nox -s docs-build
xdg-open docs/_build/index.html  # Linux
open docs/_build/index.html      # macOS
start docs/_build/index.html     # Windows

Or manually open docs/_build/index.html in your browser.

OpenAPI docs

These are available when the application is running. They are found at: http://localhost:8000/docs

Frontend Storybook

To run Storybook for the frontend using Nox:

  1. Navigate to the frontend directory:
cd frontend
  1. Run the Nox storybook session:
nox -s storybook

This will start Storybook and open your browser.

Alternative (static build):

If you want to build the static Storybook docs only:

cd frontend
nox -s storybook-build
xdg-open storybook-static/index.html  # Linux
open storybook-static/index.html      # macOS
start storybook-static/index.html     # Windows

Or manually open storybook-static/index.html in your browser.


Development Environment Setup

Local Development Setup & Running with Docker

1. Clone the Repository

git clone https://github.com/xlorepdarkhelm/numinar-coding-project.git
cd numinar-coding-project

2. Copy Environment Variables

cp .env.example .env
# Or, if present:
cp .env.sample .env

Edit .env as needed for your local environment (ports, DB credentials, etc).

3. Install Dependencies (Optional for advanced dev)

  • Backend:

    cd backend
    poetry install
    pre-commit install
    
  • Frontend:

    cd ../frontend
    yarn install
    

    Note: For most users, you can skip manual dependency installation and just use Docker Compose for a full-stack local environment.

4. Start All Services with Docker Compose

From the project root:

docker compose -f compose.dev.yml up
  • Backend: FastAPI (default port 8000, hot-reload)
  • Frontend: React (default port 3000, hot-reload)
  • Database: Postgres (default port 5432)

All services are networked together and use volumes for persistent data.

5. Access the Application

6. Run Tests

  • Backend:

    cd backend
    nox           # Run all tests, lint, and docs
    # or just unit tests:
    nox --session=tests
    
  • Frontend:

    cd frontend
    nox           # Run all frontend tests and checks
    # or just Jest tests:
    nox --session=jest
    
  • All tests from root:

    nox
    

7. Stopping and Cleaning Up

  • To stop all services:

    docker compose -f compose.dev.yml down
    
  • To remove volumes (DB data):

    docker compose -f compose.dev.yml down -v
    

Additional Guidelines

  • Pre-commit hooks: Run pre-commit install in backend to enable auto-formatting and linting on commit.
  • Docs & Storybook: See README for nox -s docs (backend API docs) and nox -s storybook (frontend UI docs).
  • Troubleshooting: If you hit dependency or environment issues, delete .venv or node_modules and reinstall.

Summary:

  • Use Docker Compose for a seamless local dev environment.
  • Use Nox for running tests and code quality checks.
  • All config and orchestration is at the project root for easy onboarding.

Security Concerns

Backend (Python/FastAPI)

  1. No Passwords or Authentication
  • The User model and API do not include password fields or authentication logic. Anyone can potentially access user data or perform actions if the API is public.
  1. Open CORS Policy
  • The backend allows all methods and headers from multiple origins, including localhost and Docker Compose service names. In production, this should be restricted to trusted domains only.
  1. Sensitive Data in Environment
  • Default values for DATABASE_PASSWORD and other secrets are hardcoded as "password" in env.py. These should be set securely in production and never committed with real values.
  1. No CSRF Protection
  • There is no mention of CSRF protection for state-changing endpoints. If the frontend and backend are on different domains, this could be a risk.
  1. No Rate Limiting or Brute Force Protection
  • There is no evidence of rate limiting or brute force protection on any endpoints.
  1. No HTTPS Enforcement
  • The backend defaults to http for both frontend and backend protocols. In production, HTTPS should be enforced.
  1. No Input Validation for Dangerous Types
  • While Pydantic is used for schemas, there is no evidence of additional validation or sanitization for fields that could be used in injection attacks (e.g., SQL, command, or path injection).
  1. Verbose Logging
  • Logging user actions and errors is good, but care must be taken not to log sensitive data (such as emails or internal errors) in production logs.

Frontend (React/TypeScript)

  1. No Authentication or Session Handling
  • There is no evidence of authentication tokens, session management, or secure storage of sensitive data in the frontend.
  1. Open API URLs
  • The API URL is configurable via environment variables, but there is no mention of secure token handling or CORS restrictions.
  1. No XSS or Input Sanitization
  • There is no evidence of input sanitization for user-provided data before rendering in the UI.
  1. No Secure Storage
  • There is no mention of secure storage for any sensitive data (e.g., tokens, user info).

General

  • No Mention of Security Headers

    • There is no mention of setting HTTP security headers (e.g., Content-Security-Policy, X-Frame-Options, etc.).
  • No Mention of Dependency Scanning

    • There is no mention of automated dependency vulnerability scanning.

Summary: The project currently lacks authentication, authorization, and secure handling of sensitive data. CORS is open, and there is no CSRF, rate limiting, or HTTPS enforcement. These are critical for any production deployment.


Troubleshooting & FAQ

Q: Why does isort or black change files differently in Nox vs pre-commit? A: Make sure both Nox and pre-commit are using the same pinned tool versions and config files. See .isort.cfg and .pre-commit-config.yaml for details.

Q: Why does Storybook open the browser or not exit when run in CI/Nox? A: Use yarn build-storybook for static builds (exits automatically). For dev server, use yarn storybook dev --ci to prevent browser opening.

Q: Why do pre-commit hooks modify files after a commit or behave differently in CI? A: Pre-commit runs in its own environment. Always run pre-commit run --all-files locally to reproduce CI behavior. Ensure your config files are up to date and tool versions are pinned.

Q: How do I run only frontend or backend tests? A: Use Nox in the respective directory (cd frontend && nox, cd backend && nox).

Q: How do I fix environment or dependency issues? A: Delete .venv or node_modules and reinstall dependencies (poetry install for backend, yarn install for frontend). Check for version mismatches in config files.

Q: Where are config files for formatting and type checking? A: See .isort.cfg, .coveragerc, and mypy.ini in the project root. These are derived from backend settings for consistency.

Q: Docker Compose fails to start or containers exit immediately. A: Check that your .env file is present and correctly configured. Make sure required ports (3000, 8000, 5432) are not in use. Run docker compose logs for more details.

Q: Database migrations are not applied or backend fails with DB errors. A: Ensure the Postgres container is healthy and running. You may need to run migrations manually with alembic upgrade head inside the backend container.

Q: Frontend changes are not reflected in the browser. A: Make sure the frontend container is running in development mode (hot-reload enabled). Try rebuilding the frontend container or clearing your browser cache.

Q: Backend API changes are not picked up. A: The backend should auto-reload in development. If not, restart the backend container or check for syntax errors in your code.

Q: How do I reset the database to a clean state? A: Stop all containers, then run docker compose -f compose.dev.yml down -v to remove volumes and start fresh.

Q: How do I add new environment variables? A: Add them to your .env file and ensure they are referenced in the relevant Docker Compose and application config files. Rebuild containers if necessary.

Q: Nox or pre-commit fails due to missing Python or Node dependencies. A: Ensure you have the correct Python and Node versions installed. Reinstall dependencies with poetry install (backend) or yarn install (frontend).

Q: How do I debug a failing test? A: Run the test suite with verbose output (nox -- -v), or run individual tests using pytest or jest directly for more detailed error messages.


Diagrams

Docker Compose Components

%% Docker Compose Architecture (with hot-reload)
graph TD
  subgraph app-network
    FE[Frontend<br/>Port: 3000<br/>.env, hot-reload]
    BE[Backend<br/>Port: 8000<br/>.env, hot-reload]
    DB[(PostgreSQL<br/>Port: 5432<br/>Persistent Volume<br/>Healthcheck)]
    BE -- "SQL: 5432" --> DB
  end
  Client[Browser/Client] -- "HTTP: 3000" --> FE
  FE -- "REST API: 8000" --> BE
  note1[.env files for config] -.-> FE
  note1 -.-> BE
  note2[Volume for DB data] -.-> DB
  note3[Hot-reload enabled in dev mode] -.-> FE
  note3 -.-> BE

Database Schema Diagram

%% Database Schema with Constraints and Notes
classDiagram
  class User {
    string email [PK]
    string name
    -- Relationships --
    Invitee[*] invitees
  }
  class Room {
    int id [PK]
    string name [unique]
    string location
    string equipment
    int capacity
    -- Relationships --
    Booking[*] bookings
  }
  class Booking {
    int id [PK]
    int room_id [FK]
    datetime start_time
    datetime end_time
    string title [nullable]
    -- Relationships --
    Invitee[*] invitees
  }
  class Invitee {
    int id [PK]
    int booking_id [FK, ON DELETE CASCADE]
    string user_email [FK, ON DELETE CASCADE]
    -- Constraints --
    unique(booking_id, user_email)
  }

  User "1" <|-- "*" Invitee : user_email [FK]
  Room "1" <|-- "*" Booking : room_id [FK]
  Booking "1" <|-- "*" Invitee : booking_id [FK]

Component Diagram

%% Component Relationships and Data Flow
graph TD
  subgraph Frontend
    F1[Pages]
    F2[Components]
    F3[API Helpers]
    F4[Context Providers]
    F1 --> F2
    F2 --> F4
    F4 --> F3
  end
  subgraph Backend
    B1[FastAPI Routers]
    B2[Pydantic Schemas]
    B3[Service Layer]
    B4[SQLAlchemy Models]
    B5[PostgreSQL]
    B6[Alembic Migrations]
    B1 --> B2
    B1 --> B3
    B3 --> B4
    B4 --> B5
    B6 -.->|"Schema Management"| B5
  end
  F3 -- REST --> B1
  B5 -.->|External| PG[PostgreSQL Service]

Backend Request Data Path

%% Backend Request Data Path (no auth, but placeholder shown)
sequenceDiagram
  participant Frontend
  participant FastAPI_Router as FastAPI Router
  participant Dependency_Injector as Dependency Injector
  participant API_Endpoint as API Endpoint
  participant Pydantic_Schema as Pydantic Schema
  participant Service_Layer as Service Layer
  participant DB_Model as DB Model
  participant Database
  participant Logger as Logger
  %% Auth is not currently set up, but shown for future extension
  participant Auth as Auth (not enabled)

  Frontend->>FastAPI_Router: HTTP Request (REST)
  FastAPI_Router->>Dependency_Injector: Resolve Dependencies (DB, Service)
  Dependency_Injector-->>API_Endpoint: Injected Dependencies
  %% Auth would go here if enabled
  API_Endpoint->>Auth: (Auth check placeholder)
  note right of Auth: Auth is not currently set up
  API_Endpoint->>Pydantic_Schema: Validate & Parse Input
  alt Validation Error
    Pydantic_Schema-->>Frontend: HTTP 422 Unprocessable Entity
  else Validated
    Pydantic_Schema-->>API_Endpoint: Validated Data
    API_Endpoint->>Logger: Log Request
    API_Endpoint->>Service_Layer: Pass Data
    Service_Layer->>DB_Model: Business Logic
    DB_Model->>Database: DB Query/Update
    Database-->>DB_Model: Query Result
    DB_Model-->>Service_Layer: Data
    Service_Layer-->>API_Endpoint: Response Data
    API_Endpoint->>Pydantic_Schema: Serialize Response
    API_Endpoint->>Logger: Log Response
    API_Endpoint-->>Frontend: HTTP Response (JSON)
  end
  alt Not Found/Error
    API_Endpoint-->>Frontend: HTTP 404/500 Error
  end

SSE Update Flow

%% SSE Update Flow with Triggers and Error Handling
sequenceDiagram
  participant Frontend as Frontend (Client)
  participant SSE_Endpoint as FastAPI SSE Endpoint
  participant Event_Generator as Event Generator
  participant Publisher as Publisher (Booking/Room Service)
  participant DB as Database

  Frontend->>SSE_Endpoint: Open EventSource /availability/stream
  SSE_Endpoint->>Event_Generator: Register Subscriber
  loop While Connected
    Publisher->>DB: Booking/Room Create/Update/Delete
    DB-->>Publisher: DB Change
    Publisher-->>Event_Generator: Trigger Event (type: "availability")
    Event_Generator-->>SSE_Endpoint: Send SSE Event (data: {type: "availability"})
    SSE_Endpoint-->>Frontend: Push SSE Event
    Note over Publisher,Frontend: Trigger: Booking created/updated/deleted
    alt No Event (timeout)
      Event_Generator-->>SSE_Endpoint: Send SSE Event (data: {type: "keep-alive"})
      SSE_Endpoint-->>Frontend: Push SSE Event
    end
    alt Connection Error/Drop
      Frontend-->>SSE_Endpoint: Disconnect (close EventSource)
      SSE_Endpoint-->>Event_Generator: Cleanup Subscriber
    end
  end

Description
The code I used for a coding challenge.
Readme 50 MiB
Languages
Python 54.2%
TypeScript 39%
CSS 5.3%
JavaScript 1%
HTML 0.4%
Other 0.1%