# Manager Dashboard

A personal management dashboard for tracking direct reports, projects, commitments, and generating status updates. Single-user, password-protected, runs on your own server.

## What's in this directory

```
manager-dashboard-server/
├── server.js                       # Express app
├── db.js                           # SQLite schema + helpers
├── import.js                       # CLI tool for one-off data imports
├── package.json
├── public/
│   └── index.html                  # Frontend (all CSS/JS inline)
├── .env.example                    # Configuration template
├── .gitignore
└── manager-dashboard.service       # systemd unit file
```

The SQLite database lives in `data/dashboard.db` by default — that's the only thing you need to back up.

## Deploying on a Linux VPS

These instructions assume Ubuntu/Debian. Adapt as needed.

### 1. Install Node 18+ on the server

```bash
# Node 20 LTS from NodeSource
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
sudo apt-get install -y nodejs build-essential
node --version
```

`build-essential` is needed because `better-sqlite3` compiles a native module.

### 2. Create a dedicated user and directory

```bash
sudo useradd --system --create-home --shell /usr/sbin/nologin manager-dashboard
sudo mkdir -p /opt/manager-dashboard
sudo chown manager-dashboard:manager-dashboard /opt/manager-dashboard
```

### 3. Copy the files up

From your local machine:

```bash
scp -r manager-dashboard-server/* you@your-server:/tmp/dashboard/
ssh you@your-server "sudo mv /tmp/dashboard/* /opt/manager-dashboard/ && sudo chown -R manager-dashboard:manager-dashboard /opt/manager-dashboard"
```

### 4. Configure

On the server:

```bash
cd /opt/manager-dashboard
sudo -u manager-dashboard cp .env.example .env
# Generate a session secret:
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"
# Edit .env and set PASSWORD and SESSION_SECRET
sudo -u manager-dashboard nano .env
```

### 5. Install dependencies

```bash
cd /opt/manager-dashboard
sudo -u manager-dashboard npm install --omit=dev
```

### 6. Test it runs

```bash
sudo -u manager-dashboard node server.js
# Should print: "Manager Dashboard listening on http://localhost:3000"
# Ctrl+C to stop
```

### 7. Install as a systemd service

```bash
sudo cp /opt/manager-dashboard/manager-dashboard.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now manager-dashboard
sudo systemctl status manager-dashboard
```

Logs: `sudo journalctl -u manager-dashboard -f`

### 8. Put it behind a reverse proxy (nginx example)

You don't want to expose port 3000 to the internet directly, and you definitely want HTTPS. Nginx config:

```nginx
server {
    server_name dashboard.example.com;
    listen 443 ssl http2;
    # SSL certs (use certbot to provision):
    ssl_certificate /etc/letsencrypt/live/dashboard.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dashboard.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name dashboard.example.com;
    return 301 https://$host$request_uri;
}
```

Make sure `TRUST_PROXY=1` and `NODE_ENV=production` are set in `.env` — this enables the `Secure` flag on the session cookie.

Caddy is an even simpler alternative: a single `Caddyfile`:

```
dashboard.example.com {
    reverse_proxy 127.0.0.1:3000
}
```

Caddy provisions TLS automatically.

## Importing from the standalone version

If you used the original single-file `manager-dashboard.html` version:

1. Open it, go to **Settings → Export data**, save the JSON file.
2. Copy that file to the server.
3. Run:

```bash
cd /opt/manager-dashboard
sudo systemctl stop manager-dashboard
sudo -u manager-dashboard node import.js /path/to/your-export.json
sudo systemctl start manager-dashboard
```

Or, after the server is running, sign in and use **Settings → Import** in the UI.

## Backups

SQLite makes this easy. The whole database is one file:

```bash
# Quick copy (safe with WAL mode):
sudo -u manager-dashboard sqlite3 /opt/manager-dashboard/data/dashboard.db ".backup /opt/manager-dashboard/data/backup-$(date +%F).db"
```

Or schedule it with cron:

```cron
# /etc/cron.d/manager-dashboard-backup
0 3 * * * manager-dashboard sqlite3 /opt/manager-dashboard/data/dashboard.db ".backup /opt/manager-dashboard/data/backup-$(date +\%F).db" && find /opt/manager-dashboard/data/ -name 'backup-*.db' -mtime +14 -delete
```

For off-server backups, rsync the file to another host or upload to S3 / B2 / similar.

## Updating

```bash
# On your local machine, edit the files, then:
scp -r manager-dashboard-server/* you@your-server:/tmp/dashboard/
ssh you@your-server "sudo cp -r /tmp/dashboard/* /opt/manager-dashboard/ && sudo chown -R manager-dashboard:manager-dashboard /opt/manager-dashboard && sudo systemctl restart manager-dashboard"
```

If `package.json` changed, run `npm install --omit=dev` again before restarting.

## Configuration reference

Set via `.env`:

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `PASSWORD` | yes | — | The password to sign in. |
| `SESSION_SECRET` | yes | — | Random 32+ character string used to sign session cookies. |
| `PORT` | no | 3000 | Port the server listens on. |
| `NODE_ENV` | no | development | Set to `production` to enable secure cookies. |
| `TRUST_PROXY` | no | 0 | Set to `1` if behind a reverse proxy. |
| `DB_PATH` | no | `./data/dashboard.db` | Path to the SQLite file. |

## Security notes

The app stores a single password as plaintext in `.env`. The threat model is: if someone has shell access to your server, they already win. Make sure `.env` is `chmod 600` and owned by `manager-dashboard`.

The session cookie is signed but not encrypted (cookie-session, not express-session). It contains only `{authed: true, loginAt: timestamp}` — no sensitive data — so this is fine.

There is no rate limiting on the login endpoint. If you expose this to the public internet, put it behind a reverse proxy with rate limiting (nginx `limit_req`, Caddy `rate_limit`, or just don't expose it — use Tailscale).

## API surface

If you want to script against it (or build other clients):

```
POST   /api/auth/login              { password }
POST   /api/auth/logout
GET    /api/auth/check
GET    /api/state                   → all data
POST   /api/people                  { name, role?, currentWork? }
PATCH  /api/people/:id              partial update
DELETE /api/people/:id
POST   /api/projects                { name, ... }
PATCH  /api/projects/:id
DELETE /api/projects/:id
POST   /api/tasks                   { text, kind?, priority?, due? }
PATCH  /api/tasks/:id
DELETE /api/tasks/:id
POST   /api/activity                { text, kind? }
GET    /api/export                  → full JSON download
POST   /api/import                  → replace all data
```

All endpoints except `/api/auth/*` require an authenticated session.

## Troubleshooting

**`better-sqlite3` build fails on install.** You're missing `build-essential` (Debian/Ubuntu) or Xcode CLI tools (macOS). Install those and run `npm install` again.

**Login works, then immediately fails on next request.** Your reverse proxy is terminating TLS but you have `TRUST_PROXY=0` or `NODE_ENV=production` without `TRUST_PROXY=1`. The `Secure` cookie is being set but the request comes in over HTTP from the proxy's perspective. Set `TRUST_PROXY=1`.

**Sessions don't persist across restarts.** Expected — cookie-session stores session data in the cookie itself (signed by `SESSION_SECRET`). If you change `SESSION_SECRET`, all existing sessions are invalidated. To keep them valid, don't rotate the secret.
