# Arquivo (Drive) — Arquitetura

> **Escopo:** v5 (`v5/`). A v6 é tratada em `08-GAPS-E-DIVIDAS.md`.

---

## 1. Visão geral

O módulo é transversal: **não há tela própria no portal** e **não há CRUD
clássico no CMS**. O que existe é:

1. Uma **API JSON** (`ArchiveController`) que serve três pés da galeria do
   CMS, o seletor de mídias e a reordenação.
2. Um **endpoint de categorias** (`ArchiveCategoryController`), exclusivo
   da v5.
3. Uma **tabela** (`archives`) que todo o resto do sistema consome
   diretamente via Model.

```
┌───────────────────────────────────────────────────────────────────┐
│                        CAMADA DE CONSUMO                          │
│  ConteúdoController │ StaticPageController │ Works\WorkController │
│  BiddingController  │ ContractController    │ AnswerController    │
│  EmendaParlamentar │ OmbudsmanAnswer      │ DiarioController     │
│  RequerimentoDiarias│ PagesSecretaryController│ CalendarEvent      │
└───────────────┬───────────────────────────────────────────────────┘
                │  Eloquent (hasMany / belongsToMany / belongsTo)
                ▼
┌───────────────────────────────────────────────────────────────────┐
│              App\Prefecture\Archive  (v5/app/Http/Model/…)        │
│              App\Prefecture\ArchiveCategory                        │
│              App\Prefecture\Works\WorkArchive  (pivot obras)      │
└───────────────┬───────────────────────────────────────────────────┘
                │  PDO / PostgreSQL 10
                ▼
┌───────────────────────────────────────────────────────────────────┐
│  archives  │ archive_categories │ work_archives                   │
│  biddings_has_archives │ answer_archives │ contract_archives      │
│  ombudsman_answer_archives │ emendas_has_archives                │
└───────────────────────────────────────────────────────────────────┘
```

## 2. Fluxo de requisição

### 2.1 Upload de mídia no Drive

```
POST /cms/galeria  (multipart: file, table?, id?, nameFile?, category?)
  │
  ├─ Route::group(['prefix'=>'cms','before'=>'csrf'])   v5/routes/basepro.php:37
  │     └─ Route::group(['prefix'=>'galeria','as'=>'galeria.'])  :381
  │           └─ POST /  → ArchiveController@saveArchivesFromGalleryOrDriver
  │
  ├─ [FALHA] NÃO há check.route.permission nem check.logged
  │          (ArchiveController sobrescreve __construct e não chama parent)
  │          ⚠ o CSRF ainda é validado pelo grupo de middleware `web`
  │            (VerifyCsrfToken em v5/app/Http/Kernel.php:36). O atributo
  │            'before' => 'csrf' do grupo `cms` (v5/routes/basepro.php:37)
  │            é herança do Laravel 4 e é IGNORADO no Laravel 5.5
  │            (RouteGroup::merge só trata namespace/prefix/where/as).
  │
  ├─ ArchiveController@saveArchivesFromGalleryOrDriver
  │     ├─ resolve bucket:
  │     │    table=content        → dir=/content/{catSlug}/{id}/galeria   col=contents_id
  │     │    table=calendar_event → dir=/calendar-events/{catSlug}/{id}/galeria  col=calendar_events_id
  │     │    table=static_pages   → dir=/statics/{slug}/galeria          col=static_pages_id
  │     │    (sem table)          → dir=/content/midias
  │     ├─ FileTrait::saveUpload()  → $file->storeAs($dir, $name) + compress()
  │     ├─ Archive::create([name, path, extension, type, hash, category_id, <col>])
  │     ├─ se path contém 'midias' → $archive->order = $archive->id
  │     └─ responde JSON { success, data: archive->load('category:id,name') }
  │
  └─ Front (midias/index.js): adiciona o item à galeria via
        adicionarImagemGaleria(data, true)
```

### 2.2 Listagem do Drive

```
GET /cms/galeria            → todos os arquivos de /content/midias
GET /cms/galeria/{col}/{id} → arquivos de uma galeria específica
  │
  └─ ArchiveController@getArchivesFromGalleryOrDriver
        ├─ com id  → where($colunm, $id)->with('category')->whereNull('highlight')
        │            ->whereIn('extension', [jpg,jpeg,png,gif,bmp,tiff,svg,webp,ico,heic,raw])
        │            ->orderBy('order','DESC')->orderBy('id','DESC')
        ├─ sem id  → where('path','like','%midias%')->orderBy('order','DESC')->orderBy('id','DESC')
        └─ ->where('extension','!=',null)->with('category:id,name')
              ->get(['id','path','extension','name','order','category_id'])
```

### 2.3 Reordenação

Duas mecânicas **independentes** na v5:

```
(a) POST /order  → ArchiveController@defaultOrder   (nome: fetch.default.order)
    body: { list: [ids], contents_id }
    └─ AppTrait::order()  → para cada item sem 'order', atribui max(order)+1
       (só funciona para galeria de CONTEÚDO; ignora static_pages/calendar_events)

(b) PUT /order   → ArchiveController@putOrder         (nome: fetch.set.order)
    body: { list: [ids], contents_id }
    └─ AppTrait::reorder() → order = index+1 para cada id da lista

(c) POST /cms/drive/atualizar-ordem → ArchiveController@updateOrder  (nome: drive.update.order)
    body: { dragged_id, target_id }
    └─ DB::table('archives') — troca direta dos valores de 'order' entre 2 linhas
       (sem escopo por módulo: pode trocar a ordem de arquivos de contextos diferentes!)
```

O chamador de (a) e (b) é `v5/public/assets/js/custom/utilities/helpers/sortable-galerias.js`
(usa `fetch('/order', …)`), que **não é incluído por nenhuma view** — código
morto (verificado por `grep` em `v5/resources/views/`).
O chamador de (c) é `v5/public/assets/js/custom/contents/midias/index.js:449-465`
(jQuery UI `sortable`).

### 2.4 Exclusão

```
POST /cms/galeria/delete/{id}  → ArchiveController@deleteArchives
  └─ $this->repository->getModel()->destroy($id)   → SoftDeletes (deleted_at)
     ⚠ o binário NÃO é apagado do storage
```

### 2.5 Categorias (exclusivo da v5)

```
GET  /cms/drive/categorias            → ArchiveCategoryController@getCategories
POST /cms/drive/cadastrar/categoria   → ArchiveCategoryController@store
  └─ slug = slug_fix($data['name'])  (helper global, v5/app/Helpers/functions.php:771)
     ArchiveCategory::create(['name'=>…, 'slug'=>…])
```

## 3. Classes envolvidas

| Classe | Caminho | Papel |
|---|---|---|
| `ArchiveController` | `v5/app/Http/Controllers/Prefecture/ArchiveController.php` | API JSON de upload/listagem/ordenação/exclusão. Usa `FileTrait`. |
| `ArchiveCategoryController` | `v5/app/Http/Controllers/Prefecture/ArchiveCategoryController.php` | CRUD parcial de categorias (só `getCategories` e `store`). Usa `FileTrait` (sem uso). |
| `Controller` (base) | `v5/app/Http/Controllers/Prefecture/Controller.php` | Aplica `check.route.permission` no construtor. **Não é chamado** por `ArchiveController` (ver `01` §5). |
| `ArchiveRepository` | `v5/app/Repositories/Eloquent/Prefecture/Archive/ArchiveRepository.php` | Repositório genérico (`Repository`) com `upInsert()` customizado que grava o upload. |
| `IArchiveRepository` | `v5/app/Repositories/Contracts/IArchiveRepository.php` | Interface **vazia** (contrato formal inexistente). |
| `Repository` (base) | `v5/app/Repositories/Repository.php` | `getModel()`, `upInsert()`, `destroy()`, `dataTrait()`. |
| `App\Prefecture\Archive` | `v5/app/Http/Model/Prefecture/Archive.php` | Model da tabela `archives`. |
| `App\Prefecture\ArchiveCategory` | `v5/app/Http/Model/Prefecture/ArchiveCategory.php` | Model de `archive_categories`. |
| `App\Prefecture\Works\WorkArchive` | `v5/app/Http/Model/Prefecture/Works/WorkArchive.php` | Pivot obras ↔ arquivos, com `type` (`gallery`/`file`). |
| `App\Prefecture\BiddingHasArchive` | `v5/app/Http/Model/Prefecture/BiddingHasArchive.php` | Pivot licitações ↔ arquivos. |
| `App\Prefecture\Answer` | `v5/app/Http/Model/Prefecture/Answer.php` | Respostas de licitações; `belongsToMany` `answer_archives`. |
| `App\Prefecture\OmbudsmanAnswer` | `v5/app/Http/Model/Prefecture/OmbudsmanAnswer.php` | Respostas de ouvidoria; `belongsToMany` `ombudsman_answer_archives`. |
| `App\Prefecture\Contract` | `v5/app/Http/Model/Prefecture/Contract.php` | `belongsToMany` `contract_archives`. |
| `App\Prefecture\EmendaParlamentar` | `v5/app/Http/Model/Prefecture/EmendaParlamentar.php` | `belongsToMany` `emendas_has_archives`. |
| `App\Prefecture\Works\Work` | `v5/app/Http/Model/Prefecture/Works/Work.php` | `belongsToMany` `work_archives`. |
| `App\Prefecture\PaginasSecretaria` | `v5/app/Http/Model/Prefecture/PaginasSecretaria.php` | Usa `reference_id` (FK lógica em `text`). |
| `App\Prefecture\Highlight` | `v5/app/Http/Model/Prefecture/Highlight.php` | `hasOne` via `archives.highlight_id`. |
| `App\Prefecture\Diario` / `DiarioConfig` | `v5/app/Http/Model/Prefecture/Diario.php`, `…/DiarioConfig.php` | `cover_image_id` / `header_img` → `archives.id`. |
| `App\Prefecture\TypesSizes` | `v5/app/Http/Model/Prefecture/TypesSizes.php` | Lookup de tamanho de exibição. |
| `App\Enum\Prefecture\Works\ArchiveTypesEnum` | `v5/app/Enum/Prefecture/Works/ArchiveTypesEnum.php` | `GALLERY='gallery'`, `FILE='file'`. |
| `FileTrait` | `v5/app/Http/Traits/FileTrait.php` | `saveUpload()`, `compress()`, `deleteFile()`, `getFileName()`, `makeSlug()`. |
| `AppTrait` | `v5/app/Http/Traits/AppTrait.php` | `order()` e `reorder()` (linhas 725 e 746). |
| `slug_fix()` / `getMinFile()` / `getLargFile()` / `file_exist()` | `v5/app/Helpers/functions.php` | Helpers globais de slug e resolução de variantes de imagem. |

## 4. Dependências externas

| Pacote | Versão (v5) | Uso no módulo |
|---|---|---|
| `laravel/framework` | `5.5.*` | Eloquent, Filesystem, Route. |
| `intervention/image` | `^2.7` | `Image::make()->resize()` em `FileTrait::compress()` e `extractedCompress()`. |
| `blueimp/jquery-file-upload` | `^9.22` | Plugin `#multipleupload` no Drive (`v5/public/assets/js/custom/contents/midias/index.js`). |
| `cartalyst/sentinel` | `2.0.*` | `Sentinel::check()` / `hasAnyAccess()` no middleware de permissão. |
| `spatie/laravel-activitylog` | `^2.8` | `activity()->performedOn($archive)` ao cadastrar arquivo. |
| `laracasts/flash` | `3.0.*` | Mensagens `flash()->success()/error()`. |
| `laralabs/toaster` | `^2.0` | `toastr.success()` / `toastr.error()` no JS. |
| CDN `cropperjs 1.5.13` | — | Edição/rotação da imagem antes do upload. |
| CDN `jquery-ui 1.13.2` | — | `sortable` da galeria do Drive. |

## 5. Bug de arquitetura: construtor sobrescrito

`Prefecture\Controller` registra o middleware de permissão no construtor:

```php
// v5/app/Http/Controllers/Prefecture/Controller.php:19-23
public function __construct()
{
    $this->middleware('check.route.permission');
}
```

`ArchiveController` sobrescreve o construtor **sem chamar o pai**:

```php
// v5/app/Http/Controllers/Prefecture/ArchiveController.php:38-41
public function __construct()
{
    $this->repository = new ArchiveRepository();
}
```

Como o `ControllerDispatcher` do Laravel 5.5 lê os middlewares do
controller já instanciado (`Route::gatherMiddleware()` →
`controllerMiddleware()` → `getMiddleware($this->getController(), …)`),
o middleware **nunca é registrado** para `ArchiveController` nem para
`ArchiveCategoryController` (que também sobrescreve o construtor).

Comprovação por `php artisan route:list` na v5:

| Rota | Middleware |
|---|---|
| `cms/galeria` (POST) | `web` |
| `cms/galeria/delete/{id}` (POST) | `web` |
| `cms/galeria/{column?}/{id?}` (GET) | `web` |
| `cms/drive/categorias` (GET) | `web` |
| `cms/drive/cadastrar/categoria` (POST) | `web` |
| `cms/drive/atualizar-ordem` (POST) | `web` |
| `cms/secretarias_paginas/arquivo/{id}` (DELETE) | `web,check.route.permission` |

O mesmo ocorre na v6 (`v6/app/Http/Controllers/Prefecture/ArchiveController.php:33-37`).

## 6. Diagrama de fluxo (Mermaid)

```mermaid
sequenceDiagram
    participant U as Operador (CMS)
    participant J as midias/index.js
    participant R as routes/basepro.php
    participant C as ArchiveController
    participant T as FileTrait
    participant DB as PostgreSQL
    participant S as Storage (public)

    U->>J: clica "Upload" e escolhe o arquivo
    J->>J: cropper (imagem) → Swal (nome + categoria)
    J->>J: GET /cms/drive/categorias (lista categorias)
    R->>C: POST /cms/galeria (multipart)
    C->>T: saveUpload(file, dir, null, nameFile, true)
    T->>S: $file->storeAs($dir, $name)
    T->>T: compress() → gera _min/_med/_larg
    C->>DB: INSERT archives (name, path, extension, type, hash, category_id, <col>)
    C-->>J: 200 {success, data}
    J->>J: adicionarImagemGaleria(data, true)

    U->>J: arrasta item na galeria
    J->>R: POST /cms/drive/atualizar-ordem {dragged_id, target_id}
    R->>C: updateOrder()
    C->>DB: UPDATE archives SET order = ... (transação)
```

## 7. Onde o módulo é consumido (mapa de dependências)

| Consumidor | Arquivo | Como usa `archives` |
|---|---|---|
| Conteúdos (notícias, avisos, legislação…) | `v5/app/Http/Controllers/Prefecture/ContentController.php` | `hasMany` via `contents_id`; grava destaque e galeria; `pathUpdate()` reprocessa imagens. |
| Páginas estáticas | `v5/app/Http/Controllers/Prefecture/StaticPageController.php` | `where('static_pages_id', …)`. |
| Agenda de eventos | `v5/app/Http/Controllers/Prefecture/CalendarEvent/CalendarEventController.php` | `archives` da galeria do evento. |
| Licitações | `v5/app/Http/Controllers/Prefecture/Bidding/BiddingController.php` | `belongsToMany` `biddings_has_archives`. |
| Contratos | `v5/app/Http/Controllers/Prefecture/ContractController.php` | `belongsToMany` `contract_archives`. |
| Respostas de licitações | `v5/app/Http/Controllers/Prefecture/AnswerController.php` | `belongsToMany` `answer_archives`. |
| Respostas de ouvidoria | `v5/app/Http/Controllers/Prefecture/OmbudsmanAnswerController.php` | `belongsToMany` `ombudsman_answer_archives`. |
| Emendas parlamentares | `v5/app/Http/Controllers/Prefecture/EmendaParlamentar/EmendaParlamentarController.php` | `belongsToMany` `emendas_has_archives`. |
| Obras | `v5/app/Http/Controllers/Prefecture/Works/WorkController.php` | `Archive::create()` + `WorkArchive::create()`. |
| Requerimento de diárias | `v5/app/Http/Controllers/Prefecture/RequerimentoDiariasController.php` | `where('requerimento_id', …)->delete()`. |
| Páginas de secretarias | `v5/app/Http/Controllers/Prefecture/PagesSecretaryController.php` | `where('reference_id', $id)`. |
| Diário Oficial | `v5/app/Http/Controllers/Prefecture/DiarioController.php` | `diarios.cover_image_id`, `diario_configs.header_img`. |
| Manual do sistema | `v5/database/migrations/2023_02_01_083626_add_itens_to_archives.php` | Popula 91 arquivos do manual. |
| Portal | `v5/app/Http/Controllers/Portal/*.php` | `where('view_pdf', true)`, `where('highlight', null)`. |
