Ingests Square invoices/payments, extracts event scheduling from order notes, and pushes approved entries to a CalDAV calendar after human review. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
82 lines
3.5 KiB
Markdown
82 lines
3.5 KiB
Markdown
# Order to Calendar
|
|
|
|
Pulls invoices and payments from Square, lets a human review and edit each one, and pushes approved
|
|
entries to a CalDAV calendar. Mobile-friendly (works as a PWA — "Add to Home Screen"), multi-user
|
|
login, ships as a single Docker image plus Postgres.
|
|
|
|
## How it works
|
|
|
|
1. A background job (or the "Sync now" button) polls the Square Invoices and Payments APIs and
|
|
turns new/updated invoices and payments into **candidate** calendar entries with status `PENDING`.
|
|
2. Signed-in users review the pending queue on any device, edit fields if needed (title, date/time,
|
|
description, location, customer info, amount), and either **Approve** or **Reject** each one.
|
|
3. Approving creates (or updates) an event on your CalDAV calendar. Nothing reaches the calendar
|
|
without a human approving it first.
|
|
|
|
## Requirements
|
|
|
|
- A Square account with API credentials ([developer.squareup.com](https://developer.squareup.com) → your app → Credentials).
|
|
- A CalDAV calendar (Nextcloud, Fastmail, iCloud, or self-hosted) — see [docs/caldav-setup.md](docs/caldav-setup.md).
|
|
- Docker + Docker Compose on the server you're deploying to.
|
|
|
|
## Deploying
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
# edit .env: JWT_SECRET, BOOTSTRAP_ADMIN_*, POSTGRES_*, SQUARE_*, CALDAV_*
|
|
docker compose up -d --build
|
|
```
|
|
|
|
The app listens on port 3000 (`PORT` in `.env`) — put your existing reverse proxy (nginx, Caddy,
|
|
Traefik, etc.) in front of it for TLS. On first boot it runs database migrations and creates the
|
|
initial admin account from `BOOTSTRAP_ADMIN_EMAIL` / `BOOTSTRAP_ADMIN_PASSWORD`. Log in as that
|
|
admin, then create accounts for everyone else from **Admin → New user** — there is no open
|
|
self-registration.
|
|
|
|
To go live, generate a real Square access token for `SQUARE_ENVIRONMENT=production` once you've
|
|
verified the sandbox flow end to end.
|
|
|
|
### Updating
|
|
|
|
```bash
|
|
git pull
|
|
docker compose up -d --build
|
|
```
|
|
|
|
Migrations run automatically on container start; existing data in the `db-data` volume is preserved.
|
|
|
|
## Local development
|
|
|
|
```bash
|
|
npm install
|
|
npm run prisma:generate
|
|
# start a local Postgres however you like, then create the schema:
|
|
DATABASE_URL=postgresql://... npx prisma migrate dev --schema server/prisma/schema.prisma
|
|
|
|
npm run dev:server # backend on :3000
|
|
npm run dev:web # frontend on :5173, proxies /api to :3000
|
|
```
|
|
|
|
## Configuration reference
|
|
|
|
See [.env.example](.env.example) for every environment variable, with comments.
|
|
|
|
Key ones worth knowing:
|
|
|
|
| Variable | Purpose |
|
|
|---|---|
|
|
| `SQUARE_LOCATION_ID` | Leave blank to sync every location on the account, or set a comma-separated list to scope it. |
|
|
| `SYNC_INTERVAL_CRON` | How often the background sync runs (default: every 15 minutes). |
|
|
| `PAYMENT_DEFAULT_DURATION_MINUTES` | Payments have no inherent duration in Square — this sets the calendar event length. |
|
|
| `CALDAV_CALENDAR_URL` | Must be the specific calendar collection URL, not just the server root, and must end with `/`. |
|
|
|
|
## Architecture notes
|
|
|
|
- **Idempotent sync**: re-running sync never duplicates a candidate (unique on Square ID + source
|
|
type + environment). Once a candidate is approved or rejected it's frozen — a later Square-side
|
|
edit won't silently overwrite a human decision.
|
|
- **Idempotent CalDAV writes**: each approved candidate keeps its CalDAV event UID/URL/etag, so
|
|
editing an already-approved item and re-approving updates the existing event instead of creating
|
|
a duplicate.
|
|
- **Auth**: httpOnly JWT cookie sessions (no separate session store), passwords hashed with argon2id.
|