0ed1bc4a0e
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
112 lines
5.7 KiB
Markdown
112 lines
5.7 KiB
Markdown
# WhatToDo
|
||
|
||
A small, self-hosted to-do app for one person. It works in a desktop browser and on a phone, and you can install it to your home screen.
|
||
|
||
- Tasks have an optional **plan date** (when you'll do it), an optional **deadline** (when it must be done), and an optional **time range** (e.g. 10:00–12:00). Each task also has a **priority**, **category**, **tags**, **notes**, and a **checklist** of steps.
|
||
- **Postpone** any task from the ⏭ button: today, tomorrow, the weekend, next week, in 2 weeks, next month, or a date you pick. You can move either the plan date or the deadline.
|
||
- Unfinished planned tasks **roll over**: they stay in Today until you do them or postpone them, and they never turn red. Only missed **deadlines** count as overdue.
|
||
- **Backlog** lists tasks with no date at all. Today suggests a couple of them so they don't get forgotten.
|
||
- **Projects** group the tasks of a bigger goal and have their own deadline, notes and progress bar. Today shows each active project's **next step**, how much time is left, and a warning if it's falling behind or nothing was done on it in over a week.
|
||
- **Recurring tasks** (every N days/weeks/months/years): completing one creates the next occurrence.
|
||
- When you pick a date and time, the task form lists what else is planned that day and warns about overlaps.
|
||
- Views: **Today**, **Upcoming**, **Calendar** (day timeline with week strip; tap an hour to add a task there), **Backlog**, **All open**, **Done**, plus one view per project and per category.
|
||
- You can rename, recolor, reorder, add, or delete categories in **Settings**. It starts with Work, Life, Personal, YouTube.
|
||
- **Export / import** of all data as JSON, from **Settings**.
|
||
- Login with a single password.
|
||
|
||
Stack: Python (FastAPI) + SQLite with a plain HTML/CSS/JS frontend and no build step. Everything runs in Docker.
|
||
|
||
## Run
|
||
|
||
Requirements: Docker with the Compose plugin. You don't need the source code: only `docker-compose.yaml` and a `.env`.
|
||
|
||
```sh
|
||
mkdir whattodo && cd whattodo
|
||
# copy docker-compose.yaml and .env.example from this repo into this folder
|
||
cp .env.example .env # then edit .env: set APP_PASSWORD and SECRET_KEY
|
||
docker compose up -d
|
||
```
|
||
|
||
Open <http://localhost:8080> and log in with `APP_PASSWORD`.
|
||
|
||
The database lives in `./data/whattodo.db`.
|
||
|
||
Useful commands:
|
||
|
||
```sh
|
||
docker compose logs -f # follow logs
|
||
docker compose down # stop
|
||
docker compose pull && docker compose up -d # update to the newest published version
|
||
```
|
||
|
||
To pin a version, set `WHATTODO_TAG=<version>` in `.env`, e.g. `v1.0` or a commit (default: `latest`).
|
||
|
||
## Develop (build from source)
|
||
|
||
`docker-compose.build.yaml` builds the image from this checkout. Add this line to your `.env` so plain `docker compose` uses it:
|
||
|
||
```sh
|
||
COMPOSE_FILE=docker-compose.build.yaml
|
||
```
|
||
|
||
```sh
|
||
docker compose up -d --build # build and start
|
||
docker compose run --rm test # run the test suite (in Docker)
|
||
```
|
||
|
||
### Publishing the image
|
||
|
||
`./publish.sh` builds the last commit (uncommitted changes are left out) and pushes it to
|
||
`git.goyban.com/goyban/whattodo` as `:latest` and `:<commit>`. `./publish.sh <tag|branch|commit>` publishes another version.
|
||
To release a numbered version, tag the commit first: `git tag v1.1 && ./publish.sh v1.1` also pushes the image as `:v1.1`.
|
||
Log in once first with `docker login git.goyban.com`, using a Gitea token with package write access as the password.
|
||
|
||
### Testing on your phone (same Wi-Fi)
|
||
|
||
Set `BIND_ADDR=0.0.0.0` in `.env`, run `docker compose up -d`, then open `http://<this-computer's-LAN-IP>:8080` on the phone.
|
||
Browsers only allow "Install app" / "Add to Home Screen" with offline support over **HTTPS** (or on `localhost`). Over plain LAN HTTP the app still works, but it won't install as a full app.
|
||
|
||
## Deploy to production
|
||
|
||
1. On the server, put `docker-compose.yaml` in a folder and create `.env` from `.env.example`. Use a **new strong** `APP_PASSWORD` and `SECRET_KEY`, and set `COOKIE_SECURE=true`.
|
||
2. `docker compose up -d`. The app listens on `127.0.0.1:${PORT}`.
|
||
3. Put it behind a reverse proxy with HTTPS. For example, with Caddy:
|
||
|
||
```
|
||
todo.example.com {
|
||
reverse_proxy 127.0.0.1:8080
|
||
}
|
||
```
|
||
|
||
If the proxy runs in another container instead of on the host, set `BIND_ADDR`/networking to match and set `FORWARDED_ALLOW_IPS` to the proxy's IP. The app then sees the real client IP, which login rate limiting uses.
|
||
|
||
### Moving your data from this PC to production
|
||
|
||
1. Local app → **Settings → Export data**. The export includes tasks, projects and categories. This downloads `whattodo-export-YYYYMMDD-HHMM.json`.
|
||
2. Production app → **Settings → Import**, pick the file:
|
||
- **Replace** deletes everything on that install and loads the file. Use this for the first move to prod.
|
||
- **Merge** adds the file's tasks to what is already there. Categories with the same name are reused.
|
||
|
||
The export file contains your tasks. Keep it out of the repo (`whattodo-export-*.json` is git-ignored).
|
||
|
||
### Backups
|
||
|
||
Export regularly from Settings, or copy `./data/whattodo.db` while the app is stopped.
|
||
|
||
## Security notes
|
||
|
||
- `.env` and `data/` are git-ignored. Never commit them; the repository is public.
|
||
- Changing `APP_PASSWORD` or `SECRET_KEY` logs out all sessions.
|
||
- Failed logins are rate limited: 10 failures per 15 minutes per IP.
|
||
- Write requests require an `X-WhatToDo` header (CSRF protection). The app also sends a strict Content-Security-Policy.
|
||
|
||
## Project layout
|
||
|
||
```
|
||
app/main.py API: auth, tasks, categories, export/import
|
||
app/db.py SQLite schema and setup
|
||
app/static/ frontend (index.html, app.js, style.css, PWA files, icons)
|
||
tests/ API tests (run with: docker compose -f docker-compose.build.yaml run --rm test)
|
||
tools/make_icons.py regenerates the PNG icons
|
||
```
|