# STApp Store — Guia para LLMs

Este documento ensina um modelo de linguagem (você, LLM) a **enviar um app** ou uma **nova versão** para o **STApp Store** do Edson.

> Você é uma LLM com acesso a ferramentas (shell, leitura de arquivos, execução de comandos, etc). Use-as para empacotar o app e fazer a requisição HTTP. Não invente o endpoint — copie daqui.

---

## 0. Variáveis que você precisa pedir/pegar do usuário

Antes de qualquer coisa, garanta que você tem:

| Variável            | Exemplo                                              | Obrigatório |
|---------------------|------------------------------------------------------|-------------|
| `STAPP_BASE_URL`    | `https://apps.exemplo.com`                           | sim         |
| `STAPP_API_KEY`     | `gere-um-token-aleatorio-longo-aqui`                 | sim         |
| `STAPP_USER`        | `admin`                                              | sim         |
| `STAPP_PASS`        | `troque-esta-senha`                                  | sim         |

> Dica: peça essas variáveis em **uma única mensagem** e não prossiga sem elas.

---

## 1. Plataformas aceitas

Use **exatamente** um destes valores no campo `platform`:

| Valor         | Significado                |
|---------------|----------------------------|
| `windows`     | Windows (x86/x64)          |
| `linux-x86`   | Linux x86_64               |
| `linux-arm`   | Linux ARM64                |
| `android`     | Android                    |
| `web`         | Web (URL/PWA/HTML estático)|

Para consultar a lista atualizada em tempo de execução (caso o usuário adicione novas):

```bash
curl -s "$STAPP_BASE_URL/api.php?action=platforms" \
  -u "$STAPP_USER:$STAPP_PASS"
```

---

## 2. Enviar um app NOVO

**Endpoint:** `POST {STAPP_BASE_URL}/api.php?action=create`
**Content-Type:** `multipart/form-data`

### Campos

| Campo            | Tipo     | Obrigatório | Notas |
|------------------|----------|-------------|-------|
| `name`           | string   | sim         | Nome exibido na loja. |
| `slug`           | string   | não         | Gerado do `name` se vazio. Use `a-z`, `0-9` e `-`. Único. |
| `description`    | string   | não         | Texto livre, suporta quebras de linha. |
| `category`       | string   | não         | Ex.: `Produtividade`, `Jogos`, `Utilitários`. Aparece no filtro. |
| `developer`      | string   | não         | Se vazio, usa o nome do dono configurado. |
| `platform`       | string   | sim         | Veja a tabela da seção 1. |
| `version`        | string   | sim         | Semver sugerido: `1.0.0`, `0.2.1`. |
| `release_notes`  | string   | não         | "O que mudou nesta versão". |
| `file`           | arquivo  | sim         | Binário/zip do app. **Qualquer extensão.** Limite padrão: 500 MB. |
| `icon`           | arquivo  | não         | `png`, `jpg`, `jpeg`, `webp`, `svg`, `ico`, `icns`. |
| `screenshots[]`  | arquivo× | não         | Até 8 imagens. `png`, `jpg`, `jpeg`, `webp`. Use **múltiplos** campos com o mesmo nome `screenshots[]`. |

### Exemplo (curl) — com screenshots

```bash
curl -X POST "$STAPP_BASE_URL/api.php?action=create" \
  -u "$STAPP_USER:$STAPP_PASS" \
  -F "name=Meu App Incrível" \
  -F "slug=meu-app-incrivel" \
  -F "description=Um app que faz coisas incríveis." \
  -F "category=Produtividade" \
  -F "platform=linux-x86" \
  -F "version=1.0.0" \
  -F "release_notes=Primeira versão pública." \
  -F "file=@./build/meu-app-incrivel-1.0.0.tar.gz" \
  -F "icon=@./assets/icon.png" \
  -F "screenshots[]=@./assets/shot1.png" \
  -F "screenshots[]=@./assets/shot2.png" \
  -F "screenshots[]=@./assets/shot3.png"
```

> Alternativa: use o header `X-API-Key: $STAPP_API_KEY` no lugar de `-u`.

### Resposta de sucesso (201)

```json
{
  "ok": true,
  "app": {
    "slug": "meu-app-incrivel",
    "name": "Meu App Incrível",
    "category": "Produtividade",
    "versions": [
      {
        "id": 7,
        "version": "1.0.0",
        "platform": "linux-x86",
        "file_size": 12345678,
        "sha256": "9b8f...c0d",
        "signature": "1f3a...e8 (HMAC se secret_key configurada)",
        "download_url": "https://apps.exemplo.com/api.php?action=download&id=7"
      }
    ],
    "screenshots": [
      { "id": 1, "url": "https://apps.exemplo.com/storage/screenshots/meu-app-incrivel/...", "caption": "" }
    ],
    "total_downloads": 0
  }
}
```

> Sempre que a resposta trouxer `sha256` e `signature`, exiba-os ao usuário — ele pode usar para verificar a integridade do arquivo depois de baixá-lo.

### Erros comuns

| HTTP | Causa provável                                            |
|------|-----------------------------------------------------------|
| 400  | Campo obrigatório faltando, plataforma inválida ou screenshot com extensão não permitida |
| 401  | Credenciais erradas                                       |
| 409  | Já existe app com esse `slug` (use `action=update`)       |
| 413  | Arquivo excedeu `max_upload_bytes` (padrão 500 MB)        |

---

## 3. Enviar uma NOVA VERSÃO de um app existente

**Endpoint:** `POST {STAPP_BASE_URL}/api.php?action=update&slug={slug-do-app}`

Mesma forma de `create`, mas apenas `platform`, `version`, `file` (e opcionais `release_notes`, `icon`, `screenshots[]`) são necessários. Os metadados (`name`, `description`, `category`, `developer`) **não** são alterados pelo `update` — eles ficam como estavam no `create`.

```bash
curl -X POST "$STAPP_BASE_URL/api.php?action=update&slug=meu-app-incrivel" \
  -u "$STAPP_USER:$STAPP_PASS" \
  -F "platform=windows" \
  -F "version=1.1.0" \
  -F "release_notes=Corrigido bug X; adicionado recurso Y." \
  -F "file=@./build/meu-app-incrivel-1.1.0-setup.exe" \
  -F "screenshots[]=@./assets/windows-shot1.png"
```

### Regras

- A combinação `(slug, version, platform)` é **única** — não dá pra subir a mesma versão pra mesma plataforma duas vezes. Suba a versão (`1.1.0`, `1.1.1`, ...) ou use outra plataforma.
- O mesmo app pode ter `1.0.0` para `linux-x86` **e** `1.0.0` para `windows` — são registros de versão diferentes.
- Enviar `icon` no update **substitui** o ícone do app para todas as plataformas.
- Screenshots enviados no update **são adicionados** aos já existentes; não substituem.

---

## 4. Verificação de integridade (sha256 + HMAC)

A cada upload, a API calcula:

- `sha256` — hash do arquivo (sempre).
- `signature` — HMAC-SHA256 do `sha256` usando a `secret_key` do servidor (somente se `secret_key` estiver configurada em `config.php`).

Para verificar manualmente após o download:

```bash
# 1. Sha256
sha256sum meu-app-incrivel-1.0.0.tar.gz

# 2. HMAC (se houver signature):
#    echo -n "<sha256>" | openssl dgst -sha256 -hmac "<secret_key>"
```

O `secret_key` vive no `config.php` do servidor. Quem hospeda decide se compartilha essa chave (e aí qualquer um pode verificar) ou se mantém privada (e aí a assinatura serve só para o próprio dono auditar).

---

## 5. Outras ações (úteis para a LLM verificar antes de enviar)

```bash
# Listar todos os apps
curl -s "$STAPP_BASE_URL/api.php?action=list" -u "$STAPP_USER:$STAPP_PASS"

# Listar apps filtrando por plataforma
curl -s "$STAPP_BASE_URL/api.php?action=list&platform=android" -u "$STAPP_USER:$STAPP_PASS"

# Detalhe de um app (com todas as versões, screenshots e links de download)
curl -s "$STAPP_BASE_URL/api.php?action=get&slug=meu-app-incrivel" -u "$STAPP_USER:$STAPP_PASS"

# Plataformas disponíveis
curl -s "$STAPP_BASE_URL/api.php?action=platforms" -u "$STAPP_USER:$STAPP_PASS"
```

> `list` e `get` **não exigem autenticação**; só `create`, `update` e `delete` exigem.

---

## 6. Workflow recomendado para a LLM

1. **Pedir variáveis** (`STAPP_BASE_URL`, `STAPP_API_KEY` *ou* `STAPP_USER`/`STAPP_PASS`).
2. **Verificar se o app já existe** com `?action=get&slug=<slug-proposto>`. Se existir, é um `update`; se não, é `create`.
3. **Empacotar o app** num arquivo (zip, tar.gz, exe, apk, etc.) e gerar o ícone + screenshots se houver.
4. **Validar o `slug`** (apenas `[a-z0-9-]`, começando com `[a-z0-9]`).
5. **Fazer o POST** com `curl` (ou `requests` em Python, ou `fetch` em Node).
6. **Reportar o resultado** ao usuário com o link de download e o `sha256`.

### Exemplo em Python (requests) — com screenshots

```python
import os, requests
from requests.auth import HTTPBasicAuth

BASE = os.environ["STAPP_BASE_URL"]
auth = HTTPBasicAuth(os.environ["STAPP_USER"], os.environ["STAPP_PASS"])

data = {
    "name": "Meu App",
    "slug": "meu-app",
    "description": "Faz coisas úteis.",
    "category": "Produtividade",
    "platform": "linux-x86",
    "version": "1.0.0",
    "release_notes": "Versão inicial.",
}

files = [
    ("file",  ("app-1.0.0.zip", open("build/app-1.0.0.zip", "rb"), "application/zip")),
    ("icon",  ("icon.png",      open("assets/icon.png", "rb"),  "image/png")),
]
for i, path in enumerate(["assets/shot1.png", "assets/shot2.png"]):
    files.append(("screenshots[]", (f"shot{i}.png", open(path, "rb"), "image/png")))

r = requests.post(f"{BASE}/api.php?action=create", auth=auth, data=data, files=files, timeout=600)
r.raise_for_status()
print(r.json())
```

### Exemplo em Node 20+ (fetch nativo)

```js
import { readFile } from 'node:fs/promises';

const fd = new FormData();
fd.set('name', 'Meu App');
fd.set('platform', 'windows');
fd.set('version', '1.0.0');
fd.set('file', new Blob([await readFile('build/app-setup.exe')]), 'app-setup.exe');
fd.set('icon', new Blob([await readFile('assets/icon.png')]), 'icon.png');
fd.set('screenshots[]', new Blob([await readFile('assets/shot1.png')]), 'shot1.png');

const r = await fetch(`${process.env.STAPP_BASE_URL}/api.php?action=create`, {
  method: 'POST',
  headers: {
    'Authorization': 'Basic ' + Buffer.from(`${process.env.STAPP_USER}:${process.env.STAPP_PASS}`).toString('base64'),
  },
  body: fd,
});
console.log(await r.json());
```

---

## 7. Não fazer

- ❌ Não enviar HTML, JSON ou string solta no lugar do `file`. Tem que ser o **binário real**.
- ❌ Não inventar plataformas fora da lista. Se precisar de uma nova, peça ao usuário para adicioná-la em `config.php`.
- ❌ Não tentar “criar” um app que já existe — use `update`.
- ❌ Não enviar screenshots em extensões não suportadas (apenas `png/jpg/jpeg/webp`).
- ❌ Não subir arquivos que violem leis locais. Você é responsável pelo conteúdo.

---

## 8. Quando algo der errado

- Leia o JSON de resposta — ele sempre traz `error` e às vezes `detail`.
- HTTP 401 → credenciais erradas (verifique `STAPP_USER`/`STAPP_PASS` ou `STAPP_API_KEY`).
- HTTP 413 → app grande demais; comprima, fatie em partes, ou peça para aumentar `max_upload_bytes` em `config.php`.
- HTTP 5xx → bug no servidor; peça para o usuário olhar `error_log` do PHP.

