# mediator

A tiny self-hosted web app for scheduling get-togethers with your friend group.
You circle dates on a calendar, optionally add a time per day, and share one
link. Friends open the same calendar, circle the days that work for them, and
the best date floats to the top. No accounts, no sign-up — the link is the key.

Built with **Go standard library only**: no external dependencies, one static
binary, polls stored in a plain JSON file.

## Run it

Requires Go 1.22+.

```sh
go build -o mediator .
./mediator
```

Open http://localhost:8080 — that's the page for creating a poll.
þ
Flags:

| Flag    | Default | Meaning                          |
|---------|---------|----------------------------------|
| `-addr` | `:8080` | Listen address                   |
| `-data` | `data`  | Directory for `polls.json`       |

To let friends reach it, run it on a machine they can access (a small VPS, a
Raspberry Pi behind a port forward, etc.) and put it behind HTTPS — e.g. a
Caddy or nginx reverse proxy. The share links use whatever host your friends
open the page on, so no configuration is needed.

## How it works

1. **Create a poll** — give it a title, click every date you want to offer on
   the calendar (only today and future dates are clickable), and optionally
   set a time for each date in the list below the calendar.
2. **Share** — after creating you get two links:
   - **Share link** (`/p/<id>`) — send this to the group.
   - **Admin link** (`/p/<id>?admin=<token>`) — keep this. It's the only way
     to close or delete the poll.
3. **Friends answer** — they open the share link, see the same calendar with
   the offered days highlighted, circle the ones that work, enter their name,
   and send. Answering again with the same name (case-insensitive) replaces
   the earlier answer.
4. **Close or delete** — polls can never be edited after creation. From the
   admin link you can *close* the poll (answers freeze, results stay visible)
   or *delete* it entirely.

The poll page refreshes results every 30 seconds while open.

## API (if you're curious)

| Method   | Path                   | What it does                          |
|----------|------------------------|---------------------------------------|
| `POST`   | `/api/polls`           | Create a poll, returns id + admin token |
| `GET`    | `/api/polls/{id}`      | Poll data (admin token never included) |
| `POST`   | `/api/polls/{id}/votes`| Submit / replace an answer            |
| `POST`   | `/api/polls/{id}/close`| Close (admin token required)          |
| `DELETE` | `/api/polls/{id}`      | Delete (admin token required)         |

Admin token goes in the `X-Admin-Token` header or `?admin=` query parameter.

## Storage

Everything lives in `<data-dir>/polls.json`, written atomically on every
change. Back it up by copying that one file. Deleting a poll removes it from
the file immediately.

## Project layout

```
main.go              server, storage, API handlers (stdlib only)
static/index.html    create-a-poll page
static/poll.html     voting + results page
static/calendar.js   the shared calendar widget both pages use
static/create.js     create-page logic
static/poll.js       poll-page logic
static/style.css     styles
```

Static files are embedded into the binary with `go:embed`, so the compiled
`mediator` binary is all you need to deploy.
