- Track last_login_at on User model, updated on every successful login - Show last login date in admin panel user table - Fix admin/garden date display (datetime strings already contain T separator) - Fix My Garden Internal Server Error (MySQL does not support NULLS LAST syntax) - Fix Log Batch infinite loop when user has zero varieties - Auto-fill batch dates from today when creating a new batch, calculated from selected variety's week offsets (germination, greenhouse, garden) - Update README with new features and batch date auto-fill formula table Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
170 lines
7.2 KiB
Markdown
170 lines
7.2 KiB
Markdown
# Sproutly 🌱
|
||
|
||
A self-hosted plant growth tracking web app designed to help home gardeners manage their seed starting schedules, growing cycles, and garden transitions — all anchored to their local last frost date.
|
||
|
||
## Overview
|
||
|
||
Sproutly takes the guesswork out of seed starting. Enter your plant varieties once with their frost-relative timing (e.g. "start 8 weeks before last frost"), set your last frost date, and Sproutly calculates exactly when to start seeds, move to the greenhouse, and transplant to the garden. A daily push notification via [ntfy](https://ntfy.sh) keeps you on track without having to check the app.
|
||
|
||
## Features
|
||
|
||
- **Multi-user** — each user has their own account with fully isolated data
|
||
- **Admin panel** — manage all user accounts: view content, reset passwords, disable, or delete; shows each user's join date and last login
|
||
- **Dashboard** — at-a-glance view of overdue, today's, and upcoming tasks with a full year planting timeline
|
||
- **Seed Library** — manage plant varieties with frost-relative timing, germination days, sun/water requirements
|
||
- **Garden Tracker** — log growing batches and track status from `planned` → `germinating` → `seedling` → `potted up` → `hardening off` → `garden` → `harvested`
|
||
- **Smart date auto-fill** — when logging a new batch, sow date defaults to today and all subsequent dates (germination, greenhouse, transplant) are calculated automatically from the selected variety's timing
|
||
- **Year Timeline** — visual calendar showing when each variety's stages fall across the year
|
||
- **Ntfy Notifications** — per-user daily summary push notifications to your phone, configurable time, topic, and authentication
|
||
- **Settings** — set your last frost date, fall frost date, location, timezone, and notification preferences
|
||
|
||
## Stack
|
||
|
||
| Layer | Technology |
|
||
|-----------|-------------------------|
|
||
| Frontend | Nginx (static HTML/CSS/JS SPA) |
|
||
| Backend | FastAPI (Python) |
|
||
| Database | MySQL 8.0 |
|
||
| Auth | JWT (python-jose + bcrypt) |
|
||
| Notifications | Ntfy |
|
||
| Runtime | Docker Compose |
|
||
|
||
## Getting Started
|
||
|
||
### Prerequisites
|
||
|
||
- Docker and Docker Compose
|
||
|
||
### Run
|
||
|
||
```bash
|
||
git clone https://git.chns.tech/CooperandGoodman/sproutly.git
|
||
cd sproutly
|
||
cp .env.example .env # set a strong SECRET_KEY and DB credentials
|
||
docker compose up --build -d
|
||
```
|
||
|
||
Access the app at **http://localhost:8053** — create an account to get started.
|
||
|
||
### First Steps
|
||
|
||
1. Register an account on the login screen
|
||
2. Go to **Settings** and enter your last frost date — this anchors the planting timeline and dashboard calculations
|
||
3. Optionally configure an [ntfy](https://ntfy.sh) topic for push notifications
|
||
4. Add varieties to your **Seed Library** — each variety stores its frost-relative week offsets and germination days
|
||
5. Head to **My Garden** and log a batch — dates auto-fill based on today and the selected variety's timing
|
||
|
||
## Environment Variables
|
||
|
||
Copy `.env.example` to `.env` and fill in all values:
|
||
|
||
```env
|
||
MYSQL_ROOT_PASSWORD=sproutly_root_secret
|
||
MYSQL_USER=sproutly
|
||
MYSQL_PASSWORD=sproutly_secret
|
||
SECRET_KEY=your-secret-key-change-this
|
||
ADMIN_EMAIL=admin@example.com
|
||
ADMIN_PASSWORD=change-this-password
|
||
```
|
||
|
||
`SECRET_KEY` is used to sign JWT tokens. Generate a secure value with:
|
||
|
||
```bash
|
||
python3 -c "import secrets; print(secrets.token_hex(32))"
|
||
```
|
||
|
||
`ADMIN_EMAIL` and `ADMIN_PASSWORD` define the super admin account. This account is created (or updated) automatically every time the backend starts — changing these values in `.env` and restarting is all that's needed to update the credentials.
|
||
|
||
## Project Structure
|
||
|
||
```
|
||
sproutly/
|
||
├── docker-compose.yml
|
||
├── .env
|
||
├── mysql/
|
||
│ └── init.sql # Schema (multi-user)
|
||
├── backend/ # FastAPI application
|
||
│ ├── main.py
|
||
│ ├── auth.py # JWT + bcrypt utilities
|
||
│ ├── models.py
|
||
│ ├── schemas.py
|
||
│ ├── database.py
|
||
│ └── routers/
|
||
│ ├── auth.py # /auth/register, /auth/login, /auth/me
|
||
│ ├── admin.py # /admin/users — admin-only user management
|
||
│ ├── varieties.py
|
||
│ ├── batches.py
|
||
│ ├── dashboard.py
|
||
│ ├── settings.py
|
||
│ └── notifications.py
|
||
└── nginx/
|
||
├── nginx.conf
|
||
└── html/ # Static frontend SPA
|
||
├── index.html
|
||
├── css/style.css
|
||
└── js/app.js
|
||
```
|
||
|
||
## API
|
||
|
||
The FastAPI backend is available at `/api/` and includes automatic docs at **http://localhost:8053/api/docs**
|
||
|
||
All endpoints except `/auth/register` and `/auth/login` require a `Authorization: Bearer <token>` header.
|
||
|
||
Key endpoints:
|
||
- `POST /api/auth/register` — create account
|
||
- `POST /api/auth/login` — get JWT token
|
||
- `GET /api/auth/me` — current user info
|
||
- `GET /api/dashboard/` — dashboard data, tasks, and timeline
|
||
- `GET/POST/PUT/DELETE /api/varieties/` — seed variety management
|
||
- `GET/POST/PUT/DELETE /api/batches/` — growing batch management
|
||
- `GET/PUT /api/settings/` — app settings
|
||
- `POST /api/notifications/test` — send test ntfy notification
|
||
- `POST /api/notifications/daily` — trigger daily summary
|
||
- `GET /api/notifications/log` — recent notification history
|
||
- `GET /api/admin/users` — list all users with stats (admin only)
|
||
- `GET /api/admin/users/{id}/varieties` — view a user's seed library (admin only)
|
||
- `GET /api/admin/users/{id}/batches` — view a user's batches (admin only)
|
||
- `POST /api/admin/users/{id}/reset-password` — reset a user's password (admin only)
|
||
- `POST /api/admin/users/{id}/disable` — toggle account disabled state (admin only)
|
||
- `DELETE /api/admin/users/{id}` — delete a user and all their data (admin only)
|
||
|
||
## Admin Panel
|
||
|
||
Log in with the `ADMIN_EMAIL` / `ADMIN_PASSWORD` credentials from your `.env`. Once logged in, an **Admin** link appears in the sidebar. From there you can:
|
||
|
||
- View all registered users with join date, last login, variety count, and batch count
|
||
- Browse any user's seed library and growing batches
|
||
- Reset a user's password
|
||
- Disable or re-enable an account
|
||
- Permanently delete an account and all associated data
|
||
|
||
The admin account itself cannot be disabled or deleted through the panel.
|
||
|
||
## Ntfy Authentication
|
||
|
||
For private ntfy servers or access-controlled topics, the Settings page supports three auth modes:
|
||
|
||
| Mode | When to use |
|
||
|------|-------------|
|
||
| None | Public ntfy.sh topics |
|
||
| Username & Password | ntfy server with basic auth enabled |
|
||
| API Key / Token | ntfy account access token (generate in ntfy account settings) |
|
||
|
||
## Batch Date Auto-fill
|
||
|
||
When logging a new batch, Sproutly pre-fills all date fields based on the selected variety's timing offsets, using **today as the sow date**:
|
||
|
||
| Field | Calculation |
|
||
|-------|-------------|
|
||
| Sow date | Today |
|
||
| Germination date | Sow date + `days_to_germinate` |
|
||
| Greenhouse / pot-up date | Sow date + (`weeks_to_start` − `weeks_to_greenhouse`) × 7 |
|
||
| Garden transplant date | Sow date + (`weeks_to_start` + `weeks_to_garden`) × 7 |
|
||
|
||
All dates remain fully editable. Fields are left blank if the variety is missing the relevant timing value.
|
||
|
||
## Status
|
||
|
||
Core infrastructure is functional. UI design and feature set are evolving based on user feedback.
|