A mobile-first home inventory app that remembers what you bought, where you stored it, and when it will expire. Built to start simple and grow into image-assisted intake — place items on a table, take a photo, and let the app identify products, read visible dates, and suggest storage locations.
Current product status
- Docker Compose stack (frontend + backend + PostgreSQL), Alembic migrations, async SQLAlchemy
- Inventory: batches with quantity, location, optional expiry/opened/purchased dates; batch status (
active/consumed/discarded); move, consume, discard; expiry PATCH; paginated inventory and expiry-focused listing - Products: CRUD, alias-aware semantic search, duplicate candidates, recent products and recent locations per product for fast re-entry; optional minimum stock and unit for restock; relational tags
- Restock & shopping list: when total active quantity for a product drops below its minimum (same shopping-friendly unit), an open shopping-list item is created or updated with a suggested buy quantity; it resolves when stock is back at or above the minimum; resolve / dismiss / reopen via API
- Events: inventory event history for lifecycle changes (API:
/api/v1/inventory/events) - Scans & barcode entry: single-item photo scan with review modal, plus barcode-assisted entry with local-first barcode lookup, semantic candidate matching, external provider fallback, manual attach/create review, and batch save from the Scans page
- Semantic memory: product aliases, product and scan-text embedding helper tables, natural-language inventory search, semantic product search, OCR/barcode candidate matching, and duplicate product candidates; semantic reindexing uses Ollama embeddings when enabled and falls back to local deterministic embeddings
- UI: Dashboard, Inventory, Products, Locations, Scans, Expiry dashboard, Shopping list page; quick-add patterns tied to recent products/locations
- Local file storage abstraction for future uploads; pytest + Vitest test suites
See plan/ for the full product vision and implementation plans.
| Layer | Technology |
|---|---|
| Frontend | React 18, TypeScript, Vite, Tailwind CSS, TanStack Query, React Router |
| Backend | FastAPI, SQLAlchemy (async), Alembic, Pydantic v2, asyncpg |
| Database | PostgreSQL 16 |
| Dev environment | Docker Compose |
| Backend tests | pytest, pytest-asyncio, httpx, pytest-cov |
| Frontend tests | Vitest, React Testing Library, jsdom |
| Python linting | ruff (lint + format) |
- Docker and Docker Compose
- Node.js 20+ (for local frontend development without Docker)
- Python 3.11+ (for local backend development without Docker)
- GNU
make(pre-installed on macOS and most Linux distros; available for Windows)
git clone https://github.com/uab2411/shelfsense.git
cd shelfsense
cp .env.example .env
# Start all services (Docker required)
make build
# Apply database migrations (enables extensions such as pg_trgm for search indexes)
make migrateThe app is then available at:
| Service | URL |
|---|---|
| Frontend (Vite dev server) | http://localhost:5173 |
| Backend (FastAPI) | http://localhost:8000 |
| API docs (Swagger) | http://localhost:8000/docs |
| PostgreSQL | localhost:5432 |
To use the app from another device on the same Wi-Fi network, open the frontend with
your laptop's LAN IP, for example http://192.168.1.25:5173. The frontend uses
same-origin API calls by default, so requests from that device go back through the
laptop's Vite dev server and are proxied to the backend.
Run make or make help for the full target list. Common commands:
| Area | Examples |
|---|---|
| First run | make setup (copy .env, install deps) · make build · make migrate |
| Everyday | make up · make down · make logs · make seed |
| DB | make migrate · make migrate-new MSG="..." · make migrate-down |
| Quality | make lint · make format-be |
| Tests | make test · make test-be · make test-fe |
| Local (no Docker) | make dev-be · make dev-fe (after install-be / install-fe) |
Tests live in backend/tests/ (pytest, pytest-asyncio, httpx).
make test-be # run + coverage summary
make test-be-v # verbose output
make test-be-cov # run + print path to HTML reportCoverage is reported for the app/ package. The HTML report is written to backend/htmlcov/.
Open backend/htmlcov/index.html after make test-be-cov to inspect up-to-date per-module coverage.
Tests live in frontend/src/__tests__/ (Vitest, React Testing Library).
make test-fe # single run
make test-fe-watch # interactive watch mode
make test-fe-cov # single run with coverage reportThe coverage report is written to frontend/coverage/.
shelfsense/
├── Makefile # Developer convenience targets
├── backend/ # FastAPI application
│ ├── app/
│ │ ├── api/ # Route handlers (v1: products, inventory, locations, shopping list, system)
│ │ ├── models/ # SQLAlchemy ORM models
│ │ ├── schemas/ # Pydantic request/response schemas
│ │ ├── services/ # Business logic (storage, restock, tags, etc.)
│ │ ├── config.py # App configuration (pydantic-settings)
│ │ ├── database.py # Async DB session setup
│ │ └── main.py # FastAPI app entry point
│ ├── migrations/ # Alembic migration files
│ ├── scripts/ # Utility scripts (seed, etc.)
│ ├── tests/ # pytest (api/, models, services)
│ ├── pytest.ini
│ ├── Dockerfile
│ └── requirements.txt
├── frontend/ # React application
│ ├── src/
│ │ ├── __tests__/ # Vitest (pages, components, lib)
│ │ ├── app/ # Router and top-level App component
│ │ ├── components/ # Shared UI components
│ │ ├── lib/ # API client, query config, theme
│ │ ├── pages/ # Dashboard, Inventory, Products, Expiry, Shopping list, etc.
│ │ └── test/
│ │ └── setup.ts # Vitest global setup
│ ├── vitest.config.ts
│ ├── Dockerfile
│ ├── package.json
│ └── vite.config.ts
├── plan/ # Vision documents and implementation plans
├── storage/ # Local file storage mount
│ └── uploads/ # Uploaded images (gitignored, .gitkeep preserves dir)
├── .env.example # Environment variable template
├── docker-compose.yml # Development orchestration
└── LICENSE
make install-be
# then edit .env to point DATABASE_URL at a local PostgreSQL instance
make dev-bemake install-fe
make dev-feThe Vite dev server reads the root .env file and proxies /api, /health, and
/ready to the backend automatically.
All runtime configuration belongs in the root .env file. Do not create separate
frontend or backend env files.
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
postgresql+asyncpg://shelfsense:shelfsense@localhost:5432/shelfsense |
Async PostgreSQL connection URL |
STORAGE_PATH |
./storage/uploads |
Path for uploaded file storage |
ENVIRONMENT |
development |
development or production |
APP_VERSION |
0.1.0 |
Reported in /api/version |
DEV_SERVER_HTTPS |
unset | Set to 1, true, yes, or on to run Vite dev server over HTTPS |
VITE_API_BASE_URL |
unset | Browser API base URL; leave unset/empty for same-origin Vite proxying |
DEV_PROXY_TARGET |
http://localhost:8000 |
Backend target used by Vite's dev proxy |
System (app root)
| Method | Path | Description |
|---|---|---|
| GET | /health |
Liveness |
| GET | /ready |
Readiness (includes DB) |
| GET | /api/version |
App version |
API v1 (prefix /api/v1; full detail in Swagger at http://localhost:8000/docs)
| Area | Highlights |
|---|---|
| Products | CRUD, search, recent, recent locations, tags and minimum stock on PATCH |
| Inventory | List batches, create batch, PATCH batch, expiry PATCH, consume / discard / move, expiry listing, events |
| Locations | CRUD + list |
| Shopping list | List items, resolve, dismiss, reopen |