Closet app with FastAPI backend and Next.js frontend
  • TypeScript 61.2%
  • Python 25.4%
  • CSS 9.4%
  • Shell 3.6%
  • Dockerfile 0.2%
  • Other 0.2%
Find a file
2026-09-21 06:55:51 -05:00
.run added analytics to the frontend and backend 2026-08-26 18:38:56 -05:00
.venv feat: AI suggest-item endpoint with vision support via llama.cpp 2026-08-06 15:07:47 -05:00
backend just some running updates 2026-09-21 06:55:51 -05:00
data just some running updates 2026-09-21 06:55:51 -05:00
frontend somr style and behaviour changes 2026-09-17 08:24:30 -05:00
scripts app improvements and hardening 2026-08-28 14:03:34 -05:00
systemd split systemd/closet-backend.service for more 2026-08-08 09:27:52 -05:00
.env.example fixed image routing and added api key to lock 2026-08-07 07:45:21 -05:00
.gitignore split systemd/closet-backend.service for more 2026-08-08 09:27:52 -05:00
boot-backend.sh app improvements and hardening 2026-08-28 14:03:34 -05:00
boot-frontend.sh finally fixed, requires reboot of host, but works 2026-08-09 06:47:28 -05:00
boot-start.sh added easier catagory management and db backup 2026-08-08 09:15:49 -05:00
DESIGN_DECISIONS.md app improvements and hardening 2026-08-28 14:03:34 -05:00
docker-compose.yml Initial import of closet app 2026-08-05 22:03:22 -05:00
install-autostart.sh split systemd/closet-backend.service for more 2026-08-08 09:27:52 -05:00
README.md improved random outfit generation based on capsule 2026-08-11 06:13:17 -05:00
start-local.sh app improvements and hardening 2026-08-28 14:03:34 -05:00

Closet App

A fullstack closet management web app with AI-assisted item tagging, outfit building, capsule management, favorites, calendar-based outfit history, and a daily outfit planner.

Stack

  • Frontend: Next.js 15 + TypeScript, Fraunces (display) + Manrope (body) via next/font
  • Backend: FastAPI + SQLite
  • AI: OpenAI-compatible local or cloud vision models (llama.cpp, Ollama, OpenAI, Groq, or custom)
  • Deployment: local start-local.sh or systemd services (closet-backend.service, closet-app.service)

Running Locally

./start-local.sh

When run from an interactive terminal, this starts the FastAPI backend (port 8000) plus the Next.js frontend in development mode (port 5050), both bound to 0.0.0.0.

This avoids a full production rebuild on every local restart, which makes startup faster and more reliable while you are editing the app.

Note: if you run ./start-local.sh from a terminal, the app stops when that terminal/session exits. Use the systemd setup below for reboot-safe background startup.

To force the production frontend path locally:

APP_START_MODE=production ./start-local.sh

To force a fresh production rebuild before startup:

APP_START_MODE=production FORCE_FRONTEND_BUILD=1 ./start-local.sh
  • Frontend: http://localhost:5050
  • Backend API: http://localhost:8000
  • API docs: http://localhost:8000/docs

Auto-start on boot (systemd)

From the project root:

  1. Create and edit your local service files from the templates:
cp systemd/closet-backend.service.example systemd/closet-backend.service
cp systemd/closet-app.service.example systemd/closet-app.service
# edit both files and set:
# - User
# - WorkingDirectory / ExecStart / HOME / PATH
# - CLOSET_API_WRITE_KEY
  1. Install and enable it:
sudo ./install-autostart.sh

This installs and enables both services so the app starts on reboot:

  • closet-backend.service for FastAPI
  • closet-app.service for the Next.js frontend

The frontend unit depends on the backend unit, but they can be restarted independently.

Production note:

  • boot-frontend.sh runs a production next build before launching the standalone server.
  • During that rebuild window after restart, reverse proxies may briefly return 502 until the frontend is ready.

Useful commands:

sudo systemctl status closet-backend closet-app
sudo systemctl restart closet-backend
sudo systemctl restart closet-app
journalctl -u closet-backend -u closet-app -f

For reverse-proxied deployments, ensure the service environment includes:

NEXT_PUBLIC_API_BASE_URL=

This keeps API/image calls on same-origin /api/... through the proxy.

Reverse proxy runtime behavior (important)

For deployments behind domain auth/proxy (for example https://closet.hybridcloudhq.com):

  • Frontend write/read API calls are expected to use same-origin /api/*.
  • Frontend static assets use Next.js's default /_next/static/* paths. Verify this path through the live proxy after any proxy or Next.js configuration change.
  • Uploaded images continue to resolve through /api/files/items/*.

Reverse proxy note for images

Uploaded item photos are served from /api/files/items/....

If you proxy the app with Caddy/Nginx/Traefik, make sure your backend route includes /api/files/* (or routes all /api/*), otherwise item images will not load.

One-time image URL cleanup

Run this if you want to normalize legacy item image URLs in the database to the current canonical format (/api/image?file=...).

./scripts/normalize-image-urls.sh

The script creates a timestamped backup in data/closet.db.bak.YYYYMMDD-HHMMSS before updating rows.

Database backup and restore

The Profile page now includes a Database section where you can:

  • download a full SQLite backup of the current closet database
  • restore the app from a previously saved .db / .sqlite backup file

Restore uploads are validated before replacement, and the app automatically creates a fresh pre-restore backup of the current database file before swapping it out.

If you prefer using the API directly:

GET  /api/profile/database/backup
POST /api/profile/database/restore

Shared closet options

Closet categories, fit/style/fabric options, and color swatches are centralized in:

frontend/lib/closet-options.ts

Update that file when you want to add or rename selectable closet metadata across the add-item and edit-item flows.

Optional write API key protection

You can require an API key for all write routes (POST, PUT, PATCH, DELETE) under /api/*.

  1. Set a key for the backend service:
CLOSET_API_WRITE_KEY=your-long-random-key
  1. Send the same value in request header X-API-Key for write calls.

If you run behind Nginx Proxy Manager, set this as a Custom Request Header so browser clients do not need to embed a public key:

X-API-Key: your-long-random-key

Read-only endpoints (GET) are not affected.

Remote access

The frontend is accessible from other machines on the LAN at http://<host-ip>:5050. The API base URL is set in frontend/.env.local:

NEXT_PUBLIC_API_BASE_URL=http://<host-ip>:8000

For reverse-proxied deployments (for example, https://closet.hybridcloudhq.com), set:

NEXT_PUBLIC_API_BASE_URL=

This makes frontend requests use same-origin /api/... paths through the proxy.

The backend runs with allow_origins=["*"] — suitable for homelab use. Backend CORS can be tightened via the CORS_ORIGINS environment variable.

What's Built

Closet Management

  • Add, edit, and delete clothing items with rich metadata (category, colors, fabric, fit, cut, neck, sleeve, waist, length, rise, capsule membership, love factor, price, shopping links)
  • Photo upload with camera capture support
  • Capsule groupings — create subsets of your closet and filter the view by capsule
  • Item thumbnails in closet grid view
  • Tracked categories: Layers, Shirts, Pants, Dresses, Rhompers, Skirts, Shorts, Shoes, Accessories

AI Item Suggestions

  • Upload a photo when adding an item, tap ✦ Suggest with AI and the model fills in name, category, colors, fabric, fit, cut, sleeve, neck, and style
  • Works with any OpenAI-compatible vision endpoint
  • Supports Qwen3 / llama.cpp models that return reasoning in reasoning_content instead of content
  • Configurable AI provider in Profile settings: Local (llama.cpp), Ollama, OpenAI, Groq, or Custom

Outfit Engine

  • Random outfit — color-harmony-scored generator:
    • Shirt + bottom (pants/skirt/shorts), or dress, or rhomper + optional shirt
    • One shoe; optional accessory
    • Layer added automatically when temperature is below 65°F
  • Custom builder — slot-by-slot picker with dense visual tiles, explicit click-to-expand/click-again-to-collapse behavior, and no default preselected slot
  • Edit outfit — modify items on any saved outfit
  • Save or Save & wear today from any generated or custom outfit
  • Favorites — persist outfit favorites with quick access under the Outfits Favorites tab

Daily Outfit Planner

  • Home page shows today's outfit, or a random suggestion if none is set
  • Wear this today — saves and locks the suggestion as today's outfit
  • Change outfit — clears today's outfit and navigates to the outfits page
  • Recent outfits section on home shows the last 5 worn outfits (3-column on tablet, 2-column on mobile)

Outfit Planning & History

  • Create tab feed — defaults to last 7 days (including today)
  • Plan outfit — assign any saved outfit to a future date with visual item thumbnails on each selectable outfit card
  • Past outfits — calendar view with clickable dates, day markers for logged outfits, and visual outfit cards for the selected day
  • Saved outfits list with Wear today, Edit, Favorite, and Remove actions

Home and Navigation

  • Home Closet sections quick links route to Outfits sub-tabs:
    • Favorites -> /outfits?tab=favorites
    • Recent -> /outfits?tab=create

Visual Design

  • Full OKLCH color palette: Chantilly Lace base, Oregano olive accent, Hale Navy text, Topsail teal, Tender Pink, Kentucky Haze
  • Fraunces serif display font + Manrope body font
  • Responsive layout: 3-breakpoint system (≥900px desktop, 640–900px tablet, ≤640px mobile)
  • Bottom navigation bar, floating pill controls, sheet overlays
  • Denser closet item cards (desktop-targeted 5-up layout) and compact builder/selector cards to reduce unused whitespace

Other

  • Weather-aware outfit suggestions (Open-Meteo API, no key required)
  • Profile settings: display name, avatar upload, location, weather preferences
  • Shopping needs list with priority ranking
  • Analytics page (stub for future expansion)

API Surface

GET  /api/health
GET  /api/dashboard
GET  /api/items
POST /api/items
GET  /api/items/{id}
PUT  /api/items/{id}
DELETE /api/items/{id}
POST /api/uploads/item-photo

GET  /api/capsules
POST /api/capsules
PUT  /api/capsules/{name}
DELETE /api/capsules/{name}

GET  /api/outfits
POST /api/outfits
GET  /api/outfits/random?temperature=72
GET  /api/outfits/today
PUT  /api/outfits/{id}
PUT  /api/outfits/{id}/favorite
DELETE /api/outfits/{id}
POST /api/outfits/{id}/wear-today
POST /api/outfits/{id}/wear-date
DELETE /api/outfits/today

GET  /api/ai/config
PUT  /api/ai/config
POST /api/ai/suggest-item

GET  /api/profile
POST /api/profile
POST /api/profile/change-password
POST /api/profile/logout
GET  /api/profile/database/backup
POST /api/profile/database/restore
GET  /api/location/reverse

Project Layout

closet-app/
├── backend/
│   ├── main.py           # FastAPI app — all routes, DB schema, outfit engine
│   ├── requirements.txt
│   └── uploads/
│       └── items/        # Uploaded item photos
├── frontend/
│   ├── app/
│   │   ├── closet/       # Closet grid + item detail + add-item sheet
│   │   ├── outfits/      # Outfit builder, planner, history
│   │   ├── analytics/    # Wear stats
│   │   ├── shopping/     # Shopping needs
│   │   ├── profile/      # Profile, AI config, preferences
│   │   ├── globals.css   # Design system tokens and shared classes
│   │   └── page.tsx      # Home dashboard
│   ├── components/
│   │   ├── icon.tsx
│   │   └── profile-pill.tsx
│   └── lib/
│       ├── api.ts        # All API fetch functions and types
│       └── fetch-json.ts # Timeout-aware external fetch helper
├── data/
│   └── closet.db         # SQLite database (gitignored)
├── boot-backend.sh       # Production backend entrypoint for systemd
├── boot-frontend.sh      # Production frontend entrypoint for systemd
├── start-local.sh        # One-command local startup
├── docker-compose.yml
└── DESIGN_DECISIONS.md

Mobile-Ready Direction

The frontend uses a mobile-style layout (max-width 980px, bottom navigation) and a clean API boundary, making it a direct stepping stone to a React Native client or PWA.

Potential Next Features

  • Wear logging per item (track how often each piece is actually worn)
  • Cost-per-wear analytics
  • Shopping recommendations based on closet gaps
  • Multi-closet / multi-user support
  • PWA with offline support
  • Native mobile client reusing the same backend