# 08 — Armazenamento de Arquivos (Google Cloud Storage)

## Visão Geral

O upload e armazenamento de anexos usa o **Google Cloud Storage (GCS)** com o bucket `webgruppo-attachments`. O servidor PHP não armazena arquivos permanentemente — apenas usa o disco local como área temporária durante o upload.

---

## Configuração

```php
// gcs_config.php
GCS_BUCKET_NAME = 'webgruppo-attachments'
GCS_PROJECT_ID  = 'webgruppo-app'
GCS_KEY_FILE    = /webapp/api/credentials/gcs-key.json
```

A autenticação usa um **Service Account JSON Key** armazenado em `webapp/api/credentials/gcs-key.json`.

**Pacote PHP:** `google/cloud-storage` instalado via Composer.

```bash
# Instalar dependências PHP
cd webapp/
php composer.phar install
```

---

## Convenção de Paths no GCS

| Tipo de Anexo | Path no GCS |
|---------------|-------------|
| Anexo de Tarefa | `{cliente}/tarefas/{tarefa_id}/{timestamp}_{filename}` |
| Anexo de Projeto | `{cliente}/projetos/{demanda_id}/{timestamp}_{filename}` |
| Anexo de Demanda Rápida | `{cliente}/demandas/{tarefarapida_id}/{timestamp}_{filename}` |
| Comprovante de Custo | `{cliente}/custos/{demanda_custo_id}/{timestamp}_{filename}` |

**Formato do timestamp:** `YYYY-MM-DD_HHhMMmSSs` (ex: `2026-03-14_13h30m00s`)

**Sanitização do filename:**
```php
// sanitize_filename()
1. iconv UTF-8 → ASCII//TRANSLIT//IGNORE (remove acentos)
2. Regex: apenas [a-zA-Z0-9._-] (demais → underscore)
3. Múltiplos underscores → underscore simples
```

---

## Fluxo de Upload

```
App (DocumentPicker)
    │
    ├── Seleciona arquivo localmente
    │
    └── POST multipart/form-data → upload_attachment.php
              (ou upload_project_attachment.php / upload_cost_attachment.php / demand_actions.php)
              
PHP
    ├── Recebe o arquivo em $_FILES
    ├── Gera novo_nome = timestamp + sanitized_original_name
    ├── Calcula gcs_path = {cliente}/tarefas/{id}/{novo_nome}
    ├── uploadToGCS($_FILES['tmp_name'], gcs_path, mime_type)
    │     └── StorageClient → bucket->upload(fopen(tmp), ['name' => gcs_path])
    ├── INSERT tarefa_anexo (path, nome_arquivo, gcs_path, descricao, type, size, ...)
    └── Response: { success: true, gcs_path, nome_arquivo }
```

---

## Fluxo de Download

```
App pressiona anexo
    │
    └── GET download_attachment.php?tarefa_anexo_id=X&user_id=Y&cliente=Z
    
PHP
    ├── SELECT gcs_path FROM tarefa_anexo WHERE tarefa_anexo_id = X
    ├── getSignedUrl(gcs_path, 15 minutos)
    │     └── bucket->object(gcs_path)->signedUrl(+15min, ['version' => 'v4'])
    └── Response: { signed_url: "https://storage.googleapis.com/..." }

App
    └── Abre URL assinada no navegador ou compartilha via expo-sharing
```

---

## Deleção de Arquivos

```php
// Ao deletar anexo do BD:
deleteFromGCS($gcs_path);
// → bucket->object($gcs_path)->delete() se exists()
// → seguido de DELETE FROM tarefa_anexo WHERE tarefa_anexo_id = X
```

---

## Tabelas de Anexos por Entidade

| Entidade | Tabela | Coluna de referência |
|----------|--------|----------------------|
| Tarefa | `tarefa_anexo` | `tarefa_id` |
| Projeto | `demanda_anexo` | `demanda_id` |
| Demanda Rápida | `tarefarapida_anexo` | `tarefarapida_id` |
| Custo | `demanda_custo_anexo` | `demanda_custo_id` |

Todas as tabelas de anexo têm estrutura similar:
```
*_anexo_id  PK
*_id         FK para a entidade
gcs_path    VARCHAR - caminho no GCS
nome_arquivo VARCHAR - nome com timestamp
path        VARCHAR - caminho legado (mantido para compatibilidade)
descricao   VARCHAR - descrição inserida pelo usuário
type        VARCHAR - MIME type
size        INT - bytes
usuario_id  INT FK - quem fez upload
data_cadastro DATE
```

---

## Endpoints de Upload por Entidade

| Endpoint | Campo de arquivo | Tabela alvo |
|----------|-----------------|-------------|
| `upload_attachment.php` | `attachment_file` | `tarefa_anexo` |
| `upload_project_attachment.php` | `attachment_file` | `demanda_anexo` |
| `upload_cost_attachment.php` | `attachment_file` | `demanda_custo_anexo` |
| `demand_actions.php` (action=add_attachment) | `anexo_file` | `tarefarapida_anexo` |

---

## Considerações de Segurança

- URLs assinadas expiram em **15 minutos** (v4 signature)
- O bucket NÃO é público — acesso apenas via URL assinada
- A Service Account Key (`gcs-key.json`) **não está no repositório Git** (via `.gitignore`)
- Tipos de arquivo aceitos: qualquer MIME (sem whitelist no servidor atualmente)
