# 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.