# Homologação da Sprint 0 na Hostinger

Este procedimento publica exclusivamente a fundação aprovada da Sprint 0 em uma hospedagem compartilhada Hostinger. Ele não configura produção definitiva e não cria estruturas da Sprint 1.

## 1. Pré-requisitos e decisões

- Plano Web ou Cloud com SSH, Cron Jobs, Git ou SFTP e MySQL disponíveis.
- Subdomínio exclusivo, por exemplo `homologacao.example.com`.
- PHP 8.3 selecionado no hPanel e confirmado também na CLI.
- Banco, usuário, senha, `APP_KEY`, SMTP e credenciais HTTP exclusivos da homologação.
- Repositório remoto privado, caso o fluxo por Git seja escolhido.
- Build Vite gerado localmente; `node_modules` não deve ser enviado ao servidor.

A Hostinger mantém `public_html` como document root nos planos compartilhados. Para não expor arquivos privados do Laravel, use esta estrutura:

```text
/home/u123456789/domains/homologacao.example.com/
├── app/          # projeto Laravel completo, fora do document root
└── public_html/  # somente o conteúdo de app/public
```

Não publique o projeto Laravel completo dentro de `public_html`.

## 2. Criar o subdomínio e confirmar o caminho

1. No hPanel, crie ou adicione `homologacao.example.com` como website.
2. Em **Websites > Dashboard > FTP Accounts**, copie o caminho absoluto exibido.
3. Substitua nos comandos abaixo `u123456789` e `homologacao.example.com` pelos valores reais.
4. Ative o SSL e mantenha **Force HTTPS** habilitado.
5. Antes de alterar PHP pelo hPanel, inventarie os outros sites do mesmo plano: a versão selecionada pode ser aplicada a todos eles.
6. Se todos forem compatíveis, selecione PHP 8.3 em **PHP Configuration** e habilite OPcache.
7. Se houver risco para outro site, não faça a alteração global. Acrescente no início do `.htaccess` de `public_html` apenas da homologação:

```apache
<FilesMatch "\.(php4|php5|php3|php2|php|phtml)$">
    SetHandler application/x-lsphp83
</FilesMatch>
```

Essa alternativa seleciona PHP 8.3 somente para as requisições web desse diretório, mas não altera a CLI e pode não herdar as extensões/opções globais. Confirme todas as extensões no hPanel e na execução web; remova imediatamente qualquer arquivo temporário usado para exibir `phpinfo()`.

Defina os caminhos somente durante a sessão SSH:

```bash
APP_ROOT=/home/u123456789/domains/homologacao.example.com/app
PUBLIC_ROOT=/home/u123456789/domains/homologacao.example.com/public_html
PHP_BIN=/opt/alt/php83/usr/bin/php
```

Confirme o executável disponível antes de continuar:

```bash
"$PHP_BIN" -v
"$PHP_BIN" -m
```

São necessários: `ctype`, `dom`, `fileinfo`, `filter`, `hash`, `iconv`, `json`, `libxml`, `mbstring` ou polyfill compatível, `openssl`, `pcre`, `pdo`, `pdo_mysql`, `session`, `tokenizer`, `xml` e `xmlwriter`. Confirme também suporte a Argon2id:

```bash
"$PHP_BIN" -r "var_export(in_array('argon2id', password_algos(), true));"
```

O resultado esperado é `true`.

## 3. Enviar a aplicação

### Opção recomendada: Git privado via SSH

O repositório ainda não possui remote. Depois que um responsável criar um repositório privado e autorizar a chave SSH da hospedagem:

```bash
git clone --branch main --single-branch REPOSITORY_SSH_URL "$APP_ROOT"
cd "$APP_ROOT"
git rev-parse HEAD
```

O hash deve ser o commit aprovado para homologação. Não use auto-deploy até o fluxo manual ser homologado.

### Alternativa: SFTP

Envie o projeto para `app/`, excluindo `.env`, `.git`, `.tools`, `node_modules`, testes locais, caches e logs. O arquivo `.env` será criado diretamente no servidor.

## 4. Dependências e assets

Confirme primeiro que o Composer usa PHP 8.3. A versão PHP da CLI pode ser diferente da versão do website:

```bash
cd "$APP_ROOT"
"$PHP_BIN" -d memory_limit=-1 /usr/local/bin/composer2 install --no-dev --prefer-dist --optimize-autoloader --no-interaction
"$PHP_BIN" /usr/local/bin/composer2 check-platform-reqs
```

Se `/usr/local/bin/composer2` não existir, use o caminho do Composer 2 informado pelo hPanel ou instale `composer.phar` localmente conforme a documentação da Hostinger.

Gere os assets antes do upload, na estação de desenvolvimento:

```bash
npm ci
npm run build
```

Envie `public/build` junto com os demais arquivos públicos. Não execute servidor Vite em homologação.

## 5. Publicar somente o diretório público

Copie o conteúdo de `app/public` para `public_html`, incluindo `.htaccess`, `build/` e arquivos estáticos. Na cópia de `public_html/index.php`, altere somente os três caminhos privados:

```php
if (file_exists($maintenance = __DIR__.'/../app/storage/framework/maintenance.php')) {
    require $maintenance;
}

require __DIR__.'/../app/vendor/autoload.php';

$app = require_once __DIR__.'/../app/bootstrap/app.php';
```

Não altere o `public/index.php` versionado. Confirme que `public_html/.htaccess` corresponde ao `.htaccess` versionado em `app/public` e que `mod_rewrite` está funcionando. Se a versão PHP estiver isolada por site, preserve também o bloco `FilesMatch` específico da Hostinger no início da cópia publicada.

Depois de cada deploy que altere assets, copie novamente `app/public/build` para `public_html/build`. A Sprint 0 não possui upload público; portanto, `storage:link` não é necessário nesta homologação.

## 6. Banco MySQL exclusivo

No hPanel, acesse **Databases > Management** e crie banco e usuário exclusivos. A Hostinger acrescenta um prefixo de conta aos nomes. Use senha longa e única, guarde-a em um gerenciador de senhas e não a coloque no Git.

Configure no `.env`:

```dotenv
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=u123456789_cbl_homolog
DB_USERNAME=u123456789_cbl_homolog
DB_PASSWORD=SUBSTITUIR_NO_SERVIDOR
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
```

Valide a configuração antes da migration:

```bash
cd "$APP_ROOT"
"$PHP_BIN" artisan about --only=environment
"$PHP_BIN" artisan migrate:status
```

Em banco novo, `migrate:status` pode informar que a tabela de migrations ainda não existe. Execute então:

```bash
"$PHP_BIN" artisan migrate --force
"$PHP_BIN" artisan db:seed --class=FoundationSeeder --force
"$PHP_BIN" artisan migrate:status
```

O `FoundationSeeder` é idempotente e cria somente permissões, perfil estrutural e sequenciadores. Ele não cria usuário com senha fixa.

## 7. `.env` de homologação

Crie `app/.env` diretamente no servidor, com permissão restrita. Use os valores abaixo como roteiro, nunca como credenciais reais:

```dotenv
APP_NAME="Could Be Label"
APP_ENV=staging
APP_KEY=
APP_DEBUG=false
APP_URL=https://homologacao.example.com

APP_LOCALE=pt_BR
APP_FALLBACK_LOCALE=pt_BR
APP_FAKER_LOCALE=pt_BR
APP_MAINTENANCE_DRIVER=file

HASH_DRIVER=argon2id

LOG_CHANNEL=stack
LOG_STACK=daily
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=warning
LOG_DAILY_DAYS=14

DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=SUBSTITUIR_NO_SERVIDOR
DB_USERNAME=SUBSTITUIR_NO_SERVIDOR
DB_PASSWORD=SUBSTITUIR_NO_SERVIDOR
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci

SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=true
SESSION_PATH=/
SESSION_DOMAIN=null
SESSION_HTTP_ONLY=true
SESSION_SAME_SITE=lax
SESSION_SECURE_COOKIE=true
SESSION_COOKIE=could_be_label_homologacao_session

CACHE_STORE=database
CACHE_PREFIX=could_be_label_homologacao
QUEUE_CONNECTION=database

BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local

MAIL_MAILER=smtp
MAIL_SCHEME=smtps
MAIL_HOST=smtp.hostinger.com
MAIL_PORT=465
MAIL_USERNAME=SUBSTITUIR_NO_SERVIDOR
MAIL_PASSWORD=SUBSTITUIR_NO_SERVIDOR
MAIL_FROM_ADDRESS=SUBSTITUIR_NO_SERVIDOR
MAIL_FROM_NAME="${APP_NAME}"

VITE_APP_NAME="${APP_NAME}"
```

Gere uma chave exclusiva depois de salvar o arquivo:

```bash
chmod 600 "$APP_ROOT/.env"
cd "$APP_ROOT"
"$PHP_BIN" artisan key:generate --force
```

Se o SMTP SSL na porta 465 não funcionar, use `MAIL_SCHEME=tls` e porta 587, conforme os dados exibidos em **Emails > Connect Apps & Devices**.

## 8. Permissões e caches

Não use `chmod 777`.

```bash
cd "$APP_ROOT"
find storage bootstrap/cache -type d -exec chmod 775 {} \;
find storage bootstrap/cache -type f -exec chmod 664 {} \;
"$PHP_BIN" artisan optimize:clear
"$PHP_BIN" artisan config:cache
"$PHP_BIN" artisan route:cache
"$PHP_BIN" artisan view:cache
```

Valide que `storage/logs`, `storage/framework`, `bootstrap/cache` e as tabelas de cache, sessão e fila são graváveis. O arquivo `.env` deve permanecer fora de `public_html` e com permissão `600`.

## 9. Primeiro administrador

Não adicione senha ao seeder ou ao repositório. Não é necessário criar um comando Artisan específico para a primeira homologação: o bootstrap pode ser executado uma única vez via Tinker, usando variáveis temporárias e senha oculta do histórico do shell.

```bash
cd "$APP_ROOT"
read -r -p "Nome do administrador: " CBL_ADMIN_NAME
read -r -p "E-mail do administrador: " CBL_ADMIN_EMAIL
read -r -s -p "Senha forte do administrador: " CBL_ADMIN_PASSWORD
echo
export CBL_ADMIN_NAME CBL_ADMIN_EMAIL CBL_ADMIN_PASSWORD

"$PHP_BIN" artisan tinker --execute='validator(
    ["name" => getenv("CBL_ADMIN_NAME"), "email" => getenv("CBL_ADMIN_EMAIL"), "password" => getenv("CBL_ADMIN_PASSWORD")],
    ["name" => ["required", "string", "max:255"], "email" => ["required", "email:rfc", "max:255", "unique:users,email"], "password" => ["required", Illuminate\Validation\Rules\Password::min(12)->mixedCase()->numbers()->symbols()]]
)->validate(); Illuminate\Support\Facades\DB::transaction(function (): void {
    if (App\Models\User::query()->whereHas("roles", fn ($query) => $query->where("name", "admin"))->exists()) {
        throw new RuntimeException("Já existe um administrador; bootstrap cancelado.");
    }
    $role = App\Models\Role::query()->where("name", "admin")->firstOrFail();
    $user = App\Models\User::query()->create(["name" => trim(getenv("CBL_ADMIN_NAME")), "email" => mb_strtolower(trim(getenv("CBL_ADMIN_EMAIL"))), "password" => getenv("CBL_ADMIN_PASSWORD"), "is_active" => true]);
    $user->roles()->sync([$role->getKey()]);
    app(App\Actions\Auditing\CreateAuditLogAction::class)->execute(null, "system.initial_admin_created", $user, [], ["name" => $user->name, "email" => $user->email, "role_ids" => [$role->getKey()]], null, "Hostinger SSH bootstrap");
});'

unset CBL_ADMIN_NAME CBL_ADMIN_EMAIL CBL_ADMIN_PASSWORD
```

Confirme o usuário e a auditoria, faça login por HTTPS e altere a senha após o primeiro acesso. Se esse procedimento precisar ser repetido regularmente, deve-se aprovar antes a implementação de um comando Artisan dedicado, interativo, idempotente e auditado.

## 10. Cron e fila

O scheduler registra `AuditLogMaintenanceJob` diariamente às 02:00 no timezone `America/Sao_Paulo`. Como o job usa fila em banco, configure dois cron jobs do tipo **Custom**:

```cron
* * * * * /opt/alt/php83/usr/bin/php /home/u123456789/domains/homologacao.example.com/app/artisan schedule:run
* * * * * /opt/alt/php83/usr/bin/php /home/u123456789/domains/homologacao.example.com/app/artisan queue:work database --stop-when-empty --max-time=50 --tries=3
```

O agendamento do hPanel usa UTC. O Laravel interpreta o horário do scheduler com o timezone da aplicação, mas a execução a cada minuto evita conversão manual. Teste cada comando por SSH e depois consulte **Cron Jobs > View Output**, `jobs`, `failed_jobs` e `storage/logs`.

## 11. Proteção e validação online

No hPanel, use **Password Protect Directories** sobre `public_html` para acrescentar autenticação HTTP à homologação. Use credenciais diferentes das contas Laravel e compartilhe-as somente com os homologadores.

Checklist obrigatório:

1. `https://homologacao.example.com/up` responde com sucesso, sem stack trace.
2. HTTP redireciona para HTTPS e o certificado é válido.
3. Login, logout, recuperação de senha e redefinição funcionam.
4. Dashboard, usuários, perfis, permissões, configurações, auditoria e logs abrem sem erro 500.
5. Acesso sem autenticação redireciona ao login; usuário sem permissão recebe 403.
6. Uma requisição com CSRF inválido recebe 419 sem detalhes internos.
7. CSS, JavaScript, fontes e imagens carregam sem 404 ou conteúdo misto.
8. Cookies de sessão apresentam `Secure`, `HttpOnly` e `SameSite=Lax`.
9. Respostas incluem CSP, `X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy`, `Permissions-Policy` e HSTS.
10. O HTML e as respostas de erro não expõem `APP_DEBUG`, caminhos internos, credenciais ou stack traces.
11. Layout validado em desktop, notebook, tablet e celular.
12. `storage/logs` não contém senhas, tokens, cookies ou chaves.
13. Scheduler e worker registram execução sem acumular jobs ou falhas.

Após alterar qualquer variável do `.env`, execute novamente `artisan config:cache`. Antes de migrations futuras, gere backup pelo hPanel. Não faça rollback destrutivo sem backup e autorização.

## 12. Fontes operacionais

- [PHP na Hostinger](https://www.hostinger.com/support/1575755-how-to-change-the-php-version-of-your-hostinger-hosting-plan/)
- [PHP específico por site ou subdomínio](https://www.hostinger.com/support/4047803-how-to-change-the-php-version-for-subfolders-or-subdomains-in-hostinger/)
- [Extensões PHP na Hostinger](https://www.hostinger.com/support/which-php-extensions-and-configuration-options-are-supported-at-hostinger/)
- [Laravel 12 na Hostinger](https://www.hostinger.com/support/which-programming-languages-and-frameworks-are-supported-at-hostinger/)
- [Composer e PHP CLI](https://www.hostinger.com/support/5792082-how-to-solve-common-composer-issues-at-hostinger/)
- [Bancos MySQL](https://www.hostinger.com/support/1583542-how-to-create-a-new-mysql-database-in-hostinger/)
- [Cron Jobs](https://www.hostinger.com/support/1583465-how-to-set-up-a-cron-job-at-hostinger/)
- [HTTPS](https://www.hostinger.com/support/1583201-how-to-enable-or-disable-https-for-your-website-at-hostinger/)
- [Proteção por senha](https://www.hostinger.com/support/1583470-how-to-password-protect-a-website-in-hostinger/)
- [SMTP Hostinger](https://www.hostinger.com/support/1575756-how-to-get-email-account-configuration-details-for-hostinger-email/)
