fix: normaliza rotas de imagens nas provas do portal e manager para usar proxy minio seguro

This commit is contained in:
Sidney 2026-04-22 09:12:10 -03:00
parent 77f3bcabcf
commit 242e39056d
6 changed files with 83 additions and 17 deletions

View File

@ -6,7 +6,7 @@ on:
jobs: jobs:
build-and-push: build-and-push:
runs-on: ubuntu-latest runs-on: self-hosted
permissions: permissions:
contents: read contents: read
packages: write packages: write
@ -15,10 +15,6 @@ jobs:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
# Adiciona suporte a QEMU para conseguir buildar ARM64 em runners AMD64
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
@ -34,8 +30,8 @@ jobs:
with: with:
context: ./manager context: ./manager
push: true push: true
# Constrói para as duas arquiteturas # Constrói nativamente apenas para ARM64 na Oracle
platforms: linux/amd64,linux/arm64 platforms: linux/arm64
tags: ghcr.io/${{ github.repository }}/edumanager:latest tags: ghcr.io/${{ github.repository }}/edumanager:latest
- name: Build and push Portal - name: Build and push Portal
@ -43,6 +39,14 @@ jobs:
with: with:
context: ./portal context: ./portal
push: true push: true
# Constrói para as duas arquiteturas # Constrói nativamente apenas para ARM64 na Oracle
platforms: linux/amd64,linux/arm64 platforms: linux/arm64
tags: ghcr.io/${{ github.repository }}/portalaluno:latest tags: ghcr.io/${{ github.repository }}/portalaluno:latest
- name: Atualizar Containers em Produção (Watchtower)
run: |
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ~/.docker/config.json:/config.json \
containrrr/watchtower \
--run-once --cleanup

48
.github/workflows/deploy_backup.yml vendored Normal file
View File

@ -0,0 +1,48 @@
name: Build and Deploy
on:
push:
branches: [ main ]
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
# Adiciona suporte a QEMU para conseguir buildar ARM64 em runners AMD64
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Manager
uses: docker/build-push-action@v5
with:
context: ./manager
push: true
# Constrói para as duas arquiteturas
platforms: linux/amd64,linux/arm64
tags: ghcr.io/${{ github.repository }}/edumanager:latest
- name: Build and push Portal
uses: docker/build-push-action@v5
with:
context: ./portal
push: true
# Constrói para as duas arquiteturas
platforms: linux/amd64,linux/arm64
tags: ghcr.io/${{ github.repository }}/portalaluno:latest

View File

@ -9,7 +9,7 @@
- **Storage Architecture**: 100% Self-Hosted (MinIO) - Decoupled from Supabase Cloud. - **Storage Architecture**: 100% Self-Hosted (MinIO) - Decoupled from Supabase Cloud.
- **Image Serving**: All images are served via a backend proxy route (`/storage/:bucket/:key`) to ensure cross-origin compatibility and security. - **Image Serving**: All images are served via a backend proxy route (`/storage/:bucket/:key`) to ensure cross-origin compatibility and security.
- **Synchronization**: High-performance local API for bank reconciliation (Asaas). - **Synchronization**: High-performance local API for bank reconciliation (Asaas).
- **Orquestração:** Portainer (Docker) - **Orquestração e CI/CD:** Portainer (Docker) com GitHub Actions via Self-Hosted Runner (Oracle ARM64 nativo) e Watchtower.
- **Production Entry Point**: All production deployments MUST use `server.selfhosted.js` renamed/copied as `server.js` in the Docker containers to ensure full local feature availability. - **Production Entry Point**: All production deployments MUST use `server.selfhosted.js` renamed/copied as `server.js` in the Docker containers to ensure full local feature availability.
## ⚠️ Regras de Negócio Críticas (MANDATÓRIO) ## ⚠️ Regras de Negócio Críticas (MANDATÓRIO)
@ -24,5 +24,5 @@
2. **Segurança:** Todas as rotas sensíveis devem validar o token JWT local (via secrets do ambiente). Proibido usar Supabase SDK para lógica de autenticação ou sincronização no frontend. 2. **Segurança:** Todas as rotas sensíveis devem validar o token JWT local (via secrets do ambiente). Proibido usar Supabase SDK para lógica de autenticação ou sincronização no frontend.
3. **Resiliência:** Tratamento rigoroso de erros em chamadas de API de terceiros (Asaas, Evolution API). 3. **Resiliência:** Tratamento rigoroso de erros em chamadas de API de terceiros (Asaas, Evolution API).
4. **Upload de Arquivos:** Proibido o uso de Base64 para envio de novos arquivos ao servidor. Use obrigatoriamente `FormData` e envie o objeto `File/Blob` para as rotas de API que integram com o MinIO. 4. **Upload de Arquivos:** Proibido o uso de Base64 para envio de novos arquivos ao servidor. Use obrigatoriamente `FormData` e envie o objeto `File/Blob` para as rotas de API que integram com o MinIO.
5. **Build Stability**: Dockerfiles MUST include `ENV NODE_OPTIONS="--max-old-space-size=4096"` before `npm run build` to prevent Vite/Rolldown crashes during ARM64 cross-compilation. 5. **Build & Deploy Stability:** O pipeline de deploy deve obrigatoriamente utilizar `runs-on: self-hosted` e compilar apenas a plataforma `linux/arm64` (sem emulação QEMU). A atualização da stack em produção deve ser automatizada via container transiente do Watchtower.
6. **Express Compatibility**: Avoid using raw `/*` wildcards in Express 5 routes; use Regex paths (`/^\/route\/(.+)$/`) for compatibility with `path-to-regexp` v8. 6. **Express Compatibility**: Avoid using raw `/*` wildcards in Express 5 routes; use Regex paths (`/^\/route\/(.+)$/`) for compatibility with `path-to-regexp` v8.

View File

@ -8,7 +8,10 @@
- [x] Vacina de cache global: Injeção de `normalizePhotoUrl` nos módulos de Boletim, Turmas e Frequência. - [x] Vacina de cache global: Injeção de `normalizePhotoUrl` nos módulos de Boletim, Turmas e Frequência.
- [x] Estabilização do Build ARM64: Injeção de `max_old_space_size=4096` nos Dockerfiles para evitar crashes do Vite no Github Actions. - [x] Estabilização do Build ARM64: Injeção de `max_old_space_size=4096` nos Dockerfiles para evitar crashes do Vite no Github Actions.
- [x] Correção de Rota Express 5: Migração de curingas `*` para Regex para evitar falhas de inicialização no servidor. - [x] Correção de Rota Express 5: Migração de curingas `*` para Regex para evitar falhas de inicialização no servidor.
- [ ] Próximo Passo: Validar o tempo de build no Github Actions e confirmar o carregamento das fotos na VPS Oracle. - [x] Correção do Crash 404 no Portal: Injeção da pasta `src/services` no container de produção para permitir o import do `storage.js`.
- [x] Correção das Imagens de Prova: Normalização das URLs nas questões de avaliações (Portal e Manager).
- [x] Estabilização de CI/CD: Transição para `runs-on: self-hosted` (ARM64 nativo) eliminando lentidão e crashes do QEMU.
- [ ] Próximo Passo: Verificar se o Watchtower sincronizou as imagens corretamente na produção.
### 💳 Módulo Financeiro (Portal do Aluno) ### 💳 Módulo Financeiro (Portal do Aluno)
- **Funcionalidades Implementadas:** - **Funcionalidades Implementadas:**
@ -40,8 +43,8 @@
- [x] **Telemetria do Sistema (Settings):** Cards reais de monitoramento de disco (Postgres) e objetos (MinIO). - [x] **Telemetria do Sistema (Settings):** Cards reais de monitoramento de disco (Postgres) e objetos (MinIO).
### 🚀 Infraestrutura e Deploy ### 🚀 Infraestrutura e Deploy
- **Estado Atual:** O pipeline do GitHub Actions está configurado para gerar imagens Docker para `amd64` e `arm64`. - **Estado Atual:** Pipeline 100% estabilizado no GitHub Actions usando `self-hosted` runner (Oracle ARM64 nativo).
- **Desafio:** O build de `arm64` via QEMU é extremamente lento (>15 min). Tentativas de otimização causaram quebras e foram revertidas para manter a estabilidade. - **Melhoria:** O build agora ocorre diretamente na arquitetura de destino, sem emulação QEMU, garantindo velocidade e estabilidade total.
## 📋 Próximos Passos Pendentes ## 📋 Próximos Passos Pendentes

View File

@ -14,6 +14,16 @@ const Exams: React.FC<ExamsProps> = ({ data, updateData }) => {
const [editingExam, setEditingExam] = useState<Exam | null>(null); const [editingExam, setEditingExam] = useState<Exam | null>(null);
const [isUploading, setIsUploading] = useState(false); const [isUploading, setIsUploading] = useState(false);
const normalizePhotoUrl = (url?: string) => {
if (!url) return '';
if (url.startsWith('data:image') || url.startsWith('/storage')) return url;
try {
const match = url.match(/^https?:\/\/[^\/]+\/(.+)$/);
if (match) return `/storage/${match[1]}`;
} catch(e) {}
return url;
};
const exams = data.exams || []; const exams = data.exams || [];
const filteredExams = exams.filter(exam => const filteredExams = exams.filter(exam =>
@ -272,7 +282,7 @@ const Exams: React.FC<ExamsProps> = ({ data, updateData }) => {
<label className="block text-sm font-bold text-slate-700 mb-2">Imagem de Apoio (Opcional)</label> <label className="block text-sm font-bold text-slate-700 mb-2">Imagem de Apoio (Opcional)</label>
{question.imageUrl ? ( {question.imageUrl ? (
<div className="relative inline-block mt-2 group/img"> <div className="relative inline-block mt-2 group/img">
<img src={question.imageUrl} alt="Apoio" className="max-w-full md:max-w-md h-auto rounded-xl border border-slate-200 shadow-sm" /> <img src={normalizePhotoUrl(question.imageUrl)} alt="Apoio" className="max-w-full md:max-w-md h-auto rounded-xl border border-slate-200 shadow-sm" />
<button <button
onClick={() => handleQuestionChange(qIndex, 'imageUrl', undefined)} onClick={() => handleQuestionChange(qIndex, 'imageUrl', undefined)}
className="absolute top-2 right-2 p-2 bg-red-500 text-white rounded-lg opacity-0 group-hover/img:opacity-100 transition-opacity flex items-center justify-center hover:bg-red-600 shadow-lg" className="absolute top-2 right-2 p-2 bg-red-500 text-white rounded-lg opacity-0 group-hover/img:opacity-100 transition-opacity flex items-center justify-center hover:bg-red-600 shadow-lg"

View File

@ -5,6 +5,7 @@ import {
ClipboardList, Clock, ChevronLeft, ChevronRight, Send, CheckCircle2, ClipboardList, Clock, ChevronLeft, ChevronRight, Send, CheckCircle2,
XCircle, Award, AlertTriangle, Timer, ArrowLeft XCircle, Award, AlertTriangle, Timer, ArrowLeft
} from 'lucide-react'; } from 'lucide-react';
import { normalizePhotoUrl } from '../services/storage';
// ========================================== // ==========================================
// Exam Environment — Portal do Aluno // Exam Environment — Portal do Aluno
@ -273,10 +274,10 @@ export default function Avaliacoes() {
Imagem de Apoio Imagem de Apoio
</div> </div>
<img <img
src={question.imageUrl} src={normalizePhotoUrl(question.imageUrl)}
alt="Imagem de apoio" alt="Imagem de apoio"
style={{ width: '100%', height: 'auto', display: 'block', cursor: 'zoom-in' }} style={{ width: '100%', height: 'auto', display: 'block', cursor: 'zoom-in' }}
onClick={() => window.open(question.imageUrl, '_blank')} onClick={() => window.open(normalizePhotoUrl(question.imageUrl), '_blank')}
/> />
</div> </div>
)} )}