# Sudoku Studio

Player e editor de Sudoku self-hosted. Backend Node.js + SQLite, frontend HTML puro.

## Requisitos

- Node.js 18+
- npm

## Instalação no VPS

```bash
# 1. Clone ou copie os arquivos para o servidor
cd /var/www/sudoku-studio

# 2. Instale as dependências
npm install

# 3. Configure o ambiente
cp .env.example .env
nano .env
# Defina ADMIN_PASSWORD e JWT_SECRET

# 4. Inicie o servidor
npm start
```

## Variáveis de ambiente (.env)

| Variável | Descrição |
|---|---|
| `ADMIN_PASSWORD` | Senha do editor. Use algo forte, ex: `openssl rand -base64 32` |
| `JWT_SECRET` | Segredo JWT. Use algo longo e aleatório, ex: `openssl rand -base64 64` |
| `PORT` | Porta do servidor (padrão: 3000) |
| `NODE_ENV` | `production` ou `development` |

## Produção com nginx (recomendado)

```nginx
server {
    listen 80;
    server_name brchad.com;

    location / {
        proxy_pass http://localhost: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;
    }
}
```

Configure SSL com certbot:
```bash
certbot --nginx -d brchad.com
```

## PM2 (processo em background)

```bash
npm install -g pm2
pm2 start server/index.js --name sudoku-studio
pm2 save
pm2 startup
```

## Estrutura

```
sudoku-studio/
├── server/
│   ├── index.js        # Entry point Express
│   ├── db.js           # SQLite + prepared statements
│   ├── auth.js         # JWT + bcrypt
│   ├── routes/
│   │   ├── games.js    # GET /api/games (público)
│   │   └── admin.js    # /api/admin/* + /api/auth/* (privado)
│   └── games.db        # Banco de dados (criado automaticamente)
├── public/
│   ├── index.html      # Galeria pública
│   ├── player.html     # Player completo
│   ├── embed.html      # Player para iframe
│   ├── admin.html      # Editor (protegido)
│   ├── login.html      # Login
│   └── src/
│       ├── sudoku.js   # Engine: gerador, solver, validador
│       ├── storage.js  # localStorage (progresso do jogador)
│       └── api.js      # Cliente HTTP
├── .env                # Segredos (não commitar)
├── .env.example        # Template
└── package.json
```

## API

### Pública (CORS aberto)
- `GET /api/games` — lista jogos não-listados
- `GET /api/games/:id` — puzzle completo (grid + solution)

### Privada (requer cookie de sessão)
- `POST /api/auth/login` — `{ password }` → seta cookie httpOnly
- `POST /api/auth/logout` — limpa cookie
- `GET /api/auth/me` — verifica sessão
- `GET /api/admin/games` — lista todos (incluindo unlisted)
- `GET /api/admin/games/:id` — jogo completo
- `POST /api/admin/games` — criar jogo
- `PUT /api/admin/games/:id` — atualizar jogo
- `PATCH /api/admin/games/:id/unlisted` — `{ unlisted: bool }`
- `DELETE /api/admin/games/:id` — excluir

## Embed via iframe

```html
<!-- Com ID do jogo -->
<iframe src="https://brchad.com/embed.html?id=meu-jogo" width="100%" height="560" frameborder="0"></iframe>

<!-- Com seed (sem servidor) -->
<iframe src="https://brchad.com/embed.html?seed=530070000..." width="100%" height="560" frameborder="0"></iframe>

<!-- Parâmetros opcionais -->
<!-- hidenav=1  — oculta header -->
<!-- hidetimer=1 — oculta timer -->
<!-- theme=dark  — tema escuro -->
```

### Eventos postMessage do player
```javascript
window.addEventListener('message', (e) => {
  switch (e.data.event) {
    case 'puzzle_loaded':   // { id, name, difficulty }
    case 'puzzle_started':  // { id }
    case 'puzzle_progress': // { id, filled, total }
    case 'puzzle_complete': // { id, time_seconds, errors, hints, stars }
    case 'puzzle_failed':   // { id, time_seconds, errors }
    case 'resize':          // { height }
  }
});
```
