perf(ranking): otimizar paginação usando Oracle direto
- Remover carregamento de 350k registros em memória no startup - Refatorar endpoints para buscar dados direto do Oracle: - ranking_paginado - buscar_por_nome - ranking_estatisticas - obter_posicao_ranking - Adicionar healthcheck leve no Oracle com start_period de 60s - Corrigir start-ngrok.sh para subir todos os containers - Adicionar domínio ngrok-free.dev no vite.config.js
This commit is contained in:
@@ -1,6 +1,4 @@
|
|||||||
import logging
|
import logging
|
||||||
import json
|
|
||||||
import asyncio
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
@@ -14,77 +12,6 @@ logger = logging.getLogger(__name__)
|
|||||||
from .config import settings
|
from .config import settings
|
||||||
from .dependencies import es_client, oracle_client, ranking_oracle_repo, get_processar_job
|
from .dependencies import es_client, oracle_client, ranking_oracle_repo, get_processar_job
|
||||||
from ...application.jobs.scheduler import RankingScheduler
|
from ...application.jobs.scheduler import RankingScheduler
|
||||||
from ...infrastructure.ranking_store import ranking_store, RankingEntry
|
|
||||||
|
|
||||||
|
|
||||||
async def carregar_ranking_do_oracle() -> int:
|
|
||||||
if not ranking_oracle_repo or not oracle_client:
|
|
||||||
logger.warning("Oracle não configurado - ranking será carregado do Elasticsearch quando solicitado")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
try:
|
|
||||||
if not oracle_client.is_connected:
|
|
||||||
oracle_client.connect()
|
|
||||||
if not oracle_client.is_connected:
|
|
||||||
logger.warning("Não foi possível conectar ao Oracle")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def _sync_load():
|
|
||||||
total = ranking_oracle_repo.contar_total()
|
|
||||||
if total == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
consultores = []
|
|
||||||
batch_size = 10000
|
|
||||||
total_pages = (total + batch_size - 1) // batch_size
|
|
||||||
logger.info(f"Carregando {total} consultores do Oracle em {total_pages} batches...")
|
|
||||||
|
|
||||||
for page in range(1, total_pages + 1):
|
|
||||||
batch = ranking_oracle_repo.buscar_paginado(page=page, size=batch_size)
|
|
||||||
consultores.extend(batch)
|
|
||||||
|
|
||||||
return consultores
|
|
||||||
|
|
||||||
consultores = await asyncio.wait_for(
|
|
||||||
asyncio.get_event_loop().run_in_executor(None, _sync_load),
|
|
||||||
timeout=300.0
|
|
||||||
)
|
|
||||||
|
|
||||||
if not consultores:
|
|
||||||
logger.info("Nenhum dado encontrado no Oracle - ranking vazio")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
entries = []
|
|
||||||
for c in consultores:
|
|
||||||
try:
|
|
||||||
detalhes = json.loads(c.json_detalhes) if isinstance(c.json_detalhes, str) else c.json_detalhes or {}
|
|
||||||
except (json.JSONDecodeError, TypeError, ValueError):
|
|
||||||
detalhes = {}
|
|
||||||
|
|
||||||
entries.append(
|
|
||||||
RankingEntry(
|
|
||||||
id_pessoa=c.id_pessoa,
|
|
||||||
nome=c.nome,
|
|
||||||
posicao=c.posicao or 0,
|
|
||||||
pontuacao_total=int(c.pontuacao_total),
|
|
||||||
bloco_a=int(c.componente_a),
|
|
||||||
bloco_b=int(c.componente_b),
|
|
||||||
bloco_c=int(c.componente_c),
|
|
||||||
bloco_d=int(c.componente_d),
|
|
||||||
ativo=c.ativo,
|
|
||||||
anos_atuacao=float(c.anos_atuacao or 0),
|
|
||||||
detalhes=detalhes,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
await ranking_store.set_entries(entries)
|
|
||||||
return len(entries)
|
|
||||||
except asyncio.TimeoutError:
|
|
||||||
logger.warning("Timeout ao carregar ranking do Oracle")
|
|
||||||
return 0
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Erro ao carregar ranking do Oracle: {e}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
@@ -95,12 +22,8 @@ async def lifespan(app: FastAPI):
|
|||||||
if oracle_client:
|
if oracle_client:
|
||||||
oracle_client.connect()
|
oracle_client.connect()
|
||||||
if oracle_client.is_connected:
|
if oracle_client.is_connected:
|
||||||
logger.info("Conectado ao Oracle")
|
total = ranking_oracle_repo.contar_total() if ranking_oracle_repo else 0
|
||||||
total = await carregar_ranking_do_oracle()
|
logger.info(f"Conectado ao Oracle - {total} consultores no ranking")
|
||||||
if total > 0:
|
|
||||||
logger.info(f"Ranking carregado do Oracle: {total} consultores")
|
|
||||||
else:
|
|
||||||
logger.info("Ranking vazio no Oracle - aguardando processamento")
|
|
||||||
else:
|
else:
|
||||||
logger.warning("Não foi possível conectar ao Oracle")
|
logger.warning("Não foi possível conectar ao Oracle")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import html
|
import html
|
||||||
|
import re
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -10,6 +11,42 @@ from pydantic import BaseModel
|
|||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
|
|
||||||
|
|
||||||
|
LATIN1_BYTE_MAP = {
|
||||||
|
192: 'À', 193: 'Á', 194: 'Â', 195: 'Ã', 196: 'Ä',
|
||||||
|
199: 'Ç',
|
||||||
|
200: 'È', 201: 'É', 202: 'Ê', 203: 'Ë',
|
||||||
|
204: 'Ì', 205: 'Í', 206: 'Î', 207: 'Ï',
|
||||||
|
209: 'Ñ',
|
||||||
|
210: 'Ò', 211: 'Ó', 212: 'Ô', 213: 'Õ', 214: 'Ö',
|
||||||
|
217: 'Ù', 218: 'Ú', 219: 'Û', 220: 'Ü',
|
||||||
|
224: 'à', 225: 'á', 226: 'â', 227: 'ã', 228: 'ä',
|
||||||
|
231: 'ç',
|
||||||
|
232: 'è', 233: 'é', 234: 'ê', 235: 'ë',
|
||||||
|
236: 'ì', 237: 'í', 238: 'î', 239: 'ï',
|
||||||
|
241: 'ñ',
|
||||||
|
242: 'ò', 243: 'ó', 244: 'ô', 245: 'õ', 246: 'ö',
|
||||||
|
249: 'ù', 250: 'ú', 251: 'û', 252: 'ü',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def corrigir_encoding(texto: str) -> str:
|
||||||
|
if not texto:
|
||||||
|
return texto
|
||||||
|
|
||||||
|
def substituir_byte(match):
|
||||||
|
byte_val = int(match.group(1))
|
||||||
|
return LATIN1_BYTE_MAP.get(byte_val, match.group(0))
|
||||||
|
|
||||||
|
pattern = r'(?<=[\w\u00C0-\u00FF])(\d{3})(?=[\w\u00C0-\u00FF])'
|
||||||
|
resultado = texto
|
||||||
|
for _ in range(5):
|
||||||
|
novo = re.sub(pattern, substituir_byte, resultado)
|
||||||
|
if novo == resultado:
|
||||||
|
break
|
||||||
|
resultado = novo
|
||||||
|
return resultado
|
||||||
|
|
||||||
|
|
||||||
def normalizar_texto(texto: str) -> str:
|
def normalizar_texto(texto: str) -> str:
|
||||||
if not texto:
|
if not texto:
|
||||||
return ""
|
return ""
|
||||||
@@ -41,7 +78,7 @@ from ..schemas.ranking_schema import (
|
|||||||
SugerirConsultoresResponseSchema,
|
SugerirConsultoresResponseSchema,
|
||||||
AreaAvaliacaoSchema,
|
AreaAvaliacaoSchema,
|
||||||
)
|
)
|
||||||
from .dependencies import get_repository, get_ranking_store, get_processar_job, get_es_client
|
from .dependencies import get_repository, get_ranking_store, get_processar_job, get_es_client, get_ranking_oracle_repo
|
||||||
from ...infrastructure.elasticsearch.client import ElasticsearchClient
|
from ...infrastructure.elasticsearch.client import ElasticsearchClient
|
||||||
from ...application.jobs.job_status import job_status
|
from ...application.jobs.job_status import job_status
|
||||||
|
|
||||||
@@ -168,35 +205,43 @@ async def ranking_paginado(
|
|||||||
size: int = Query(default=50, ge=1, le=1000, description="Tamanho da página (máx 1000)"),
|
size: int = Query(default=50, ge=1, le=1000, description="Tamanho da página (máx 1000)"),
|
||||||
ativo: Optional[bool] = Query(default=None, description="Filtrar por status ativo"),
|
ativo: Optional[bool] = Query(default=None, description="Filtrar por status ativo"),
|
||||||
selos: Optional[str] = Query(default=None, description="Filtrar por selos (separados por vírgula)"),
|
selos: Optional[str] = Query(default=None, description="Filtrar por selos (separados por vírgula)"),
|
||||||
store = Depends(get_ranking_store),
|
oracle_repo = Depends(get_ranking_oracle_repo),
|
||||||
):
|
):
|
||||||
if not store.is_ready():
|
import json as json_lib
|
||||||
|
|
||||||
|
if not oracle_repo:
|
||||||
|
raise HTTPException(status_code=503, detail="Oracle não configurado")
|
||||||
|
|
||||||
|
total = oracle_repo.contar_total(filtro_ativo=ativo)
|
||||||
|
if total == 0:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=503,
|
status_code=503,
|
||||||
detail="Ranking ainda não foi processado. Execute POST /api/v1/ranking/processar.",
|
detail="Ranking ainda não foi processado. Execute POST /api/v1/ranking/processar.",
|
||||||
)
|
)
|
||||||
|
|
||||||
filtro_selos = [s.strip() for s in selos.split(",") if s.strip()] if selos else None
|
consultores = oracle_repo.buscar_paginado(page=page, size=size, filtro_ativo=ativo)
|
||||||
total, entries = store.get_page(page=page, size=size, filtro_ativo=ativo, filtro_selos=filtro_selos)
|
|
||||||
|
|
||||||
total_pages = (total + size - 1) // size
|
total_pages = (total + size - 1) // size
|
||||||
|
|
||||||
consultores_schema = []
|
consultores_schema = []
|
||||||
for e in entries:
|
for c in consultores:
|
||||||
d = e.detalhes
|
try:
|
||||||
|
d = json_lib.loads(c.json_detalhes) if isinstance(c.json_detalhes, str) else c.json_detalhes or {}
|
||||||
|
except (json_lib.JSONDecodeError, TypeError):
|
||||||
|
d = {}
|
||||||
|
|
||||||
tipos_atuacao = RankingMapper._extrair_tipos_atuacao(d)
|
tipos_atuacao = RankingMapper._extrair_tipos_atuacao(d)
|
||||||
consultores_schema.append(
|
consultores_schema.append(
|
||||||
ConsultorRankingResumoSchema(
|
ConsultorRankingResumoSchema(
|
||||||
id_pessoa=e.id_pessoa,
|
id_pessoa=c.id_pessoa,
|
||||||
nome=e.nome,
|
nome=c.nome,
|
||||||
posicao=e.posicao,
|
posicao=c.posicao,
|
||||||
pontuacao_total=float(e.pontuacao_total),
|
pontuacao_total=float(c.pontuacao_total),
|
||||||
bloco_a=float(e.bloco_a),
|
bloco_a=float(c.componente_a),
|
||||||
bloco_b=float(e.bloco_b),
|
bloco_b=float(c.componente_b),
|
||||||
bloco_c=float(e.bloco_c),
|
bloco_c=float(c.componente_c),
|
||||||
bloco_d=float(e.bloco_d),
|
bloco_d=float(c.componente_d),
|
||||||
ativo=e.ativo,
|
ativo=c.ativo,
|
||||||
anos_atuacao=float(e.anos_atuacao),
|
anos_atuacao=float(c.anos_atuacao),
|
||||||
tipos_atuacao=tipos_atuacao,
|
tipos_atuacao=tipos_atuacao,
|
||||||
coordenador_ppg=bool(d.get("coordenador_ppg", False)),
|
coordenador_ppg=bool(d.get("coordenador_ppg", False)),
|
||||||
consultoria=d.get("consultoria"),
|
consultoria=d.get("consultoria"),
|
||||||
@@ -225,15 +270,12 @@ async def ranking_paginado(
|
|||||||
async def buscar_por_nome(
|
async def buscar_por_nome(
|
||||||
nome: str = Query(..., min_length=3, description="Nome (ou parte) para buscar"),
|
nome: str = Query(..., min_length=3, description="Nome (ou parte) para buscar"),
|
||||||
limit: int = Query(default=5, ge=1, le=20, description="Limite de resultados"),
|
limit: int = Query(default=5, ge=1, le=20, description="Limite de resultados"),
|
||||||
store = Depends(get_ranking_store),
|
oracle_repo = Depends(get_ranking_oracle_repo),
|
||||||
):
|
):
|
||||||
if not store.is_ready():
|
if not oracle_repo:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=503, detail="Oracle não configurado")
|
||||||
status_code=503,
|
|
||||||
detail="Ranking ainda não foi processado. Execute POST /api/v1/ranking/processar.",
|
|
||||||
)
|
|
||||||
|
|
||||||
resultados = store.buscar_por_nome(nome=nome, limit=limit)
|
resultados = oracle_repo.buscar_por_nome(nome=nome, limit=limit)
|
||||||
return [
|
return [
|
||||||
ConsultaNomeSchema(
|
ConsultaNomeSchema(
|
||||||
id_pessoa=r["ID_PESSOA"],
|
id_pessoa=r["ID_PESSOA"],
|
||||||
@@ -247,53 +289,20 @@ async def buscar_por_nome(
|
|||||||
|
|
||||||
@router.get("/ranking/estatisticas", response_model=EstatisticasRankingSchema)
|
@router.get("/ranking/estatisticas", response_model=EstatisticasRankingSchema)
|
||||||
async def ranking_estatisticas(
|
async def ranking_estatisticas(
|
||||||
store = Depends(get_ranking_store),
|
oracle_repo = Depends(get_ranking_oracle_repo),
|
||||||
):
|
):
|
||||||
if not store.is_ready():
|
if not oracle_repo:
|
||||||
|
raise HTTPException(status_code=503, detail="Oracle não configurado")
|
||||||
|
|
||||||
|
total = oracle_repo.contar_total()
|
||||||
|
if total == 0:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=503,
|
status_code=503,
|
||||||
detail="Ranking ainda não foi processado. Execute POST /api/v1/ranking/processar.",
|
detail="Ranking ainda não foi processado. Execute POST /api/v1/ranking/processar.",
|
||||||
)
|
)
|
||||||
|
|
||||||
total = store.total()
|
estatisticas = oracle_repo.obter_estatisticas()
|
||||||
ativos = store.total(filtro_ativo=True)
|
distribuicao = oracle_repo.obter_distribuicao()
|
||||||
inativos = total - ativos
|
|
||||||
entries = store.get_page(page=1, size=total)[1] if total else []
|
|
||||||
totais = [e.pontuacao_total for e in entries]
|
|
||||||
distribuicao = []
|
|
||||||
if total:
|
|
||||||
buckets = [
|
|
||||||
("800+", lambda x: x >= 800),
|
|
||||||
("600-799", lambda x: 600 <= x < 800),
|
|
||||||
("400-599", lambda x: 400 <= x < 600),
|
|
||||||
("200-399", lambda x: 200 <= x < 400),
|
|
||||||
("0-199", lambda x: x < 200),
|
|
||||||
]
|
|
||||||
for faixa, pred in buckets:
|
|
||||||
qtd = sum(1 for x in totais if pred(x))
|
|
||||||
distribuicao.append(
|
|
||||||
{
|
|
||||||
"faixa": faixa,
|
|
||||||
"quantidade": qtd,
|
|
||||||
"percentual": round((qtd * 100.0 / total), 2) if total else 0,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
estatisticas = {
|
|
||||||
"total_consultores": total,
|
|
||||||
"total_ativos": ativos,
|
|
||||||
"total_inativos": inativos,
|
|
||||||
"ultima_atualizacao": store.last_update.isoformat() if store.last_update else None,
|
|
||||||
"pontuacao_media": (sum(totais) / total) if total else 0,
|
|
||||||
"pontuacao_maxima": max(totais) if totais else 0,
|
|
||||||
"pontuacao_minima": min(totais) if totais else 0,
|
|
||||||
"media_componentes": {
|
|
||||||
"a": (sum(e.bloco_a for e in entries) / total) if total else 0,
|
|
||||||
"b": (sum(e.bloco_b for e in entries) / total) if total else 0,
|
|
||||||
"c": (sum(e.bloco_c for e in entries) / total) if total else 0,
|
|
||||||
"d": (sum(e.bloco_d for e in entries) / total) if total else 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return EstatisticasRankingSchema(
|
return EstatisticasRankingSchema(
|
||||||
total_consultores=estatisticas.get("total_consultores", 0),
|
total_consultores=estatisticas.get("total_consultores", 0),
|
||||||
@@ -333,16 +342,13 @@ async def processar_ranking(
|
|||||||
@router.get("/ranking/posicao/{id_pessoa}", response_model=PosicaoRankingSchema)
|
@router.get("/ranking/posicao/{id_pessoa}", response_model=PosicaoRankingSchema)
|
||||||
async def obter_posicao_ranking(
|
async def obter_posicao_ranking(
|
||||||
id_pessoa: int,
|
id_pessoa: int,
|
||||||
store = Depends(get_ranking_store),
|
oracle_repo = Depends(get_ranking_oracle_repo),
|
||||||
):
|
):
|
||||||
if not store.is_ready():
|
if not oracle_repo:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=503, detail="Oracle não configurado")
|
||||||
status_code=503,
|
|
||||||
detail="Ranking ainda não foi processado. Execute POST /api/v1/ranking/processar.",
|
|
||||||
)
|
|
||||||
|
|
||||||
entry = store.get_by_id(id_pessoa)
|
total = oracle_repo.contar_total()
|
||||||
total = store.total()
|
entry = oracle_repo.buscar_por_id(id_pessoa)
|
||||||
|
|
||||||
if not entry:
|
if not entry:
|
||||||
return PosicaoRankingSchema(
|
return PosicaoRankingSchema(
|
||||||
@@ -365,10 +371,10 @@ async def obter_posicao_ranking(
|
|||||||
posicao=entry.posicao,
|
posicao=entry.posicao,
|
||||||
total_consultores=total,
|
total_consultores=total,
|
||||||
pontuacao_total=float(entry.pontuacao_total),
|
pontuacao_total=float(entry.pontuacao_total),
|
||||||
bloco_a=float(entry.bloco_a),
|
bloco_a=float(entry.componente_a),
|
||||||
bloco_b=float(entry.bloco_b),
|
bloco_b=float(entry.componente_b),
|
||||||
bloco_c=float(entry.bloco_c),
|
bloco_c=float(entry.componente_c),
|
||||||
bloco_d=float(entry.bloco_d),
|
bloco_d=float(entry.componente_d),
|
||||||
ativo=entry.ativo,
|
ativo=entry.ativo,
|
||||||
encontrado=True,
|
encontrado=True,
|
||||||
)
|
)
|
||||||
@@ -531,14 +537,14 @@ async def sugerir_consultores(
|
|||||||
|
|
||||||
for area in dados.get("areaConhecimentoPos", []):
|
for area in dados.get("areaConhecimentoPos", []):
|
||||||
if area.get("nome"):
|
if area.get("nome"):
|
||||||
area_nome = html.unescape(area["nome"])
|
area_nome = corrigir_encoding(html.unescape(area["nome"]))
|
||||||
areas_conhecimento.add(area_nome)
|
areas_conhecimento.add(area_nome)
|
||||||
area_norm = normalizar_texto(area["nome"])
|
area_norm = normalizar_texto(area["nome"])
|
||||||
if tema_norm in area_norm or any(p in area_norm for p in tema_palavras if len(p) > 3):
|
if tema_norm in area_norm or any(p in area_norm for p in tema_palavras if len(p) > 3):
|
||||||
motivos_match.add(f"Area: {area_nome}")
|
motivos_match.add(f"Area: {area_nome}")
|
||||||
area_aval = area.get("areaAvaliacao", {})
|
area_aval = area.get("areaAvaliacao", {})
|
||||||
if area_aval and area_aval.get("nome"):
|
if area_aval and area_aval.get("nome"):
|
||||||
aval_nome = html.unescape(area_aval["nome"])
|
aval_nome = corrigir_encoding(html.unescape(area_aval["nome"]))
|
||||||
areas_avaliacao.add(aval_nome)
|
areas_avaliacao.add(aval_nome)
|
||||||
aval_norm = normalizar_texto(area_aval["nome"])
|
aval_norm = normalizar_texto(area_aval["nome"])
|
||||||
if tema_norm in aval_norm or any(p in aval_norm for p in tema_palavras if len(p) > 3):
|
if tema_norm in aval_norm or any(p in aval_norm for p in tema_palavras if len(p) > 3):
|
||||||
@@ -546,7 +552,7 @@ async def sugerir_consultores(
|
|||||||
|
|
||||||
for pesq in dados.get("areaPesquisa", []):
|
for pesq in dados.get("areaPesquisa", []):
|
||||||
if pesq.get("descricao"):
|
if pesq.get("descricao"):
|
||||||
pesq_desc = html.unescape(pesq["descricao"])
|
pesq_desc = corrigir_encoding(html.unescape(pesq["descricao"]))
|
||||||
linhas_pesquisa.add(pesq_desc)
|
linhas_pesquisa.add(pesq_desc)
|
||||||
pesq_norm = normalizar_texto(pesq["descricao"])
|
pesq_norm = normalizar_texto(pesq["descricao"])
|
||||||
if tema_norm in pesq_norm or any(p in pesq_norm for p in tema_palavras if len(p) > 3):
|
if tema_norm in pesq_norm or any(p in pesq_norm for p in tema_palavras if len(p) > 3):
|
||||||
|
|||||||
@@ -33,8 +33,6 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: ./frontend
|
context: ./frontend
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
depends_on:
|
|
||||||
- backend
|
|
||||||
ports:
|
ports:
|
||||||
- "5173:5173"
|
- "5173:5173"
|
||||||
environment:
|
environment:
|
||||||
@@ -52,26 +50,27 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
oracle18c:
|
oracle18c:
|
||||||
container_name: oracle18c
|
container_name: oracle18c
|
||||||
image: gvenzl/oracle-xe:18-slim
|
image: gvenzl/oracle-xe:18-slim
|
||||||
environment:
|
environment:
|
||||||
- ORACLE_PASSWORD=local123
|
- ORACLE_PASSWORD=local123
|
||||||
- ORACLE_CHARACTERSET=AL32UTF8
|
- ORACLE_CHARACTERSET=AL32UTF8
|
||||||
- APP_USER=local123
|
- APP_USER=local123
|
||||||
- APP_USER_PASSWORD=local123
|
- APP_USER_PASSWORD=local123
|
||||||
- TZ=America/Sao_Paulo
|
- TZ=America/Sao_Paulo
|
||||||
ports:
|
ports:
|
||||||
- "1521:1521"
|
- "1521:1521"
|
||||||
- "5500:5500"
|
- "5500:5500"
|
||||||
volumes:
|
volumes:
|
||||||
- oracle_data:/opt/oracle/oradata
|
- oracle_data:/opt/oracle/oradata
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "bash", "-c", "echo 'SELECT 1 FROM DUAL;' | sqlplus -s SYSTEM/\"$${ORACLE_PASSWORD}\"@localhost:1521/XEPDB1"]
|
test: ["CMD", "bash", "-c", "echo 'SELECT 1 FROM DUAL;' | sqlplus -s SYSTEM/local123@localhost:1521/XEPDB1 | grep -q 1"]
|
||||||
interval: 30s
|
interval: 10s
|
||||||
timeout: 10s
|
timeout: 5s
|
||||||
retries: 20
|
retries: 30
|
||||||
networks:
|
start_period: 60s
|
||||||
- shared_network
|
networks:
|
||||||
|
- shared_network
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
shared_network:
|
shared_network:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default defineConfig({
|
|||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: 5173,
|
port: 5173,
|
||||||
allowedHosts: ['.ngrok-free.app', 'localhost', '127.0.0.1'],
|
allowedHosts: ['.ngrok-free.app', '.ngrok-free.dev', 'localhost', '127.0.0.1'],
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://backend:8000',
|
target: 'http://backend:8000',
|
||||||
|
|||||||
@@ -16,10 +16,9 @@ pkill -f "ngrok http 5173" 2>/dev/null
|
|||||||
|
|
||||||
echo "[4/5] Criando rede e subindo containers..."
|
echo "[4/5] Criando rede e subindo containers..."
|
||||||
docker network create shared_network 2>/dev/null
|
docker network create shared_network 2>/dev/null
|
||||||
docker compose up -d backend frontend
|
docker compose up -d
|
||||||
echo "Aguardando backend subir..."
|
echo "Aguardando containers subirem..."
|
||||||
sleep 10
|
sleep 10
|
||||||
docker compose up -d backend frontend >/dev/null
|
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user