Add self-hosted kids calendar app
Express + SQLite (node:sqlite, no native build step) family calendar: parent accounts with household invites, per-child calendars with save/duplicate/print, a token-gated read-only kiosk view for tablets, and polling to keep parent and kiosk views in sync. Defaults to port 3007.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# Kids Calendar
|
||||
|
||||
A self-hosted weekly calendar for your family. Parents log in and edit; each
|
||||
child gets a read-only "kiosk" link for a tablet, where they can only tick
|
||||
off tasks that are already there.
|
||||
|
||||
- Parents: sign up, invite your spouse, create a calendar per child, edit
|
||||
freely, print, duplicate a calendar for the next week.
|
||||
- Kids: open their kiosk link on a tablet (add it to the home screen). They
|
||||
can see the board and check boxes — nothing else.
|
||||
- Everything a child checks off shows up on the parents' devices within a
|
||||
few seconds, no reload needed.
|
||||
|
||||
## Running it
|
||||
|
||||
Requires Node.js 22.5+ (for the built-in `node:sqlite` module — nothing else
|
||||
to compile, no native build tools needed).
|
||||
|
||||
```bash
|
||||
npm install
|
||||
export SESSION_SECRET=$(node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))")
|
||||
node server.js
|
||||
```
|
||||
|
||||
Plain `node` doesn't read `.env` files — export the variables from
|
||||
`.env.example` yourself (as above), or use a process manager (pm2, systemd)
|
||||
that does it for you. Docker (below) reads `.env` automatically. Then open
|
||||
http://localhost:3007/signup.html.
|
||||
|
||||
### Docker (recommended for a NAS / home server)
|
||||
|
||||
```bash
|
||||
cp .env.example .env # set SESSION_SECRET
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
Data lives in a named volume (`kids-calendar-data`), so it survives
|
||||
container rebuilds. To back it up, back up that volume (or bind-mount
|
||||
`./data:/app/data` in `docker-compose.yml` instead and back up that folder).
|
||||
|
||||
### Environment variables
|
||||
|
||||
| Variable | Default | Notes |
|
||||
|---|---|---|
|
||||
| `SESSION_SECRET` | *(insecure dev default)* | Required. A long random string — see `.env.example` for how to generate one. |
|
||||
| `PORT` | `3007` | |
|
||||
| `DATA_DIR` | `./data` | Where the SQLite file lives. |
|
||||
| `COOKIE_SECURE` | `false` | Set `true` once this is behind HTTPS (e.g. a reverse proxy with a real certificate), so cookies are marked secure. Leave `false` for plain-HTTP LAN access, or login cookies won't be sent. |
|
||||
| `DISABLE_PUBLIC_SIGNUP` | `false` | Set `true` once your household(s) exist, to stop the `/signup.html` page from creating new ones. Existing invite links still work. |
|
||||
|
||||
## How access works
|
||||
|
||||
- **Parents** have real accounts (email + password) tied to a household.
|
||||
The first person to sign up creates the household; from the dashboard
|
||||
they can generate an invite link (valid 7 days, one-time use) for a
|
||||
spouse to join the same household with their own login.
|
||||
- **Kids** never get an account. Each child has an unguessable link
|
||||
(`/k/<token>`) that shows only their *active* calendar (set from the
|
||||
dashboard) and lets them toggle checkboxes — the server has no route at
|
||||
all for a kiosk link to edit text, add/delete tasks, or see other
|
||||
children's data. If a tablet is lost, regenerate that child's link from
|
||||
the dashboard to invalidate the old one.
|
||||
|
||||
## Exposing this beyond your home network
|
||||
|
||||
This is built for LAN use by default (plain HTTP, cookies not marked
|
||||
secure). If you want access from outside your home:
|
||||
|
||||
- Put it behind a reverse proxy (e.g. Caddy, nginx, Traefik) that terminates
|
||||
HTTPS, then set `COOKIE_SECURE=true`.
|
||||
- Set `SESSION_SECRET` to a real random value (never the dev default).
|
||||
- Consider setting `DISABLE_PUBLIC_SIGNUP=true` once your family's
|
||||
household(s) are created.
|
||||
Reference in New Issue
Block a user