🩺 Diagnóstico do Ambiente Syrios

Gerado em 2025-12-07 08:26:22


✅ Status Geral

🌐 Variáveis de Ambiente (mascaradas)

ChaveValor
APP_DEBUG true
APP_ENV production
APP_KEY **********
APP_NAME Syrios
APP_TIMEZONE America/Sao_Paulo
APP_TRUSTED_PROXIES *
APP_URL https://syrios.up.railway.app
ASSET_URL null
CACHE_DRIVER file
DB_CONNECTION mysql
DB_DATABASE **********
DB_HOST **********
DB_PASSWORD **********
DB_PORT 3306
DB_USERNAME **********
GPG_KEYS 39B641343D8C104B2B146DC3F9C39DC0B9698544 E60913E4DF209907D8E30D96659A97C9CF2A795A 1198C0117593497A5EC5C199286AF1F9897469DC
HOME /root
HOSTNAME c202a791efda
LOG_CHANNEL stack
LOG_LEVEL debug
PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PHPIZE_DEPS autoconf dpkg-dev file g++ gcc libc-dev make pkg-config re2c
PHP_ASC_URL https://www.php.net/distributions/php-8.2.29.tar.xz.asc
PHP_CFLAGS -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
PHP_CPPFLAGS -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
PHP_INI_DIR /usr/local/etc/php
PHP_LDFLAGS -Wl,-O1 -pie
PHP_SHA256 475f991afd2d5b901fb410be407d929bc00c46285d3f439a02c59e8b6fe3589c
PHP_URL https://www.php.net/distributions/php-8.2.29.tar.xz
PHP_VERSION 8.2.29
PORT 8080
PWD /app/public
QUEUE_CONNECTION sync
RAILWAY_BETA_ENABLE_RUNTIME_V2 1
RAILWAY_DEPLOYMENT_ID d48dc75c-0f61-4410-98f1-b597c6aa02d7
RAILWAY_ENVIRONMENT production
RAILWAY_ENVIRONMENT_ID 5e108015-ced5-4394-8462-27f0ac14d80d
RAILWAY_ENVIRONMENT_NAME production
RAILWAY_GIT_AUTHOR DavidFenix
RAILWAY_GIT_BRANCH main
RAILWAY_GIT_COMMIT_MESSAGE idem
RAILWAY_GIT_COMMIT_SHA ceb7151001aeaa6b8c7d9dfe150fb9caa315ffec
RAILWAY_GIT_REPO_NAME syrios
RAILWAY_GIT_REPO_OWNER DavidFenix
RAILWAY_PRIVATE_DOMAIN virtuous-curiosity.railway.internal
RAILWAY_PROJECT_ID 00910e33-8565-4fe0-a225-fec4029d7d61
RAILWAY_PROJECT_NAME Projeto Syrios
RAILWAY_PUBLIC_DOMAIN syrios.up.railway.app
RAILWAY_REPLICA_ID e769e47b-c31e-4338-a837-e4b75d095d93
RAILWAY_REPLICA_REGION us-west2
RAILWAY_SERVICE_ID 2b198754-5238-4ad4-9aff-c98e6508e955
RAILWAY_SERVICE_NAME Syrios
RAILWAY_SERVICE_SYRIOSIA_URL syriosia.up.railway.app
RAILWAY_SERVICE_SYRIOS_FAIL_URL syrios-fail.up.railway.app
RAILWAY_SERVICE_SYRIOS_URL syrios.up.railway.app
RAILWAY_SNAPSHOT_ID dfa0aac7-8219-4f41-8a21-2ac0babb2066
RAILWAY_STATIC_URL syrios.up.railway.app
SESSION_DOMAIN syrios.up.railway.app
SESSION_DRIVER file
SESSION_LIFETIME 120
SESSION_SAME_SITE none
SESSION_SECURE_COOKIE true
SHELL_VERBOSITY 0
container podman

🧩 Configuração CORS (config/cors.php)

{
    "paths": [
        "api/*",
        "login",
        "sanctum/csrf-cookie"
    ],
    "allowed_methods": [
        "*"
    ],
    "allowed_origins": [
        "*"
    ],
    "allowed_origins_patterns": [],
    "allowed_headers": [
        "*"
    ],
    "exposed_headers": [],
    "max_age": 0,
    "supports_credentials": false
}

📁 Arquivos Importantes

Dockerfile
# Usa imagem leve e moderna do PHP
FROM php:8.2-cli

# Define o diretório de trabalho
WORKDIR /app

# Copia todos os arquivos do projeto Laravel para dentro da imagem
COPY . .

# Instala dependências do sistema e extensões do PHP
RUN apt-get update && apt-get install -y unzip git libzip-dev && \
    docker-php-ext-install pdo_mysql zip

# 🔹 Instala o Composer (copiando da imagem oficial do Composer)
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

# 🔹 Instala as dependências do Laravel
RUN composer install --no-dev --optimize-autoloader

# Expõe a porta padrão usada pelo Railway
EXPOSE 8080

# Comando de inicialização do Laravel (servidor embutido)
CMD ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8080"]
AppServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\HttpFoundation\Request;
use Carbon\Carbon;
use Illuminate\Pagination\Paginator;
use Illuminate\Database\Migrations\Migrator;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        //
    }

    public function boot()
    {
        // debug:passo2::Impede execução de migrações definindo um diretório vazio
        $this->app->afterResolving('migrator', function (Migrator $migrator) {
            $migrator->path('database/migrations_disabled');
        });

        // 🔧 Configurações gerais
        Schema::defaultStringLength(191);
        Carbon::setLocale('pt_BR');
        Paginator::defaultView('vendor.pagination.default');

        /*
        |--------------------------------------------------------------------------
        | Força HTTPS somente quando houver proxy indicando isso
        |--------------------------------------------------------------------------
        */
        if ($this->app->environment('production')) {
            if (request()->header('x-forwarded-proto') === 'https') {
                URL::forceScheme('https');
            }
        }

        /*
        |--------------------------------------------------------------------------
        | Criação automática do symlink storage → public/storage
        | Somente em produção e somente se ainda não existir
        |--------------------------------------------------------------------------
        */
        if ($this->app->environment('production')) {
            $public = public_path('storage');
            $target = storage_path('app/public');

            // Se o link ainda NÃO existir
            if (!is_link($public)) {
                try {
                    // garante que o diretório de destino existe
                    if (!is_dir($target)) {
                        @mkdir($target, 0755, true);
                    }

                    // cria o link
                    symlink($target, $public);
                } catch (\Throwable $e) {
                    // silencioso para não quebrar o sistema
                    // railway não permite mkdir em certas horas
                }
            }
        }
    }


    // public function boot()
    // {
    //     // 🔧 Configurações gerais
    //     Schema::defaultStringLength(191);
    //     Carbon::setLocale('pt_BR');
    //     //date_default_timezone_set('America/Sao_Paulo');
    //     Paginator::defaultView('vendor.pagination.default');

    //     // if (App::environment('production')) {
    //     //     URL::forceScheme('https');
    //     // }

    //     if ($this->app->environment('production')) {
    //         if (request()->header('x-forwarded-proto') === 'https') {
    //             URL::forceScheme('https');
    //         }
    //     }


    // }
}
Kernel.php
<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array<int, class-string|string>
     */
    protected $middleware = [
        // \App\Http\Middleware\TrustHosts::class,
        \App\Http\Middleware\TrustProxies::class,
        \Fruitcake\Cors\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];

    /**
     * The application's route middleware groups.
     *
     * @var array<string, array<int, class-string|string>>
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array<string, class-string|string>
     */
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
        'role' => \App\Http\Middleware\RoleMiddleware::class,
        'ensure.context' => \App\Http\Middleware\EnsureContextSelected::class,

    ];
}
TrustProxies.php
<?php

namespace App\Http\Middleware;

use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;

class TrustProxies extends Middleware
{
    protected $proxies = '*';
    protected $headers = Request::HEADER_X_FORWARDED_ALL;
}
VerifyCsrfToken.php
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    protected $addHttpCookie = true;

    protected $except = [
        // Rotas públicas sem CSRF (se houver)
    ];
}
config/cors.php
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Cross-Origin Resource Sharing (CORS) Configuration
    |--------------------------------------------------------------------------
    |
    | Here you may configure your settings for cross-origin resource sharing
    | or "CORS". This determines what cross-origin operations may execute
    | in web browsers. You are free to adjust these settings as needed.
    |
    | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
    |
    */

    'paths' => ['api/*', 'login', 'sanctum/csrf-cookie'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => false,

];
routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Artisan;
use Symfony\Component\HttpFoundation\Cookie;

// Auth
use App\Http\Controllers\Auth\LoginController;
use App\Http\Controllers\DiagController;

// Master
use App\Http\Controllers\Master\EscolaController as MasterEscolaController;
use App\Http\Controllers\Master\RoleController as MasterRoleController;
use App\Http\Controllers\Master\UsuarioController as MasterUsuarioController;
use App\Http\Controllers\Master\DashboardController as MasterDashboardController;
use App\Http\Controllers\Master\ImagemController;

// Secretaria
use App\Http\Controllers\Secretaria\EscolaController as SecretariaEscolaController;
use App\Http\Controllers\Secretaria\UsuarioController as SecretariaUsuarioController;

// Escola
use App\Http\Controllers\Escola\AlunoController;
use App\Http\Controllers\Escola\DashboardController;
use App\Http\Controllers\Escola\DisciplinaController;
use App\Http\Controllers\Escola\ProfessorController;
use App\Http\Controllers\Escola\TurmaController;
use App\Http\Controllers\Escola\UsuarioController as EscolaUsuarioController;
use App\Http\Controllers\Escola\RegimentoController;
use App\Http\Controllers\Escola\ModeloMotivoController;
use App\Http\Controllers\Escola\AlunoFotoController;
use App\Http\Controllers\Escola\AlunoFotoLoteController;
use App\Http\Controllers\Escola\EnturmacaoController;
use App\Http\Controllers\Escola\LotacaoController;
use App\Http\Controllers\Escola\DiretorTurmaController;
use App\Http\Controllers\Escola\IdentidadeController;

// Professor
use App\Http\Controllers\Professor\{
    DashboardController as ProfessorDashboardController,
    OfertaController,
    OcorrenciaController,
    RelatorioController,
    PerfilController
};

//somente para testes, remova em produção
Route::get('/kill-cookie', function () {
    return response('Cookie removido')
        ->cookie('syriosia_session', null, -1, '/', 'syriosia.up.railway.app', true, true, false, 'None')
        ->cookie('XSRF-TOKEN', null, -1, '/', 'syriosia.up.railway.app', true, true, false, 'None');
});

/*
|--------------------------------------------------------------------------
| Rotas Públicas (sem login)
|--------------------------------------------------------------------------
| Observação: por padrão, o RouteServiceProvider aplica o grupo "web".
| Mantemos explícito aqui para clareza do fluxo.
*/
Route::middleware(['web'])->group(function () {

    // Página inicial → login
    Route::get('/', fn() => redirect()->route('login'));

    Route::prefix('diag')->group(function () { 

        Route::get('/', [DiagController::class, 'index'])->name('diag.index'); 
        Route::get('/headers', [DiagController::class, 'headers'])->name('diag.headers'); 
        Route::get('/cookies', [DiagController::class, 'cookies'])->name('diag.cookies'); 
        Route::get('/set-cookie', [DiagController::class, 'setCookie'])->name('diag.setcookie'); 
        Route::get('/configs', [DiagController::class, 'configs'])->name('diag.configs'); 
        
        Route::get('/cookie-test', function () { 
            return response('ok')->cookie( 'probe', '1', 0, null, null, true, true, false, 'None' ); 
        });

    }); 

    Route::get('/cache-clear', function () {
        Artisan::call('config:clear');
        Artisan::call('cache:clear');
        Artisan::call('route:clear');
        Artisan::call('view:clear');
        return "Cache limpo!";
    });

    // Login / Logout (públicas)
    Route::get('/login', [LoginController::class, 'showLoginForm'])->name('login');
    Route::post('/login', [LoginController::class, 'login']);
    Route::post('/logout', [LoginController::class, 'logout'])->name('logout');

    // Rotas de diagnóstico e testes rápidos (públicas)
    Route::get('/way', function () {
        return '
        <h2>Login Teste</h2>
        <form method="post" action="/waylogin">
          <input type="email" name="email" placeholder="Email" required><br><br>
          <input type="password" name="password" placeholder="Senha" required><br><br>
          <button type="submit">Entrar</button>
        </form>
        <hr><a href="/waydiag">Diagnóstico</a>';
    });

    Route::post('/waylogin', function (Request $request) {
        Session::put('user', [
            'email' => $request->email,
            'logged_at' => now()->toDateTimeString()
        ]);
        return redirect('/waydashboard');
    });

    Route::get('/waydashboard', function () {
        if (!Session::has('user')) {
            return redirect('/way');
        }
        $u = Session::get('user');
        return "
        <h2>Área Protegida</h2>
        <p>Email: <b>{$u['email']}</b></p>
        <p>Login em: {$u['logged_at']}</p>
        <a href='/waylogout'>Sair</a> | <a href='/waydiag'>Diagnóstico</a>";
    });

    Route::get('/waylogout', function () {
        Session::flush();
        return redirect('/way');
    });

    Route::get('/waydiag', function (Request $r) {
        $headers = [];
        foreach ($r->headers->all() as $k => $v) {
            $headers[$k] = implode('; ', $v);
        }

        return response()->make("
        <h2>Diagnóstico</h2>
        <p>HTTPS detectado: " . ($r->isSecure() ? 'Sim' : 'Não') . "</p>
        <h3>Cookies</h3><pre>" . print_r($r->cookies->all(), true) . "</pre>
        <h3>Sessão</h3><pre>" . print_r(session()->all(), true) . "</pre>
        <h3>Headers</h3><pre>" . print_r($headers, true) . "</pre>
        <a href='/way'>Voltar</a>", 200, ['Content-Type' => 'text/html']);
    });

    // Debugs e testes auxiliares
    Route::get('/header-debug', function (Request $request) {
        if (!Session::isStarted()) {
            Session::start();
        }
        Session::put('debug_test', now()->toDateTimeString());

        $data = [
            'timestamp' => now()->toDateTimeString(),
            'client_ip' => $request->ip(),
            'session_value' => Session::get('debug_test'),
            'cookies_received' => $request->cookies->all(),
            'headers_received' => $request->headers->all(),
        ];

        $response = response()->json([
            'debug_info' => $data,
            'note' => 'Check if headers or cookies below are modified by proxies.'
        ]);

        $response->headers->set('X-Debug-App', 'Syrios');
        $response->headers->set('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
        $response->headers->set('Pragma', 'no-cache');

        $testCookie = new Cookie(
            'test_cookie',
            'ok',
            time() + 3600,
            '/',
            null,
            true,
            false,
            false,
            'None'
        );

        $response->headers->setCookie($testCookie);

        return $response;
    });

    Route::get('/debug', fn() => ['secure' => request()->isSecure(), 'url' => url('/')]);
    Route::get('/debug-headers', fn() => response()->json(['headers' => request()->headers->all()]));

    Route::get('/cookie-test', function (Request $request) {
        $response = response('<h1>cookie test</h1>');
        // Observação: domínio aqui era "syrios.onrender.com" no original; mantive como estava,
        // mas considere ajustar para o domínio atual quando usar este teste.
        $response->cookie('cookie_test', 'ok', 10, '/', 'syrios.onrender.com', true, true, false, 'None');
        return $response;
    });

    Route::get('/session-debug', function () {
        return response()->json([
            'session_id' => session()->getId(),
            'has_token'  => session()->has('_token'),
            'csrf_token' => csrf_token(),
            'cookies'    => request()->cookies->all(),
        ]);
    });

    Route::get('/cookie-proxy-test', function () {
        $response = new Response('<h1>Teste manual de cookie</h1><p>Verifique se o navegador recebeu o cookie chamado <b>proxy_test_cookie</b>.</p>');
        $response->header('Set-Cookie', 'proxy_test_cookie=OK_FROM_SERVER; Path=/; Max-Age=600; SameSite=None; Secure');
        return $response;
    });

    // Regimento público
    Route::get('regimento/{school}', [RegimentoController::class, 'visualizar'])
        ->name('regimento.visualizar');


});

/*
|--------------------------------------------------------------------------
| Pós-Login (com sessão carregada) — Escolha de Contexto
|--------------------------------------------------------------------------
| Essas rotas precisam apenas do usuário autenticado, sem exigir contexto.
| Aqui o cookie de sessão já foi entregue ao navegador.
*/
Route::middleware(['auth'])->group(function () {
    Route::get('/choose-school', [LoginController::class, 'chooseSchool'])->name('choose.school');
    Route::get('/choose-role/{schoolId}', [LoginController::class, 'chooseRole'])->name('choose.role');
    Route::post('/set-context', [LoginController::class, 'setContextPost'])->name('set.context');
});

/*
|--------------------------------------------------------------------------
| Rotas Protegidas por Contexto (auth + ensure.context)
|--------------------------------------------------------------------------
| A partir daqui, o contexto (current_school_id/current_role) já deve existir.
| Evitamos rodar ensure.context antes do cookie ser entregue (problema original).
*/
Route::middleware(['auth', 'ensure.context'])->group(function () {

    /*
    |--------------------------------------------------------------------------
    | Rotas do Master
    |--------------------------------------------------------------------------
    */
    Route::prefix('master')
        ->middleware(['role:master'])
        ->name('master.')
        ->group(function () {
            Route::get('dashboard', [MasterDashboardController::class, 'index'])->name('dashboard');
            Route::get('/', fn () => redirect()->route('master.dashboard'));

            Route::resource('escolas', MasterEscolaController::class)->except(['show']);
            Route::get('escolas/{escola}/detalhes', [MasterEscolaController::class, 'detalhes'])
                ->name('escolas.detalhes');

            Route::resource('roles', MasterRoleController::class)->only(['index']);
            Route::resource('usuarios', MasterUsuarioController::class);

            // Associações Escola Mãe ↔ Escola Filha
            Route::get('associacoes', [MasterEscolaController::class, 'associacoes'])->name('escolas.associacoes');
            Route::post('associacoes', [MasterEscolaController::class, 'associarFilha'])->name('escolas.associar');

            Route::post('usuarios/{usuario}/vincular', [MasterUsuarioController::class, 'vincular'])
                ->name('usuarios.vincular');

            // Gestão de roles específicas por usuario
            Route::get('usuarios/{usuario}/roles', [MasterUsuarioController::class, 'editRoles'])
                ->name('usuarios.roles.edit');
            Route::post('usuarios/{usuario}/roles', [MasterUsuarioController::class, 'updateRoles'])
                ->name('usuarios.roles.update');

            // Confirmação/Exclusão
            Route::get('usuarios/{usuario}/confirm-destroy', [MasterUsuarioController::class, 'confirmDestroy'])
                ->name('usuarios.confirmDestroy');
            Route::delete('usuarios/{usuario}', [MasterUsuarioController::class, 'destroy'])
                ->name('usuarios.destroy');

            // Imagens
            Route::get('imagens', [ImagemController::class, 'index'])->name('imagens.index');
            Route::post('imagens/limpar', [ImagemController::class, 'limpar'])->name('imagens.limpar');
        });

    /*
    |--------------------------------------------------------------------------
    | Rotas da Secretaria
    |--------------------------------------------------------------------------
    */
    Route::prefix('secretaria')
        ->middleware(['role:secretaria'])
        ->name('secretaria.')
        ->group(function () {
            Route::get('/', fn () => redirect()->route('secretaria.escolas.index'))->name('dashboard');

            Route::resource('escolas', SecretariaEscolaController::class)->except(['show']);
            Route::resource('usuarios', SecretariaUsuarioController::class)->except(['show']);

            Route::post('usuarios/{usuario}/vincular', [SecretariaUsuarioController::class, 'vincular'])
                ->name('usuarios.vincular');

            Route::get('usuarios/{usuario}/roles', [SecretariaUsuarioController::class, 'editRoles'])
                ->name('usuarios.roles.edit');
            Route::post('usuarios/{usuario}/roles', [SecretariaUsuarioController::class, 'updateRoles'])
                ->name('usuarios.roles.update');
        });

    /*
    |--------------------------------------------------------------------------
    | Rotas da Escola
    |--------------------------------------------------------------------------
    */
    Route::prefix('escola')
        ->middleware(['role:escola'])
        ->name('escola.')
        ->group(function () {
            Route::get('/', fn () => redirect()->route('escola.dashboard'));
            Route::get('dashboard', [DashboardController::class, 'index'])->name('dashboard');

            // Usuários (professores, pais, etc.)
            Route::resource('usuarios', EscolaUsuarioController::class)->except(['show']);
            Route::post('usuarios/{usuario}/vincular', [EscolaUsuarioController::class, 'vincular'])->name('usuarios.vincular');

            // Professores
            Route::resource('professores', ProfessorController::class)->except(['show']);

            // Disciplinas
            Route::resource('disciplinas', DisciplinaController::class)->except(['show']);

            // Turmas
            Route::resource('turmas', TurmaController::class)->except(['show']);

            // Alunos
            Route::resource('alunos', AlunoController::class)->except(['show']);

            // Roles por usuário da Escola
            Route::get('usuarios/{usuario}/roles', [EscolaUsuarioController::class, 'editRoles'])
                ->name('usuarios.roles.edit');
            Route::post('usuarios/{usuario}/roles', [EscolaUsuarioController::class, 'updateRoles'])
                ->name('usuarios.roles.update');

            // Vincular aluno existente à escola atual
            Route::post('alunos/{aluno}/vincular', [AlunoController::class, 'vincular'])
                ->name('alunos.vincular');

            // Enturmações (vínculos aluno–turma)
            Route::resource('enturmacao', EnturmacaoController::class)->except(['show']);
            Route::post('enturmacao/storeBatch', [EnturmacaoController::class, 'storeBatch'])
                ->name('enturmacao.storeBatch');

            // Lotação
            Route::resource('lotacao', LotacaoController::class)->except(['show']);
            Route::prefix('lotacao')->name('lotacao.')->group(function () {
                Route::get('diretor_turma', [DiretorTurmaController::class, 'index'])
                    ->name('diretor_turma.index');
                Route::post('diretor_turma/update', [DiretorTurmaController::class, 'update'])
                    ->name('diretor_turma.update');
                Route::delete('diretor_turma/{id}', [DiretorTurmaController::class, 'destroy'])
                    ->name('diretor_turma.destroy');
            });

            // Identidade visual
            Route::get('identidade', [IdentidadeController::class, 'edit'])
                ->name('identidade.edit');
            Route::post('identidade', [IdentidadeController::class, 'update'])
                ->name('identidade.update');

            // Regimento (painel da escola)
            Route::get('regimento', [RegimentoController::class, 'index'])->name('regimento.index');
            Route::post('regimento', [RegimentoController::class, 'update'])->name('regimento.update');

            // Motivos de Ocorrência
            Route::resource('motivos', ModeloMotivoController::class)->except(['show']);
            // Importar motivos de outras escolas
            Route::get('motivos/importar', [ModeloMotivoController::class, 'importar'])
                ->name('motivos.importar');
            Route::post('motivos/importar', [ModeloMotivoController::class, 'importarSalvar'])
                ->name('motivos.importar.salvar');

            // Uploads de fotos
            Route::get('alunos/{aluno}/foto', [AlunoFotoController::class, 'edit'])->name('alunos.foto.edit');
            Route::post('alunos/{aluno}/foto', [AlunoFotoController::class, 'update'])->name('alunos.foto.update');

            Route::get('alunos/fotos-lote', [AlunoFotoLoteController::class, 'index'])->name('alunos.fotos.lote');
            Route::post('alunos/fotos-lote', [AlunoFotoLoteController::class, 'store'])->name('alunos.fotos.lote.store');
        });

    /*
    |--------------------------------------------------------------------------
    | Rotas do Professor
    |--------------------------------------------------------------------------
    */
    Route::prefix('professor')
        ->middleware(['role:professor'])
        ->name('professor.')
        ->group(function () {

            // Painel e perfil
            Route::get('dashboard', [ProfessorDashboardController::class, 'index'])
                ->name('dashboard');

            Route::get('perfil', [PerfilController::class, 'index'])
                ->name('perfil');

            // Ofertas
            Route::prefix('ofertas')->name('ofertas.')->group(function () {
                Route::get('/', [OfertaController::class, 'index'])->name('index');
                Route::get('{oferta}/alunos', [OfertaController::class, 'alunos'])->name('alunos');
                Route::post('{oferta}/alunos', [OfertaController::class, 'alunosPost'])->name('alunos.post');

                // Ocorrências por oferta
                Route::get('{oferta}/ocorrencias/create', [OcorrenciaController::class, 'create'])
                    ->name('ocorrencias.create');
                Route::post('ocorrencias/store', [OcorrenciaController::class, 'store'])
                    ->name('ocorrencias.store');
            });

            // Ocorrências (rotas gerais)
            Route::prefix('ocorrencias')->name('ocorrencias.')->group(function () {
                Route::get('/', [OcorrenciaController::class, 'index'])->name('index');
                Route::get('{id}', [OcorrenciaController::class, 'show'])->name('show');
                Route::get('{id}/edit', [OcorrenciaController::class, 'edit'])->name('edit');
                Route::put('{id}', [OcorrenciaController::class, 'update'])->name('update');
                Route::delete('{id}', [OcorrenciaController::class, 'destroy'])->name('destroy');
                Route::patch('{id}/status', [OcorrenciaController::class, 'updateStatus'])->name('updateStatus');

                // Encaminhar / arquivar (somente diretor)
                Route::get('{id}/encaminhar', [OcorrenciaController::class, 'encaminhar'])
                    ->name('encaminhar');
                Route::post('{id}/encaminhar', [OcorrenciaController::class, 'salvarEncaminhamento'])
                    ->name('encaminhar.salvar');

                // Histórico do aluno
                Route::get('historico/{aluno}', [OcorrenciaController::class, 'historico'])
                    ->name('historico');

                // Histórico resumido (visual e PDF)
                Route::get('historico-resumido/{aluno}', [OcorrenciaController::class, 'historicoResumido'])
                    ->name('historico_resumido');
                Route::get('pdf/{aluno}', [OcorrenciaController::class, 'gerarPdf'])
                    ->name('pdf');
            });

            // Rota pública sob /professor (mantida como no original)
            Route::get('regimento/{school}', [RegimentoController::class, 'visualizar'])
                ->name('regimento.visualizar');

            // Relatórios
            Route::get('relatorios', [RelatorioController::class, 'index'])
                ->name('relatorios.index');
    });



});
public/.htaccess
<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews -Indexes
    </IfModule>

    RewriteEngine On

    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} (.+)/$
    RewriteRule ^ %1 [L,R=301]

    # Send Requests To Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>