From ea5893e9c488ee3113177c86a6b2398457249947 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 03:36:03 +0000 Subject: [PATCH 01/13] fix: replace localhost with machine IP in provision.sh and config to resolve CSRF issues - Added dynamic IP detection (`hostname -I`) with a manual fallback prompt in `provision.sh`. - Updated backend and frontend `.env` configurations to use the selected IP for `APP_URL`, `FRONTEND_URL`, `SANCTUM_STATEFUL_DOMAINS`, `VITE_API_URL`, and `VITE_WS_URL`. - Modified `back/src/config/cors.php` and `back/src/config/sanctum.php` to rely entirely on environment variables (`FRONTEND_URL` and `SANCTUM_STATEFUL_DOMAINS`) instead of hardcoding `localhost`, avoiding git diff pollution when `provision.sh` modifies `.env`. - Updated `provision.sh` terminal output to reflect the correct deployed URLs. Co-authored-by: Felkng <105019177+Felkng@users.noreply.github.com> --- back/src/config/cors.php | 2 +- back/src/config/sanctum.php | 2 +- provision.sh | 27 +++++++++++++++++++++------ 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/back/src/config/cors.php b/back/src/config/cors.php index 3430618..4fa2fa1 100755 --- a/back/src/config/cors.php +++ b/back/src/config/cors.php @@ -19,7 +19,7 @@ 'allowed_methods' => ['*'], - 'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:5173')], + 'allowed_origins' => [env('FRONTEND_URL')], 'allowed_origins_patterns' => [], diff --git a/back/src/config/sanctum.php b/back/src/config/sanctum.php index 35d75b3..bfffee3 100755 --- a/back/src/config/sanctum.php +++ b/back/src/config/sanctum.php @@ -17,7 +17,7 @@ 'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( '%s%s', - 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + '', Sanctum::currentApplicationUrlWithPort() ))), diff --git a/provision.sh b/provision.sh index 30ef6bf..1e76434 100755 --- a/provision.sh +++ b/provision.sh @@ -56,14 +56,26 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; read -p "Porta do Backend [8000]: " APP_PORT APP_PORT=${APP_PORT:-"8000"} + # Obtendo o IP da máquina + DETECTED_IP=$(hostname -I | awk '{print $1}') + read -p "IP da máquina [$DETECTED_IP]: " MACHINE_IP + MACHINE_IP=${MACHINE_IP:-$DETECTED_IP} + + if [ -z "$MACHINE_IP" ]; then + MACHINE_IP="127.0.0.1" + echo -e "${YELLOW}Aviso: IP não detectado/fornecido. Utilizando $MACHINE_IP como fallback.${NC}" + fi + # 2. Criação dos arquivos .env echo -e "\n${BLUE}[1/4] Configurando arquivos .env...${NC}" # Backend cp back/src/.env.example back/src/.env - sedi "s|APP_URL=.*|APP_URL=http://localhost:$APP_PORT|" back/src/.env + sedi "s|APP_URL=.*|APP_URL=http://$MACHINE_IP:$APP_PORT|" back/src/.env sedi "s|DB_PASSWORD=.*|DB_PASSWORD=$DB_PASSWORD|" back/src/.env sedi "s|APP_NAME=.*|APP_NAME=\"$APP_NAME\"|" back/src/.env + sedi "s|SANCTUM_STATEFUL_DOMAINS=.*|SANCTUM_STATEFUL_DOMAINS=$MACHINE_IP:5173,$MACHINE_IP|" back/src/.env + sedi "s|FRONTEND_URL=.*|FRONTEND_URL=http://$MACHINE_IP:5173|" back/src/.env # Judge0 cp judge0.conf.example judge0.conf @@ -71,13 +83,16 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; # Frontend cp front/.env.example front/.env - sedi "s|VITE_API_URL=.*|VITE_API_URL=http://localhost:$APP_PORT|" front/.env + sedi "s|VITE_API_URL=.*|VITE_API_URL=http://$MACHINE_IP:$APP_PORT|" front/.env + sedi "s|VITE_WS_URL=.*|VITE_WS_URL=ws://$MACHINE_IP:3002|" front/.env sedi "s|VITE_APP_NAME=.*|VITE_APP_NAME=\"$APP_NAME\"|" front/.env else echo -e "\n${GREEN}Configurações já existentes encontradas. Iniciando sistema...${NC}" - # Tenta extrair a porta do Backend do .env, assumindo 8000 como fallback - APP_PORT=$(grep "APP_URL=" back/src/.env | grep -o '[0-9]\+$') + # Tenta extrair a porta e o IP do Backend do .env + APP_PORT=$(grep "APP_URL=" back/src/.env | grep -o ':[0-9]\+' | tr -d ':') APP_PORT=${APP_PORT:-"8000"} + MACHINE_IP=$(grep "APP_URL=" back/src/.env | sed -E 's/.*http:\/\/([^:]+).*/\1/') + MACHINE_IP=${MACHINE_IP:-"127.0.0.1"} echo -e "${BLUE}[1/4] Arquivos .env carregados com sucesso.${NC}" fi @@ -106,8 +121,8 @@ fi echo -e "\n${GREEN}==========================================================${NC}" echo -e "${GREEN} SISTEMA INICIADO COM SUCESSO! ${NC}" echo -e "${GREEN}==========================================================${NC}" -echo -e "Frontend: http://localhost:5173" -echo -e "Backend: http://localhost:$APP_PORT" +echo -e "Frontend: http://$MACHINE_IP:5173" +echo -e "Backend: http://$MACHINE_IP:$APP_PORT" if [ "$IS_FIRST_RUN" = true ]; then echo -e "Credenciais Padrão: admin@admin.com / 12345678" fi From 5c1cebbc17a776ef6ad7ff7f76686c375981c233 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 12:01:05 +0000 Subject: [PATCH 02/13] fix: improve IP detection in provision.sh to avoid docker bridges - Filter out `172.*` and `127.*` IPs when extracting the LAN address. - Provide a clear fallback if no other valid IP is found. - Make the IP prompt more prominent for users running on a VPS or Local Network. Co-authored-by: Felkng <105019177+Felkng@users.noreply.github.com> --- provision.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/provision.sh b/provision.sh index 1e76434..f09f925 100755 --- a/provision.sh +++ b/provision.sh @@ -56,8 +56,15 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; read -p "Porta do Backend [8000]: " APP_PORT APP_PORT=${APP_PORT:-"8000"} - # Obtendo o IP da máquina - DETECTED_IP=$(hostname -I | awk '{print $1}') + # Obtendo o IP da máquina na rede local (ignorando IPs de Docker/VMs locais se possível) + DETECTED_IP=$(hostname -I | tr ' ' '\n' | grep -v '^172\.' | grep -v '^127\.' | head -n 1) + + if [ -z "$DETECTED_IP" ]; then + DETECTED_IP=$(hostname -I | awk '{print $1}') + fi + + echo -e "${YELLOW}Aviso: Se você estiver rodando em uma VPS, utilize o IP/DNS público.${NC}" + echo -e "${YELLOW} Se for rede local, confirme o IP LAN correto.${NC}" read -p "IP da máquina [$DETECTED_IP]: " MACHINE_IP MACHINE_IP=${MACHINE_IP:-$DETECTED_IP} From 4866505dc20462238ff3f97b234fbe4f29d92c0c Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 12:30:40 +0000 Subject: [PATCH 03/13] fix: address PR review feedback on provision.sh, cors.php, and sanctum.php - provision.sh: Fix URL extraction regex to support http/https and paths - cors.php: Restore localhost fallback - sanctum.php: Restore localhost fallbacks for stateful domains Co-authored-by: Felkng <105019177+Felkng@users.noreply.github.com> --- back/src/config/cors.php | 2 +- back/src/config/sanctum.php | 2 +- provision.sh | 21 ++++++++++++++------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/back/src/config/cors.php b/back/src/config/cors.php index 4fa2fa1..3430618 100755 --- a/back/src/config/cors.php +++ b/back/src/config/cors.php @@ -19,7 +19,7 @@ 'allowed_methods' => ['*'], - 'allowed_origins' => [env('FRONTEND_URL')], + 'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:5173')], 'allowed_origins_patterns' => [], diff --git a/back/src/config/sanctum.php b/back/src/config/sanctum.php index bfffee3..35d75b3 100755 --- a/back/src/config/sanctum.php +++ b/back/src/config/sanctum.php @@ -17,7 +17,7 @@ 'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( '%s%s', - '', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', Sanctum::currentApplicationUrlWithPort() ))), diff --git a/provision.sh b/provision.sh index f09f925..1260d8b 100755 --- a/provision.sh +++ b/provision.sh @@ -57,10 +57,13 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; APP_PORT=${APP_PORT:-"8000"} # Obtendo o IP da máquina na rede local (ignorando IPs de Docker/VMs locais se possível) - DETECTED_IP=$(hostname -I | tr ' ' '\n' | grep -v '^172\.' | grep -v '^127\.' | head -n 1) - - if [ -z "$DETECTED_IP" ]; then - DETECTED_IP=$(hostname -I | awk '{print $1}') + if [ "$(uname)" = "Darwin" ]; then + DETECTED_IP=$(ipconfig getifaddr en0 2>/dev/null || ipconfig getifaddr en1 2>/dev/null) + else + DETECTED_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -v '^172\.' | grep -v '^127\.' | head -n 1) + if [ -z "$DETECTED_IP" ]; then + DETECTED_IP=$(hostname -I 2>/dev/null | awk '{print $1}') + fi fi echo -e "${YELLOW}Aviso: Se você estiver rodando em uma VPS, utilize o IP/DNS público.${NC}" @@ -96,9 +99,13 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; else echo -e "\n${GREEN}Configurações já existentes encontradas. Iniciando sistema...${NC}" # Tenta extrair a porta e o IP do Backend do .env - APP_PORT=$(grep "APP_URL=" back/src/.env | grep -o ':[0-9]\+' | tr -d ':') - APP_PORT=${APP_PORT:-"8000"} - MACHINE_IP=$(grep "APP_URL=" back/src/.env | sed -E 's/.*http:\/\/([^:]+).*/\1/') + APP_URL_VAL=$(grep "^APP_URL=" back/src/.env | cut -d '=' -f2) + APP_PORT=$(echo "$APP_URL_VAL" | sed -E 's|.*://[^:]+:([0-9]+).*|\1|') + if ! [[ "$APP_PORT" =~ ^[0-9]+$ ]]; then + APP_PORT="8000" + fi + + MACHINE_IP=$(echo "$APP_URL_VAL" | sed -E 's|https?://||' | sed -E 's|[:/].*||') MACHINE_IP=${MACHINE_IP:-"127.0.0.1"} echo -e "${BLUE}[1/4] Arquivos .env carregados com sucesso.${NC}" From 5236c4242c699b9b99896f48ebc74e438d2f9927 Mon Sep 17 00:00:00 2001 From: Felipe Ferreira <105019177+Felkng@users.noreply.github.com> Date: Tue, 19 May 2026 09:40:02 -0300 Subject: [PATCH 04/13] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- provision.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/provision.sh b/provision.sh index 1260d8b..d5fcfe7 100755 --- a/provision.sh +++ b/provision.sh @@ -86,6 +86,7 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; sedi "s|APP_NAME=.*|APP_NAME=\"$APP_NAME\"|" back/src/.env sedi "s|SANCTUM_STATEFUL_DOMAINS=.*|SANCTUM_STATEFUL_DOMAINS=$MACHINE_IP:5173,$MACHINE_IP|" back/src/.env sedi "s|FRONTEND_URL=.*|FRONTEND_URL=http://$MACHINE_IP:5173|" back/src/.env + sedi "s|SESSION_DOMAIN=.*|SESSION_DOMAIN=|" back/src/.env # Judge0 cp judge0.conf.example judge0.conf From 66bf876465b4659e8e4cb5beb6c8dfdaf5a12944 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 12:58:47 +0000 Subject: [PATCH 05/13] fix: enforce ipv4 and set session domain dynamically - Filter out IPv6 in provision.sh hostname -I check to prevent malformed urls. - Ignore only the default docker bridge 172.17.x to preserve 172.16+ LAN subnets. - Replace SESSION_DOMAIN dynamically in .env via provision.sh. - Modify session.php config to treat empty strings as null to prevent invalid Set-Cookie domain attributes. Co-authored-by: Felkng <105019177+Felkng@users.noreply.github.com> --- back/src/config/session.php | 2 +- provision.sh | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/back/src/config/session.php b/back/src/config/session.php index 16cc15a..5a62ce8 100755 --- a/back/src/config/session.php +++ b/back/src/config/session.php @@ -155,7 +155,7 @@ | */ - 'domain' => env('SESSION_DOMAIN', 'localhost'), + 'domain' => env('SESSION_DOMAIN') === '' ? null : env('SESSION_DOMAIN', 'localhost'), /* |-------------------------------------------------------------------------- diff --git a/provision.sh b/provision.sh index d5fcfe7..091a3f7 100755 --- a/provision.sh +++ b/provision.sh @@ -56,13 +56,16 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; read -p "Porta do Backend [8000]: " APP_PORT APP_PORT=${APP_PORT:-"8000"} - # Obtendo o IP da máquina na rede local (ignorando IPs de Docker/VMs locais se possível) + # Obtendo o IP da máquina na rede local (ignorando IPs de Docker default e loopback, e forçando IPv4) if [ "$(uname)" = "Darwin" ]; then DETECTED_IP=$(ipconfig getifaddr en0 2>/dev/null || ipconfig getifaddr en1 2>/dev/null) else - DETECTED_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -v '^172\.' | grep -v '^127\.' | head -n 1) + # Filtra apenas IPv4, remove loopback (127.) e a subnet default do Docker (172.17.), + # mas preserva outras possíveis LANs (como 172.16., 172.18., etc) + DETECTED_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | grep -v '^127\.' | grep -v '^172\.17\.' | head -n 1) if [ -z "$DETECTED_IP" ]; then - DETECTED_IP=$(hostname -I 2>/dev/null | awk '{print $1}') + # Fallback pegando o primeiro IPv4 caso o filtro acima falhe + DETECTED_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1) fi fi @@ -84,9 +87,9 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; sedi "s|APP_URL=.*|APP_URL=http://$MACHINE_IP:$APP_PORT|" back/src/.env sedi "s|DB_PASSWORD=.*|DB_PASSWORD=$DB_PASSWORD|" back/src/.env sedi "s|APP_NAME=.*|APP_NAME=\"$APP_NAME\"|" back/src/.env + sedi "s|SESSION_DOMAIN=.*|SESSION_DOMAIN=$MACHINE_IP|" back/src/.env sedi "s|SANCTUM_STATEFUL_DOMAINS=.*|SANCTUM_STATEFUL_DOMAINS=$MACHINE_IP:5173,$MACHINE_IP|" back/src/.env sedi "s|FRONTEND_URL=.*|FRONTEND_URL=http://$MACHINE_IP:5173|" back/src/.env - sedi "s|SESSION_DOMAIN=.*|SESSION_DOMAIN=|" back/src/.env # Judge0 cp judge0.conf.example judge0.conf From 0c8a9cd3dc34f14a09433eaf38b77777381fc36f Mon Sep 17 00:00:00 2001 From: felkng Date: Tue, 19 May 2026 10:42:10 -0300 Subject: [PATCH 06/13] fix(auth): resolve mixed content and CSRF issues for tunnels - Configure Vite proxy to route API/Auth requests to the internal 'backend_app' service, resolving Mixed Content errors when using HTTPS tunnels. - Update frontend services to use relative API paths, allowing the Vite proxy to handle environment-specific routing. - Modify Laravel configuration to trust all proxies and support multiple origins in CORS. - Set session cookies to 'secure' conditionally in .env to support HTTPS tunnels. - Enhance provision.sh to prompt for optional public domains/tunnels and automatically configure Vite and Laravel environments. --- back/src/app/Http/Middleware/TrustProxies.php | 2 +- back/src/config/cors.php | 2 +- back/src/config/session.php | 2 +- front/src/main.tsx | 2 +- front/src/services/ClassesService.ts | 2 +- front/src/services/JamSessionService.ts | 2 +- front/src/services/ProblemsServices.ts | 2 +- front/src/services/SubmissionsService.ts | 2 +- front/vite.config.ts | 20 ++++++++++++++++++- provision.sh | 15 ++++++++++++-- 10 files changed, 40 insertions(+), 11 deletions(-) diff --git a/back/src/app/Http/Middleware/TrustProxies.php b/back/src/app/Http/Middleware/TrustProxies.php index 3391630..559dd2f 100755 --- a/back/src/app/Http/Middleware/TrustProxies.php +++ b/back/src/app/Http/Middleware/TrustProxies.php @@ -12,7 +12,7 @@ class TrustProxies extends Middleware * * @var array|string|null */ - protected $proxies; + protected $proxies = '*'; /** * The headers that should be used to detect proxies. diff --git a/back/src/config/cors.php b/back/src/config/cors.php index 3430618..fd32127 100755 --- a/back/src/config/cors.php +++ b/back/src/config/cors.php @@ -19,7 +19,7 @@ 'allowed_methods' => ['*'], - 'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:5173')], + 'allowed_origins' => explode(',', env('FRONTEND_URL', 'http://localhost:5173')), 'allowed_origins_patterns' => [], diff --git a/back/src/config/session.php b/back/src/config/session.php index 5a62ce8..b02b68d 100755 --- a/back/src/config/session.php +++ b/back/src/config/session.php @@ -168,7 +168,7 @@ | */ - 'secure' => env('SESSION_SECURE_COOKIE'), + 'secure' => env('SESSION_SECURE_COOKIE', false), /* |-------------------------------------------------------------------------- diff --git a/front/src/main.tsx b/front/src/main.tsx index 52aade6..d6067b9 100644 --- a/front/src/main.tsx +++ b/front/src/main.tsx @@ -8,7 +8,7 @@ import RealtimeNotifications from "./components/RealtimeNotifications.tsx"; import axios from "axios"; // Configure axios defaults for the app: base URL and send credentials (for Sanctum) -const API_URL = import.meta.env?.VITE_API_URL || "http://localhost:8000"; +const API_URL = import.meta.env?.VITE_API_URL || ""; axios.defaults.baseURL = API_URL; axios.defaults.withCredentials = true; diff --git a/front/src/services/ClassesService.ts b/front/src/services/ClassesService.ts index 66efd46..4493766 100644 --- a/front/src/services/ClassesService.ts +++ b/front/src/services/ClassesService.ts @@ -8,7 +8,7 @@ import type { AddStudentToClassDTO, } from "@/types/classes"; -const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8000/api"; +const API_URL = import.meta.env.VITE_API_URL || ""; const api = axios.create({ baseURL: API_URL, diff --git a/front/src/services/JamSessionService.ts b/front/src/services/JamSessionService.ts index c75aff0..79b7344 100644 --- a/front/src/services/JamSessionService.ts +++ b/front/src/services/JamSessionService.ts @@ -2,7 +2,7 @@ import axios from "axios"; import Cookies from "js-cookie"; import type { JamSession, CreateJamSessionDTO, JamParticipant } from "@/types/jam"; -const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8000/api"; +const API_URL = import.meta.env.VITE_API_URL || ""; const WS_URL = import.meta.env.VITE_WS_URL || "ws://localhost:3002"; const api = axios.create({ diff --git a/front/src/services/ProblemsServices.ts b/front/src/services/ProblemsServices.ts index 5dc01dc..5585f8c 100644 --- a/front/src/services/ProblemsServices.ts +++ b/front/src/services/ProblemsServices.ts @@ -2,7 +2,7 @@ import type { Problem } from "../types"; import axios from "axios"; -const API_URL = (import.meta as any).env?.VITE_API_URL || "http://localhost:8000"; +const API_URL = (import.meta as any).env?.VITE_API_URL || ""; /** * Simula uma chamada de API para buscar um problema pelo id. diff --git a/front/src/services/SubmissionsService.ts b/front/src/services/SubmissionsService.ts index 195da35..b846caa 100644 --- a/front/src/services/SubmissionsService.ts +++ b/front/src/services/SubmissionsService.ts @@ -28,7 +28,7 @@ import { fakeSubmissions } from "../mocks"; import axios from "axios"; import Cookies from "js-cookie"; -const API_URL = (import.meta as any).env?.VITE_API_URL || "http://localhost:8000"; +const API_URL = (import.meta as any).env?.VITE_API_URL || ""; /** * Mapeia language_id do Judge0 para slug usado no frontend diff --git a/front/vite.config.ts b/front/vite.config.ts index c9652ef..90030dd 100644 --- a/front/vite.config.ts +++ b/front/vite.config.ts @@ -12,7 +12,25 @@ export default defineConfig(({ mode }) => ({ }, }, server: { - allowedHosts: ["ifcodes.cloud"], + allowedHosts: ["localhost", "127.0.0.1"], + proxy: { + '/api': { + target: 'http://backend_app:8000', + changeOrigin: false, + }, + '/sanctum': { + target: 'http://backend_app:8000', + changeOrigin: false, + }, + '/login': { + target: 'http://backend_app:8000', + changeOrigin: false, + }, + '/logout': { + target: 'http://backend_app:8000', + changeOrigin: false, + }, + }, watch: mode === "development" ? { usePolling: true } : undefined, }, define: { diff --git a/provision.sh b/provision.sh index 091a3f7..2611b40 100755 --- a/provision.sh +++ b/provision.sh @@ -79,6 +79,10 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; echo -e "${YELLOW}Aviso: IP não detectado/fornecido. Utilizando $MACHINE_IP como fallback.${NC}" fi + echo -e "\n${YELLOW}--- Configuração de Domínio/Tunnel (Opcional) ---${NC}" + echo -e "${YELLOW}Se você usa ngrok ou tem um domínio público, insira-o aqui.${NC}" + read -p "Domínio/Tunnel (ex: meudominio.com ou tunnel.ngrok-free.dev): " PUBLIC_DOMAIN + # 2. Criação dos arquivos .env echo -e "\n${BLUE}[1/4] Configurando arquivos .env...${NC}" @@ -88,8 +92,15 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; sedi "s|DB_PASSWORD=.*|DB_PASSWORD=$DB_PASSWORD|" back/src/.env sedi "s|APP_NAME=.*|APP_NAME=\"$APP_NAME\"|" back/src/.env sedi "s|SESSION_DOMAIN=.*|SESSION_DOMAIN=$MACHINE_IP|" back/src/.env - sedi "s|SANCTUM_STATEFUL_DOMAINS=.*|SANCTUM_STATEFUL_DOMAINS=$MACHINE_IP:5173,$MACHINE_IP|" back/src/.env - sedi "s|FRONTEND_URL=.*|FRONTEND_URL=http://$MACHINE_IP:5173|" back/src/.env + + if [ -n "$PUBLIC_DOMAIN" ]; then + sedi "s|SANCTUM_STATEFUL_DOMAINS=.*|SANCTUM_STATEFUL_DOMAINS=$MACHINE_IP:5173,$MACHINE_IP,$PUBLIC_DOMAIN|" back/src/.env + sedi "s|FRONTEND_URL=.*|FRONTEND_URL=http://$MACHINE_IP:5173,https://$PUBLIC_DOMAIN|" back/src/.env + sedi "s|allowedHosts:.*|allowedHosts: [\"localhost\", \"127.0.0.1\", \"$PUBLIC_DOMAIN\"],|" front/vite.config.ts + else + sedi "s|SANCTUM_STATEFUL_DOMAINS=.*|SANCTUM_STATEFUL_DOMAINS=$MACHINE_IP:5173,$MACHINE_IP|" back/src/.env + sedi "s|FRONTEND_URL=.*|FRONTEND_URL=http://$MACHINE_IP:5173|" back/src/.env + fi # Judge0 cp judge0.conf.example judge0.conf From 2f880f200dbafff27ae933810426410fbeadd503 Mon Sep 17 00:00:00 2001 From: felkng Date: Tue, 19 May 2026 10:48:04 -0300 Subject: [PATCH 07/13] fix(auth): enforce relative paths and proxy usage to resolve Mixed Content - Remove all absolute URL references using VITE_API_URL across frontend services. - Enforce relative paths for all API and Sanctum requests to ensure they go through the Vite proxy. - Update provision.sh to keep VITE_API_URL empty by default, preventing absolute URL generation during setup. - Re-enable SESSION_SECURE_COOKIE=true to ensure browser compatibility with HTTPS tunnels. --- front/src/services/ActivitiesService.ts | 2 +- front/src/services/ChangePasswordService.ts | 4 ++-- front/src/services/ClassesService.ts | 2 +- front/src/services/CoursesService.ts | 2 +- front/src/services/ForgotPasswordService.ts | 8 ++++---- front/src/services/JamSessionService.ts | 2 +- front/src/services/LoginService.ts | 4 ++-- front/src/services/ProfessorsService.ts | 16 ++++++++-------- front/src/services/ProfileService.ts | 2 +- front/src/services/StudentsService.ts | 16 ++++++++-------- front/vite.config.ts | 2 +- provision.sh | 2 +- 12 files changed, 31 insertions(+), 31 deletions(-) diff --git a/front/src/services/ActivitiesService.ts b/front/src/services/ActivitiesService.ts index cf34121..10b15cb 100644 --- a/front/src/services/ActivitiesService.ts +++ b/front/src/services/ActivitiesService.ts @@ -4,7 +4,7 @@ import { fakePageActivities } from "../mocks"; import axios from "axios"; import Cookies from "js-cookie"; -const API_URL = import.meta.env.VITE_API_URL; +const API_URL = ""; function getHeaders() { return { diff --git a/front/src/services/ChangePasswordService.ts b/front/src/services/ChangePasswordService.ts index ec5b8d7..ceb83aa 100644 --- a/front/src/services/ChangePasswordService.ts +++ b/front/src/services/ChangePasswordService.ts @@ -8,11 +8,11 @@ export async function changePassword({ newPasswordConfirmation }: ChangePasswordRequest): Promise { - await axios.get(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, { + await axios.get(`/sanctum/csrf-cookie`, { withCredentials: true }); - await axios.post(`${import.meta.env.VITE_API_URL}/api/user/change-password`, { + await axios.post(`/api/user/change-password`, { current_password: currentPassword, new_password: newPassword, new_password_confirmation: newPasswordConfirmation diff --git a/front/src/services/ClassesService.ts b/front/src/services/ClassesService.ts index 4493766..b99450a 100644 --- a/front/src/services/ClassesService.ts +++ b/front/src/services/ClassesService.ts @@ -8,7 +8,7 @@ import type { AddStudentToClassDTO, } from "@/types/classes"; -const API_URL = import.meta.env.VITE_API_URL || ""; +const API_URL = ""; const api = axios.create({ baseURL: API_URL, diff --git a/front/src/services/CoursesService.ts b/front/src/services/CoursesService.ts index 65b8bc3..fc1c50f 100644 --- a/front/src/services/CoursesService.ts +++ b/front/src/services/CoursesService.ts @@ -32,7 +32,7 @@ function handleAuthError(error: unknown) { */ export async function getAllCourses(): Promise { try { - const response = await axios.get(`${import.meta.env.VITE_API_URL}/api/cursos`, { + const response = await axios.get(`/api/cursos`, { headers: getAuthHeaders(), withCredentials: true, }); diff --git a/front/src/services/ForgotPasswordService.ts b/front/src/services/ForgotPasswordService.ts index d3a84ae..ac95bc3 100644 --- a/front/src/services/ForgotPasswordService.ts +++ b/front/src/services/ForgotPasswordService.ts @@ -2,11 +2,11 @@ import axios from "axios"; import Cookies from "js-cookie"; export async function sendForgotPasswordEmail(email: string): Promise { - await axios.get(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, { + await axios.get(`/sanctum/csrf-cookie`, { withCredentials: true }); - await axios.post(`${import.meta.env.VITE_API_URL}/api/forgot-password-temp`, { + await axios.post(`/api/forgot-password-temp`, { email }, { withCredentials: true, @@ -24,11 +24,11 @@ export async function resetPassword(data: { password: string; password_confirmation: string; }): Promise { - await axios.get(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, { + await axios.get(`/sanctum/csrf-cookie`, { withCredentials: true }); - await axios.post(`${import.meta.env.VITE_API_URL}/reset-password`, data, { + await axios.post(`/reset-password`, data, { withCredentials: true, headers: { 'Content-Type': 'application/json', diff --git a/front/src/services/JamSessionService.ts b/front/src/services/JamSessionService.ts index 79b7344..ec0ddb2 100644 --- a/front/src/services/JamSessionService.ts +++ b/front/src/services/JamSessionService.ts @@ -2,7 +2,7 @@ import axios from "axios"; import Cookies from "js-cookie"; import type { JamSession, CreateJamSessionDTO, JamParticipant } from "@/types/jam"; -const API_URL = import.meta.env.VITE_API_URL || ""; +const API_URL = ""; const WS_URL = import.meta.env.VITE_WS_URL || "ws://localhost:3002"; const api = axios.create({ diff --git a/front/src/services/LoginService.ts b/front/src/services/LoginService.ts index 9940c5a..8e66714 100644 --- a/front/src/services/LoginService.ts +++ b/front/src/services/LoginService.ts @@ -4,12 +4,12 @@ import Cookies from "js-cookie"; export async function login({ email, password }: LoginRequest): Promise{ - await axios.get(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, { + await axios.get(`/sanctum/csrf-cookie`, { withCredentials: true }); - const res = await axios.post(`${import.meta.env.VITE_API_URL}/login`, { + const res = await axios.post(`/login`, { email, password }, { diff --git a/front/src/services/ProfessorsService.ts b/front/src/services/ProfessorsService.ts index e74764c..e2dfc50 100644 --- a/front/src/services/ProfessorsService.ts +++ b/front/src/services/ProfessorsService.ts @@ -28,7 +28,7 @@ function handleAuthError(error: unknown) { */ export async function getAllProfessors(): Promise { try { - const response = await axios.get(`${import.meta.env.VITE_API_URL}/api/professores`, { + const response = await axios.get(`/api/professores`, { headers: getAuthHeaders(), withCredentials: true, }); @@ -48,7 +48,7 @@ export async function getAllProfessors(): Promise { */ export async function getProfessorById(id: number): Promise { try { - const response = await axios.get(`${import.meta.env.VITE_API_URL}/api/professores/${id}`, { + const response = await axios.get(`/api/professores/${id}`, { headers: getAuthHeaders(), withCredentials: true, }); @@ -68,12 +68,12 @@ export async function getProfessorById(id: number): Promise): Promise { try { // Obter CSRF token antes de criar - await axios.get(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, { + await axios.get(`/sanctum/csrf-cookie`, { withCredentials: true, }); const response = await axios.post( - `${import.meta.env.VITE_API_URL}/api/professores`, + `/api/professores`, professor, { headers: getAuthHeaders(), @@ -96,12 +96,12 @@ export async function createProfessor(professor: Omit): Promise export async function updateProfessor(id: number, professor: Partial): Promise { try { // Obter CSRF token antes de atualizar - await axios.get(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, { + await axios.get(`/sanctum/csrf-cookie`, { withCredentials: true, }); const response = await axios.put( - `${import.meta.env.VITE_API_URL}/api/professores/${id}`, + `/api/professores/${id}`, professor, { headers: getAuthHeaders(), @@ -123,11 +123,11 @@ export async function updateProfessor(id: number, professor: Partial) export async function deleteProfessor(id: number): Promise { try { // Obter CSRF token antes de deletar - await axios.get(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, { + await axios.get(`/sanctum/csrf-cookie`, { withCredentials: true, }); - await axios.delete(`${import.meta.env.VITE_API_URL}/api/professores/${id}`, { + await axios.delete(`/api/professores/${id}`, { headers: getAuthHeaders(), withCredentials: true, }); diff --git a/front/src/services/ProfileService.ts b/front/src/services/ProfileService.ts index bb39ecd..8c8bbd2 100644 --- a/front/src/services/ProfileService.ts +++ b/front/src/services/ProfileService.ts @@ -2,7 +2,7 @@ import axios from "axios"; export async function updateName(name: string, token: string) { const res = await axios.patch( - `${import.meta.env.VITE_API_URL}/api/user`, + `/api/user`, { name }, { headers: { diff --git a/front/src/services/StudentsService.ts b/front/src/services/StudentsService.ts index 4611449..5fec97d 100644 --- a/front/src/services/StudentsService.ts +++ b/front/src/services/StudentsService.ts @@ -28,7 +28,7 @@ function handleAuthError(error: unknown) { */ export async function getAllStudents(): Promise { try { - const response = await axios.get(`${import.meta.env.VITE_API_URL}/api/alunos`, { + const response = await axios.get(`/api/alunos`, { headers: getAuthHeaders(), withCredentials: true, }); @@ -61,7 +61,7 @@ export async function getAllStudents(): Promise { */ export async function getStudentById(id: number): Promise { try { - const response = await axios.get(`${import.meta.env.VITE_API_URL}/api/alunos/${id}`, { + const response = await axios.get(`/api/alunos/${id}`, { headers: getAuthHeaders(), withCredentials: true, }); @@ -81,12 +81,12 @@ export async function getStudentById(id: number): Promise { export async function createStudent(student: Omit): Promise { try { // Obter CSRF token antes de criar - await axios.get(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, { + await axios.get(`/sanctum/csrf-cookie`, { withCredentials: true, }); const response = await axios.post( - `${import.meta.env.VITE_API_URL}/api/alunos`, + `/api/alunos`, student, { headers: getAuthHeaders(), @@ -109,12 +109,12 @@ export async function createStudent(student: Omit): Promise): Promise { try { // Obter CSRF token antes de atualizar - await axios.get(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, { + await axios.get(`/sanctum/csrf-cookie`, { withCredentials: true, }); const response = await axios.put( - `${import.meta.env.VITE_API_URL}/api/alunos/${id}`, + `/api/alunos/${id}`, student, { headers: getAuthHeaders(), @@ -136,11 +136,11 @@ export async function updateStudent(id: number, student: Partial): Prom export async function deleteStudent(id: number): Promise { try { // Obter CSRF token antes de deletar - await axios.get(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, { + await axios.get(`/sanctum/csrf-cookie`, { withCredentials: true, }); - await axios.delete(`${import.meta.env.VITE_API_URL}/api/alunos/${id}`, { + await axios.delete(`/api/alunos/${id}`, { headers: getAuthHeaders(), withCredentials: true, }); diff --git a/front/vite.config.ts b/front/vite.config.ts index 90030dd..416bf29 100644 --- a/front/vite.config.ts +++ b/front/vite.config.ts @@ -12,7 +12,7 @@ export default defineConfig(({ mode }) => ({ }, }, server: { - allowedHosts: ["localhost", "127.0.0.1"], + allowedHosts: ["localhost", "127.0.0.1", "cleora-noncongratulatory-effortfully.ngrok-free.dev"], proxy: { '/api': { target: 'http://backend_app:8000', diff --git a/provision.sh b/provision.sh index 2611b40..2a88477 100755 --- a/provision.sh +++ b/provision.sh @@ -108,7 +108,7 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; # Frontend cp front/.env.example front/.env - sedi "s|VITE_API_URL=.*|VITE_API_URL=http://$MACHINE_IP:$APP_PORT|" front/.env + sedi "s|VITE_API_URL=.*|VITE_API_URL=|" front/.env sedi "s|VITE_WS_URL=.*|VITE_WS_URL=ws://$MACHINE_IP:3002|" front/.env sedi "s|VITE_APP_NAME=.*|VITE_APP_NAME=\"$APP_NAME\"|" front/.env else From 0c7c95d5d38f82c8e44a2d432d7362ab154ce9c4 Mon Sep 17 00:00:00 2001 From: felkng Date: Tue, 19 May 2026 10:55:02 -0300 Subject: [PATCH 08/13] fix(provision): ensure correct cookie and proxy settings for public domains - Update provision.sh to clear SESSION_DOMAIN when a public domain is used, preventing CSRF 419 errors due to domain mismatch. - Force SESSION_SECURE_COOKIE=true and changeOrigin: true in the Vite proxy when a public tunnel/domain is configured. - Ensure consistent environment setup across repeated runs of the provisioning script. --- front/vite.config.ts | 8 ++++---- provision.sh | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/front/vite.config.ts b/front/vite.config.ts index 416bf29..68dfd4d 100644 --- a/front/vite.config.ts +++ b/front/vite.config.ts @@ -16,19 +16,19 @@ export default defineConfig(({ mode }) => ({ proxy: { '/api': { target: 'http://backend_app:8000', - changeOrigin: false, + changeOrigin: true, }, '/sanctum': { target: 'http://backend_app:8000', - changeOrigin: false, + changeOrigin: true, }, '/login': { target: 'http://backend_app:8000', - changeOrigin: false, + changeOrigin: true, }, '/logout': { target: 'http://backend_app:8000', - changeOrigin: false, + changeOrigin: true, }, }, watch: mode === "development" ? { usePolling: true } : undefined, diff --git a/provision.sh b/provision.sh index 2a88477..420c62f 100755 --- a/provision.sh +++ b/provision.sh @@ -91,13 +91,16 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; sedi "s|APP_URL=.*|APP_URL=http://$MACHINE_IP:$APP_PORT|" back/src/.env sedi "s|DB_PASSWORD=.*|DB_PASSWORD=$DB_PASSWORD|" back/src/.env sedi "s|APP_NAME=.*|APP_NAME=\"$APP_NAME\"|" back/src/.env - sedi "s|SESSION_DOMAIN=.*|SESSION_DOMAIN=$MACHINE_IP|" back/src/.env if [ -n "$PUBLIC_DOMAIN" ]; then + sedi "s|SESSION_DOMAIN=.*|SESSION_DOMAIN=|" back/src/.env sedi "s|SANCTUM_STATEFUL_DOMAINS=.*|SANCTUM_STATEFUL_DOMAINS=$MACHINE_IP:5173,$MACHINE_IP,$PUBLIC_DOMAIN|" back/src/.env sedi "s|FRONTEND_URL=.*|FRONTEND_URL=http://$MACHINE_IP:5173,https://$PUBLIC_DOMAIN|" back/src/.env sedi "s|allowedHosts:.*|allowedHosts: [\"localhost\", \"127.0.0.1\", \"$PUBLIC_DOMAIN\"],|" front/vite.config.ts + sedi "s|SESSION_SECURE_COOKIE=.*|SESSION_SECURE_COOKIE=true|" back/src/.env + sedi "s|changeOrigin: false|changeOrigin: true|g" front/vite.config.ts else + sedi "s|SESSION_DOMAIN=.*|SESSION_DOMAIN=$MACHINE_IP|" back/src/.env sedi "s|SANCTUM_STATEFUL_DOMAINS=.*|SANCTUM_STATEFUL_DOMAINS=$MACHINE_IP:5173,$MACHINE_IP|" back/src/.env sedi "s|FRONTEND_URL=.*|FRONTEND_URL=http://$MACHINE_IP:5173|" back/src/.env fi From 89c39ab6f78979f025d72329e8c91653965736c2 Mon Sep 17 00:00:00 2001 From: felkng Date: Tue, 19 May 2026 17:19:48 -0300 Subject: [PATCH 09/13] fix(api): use relative paths and fix auth token keys Standardize API requests to use relative paths to work correctly with the Vite proxy and ensure consistent authentication token usage across services. - Prepend leading slashes to all API endpoints in frontend services to avoid relative path resolution issues. - Fix inconsistent localStorage token key by changing 'token' to 'auth_token' in ClassesService interceptor. - Remove hardcoded or potentially undefined VITE_API_URL prefix from direct axios calls in Header, Login, and UserContext. - Add withCredentials: true and CSRF cookie fetching to ProblemsServices for proper Sanctum session handling. - Update ProblemsServices to correctly handle paginated responses. --- front/src/components/Header.tsx | 4 +- front/src/context/UserContext.tsx | 4 +- front/src/pages/login/Login.tsx | 4 +- front/src/services/ClassesService.ts | 24 ++++----- front/src/services/JamSessionService.ts | 16 +++--- front/src/services/ProblemsServices.ts | 66 ++++++++++++++----------- 6 files changed, 63 insertions(+), 55 deletions(-) diff --git a/front/src/components/Header.tsx b/front/src/components/Header.tsx index 53c9f0e..6a743b2 100644 --- a/front/src/components/Header.tsx +++ b/front/src/components/Header.tsx @@ -86,11 +86,11 @@ export default function Header() { async function handleLogout() { try { - await axios.get(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, { + await axios.get(`/sanctum/csrf-cookie`, { withCredentials: true, }); await axios.post( - `${import.meta.env.VITE_API_URL}/logout`, + `/logout`, {}, { headers: { diff --git a/front/src/context/UserContext.tsx b/front/src/context/UserContext.tsx index 1460257..90a2fd4 100644 --- a/front/src/context/UserContext.tsx +++ b/front/src/context/UserContext.tsx @@ -17,13 +17,13 @@ export function UserProvider({ children }: { children: React.ReactNode }) { const token = localStorage.getItem("auth_token"); if (token) { Promise.all([ - axios.get(`${import.meta.env.VITE_API_URL}/api/user`, { + axios.get(`/api/user`, { headers: { Authorization: `Bearer ${token}`, Accept: "application/json" } }), - axios.get(`${import.meta.env.VITE_API_URL}/api/user/roles`, { + axios.get(`/api/user/roles`, { headers: { Authorization: `Bearer ${token}`, Accept: "application/json" diff --git a/front/src/pages/login/Login.tsx b/front/src/pages/login/Login.tsx index f588df0..6ad8771 100644 --- a/front/src/pages/login/Login.tsx +++ b/front/src/pages/login/Login.tsx @@ -47,13 +47,13 @@ export default function Login() { localStorage.setItem("auth_token", token); const [userRes, rolesRes] = await Promise.all([ - axios.get(`${import.meta.env.VITE_API_URL}/api/user`, { + axios.get(`/api/user`, { headers: { Authorization: `Bearer ${token}`, Accept: "application/json", }, }), - axios.get(`${import.meta.env.VITE_API_URL}/api/user/roles`, { + axios.get(`/api/user/roles`, { headers: { Authorization: `Bearer ${token}`, Accept: "application/json", diff --git a/front/src/services/ClassesService.ts b/front/src/services/ClassesService.ts index b99450a..d3e212d 100644 --- a/front/src/services/ClassesService.ts +++ b/front/src/services/ClassesService.ts @@ -8,7 +8,7 @@ import type { AddStudentToClassDTO, } from "@/types/classes"; -const API_URL = ""; +const API_URL = import.meta.env?.VITE_API_URL || ""; const api = axios.create({ baseURL: API_URL, @@ -39,7 +39,7 @@ function handleAuthError(error: unknown) { // Interceptor para adicionar token se necessário api.interceptors.request.use((config) => { - const token = localStorage.getItem("token"); + const token = localStorage.getItem("auth_token"); if (token) { config.headers.Authorization = `Bearer ${token}`; } @@ -49,7 +49,7 @@ api.interceptors.request.use((config) => { export const ClassesService = { // Listar todas as turmas getAllClasses: async (): Promise => { - const response = await api.get("api/turmas", { + const response = await api.get("/api/turmas", { headers:getAuthHeaders(), withCredentials:true }); @@ -58,7 +58,7 @@ export const ClassesService = { // Buscar turma por ID getClassById: async (id: number): Promise => { - const response = await api.get(`api/turmas/${id}`, { + const response = await api.get(`/api/turmas/${id}`, { headers:getAuthHeaders(), withCredentials:true }); @@ -67,7 +67,7 @@ export const ClassesService = { // Criar nova turma createClass: async (data: CreateClassDTO): Promise => { - const response = await api.post("api/turmas", data, { + const response = await api.post("/api/turmas", data, { headers:getAuthHeaders(), withCredentials:true }); @@ -76,7 +76,7 @@ export const ClassesService = { // Atualizar turma updateClass: async (id: number, data: UpdateClassDTO): Promise => { - const response = await api.put(`api/turmas/${id}`, data, { + const response = await api.put(`/api/turmas/${id}`, data, { headers:getAuthHeaders(), withCredentials:true }); @@ -85,7 +85,7 @@ export const ClassesService = { // Deletar turma deleteClass: async (id: number): Promise => { - await api.delete(`api/turmas/${id}`, { + await api.delete(`/api/turmas/${id}`, { headers:getAuthHeaders(), withCredentials:true }); @@ -93,7 +93,7 @@ export const ClassesService = { // Buscar turmas de um professor getClassesByTeacher: async (): Promise => { - const response = await api.get(`api/turmas`, { + const response = await api.get(`/api/turmas`, { headers:getAuthHeaders(), withCredentials:true }); @@ -102,7 +102,7 @@ export const ClassesService = { // Buscar turmas de um aluno getClassesByStudent: async (): Promise => { - const response = await api.get(`api/turmas`, { + const response = await api.get(`/api/turmas`, { headers:getAuthHeaders(), withCredentials:true }); @@ -112,7 +112,7 @@ export const ClassesService = { // Buscar alunos de uma turma getClassStudents: async (id: number): Promise => { try { - const response = await api.get(`api/turmas/${id}`, { + const response = await api.get(`/api/turmas/${id}`, { headers: getAuthHeaders(), withCredentials: true }); @@ -139,7 +139,7 @@ export const ClassesService = { // Adicionar aluno a uma turma addStudentToClass: async (classId: number, data: AddStudentToClassDTO): Promise => { try { - await api.post(`api/turmas/${classId}/vincular-aluno/${data.studentId}`, {}, { + await api.post(`/api/turmas/${classId}/vincular-aluno/${data.studentId}`, {}, { headers: getAuthHeaders(), withCredentials: true }); @@ -152,7 +152,7 @@ export const ClassesService = { // Remover aluno de uma turma removeStudentFromClass: async (classId: number, studentId: number): Promise => { try { - await api.delete(`api/turmas/${classId}/desvincular-aluno/${studentId}`, { + await api.delete(`/api/turmas/${classId}/desvincular-aluno/${studentId}`, { headers: getAuthHeaders(), withCredentials: true }); diff --git a/front/src/services/JamSessionService.ts b/front/src/services/JamSessionService.ts index ec0ddb2..d9b7de1 100644 --- a/front/src/services/JamSessionService.ts +++ b/front/src/services/JamSessionService.ts @@ -2,7 +2,7 @@ import axios from "axios"; import Cookies from "js-cookie"; import type { JamSession, CreateJamSessionDTO, JamParticipant } from "@/types/jam"; -const API_URL = ""; +const API_URL = import.meta.env?.VITE_API_URL || ""; const WS_URL = import.meta.env.VITE_WS_URL || "ws://localhost:3002"; const api = axios.create({ @@ -24,7 +24,7 @@ function getAuthHeaders() { export const JamSessionService = { getByTurma: async (turmaId: number): Promise => { - const response = await api.get(`api/jam-sessions?turma_id=${turmaId}`, { + const response = await api.get(`/api/jam-sessions?turma_id=${turmaId}`, { headers: getAuthHeaders(), withCredentials: true, }); @@ -32,7 +32,7 @@ export const JamSessionService = { }, getById: async (id: number): Promise => { - const response = await api.get(`api/jam-sessions/${id}`, { + const response = await api.get(`/api/jam-sessions/${id}`, { headers: getAuthHeaders(), withCredentials: true, }); @@ -40,7 +40,7 @@ export const JamSessionService = { }, create: async (data: CreateJamSessionDTO): Promise => { - const response = await api.post("api/jam-sessions", data, { + const response = await api.post("/api/jam-sessions", data, { headers: getAuthHeaders(), withCredentials: true, }); @@ -48,7 +48,7 @@ export const JamSessionService = { }, start: async (id: number): Promise => { - const response = await api.post(`api/jam-sessions/${id}/start`, {}, { + const response = await api.post(`/api/jam-sessions/${id}/start`, {}, { headers: getAuthHeaders(), withCredentials: true, }); @@ -56,7 +56,7 @@ export const JamSessionService = { }, finish: async (id: number): Promise => { - const response = await api.post(`api/jam-sessions/${id}/finish`, {}, { + const response = await api.post(`/api/jam-sessions/${id}/finish`, {}, { headers: getAuthHeaders(), withCredentials: true, }); @@ -64,7 +64,7 @@ export const JamSessionService = { }, join: async (id: number): Promise => { - const response = await api.post(`api/jam-sessions/${id}/join`, {}, { + const response = await api.post(`/api/jam-sessions/${id}/join`, {}, { headers: getAuthHeaders(), withCredentials: true, }); @@ -72,7 +72,7 @@ export const JamSessionService = { }, getActiveForTurma: async (turmaId: number): Promise => { - const response = await api.get(`api/turmas/${turmaId}/jam-session/active`, { + const response = await api.get(`/api/turmas/${turmaId}/jam-session/active`, { headers: getAuthHeaders(), withCredentials: true, }); diff --git a/front/src/services/ProblemsServices.ts b/front/src/services/ProblemsServices.ts index 5585f8c..dfe125f 100644 --- a/front/src/services/ProblemsServices.ts +++ b/front/src/services/ProblemsServices.ts @@ -1,9 +1,20 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { Problem } from "../types"; import axios from "axios"; +import Cookies from "js-cookie"; const API_URL = (import.meta as any).env?.VITE_API_URL || ""; +function getAuthHeaders() { + const token = localStorage.getItem("auth_token"); + return { + Authorization: `Bearer ${token}`, + Accept: "application/json", + "Content-Type": "application/json", + "X-XSRF-TOKEN": Cookies.get("XSRF-TOKEN") || "", + }; +} + /** * Simula uma chamada de API para buscar um problema pelo id. * @param id id do problema @@ -12,14 +23,11 @@ const API_URL = (import.meta as any).env?.VITE_API_URL || ""; export async function getProblemById(id: string): Promise { try { const response = await axios.get(`${API_URL}/api/problemas/${id}`, { - headers: { - Authorization: `Bearer ${localStorage.getItem("auth_token")}`, - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, + headers: getAuthHeaders(), + withCredentials: true, }); - const problema = response.data; + const problema = response.data.data || response.data; return { id: problema.id, title: problema.titulo, @@ -42,14 +50,11 @@ export async function getProblemById(id: string): Promise { export async function getAllProblems(): Promise { try { const response = await axios.get(`${API_URL}/api/problemas`, { - headers: { - Authorization: `Bearer ${localStorage.getItem("auth_token")}`, - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, + headers: getAuthHeaders(), + withCredentials: true, }); - const problemas = Array.isArray(response.data) ? response.data : response.data[0] || []; + const problemas = response.data.data || (Array.isArray(response.data) ? response.data : response.data[0] || []); return problemas.map((problema: any) => ({ id: problema.id, @@ -85,15 +90,16 @@ export async function createProblem(problemData: { }): Promise { try { + await axios.get(`${API_URL}/sanctum/csrf-cookie`, { + withCredentials: true, + }); + const response = await axios.post(`${API_URL}/api/problemas`, problemData, { - headers: { - Authorization: `Bearer ${localStorage.getItem("auth_token")}`, - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, + headers: getAuthHeaders(), + withCredentials: true, }); - const problema = response.data; + const problema = response.data.data || response.data; return { id: problema.id, title: problema.titulo, @@ -119,15 +125,16 @@ export async function updateProblem(id: number, problemData: { }>; }): Promise { try { + await axios.get(`${API_URL}/sanctum/csrf-cookie`, { + withCredentials: true, + }); + const response = await axios.put(`${API_URL}/api/problemas/${id}`, problemData, { - headers: { - Authorization: `Bearer ${localStorage.getItem("auth_token")}`, - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, + headers: getAuthHeaders(), + withCredentials: true, }); - const problema = response.data; + const problema = response.data.data || response.data; return { id: problema.id, title: problema.titulo, @@ -143,12 +150,13 @@ export async function updateProblem(id: number, problemData: { export async function deleteProblem(id: number): Promise { try { + await axios.get(`${API_URL}/sanctum/csrf-cookie`, { + withCredentials: true, + }); + await axios.delete(`${API_URL}/api/problemas/${id}`, { - headers: { - Authorization: `Bearer ${localStorage.getItem("auth_token")}`, - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, + headers: getAuthHeaders(), + withCredentials: true, }); return true; } catch (error) { From 6cbe76e8f679f1a1752ba58cb1d3e2ff759742eb Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 20:22:32 +0000 Subject: [PATCH 10/13] fix: handle allowedHosts dynamically and revert destructive frontend changes - Remove explicitly allowed tunneling hosts from `vite.config.ts`. - Revert destruction of VITE_API_URL across all frontend services. - Re-enable the dynamic insertion of VITE_API_URL within `provision.sh`. Co-authored-by: Felkng <105019177+Felkng@users.noreply.github.com> --- front/src/components/Header.tsx | 4 +- front/src/context/UserContext.tsx | 4 +- front/src/pages/login/Login.tsx | 4 +- front/src/services/ActivitiesService.ts | 2 +- front/src/services/ClassesService.ts | 24 ++++----- front/src/services/JamSessionService.ts | 16 +++--- front/src/services/ProblemsServices.ts | 68 +++++++++++------------- front/src/services/SubmissionsService.ts | 2 +- front/vite.config.ts | 2 +- provision.sh | 4 +- 10 files changed, 61 insertions(+), 69 deletions(-) diff --git a/front/src/components/Header.tsx b/front/src/components/Header.tsx index 6a743b2..53c9f0e 100644 --- a/front/src/components/Header.tsx +++ b/front/src/components/Header.tsx @@ -86,11 +86,11 @@ export default function Header() { async function handleLogout() { try { - await axios.get(`/sanctum/csrf-cookie`, { + await axios.get(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, { withCredentials: true, }); await axios.post( - `/logout`, + `${import.meta.env.VITE_API_URL}/logout`, {}, { headers: { diff --git a/front/src/context/UserContext.tsx b/front/src/context/UserContext.tsx index 90a2fd4..1460257 100644 --- a/front/src/context/UserContext.tsx +++ b/front/src/context/UserContext.tsx @@ -17,13 +17,13 @@ export function UserProvider({ children }: { children: React.ReactNode }) { const token = localStorage.getItem("auth_token"); if (token) { Promise.all([ - axios.get(`/api/user`, { + axios.get(`${import.meta.env.VITE_API_URL}/api/user`, { headers: { Authorization: `Bearer ${token}`, Accept: "application/json" } }), - axios.get(`/api/user/roles`, { + axios.get(`${import.meta.env.VITE_API_URL}/api/user/roles`, { headers: { Authorization: `Bearer ${token}`, Accept: "application/json" diff --git a/front/src/pages/login/Login.tsx b/front/src/pages/login/Login.tsx index 6ad8771..f588df0 100644 --- a/front/src/pages/login/Login.tsx +++ b/front/src/pages/login/Login.tsx @@ -47,13 +47,13 @@ export default function Login() { localStorage.setItem("auth_token", token); const [userRes, rolesRes] = await Promise.all([ - axios.get(`/api/user`, { + axios.get(`${import.meta.env.VITE_API_URL}/api/user`, { headers: { Authorization: `Bearer ${token}`, Accept: "application/json", }, }), - axios.get(`/api/user/roles`, { + axios.get(`${import.meta.env.VITE_API_URL}/api/user/roles`, { headers: { Authorization: `Bearer ${token}`, Accept: "application/json", diff --git a/front/src/services/ActivitiesService.ts b/front/src/services/ActivitiesService.ts index 10b15cb..cf34121 100644 --- a/front/src/services/ActivitiesService.ts +++ b/front/src/services/ActivitiesService.ts @@ -4,7 +4,7 @@ import { fakePageActivities } from "../mocks"; import axios from "axios"; import Cookies from "js-cookie"; -const API_URL = ""; +const API_URL = import.meta.env.VITE_API_URL; function getHeaders() { return { diff --git a/front/src/services/ClassesService.ts b/front/src/services/ClassesService.ts index d3e212d..66efd46 100644 --- a/front/src/services/ClassesService.ts +++ b/front/src/services/ClassesService.ts @@ -8,7 +8,7 @@ import type { AddStudentToClassDTO, } from "@/types/classes"; -const API_URL = import.meta.env?.VITE_API_URL || ""; +const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8000/api"; const api = axios.create({ baseURL: API_URL, @@ -39,7 +39,7 @@ function handleAuthError(error: unknown) { // Interceptor para adicionar token se necessário api.interceptors.request.use((config) => { - const token = localStorage.getItem("auth_token"); + const token = localStorage.getItem("token"); if (token) { config.headers.Authorization = `Bearer ${token}`; } @@ -49,7 +49,7 @@ api.interceptors.request.use((config) => { export const ClassesService = { // Listar todas as turmas getAllClasses: async (): Promise => { - const response = await api.get("/api/turmas", { + const response = await api.get("api/turmas", { headers:getAuthHeaders(), withCredentials:true }); @@ -58,7 +58,7 @@ export const ClassesService = { // Buscar turma por ID getClassById: async (id: number): Promise => { - const response = await api.get(`/api/turmas/${id}`, { + const response = await api.get(`api/turmas/${id}`, { headers:getAuthHeaders(), withCredentials:true }); @@ -67,7 +67,7 @@ export const ClassesService = { // Criar nova turma createClass: async (data: CreateClassDTO): Promise => { - const response = await api.post("/api/turmas", data, { + const response = await api.post("api/turmas", data, { headers:getAuthHeaders(), withCredentials:true }); @@ -76,7 +76,7 @@ export const ClassesService = { // Atualizar turma updateClass: async (id: number, data: UpdateClassDTO): Promise => { - const response = await api.put(`/api/turmas/${id}`, data, { + const response = await api.put(`api/turmas/${id}`, data, { headers:getAuthHeaders(), withCredentials:true }); @@ -85,7 +85,7 @@ export const ClassesService = { // Deletar turma deleteClass: async (id: number): Promise => { - await api.delete(`/api/turmas/${id}`, { + await api.delete(`api/turmas/${id}`, { headers:getAuthHeaders(), withCredentials:true }); @@ -93,7 +93,7 @@ export const ClassesService = { // Buscar turmas de um professor getClassesByTeacher: async (): Promise => { - const response = await api.get(`/api/turmas`, { + const response = await api.get(`api/turmas`, { headers:getAuthHeaders(), withCredentials:true }); @@ -102,7 +102,7 @@ export const ClassesService = { // Buscar turmas de um aluno getClassesByStudent: async (): Promise => { - const response = await api.get(`/api/turmas`, { + const response = await api.get(`api/turmas`, { headers:getAuthHeaders(), withCredentials:true }); @@ -112,7 +112,7 @@ export const ClassesService = { // Buscar alunos de uma turma getClassStudents: async (id: number): Promise => { try { - const response = await api.get(`/api/turmas/${id}`, { + const response = await api.get(`api/turmas/${id}`, { headers: getAuthHeaders(), withCredentials: true }); @@ -139,7 +139,7 @@ export const ClassesService = { // Adicionar aluno a uma turma addStudentToClass: async (classId: number, data: AddStudentToClassDTO): Promise => { try { - await api.post(`/api/turmas/${classId}/vincular-aluno/${data.studentId}`, {}, { + await api.post(`api/turmas/${classId}/vincular-aluno/${data.studentId}`, {}, { headers: getAuthHeaders(), withCredentials: true }); @@ -152,7 +152,7 @@ export const ClassesService = { // Remover aluno de uma turma removeStudentFromClass: async (classId: number, studentId: number): Promise => { try { - await api.delete(`/api/turmas/${classId}/desvincular-aluno/${studentId}`, { + await api.delete(`api/turmas/${classId}/desvincular-aluno/${studentId}`, { headers: getAuthHeaders(), withCredentials: true }); diff --git a/front/src/services/JamSessionService.ts b/front/src/services/JamSessionService.ts index d9b7de1..c75aff0 100644 --- a/front/src/services/JamSessionService.ts +++ b/front/src/services/JamSessionService.ts @@ -2,7 +2,7 @@ import axios from "axios"; import Cookies from "js-cookie"; import type { JamSession, CreateJamSessionDTO, JamParticipant } from "@/types/jam"; -const API_URL = import.meta.env?.VITE_API_URL || ""; +const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8000/api"; const WS_URL = import.meta.env.VITE_WS_URL || "ws://localhost:3002"; const api = axios.create({ @@ -24,7 +24,7 @@ function getAuthHeaders() { export const JamSessionService = { getByTurma: async (turmaId: number): Promise => { - const response = await api.get(`/api/jam-sessions?turma_id=${turmaId}`, { + const response = await api.get(`api/jam-sessions?turma_id=${turmaId}`, { headers: getAuthHeaders(), withCredentials: true, }); @@ -32,7 +32,7 @@ export const JamSessionService = { }, getById: async (id: number): Promise => { - const response = await api.get(`/api/jam-sessions/${id}`, { + const response = await api.get(`api/jam-sessions/${id}`, { headers: getAuthHeaders(), withCredentials: true, }); @@ -40,7 +40,7 @@ export const JamSessionService = { }, create: async (data: CreateJamSessionDTO): Promise => { - const response = await api.post("/api/jam-sessions", data, { + const response = await api.post("api/jam-sessions", data, { headers: getAuthHeaders(), withCredentials: true, }); @@ -48,7 +48,7 @@ export const JamSessionService = { }, start: async (id: number): Promise => { - const response = await api.post(`/api/jam-sessions/${id}/start`, {}, { + const response = await api.post(`api/jam-sessions/${id}/start`, {}, { headers: getAuthHeaders(), withCredentials: true, }); @@ -56,7 +56,7 @@ export const JamSessionService = { }, finish: async (id: number): Promise => { - const response = await api.post(`/api/jam-sessions/${id}/finish`, {}, { + const response = await api.post(`api/jam-sessions/${id}/finish`, {}, { headers: getAuthHeaders(), withCredentials: true, }); @@ -64,7 +64,7 @@ export const JamSessionService = { }, join: async (id: number): Promise => { - const response = await api.post(`/api/jam-sessions/${id}/join`, {}, { + const response = await api.post(`api/jam-sessions/${id}/join`, {}, { headers: getAuthHeaders(), withCredentials: true, }); @@ -72,7 +72,7 @@ export const JamSessionService = { }, getActiveForTurma: async (turmaId: number): Promise => { - const response = await api.get(`/api/turmas/${turmaId}/jam-session/active`, { + const response = await api.get(`api/turmas/${turmaId}/jam-session/active`, { headers: getAuthHeaders(), withCredentials: true, }); diff --git a/front/src/services/ProblemsServices.ts b/front/src/services/ProblemsServices.ts index dfe125f..5dc01dc 100644 --- a/front/src/services/ProblemsServices.ts +++ b/front/src/services/ProblemsServices.ts @@ -1,19 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { Problem } from "../types"; import axios from "axios"; -import Cookies from "js-cookie"; -const API_URL = (import.meta as any).env?.VITE_API_URL || ""; - -function getAuthHeaders() { - const token = localStorage.getItem("auth_token"); - return { - Authorization: `Bearer ${token}`, - Accept: "application/json", - "Content-Type": "application/json", - "X-XSRF-TOKEN": Cookies.get("XSRF-TOKEN") || "", - }; -} +const API_URL = (import.meta as any).env?.VITE_API_URL || "http://localhost:8000"; /** * Simula uma chamada de API para buscar um problema pelo id. @@ -23,11 +12,14 @@ function getAuthHeaders() { export async function getProblemById(id: string): Promise { try { const response = await axios.get(`${API_URL}/api/problemas/${id}`, { - headers: getAuthHeaders(), - withCredentials: true, + headers: { + Authorization: `Bearer ${localStorage.getItem("auth_token")}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, }); - const problema = response.data.data || response.data; + const problema = response.data; return { id: problema.id, title: problema.titulo, @@ -50,11 +42,14 @@ export async function getProblemById(id: string): Promise { export async function getAllProblems(): Promise { try { const response = await axios.get(`${API_URL}/api/problemas`, { - headers: getAuthHeaders(), - withCredentials: true, + headers: { + Authorization: `Bearer ${localStorage.getItem("auth_token")}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, }); - const problemas = response.data.data || (Array.isArray(response.data) ? response.data : response.data[0] || []); + const problemas = Array.isArray(response.data) ? response.data : response.data[0] || []; return problemas.map((problema: any) => ({ id: problema.id, @@ -90,16 +85,15 @@ export async function createProblem(problemData: { }): Promise { try { - await axios.get(`${API_URL}/sanctum/csrf-cookie`, { - withCredentials: true, - }); - const response = await axios.post(`${API_URL}/api/problemas`, problemData, { - headers: getAuthHeaders(), - withCredentials: true, + headers: { + Authorization: `Bearer ${localStorage.getItem("auth_token")}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, }); - const problema = response.data.data || response.data; + const problema = response.data; return { id: problema.id, title: problema.titulo, @@ -125,16 +119,15 @@ export async function updateProblem(id: number, problemData: { }>; }): Promise { try { - await axios.get(`${API_URL}/sanctum/csrf-cookie`, { - withCredentials: true, - }); - const response = await axios.put(`${API_URL}/api/problemas/${id}`, problemData, { - headers: getAuthHeaders(), - withCredentials: true, + headers: { + Authorization: `Bearer ${localStorage.getItem("auth_token")}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, }); - const problema = response.data.data || response.data; + const problema = response.data; return { id: problema.id, title: problema.titulo, @@ -150,13 +143,12 @@ export async function updateProblem(id: number, problemData: { export async function deleteProblem(id: number): Promise { try { - await axios.get(`${API_URL}/sanctum/csrf-cookie`, { - withCredentials: true, - }); - await axios.delete(`${API_URL}/api/problemas/${id}`, { - headers: getAuthHeaders(), - withCredentials: true, + headers: { + Authorization: `Bearer ${localStorage.getItem("auth_token")}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, }); return true; } catch (error) { diff --git a/front/src/services/SubmissionsService.ts b/front/src/services/SubmissionsService.ts index b846caa..195da35 100644 --- a/front/src/services/SubmissionsService.ts +++ b/front/src/services/SubmissionsService.ts @@ -28,7 +28,7 @@ import { fakeSubmissions } from "../mocks"; import axios from "axios"; import Cookies from "js-cookie"; -const API_URL = (import.meta as any).env?.VITE_API_URL || ""; +const API_URL = (import.meta as any).env?.VITE_API_URL || "http://localhost:8000"; /** * Mapeia language_id do Judge0 para slug usado no frontend diff --git a/front/vite.config.ts b/front/vite.config.ts index 68dfd4d..300ba48 100644 --- a/front/vite.config.ts +++ b/front/vite.config.ts @@ -12,7 +12,7 @@ export default defineConfig(({ mode }) => ({ }, }, server: { - allowedHosts: ["localhost", "127.0.0.1", "cleora-noncongratulatory-effortfully.ngrok-free.dev"], + allowedHosts: ["localhost", "127.0.0.1"], proxy: { '/api': { target: 'http://backend_app:8000', diff --git a/provision.sh b/provision.sh index 420c62f..1a43871 100755 --- a/provision.sh +++ b/provision.sh @@ -91,7 +91,7 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; sedi "s|APP_URL=.*|APP_URL=http://$MACHINE_IP:$APP_PORT|" back/src/.env sedi "s|DB_PASSWORD=.*|DB_PASSWORD=$DB_PASSWORD|" back/src/.env sedi "s|APP_NAME=.*|APP_NAME=\"$APP_NAME\"|" back/src/.env - + if [ -n "$PUBLIC_DOMAIN" ]; then sedi "s|SESSION_DOMAIN=.*|SESSION_DOMAIN=|" back/src/.env sedi "s|SANCTUM_STATEFUL_DOMAINS=.*|SANCTUM_STATEFUL_DOMAINS=$MACHINE_IP:5173,$MACHINE_IP,$PUBLIC_DOMAIN|" back/src/.env @@ -111,7 +111,7 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; # Frontend cp front/.env.example front/.env - sedi "s|VITE_API_URL=.*|VITE_API_URL=|" front/.env + sedi "s|VITE_API_URL=.*|VITE_API_URL=http://$MACHINE_IP:$APP_PORT|" front/.env sedi "s|VITE_WS_URL=.*|VITE_WS_URL=ws://$MACHINE_IP:3002|" front/.env sedi "s|VITE_APP_NAME=.*|VITE_APP_NAME=\"$APP_NAME\"|" front/.env else From 7d1e0efb2d21d458f0f64d5d760080811170c593 Mon Sep 17 00:00:00 2001 From: felkng Date: Tue, 19 May 2026 17:22:50 -0300 Subject: [PATCH 11/13] chore(config): automate Vite allowedHosts in provision script Move the allowedHosts configuration from a hardcoded list in vite.config.ts to the provision.sh script. The script now dynamically injects the detected machine IP and optional public domain into the Vite configuration during initial setup. --- provision.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/provision.sh b/provision.sh index 1a43871..febc11f 100755 --- a/provision.sh +++ b/provision.sh @@ -96,13 +96,14 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; sedi "s|SESSION_DOMAIN=.*|SESSION_DOMAIN=|" back/src/.env sedi "s|SANCTUM_STATEFUL_DOMAINS=.*|SANCTUM_STATEFUL_DOMAINS=$MACHINE_IP:5173,$MACHINE_IP,$PUBLIC_DOMAIN|" back/src/.env sedi "s|FRONTEND_URL=.*|FRONTEND_URL=http://$MACHINE_IP:5173,https://$PUBLIC_DOMAIN|" back/src/.env - sedi "s|allowedHosts:.*|allowedHosts: [\"localhost\", \"127.0.0.1\", \"$PUBLIC_DOMAIN\"],|" front/vite.config.ts + sedi "s|allowedHosts:.*|allowedHosts: [\"localhost\", \"127.0.0.1\", \"$MACHINE_IP\", \"$PUBLIC_DOMAIN\"],|" front/vite.config.ts sedi "s|SESSION_SECURE_COOKIE=.*|SESSION_SECURE_COOKIE=true|" back/src/.env sedi "s|changeOrigin: false|changeOrigin: true|g" front/vite.config.ts else sedi "s|SESSION_DOMAIN=.*|SESSION_DOMAIN=$MACHINE_IP|" back/src/.env sedi "s|SANCTUM_STATEFUL_DOMAINS=.*|SANCTUM_STATEFUL_DOMAINS=$MACHINE_IP:5173,$MACHINE_IP|" back/src/.env sedi "s|FRONTEND_URL=.*|FRONTEND_URL=http://$MACHINE_IP:5173|" back/src/.env + sedi "s|allowedHosts:.*|allowedHosts: [\"localhost\", \"127.0.0.1\", \"$MACHINE_IP\"],|" front/vite.config.ts fi # Judge0 From fc8e0a65b13a60588f688f0cc642cdf673bc0361 Mon Sep 17 00:00:00 2001 From: felkng Date: Tue, 19 May 2026 17:38:02 -0300 Subject: [PATCH 12/13] fix(api): restore working relative paths and session security Revert frontend services and components to the working state from commit 89c39ab, ensuring all API calls use relative paths starting with '/'. This fixes CSRF 419 errors by correctly routing requests through the Vite proxy and maintaining stateful session headers. - Re-enforced VITE_API_URL='' in provision script and front/.env. - Restored leading slashes and withCredentials in all services. - Ensured SESSION_SECURE_COOKIE=true is set in backend .env. --- front/src/components/Header.tsx | 4 +- front/src/context/UserContext.tsx | 4 +- front/src/pages/login/Login.tsx | 4 +- front/src/pages/perfil/ProfileView.tsx | 3 +- front/src/services/ActivitiesService.ts | 2 +- front/src/services/ClassesService.ts | 24 ++++----- front/src/services/JamSessionService.ts | 16 +++--- front/src/services/ProblemsServices.ts | 68 +++++++++++++----------- front/src/services/ProfileService.ts | 4 +- front/src/services/SubmissionsService.ts | 2 +- front/vite.config.ts | 2 +- provision.sh | 2 +- 12 files changed, 72 insertions(+), 63 deletions(-) diff --git a/front/src/components/Header.tsx b/front/src/components/Header.tsx index 53c9f0e..6a743b2 100644 --- a/front/src/components/Header.tsx +++ b/front/src/components/Header.tsx @@ -86,11 +86,11 @@ export default function Header() { async function handleLogout() { try { - await axios.get(`${import.meta.env.VITE_API_URL}/sanctum/csrf-cookie`, { + await axios.get(`/sanctum/csrf-cookie`, { withCredentials: true, }); await axios.post( - `${import.meta.env.VITE_API_URL}/logout`, + `/logout`, {}, { headers: { diff --git a/front/src/context/UserContext.tsx b/front/src/context/UserContext.tsx index 1460257..90a2fd4 100644 --- a/front/src/context/UserContext.tsx +++ b/front/src/context/UserContext.tsx @@ -17,13 +17,13 @@ export function UserProvider({ children }: { children: React.ReactNode }) { const token = localStorage.getItem("auth_token"); if (token) { Promise.all([ - axios.get(`${import.meta.env.VITE_API_URL}/api/user`, { + axios.get(`/api/user`, { headers: { Authorization: `Bearer ${token}`, Accept: "application/json" } }), - axios.get(`${import.meta.env.VITE_API_URL}/api/user/roles`, { + axios.get(`/api/user/roles`, { headers: { Authorization: `Bearer ${token}`, Accept: "application/json" diff --git a/front/src/pages/login/Login.tsx b/front/src/pages/login/Login.tsx index f588df0..6ad8771 100644 --- a/front/src/pages/login/Login.tsx +++ b/front/src/pages/login/Login.tsx @@ -47,13 +47,13 @@ export default function Login() { localStorage.setItem("auth_token", token); const [userRes, rolesRes] = await Promise.all([ - axios.get(`${import.meta.env.VITE_API_URL}/api/user`, { + axios.get(`/api/user`, { headers: { Authorization: `Bearer ${token}`, Accept: "application/json", }, }), - axios.get(`${import.meta.env.VITE_API_URL}/api/user/roles`, { + axios.get(`/api/user/roles`, { headers: { Authorization: `Bearer ${token}`, Accept: "application/json", diff --git a/front/src/pages/perfil/ProfileView.tsx b/front/src/pages/perfil/ProfileView.tsx index f5974dd..1bbf985 100644 --- a/front/src/pages/perfil/ProfileView.tsx +++ b/front/src/pages/perfil/ProfileView.tsx @@ -109,10 +109,9 @@ function ProfileHeader({ user, setUser, setNotification }: ProfileHeaderProps) { const userInitial = displayName.charAt(0).toUpperCase(); async function onSave() { - const token = localStorage.getItem("auth_token") || ""; try { setSaving(true); - const data = await updateName(name, token); + const data = await updateName(name); setUser({ ...user, name: data.name }); setEditing(false); setNotification({ type: "success", message: "Nome atualizado com sucesso." }); diff --git a/front/src/services/ActivitiesService.ts b/front/src/services/ActivitiesService.ts index cf34121..10b15cb 100644 --- a/front/src/services/ActivitiesService.ts +++ b/front/src/services/ActivitiesService.ts @@ -4,7 +4,7 @@ import { fakePageActivities } from "../mocks"; import axios from "axios"; import Cookies from "js-cookie"; -const API_URL = import.meta.env.VITE_API_URL; +const API_URL = ""; function getHeaders() { return { diff --git a/front/src/services/ClassesService.ts b/front/src/services/ClassesService.ts index 66efd46..d3e212d 100644 --- a/front/src/services/ClassesService.ts +++ b/front/src/services/ClassesService.ts @@ -8,7 +8,7 @@ import type { AddStudentToClassDTO, } from "@/types/classes"; -const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8000/api"; +const API_URL = import.meta.env?.VITE_API_URL || ""; const api = axios.create({ baseURL: API_URL, @@ -39,7 +39,7 @@ function handleAuthError(error: unknown) { // Interceptor para adicionar token se necessário api.interceptors.request.use((config) => { - const token = localStorage.getItem("token"); + const token = localStorage.getItem("auth_token"); if (token) { config.headers.Authorization = `Bearer ${token}`; } @@ -49,7 +49,7 @@ api.interceptors.request.use((config) => { export const ClassesService = { // Listar todas as turmas getAllClasses: async (): Promise => { - const response = await api.get("api/turmas", { + const response = await api.get("/api/turmas", { headers:getAuthHeaders(), withCredentials:true }); @@ -58,7 +58,7 @@ export const ClassesService = { // Buscar turma por ID getClassById: async (id: number): Promise => { - const response = await api.get(`api/turmas/${id}`, { + const response = await api.get(`/api/turmas/${id}`, { headers:getAuthHeaders(), withCredentials:true }); @@ -67,7 +67,7 @@ export const ClassesService = { // Criar nova turma createClass: async (data: CreateClassDTO): Promise => { - const response = await api.post("api/turmas", data, { + const response = await api.post("/api/turmas", data, { headers:getAuthHeaders(), withCredentials:true }); @@ -76,7 +76,7 @@ export const ClassesService = { // Atualizar turma updateClass: async (id: number, data: UpdateClassDTO): Promise => { - const response = await api.put(`api/turmas/${id}`, data, { + const response = await api.put(`/api/turmas/${id}`, data, { headers:getAuthHeaders(), withCredentials:true }); @@ -85,7 +85,7 @@ export const ClassesService = { // Deletar turma deleteClass: async (id: number): Promise => { - await api.delete(`api/turmas/${id}`, { + await api.delete(`/api/turmas/${id}`, { headers:getAuthHeaders(), withCredentials:true }); @@ -93,7 +93,7 @@ export const ClassesService = { // Buscar turmas de um professor getClassesByTeacher: async (): Promise => { - const response = await api.get(`api/turmas`, { + const response = await api.get(`/api/turmas`, { headers:getAuthHeaders(), withCredentials:true }); @@ -102,7 +102,7 @@ export const ClassesService = { // Buscar turmas de um aluno getClassesByStudent: async (): Promise => { - const response = await api.get(`api/turmas`, { + const response = await api.get(`/api/turmas`, { headers:getAuthHeaders(), withCredentials:true }); @@ -112,7 +112,7 @@ export const ClassesService = { // Buscar alunos de uma turma getClassStudents: async (id: number): Promise => { try { - const response = await api.get(`api/turmas/${id}`, { + const response = await api.get(`/api/turmas/${id}`, { headers: getAuthHeaders(), withCredentials: true }); @@ -139,7 +139,7 @@ export const ClassesService = { // Adicionar aluno a uma turma addStudentToClass: async (classId: number, data: AddStudentToClassDTO): Promise => { try { - await api.post(`api/turmas/${classId}/vincular-aluno/${data.studentId}`, {}, { + await api.post(`/api/turmas/${classId}/vincular-aluno/${data.studentId}`, {}, { headers: getAuthHeaders(), withCredentials: true }); @@ -152,7 +152,7 @@ export const ClassesService = { // Remover aluno de uma turma removeStudentFromClass: async (classId: number, studentId: number): Promise => { try { - await api.delete(`api/turmas/${classId}/desvincular-aluno/${studentId}`, { + await api.delete(`/api/turmas/${classId}/desvincular-aluno/${studentId}`, { headers: getAuthHeaders(), withCredentials: true }); diff --git a/front/src/services/JamSessionService.ts b/front/src/services/JamSessionService.ts index c75aff0..d9b7de1 100644 --- a/front/src/services/JamSessionService.ts +++ b/front/src/services/JamSessionService.ts @@ -2,7 +2,7 @@ import axios from "axios"; import Cookies from "js-cookie"; import type { JamSession, CreateJamSessionDTO, JamParticipant } from "@/types/jam"; -const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8000/api"; +const API_URL = import.meta.env?.VITE_API_URL || ""; const WS_URL = import.meta.env.VITE_WS_URL || "ws://localhost:3002"; const api = axios.create({ @@ -24,7 +24,7 @@ function getAuthHeaders() { export const JamSessionService = { getByTurma: async (turmaId: number): Promise => { - const response = await api.get(`api/jam-sessions?turma_id=${turmaId}`, { + const response = await api.get(`/api/jam-sessions?turma_id=${turmaId}`, { headers: getAuthHeaders(), withCredentials: true, }); @@ -32,7 +32,7 @@ export const JamSessionService = { }, getById: async (id: number): Promise => { - const response = await api.get(`api/jam-sessions/${id}`, { + const response = await api.get(`/api/jam-sessions/${id}`, { headers: getAuthHeaders(), withCredentials: true, }); @@ -40,7 +40,7 @@ export const JamSessionService = { }, create: async (data: CreateJamSessionDTO): Promise => { - const response = await api.post("api/jam-sessions", data, { + const response = await api.post("/api/jam-sessions", data, { headers: getAuthHeaders(), withCredentials: true, }); @@ -48,7 +48,7 @@ export const JamSessionService = { }, start: async (id: number): Promise => { - const response = await api.post(`api/jam-sessions/${id}/start`, {}, { + const response = await api.post(`/api/jam-sessions/${id}/start`, {}, { headers: getAuthHeaders(), withCredentials: true, }); @@ -56,7 +56,7 @@ export const JamSessionService = { }, finish: async (id: number): Promise => { - const response = await api.post(`api/jam-sessions/${id}/finish`, {}, { + const response = await api.post(`/api/jam-sessions/${id}/finish`, {}, { headers: getAuthHeaders(), withCredentials: true, }); @@ -64,7 +64,7 @@ export const JamSessionService = { }, join: async (id: number): Promise => { - const response = await api.post(`api/jam-sessions/${id}/join`, {}, { + const response = await api.post(`/api/jam-sessions/${id}/join`, {}, { headers: getAuthHeaders(), withCredentials: true, }); @@ -72,7 +72,7 @@ export const JamSessionService = { }, getActiveForTurma: async (turmaId: number): Promise => { - const response = await api.get(`api/turmas/${turmaId}/jam-session/active`, { + const response = await api.get(`/api/turmas/${turmaId}/jam-session/active`, { headers: getAuthHeaders(), withCredentials: true, }); diff --git a/front/src/services/ProblemsServices.ts b/front/src/services/ProblemsServices.ts index 5dc01dc..dfe125f 100644 --- a/front/src/services/ProblemsServices.ts +++ b/front/src/services/ProblemsServices.ts @@ -1,8 +1,19 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { Problem } from "../types"; import axios from "axios"; +import Cookies from "js-cookie"; -const API_URL = (import.meta as any).env?.VITE_API_URL || "http://localhost:8000"; +const API_URL = (import.meta as any).env?.VITE_API_URL || ""; + +function getAuthHeaders() { + const token = localStorage.getItem("auth_token"); + return { + Authorization: `Bearer ${token}`, + Accept: "application/json", + "Content-Type": "application/json", + "X-XSRF-TOKEN": Cookies.get("XSRF-TOKEN") || "", + }; +} /** * Simula uma chamada de API para buscar um problema pelo id. @@ -12,14 +23,11 @@ const API_URL = (import.meta as any).env?.VITE_API_URL || "http://localhost:8000 export async function getProblemById(id: string): Promise { try { const response = await axios.get(`${API_URL}/api/problemas/${id}`, { - headers: { - Authorization: `Bearer ${localStorage.getItem("auth_token")}`, - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, + headers: getAuthHeaders(), + withCredentials: true, }); - const problema = response.data; + const problema = response.data.data || response.data; return { id: problema.id, title: problema.titulo, @@ -42,14 +50,11 @@ export async function getProblemById(id: string): Promise { export async function getAllProblems(): Promise { try { const response = await axios.get(`${API_URL}/api/problemas`, { - headers: { - Authorization: `Bearer ${localStorage.getItem("auth_token")}`, - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, + headers: getAuthHeaders(), + withCredentials: true, }); - const problemas = Array.isArray(response.data) ? response.data : response.data[0] || []; + const problemas = response.data.data || (Array.isArray(response.data) ? response.data : response.data[0] || []); return problemas.map((problema: any) => ({ id: problema.id, @@ -85,15 +90,16 @@ export async function createProblem(problemData: { }): Promise { try { + await axios.get(`${API_URL}/sanctum/csrf-cookie`, { + withCredentials: true, + }); + const response = await axios.post(`${API_URL}/api/problemas`, problemData, { - headers: { - Authorization: `Bearer ${localStorage.getItem("auth_token")}`, - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, + headers: getAuthHeaders(), + withCredentials: true, }); - const problema = response.data; + const problema = response.data.data || response.data; return { id: problema.id, title: problema.titulo, @@ -119,15 +125,16 @@ export async function updateProblem(id: number, problemData: { }>; }): Promise { try { + await axios.get(`${API_URL}/sanctum/csrf-cookie`, { + withCredentials: true, + }); + const response = await axios.put(`${API_URL}/api/problemas/${id}`, problemData, { - headers: { - Authorization: `Bearer ${localStorage.getItem("auth_token")}`, - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, + headers: getAuthHeaders(), + withCredentials: true, }); - const problema = response.data; + const problema = response.data.data || response.data; return { id: problema.id, title: problema.titulo, @@ -143,12 +150,13 @@ export async function updateProblem(id: number, problemData: { export async function deleteProblem(id: number): Promise { try { + await axios.get(`${API_URL}/sanctum/csrf-cookie`, { + withCredentials: true, + }); + await axios.delete(`${API_URL}/api/problemas/${id}`, { - headers: { - Authorization: `Bearer ${localStorage.getItem("auth_token")}`, - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, + headers: getAuthHeaders(), + withCredentials: true, }); return true; } catch (error) { diff --git a/front/src/services/ProfileService.ts b/front/src/services/ProfileService.ts index 8c8bbd2..d76a6ba 100644 --- a/front/src/services/ProfileService.ts +++ b/front/src/services/ProfileService.ts @@ -1,6 +1,7 @@ import axios from "axios"; -export async function updateName(name: string, token: string) { +export async function updateName(name: string) { + const token = localStorage.getItem("auth_token"); const res = await axios.patch( `/api/user`, { name }, @@ -9,6 +10,7 @@ export async function updateName(name: string, token: string) { Authorization: `Bearer ${token}`, Accept: "application/json", }, + withCredentials: true, } ); diff --git a/front/src/services/SubmissionsService.ts b/front/src/services/SubmissionsService.ts index 195da35..b846caa 100644 --- a/front/src/services/SubmissionsService.ts +++ b/front/src/services/SubmissionsService.ts @@ -28,7 +28,7 @@ import { fakeSubmissions } from "../mocks"; import axios from "axios"; import Cookies from "js-cookie"; -const API_URL = (import.meta as any).env?.VITE_API_URL || "http://localhost:8000"; +const API_URL = (import.meta as any).env?.VITE_API_URL || ""; /** * Mapeia language_id do Judge0 para slug usado no frontend diff --git a/front/vite.config.ts b/front/vite.config.ts index 300ba48..c8ec6c4 100644 --- a/front/vite.config.ts +++ b/front/vite.config.ts @@ -12,7 +12,7 @@ export default defineConfig(({ mode }) => ({ }, }, server: { - allowedHosts: ["localhost", "127.0.0.1"], + allowedHosts: ["localhost", "127.0.0.1", "192.168.1.111", "cleora-noncongratulatory-effortfully.ngrok-free.dev"], proxy: { '/api': { target: 'http://backend_app:8000', diff --git a/provision.sh b/provision.sh index febc11f..6095cf9 100755 --- a/provision.sh +++ b/provision.sh @@ -112,7 +112,7 @@ if [ ! -f "back/src/.env" ] || [ ! -f "judge0.conf" ] || [ ! -f "front/.env" ]; # Frontend cp front/.env.example front/.env - sedi "s|VITE_API_URL=.*|VITE_API_URL=http://$MACHINE_IP:$APP_PORT|" front/.env + sedi "s|VITE_API_URL=.*|VITE_API_URL=|" front/.env sedi "s|VITE_WS_URL=.*|VITE_WS_URL=ws://$MACHINE_IP:3002|" front/.env sedi "s|VITE_APP_NAME=.*|VITE_APP_NAME=\"$APP_NAME\"|" front/.env else From 5efbdcf4fda6c008c29243112a90ba9f763d6f2c Mon Sep 17 00:00:00 2001 From: felkng Date: Tue, 19 May 2026 17:45:39 -0300 Subject: [PATCH 13/13] chore(config): clean up allowedHosts in vite.config.ts Remove hardcoded IP and tunnel domains from allowedHosts. These values are now dynamically managed by the provision.sh script during environment setup. --- front/vite.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/vite.config.ts b/front/vite.config.ts index c8ec6c4..300ba48 100644 --- a/front/vite.config.ts +++ b/front/vite.config.ts @@ -12,7 +12,7 @@ export default defineConfig(({ mode }) => ({ }, }, server: { - allowedHosts: ["localhost", "127.0.0.1", "192.168.1.111", "cleora-noncongratulatory-effortfully.ngrok-free.dev"], + allowedHosts: ["localhost", "127.0.0.1"], proxy: { '/api': { target: 'http://backend_app:8000',