MEJORA: usar imagenes pre-construidas del registry de Gitea en lugar de build local para evitar bind-mount en Portainer
This commit is contained in:
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
FROM python:3.12-slim
|
FROM python:3.12-slim
|
||||||
|
|
||||||
# Establecer variables de entorno
|
# Establecer variables de entorno
|
||||||
ENV PYTHONDONTWRITEBYTECODE 1
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
ENV PYTHONUNBUFFERED 1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
# Establecer directorio de trabajo
|
# Establecer directorio de trabajo
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|||||||
+3
-10
@@ -1,13 +1,10 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
build: .
|
image: git.controlmc.click/ginevra_oa/erp-efintel-2026/web:latest
|
||||||
command: gunicorn config.asgi:application -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000 --workers 3
|
command: gunicorn config.asgi:application -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000 --workers 3
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
REDIS_URL: redis://redis:6379/0
|
REDIS_URL: redis://redis:6379/0
|
||||||
# Django necesita escuchar en 0.0.0.0 (ya lo hace gunicorn)
|
|
||||||
volumes:
|
volumes:
|
||||||
- erp_media:/app/media
|
- erp_media:/app/media
|
||||||
- erp_static:/app/static
|
- erp_static:/app/static
|
||||||
@@ -35,10 +32,9 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
mem_limit: 512m
|
mem_limit: 512m
|
||||||
# Redis no necesita puertos públicos ni etiquetas Traefik
|
|
||||||
|
|
||||||
celery:
|
celery:
|
||||||
build: .
|
image: git.controlmc.click/ginevra_oa/erp-efintel-2026/web:latest
|
||||||
command: celery -A config worker -l info --concurrency=2
|
command: celery -A config worker -l info --concurrency=2
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
@@ -50,19 +46,16 @@ services:
|
|||||||
mem_limit: 512m
|
mem_limit: 512m
|
||||||
|
|
||||||
tax-api:
|
tax-api:
|
||||||
build: ./tax_api
|
image: git.controlmc.click/ginevra_oa/erp-efintel-2026/tax-api:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
mem_limit: 256m
|
mem_limit: 256m
|
||||||
# Si la API de impuestos necesita ser expuesta al público por Traefik,
|
|
||||||
# se deben agregar etiquetas similares a las del servicio 'web' con un ROUTER_NAME diferente.
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
controlmc_proxy:
|
controlmc_proxy:
|
||||||
external: true
|
external: true
|
||||||
internal:
|
internal:
|
||||||
# Red interna para que web, redis, celery y tax-api se comuniquen sin exponerse
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
erp_media:
|
erp_media:
|
||||||
|
|||||||
+1
-1
@@ -52,7 +52,7 @@ wheel==0.42.0
|
|||||||
zope.interface==8.5
|
zope.interface==8.5
|
||||||
|
|
||||||
whitenoise==6.7.0
|
whitenoise==6.7.0
|
||||||
django-storages==2.14.2
|
django-storages==1.14.6
|
||||||
boto3==1.34.69
|
boto3==1.34.69
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
uvicorn[standard]
|
uvicorn[standard]
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import django
|
||||||
|
import json
|
||||||
|
|
||||||
|
# Set up Django environment
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from django.test import Client
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.urls import get_resolver, URLPattern, URLResolver
|
||||||
|
|
||||||
|
def get_all_urls(urlpatterns, prefix=''):
|
||||||
|
urls = []
|
||||||
|
for pattern in urlpatterns:
|
||||||
|
if isinstance(pattern, URLPattern):
|
||||||
|
# Try to build a valid URL by replacing common parameters
|
||||||
|
url = prefix + str(pattern.pattern)
|
||||||
|
url = url.replace('<int:pk>', '1')
|
||||||
|
url = url.replace('<int:candidate_pk>', '1')
|
||||||
|
url = url.replace('<int:po_pk>', '1')
|
||||||
|
url = url.replace('<str:action>', 'approve')
|
||||||
|
url = url.replace('^', '').replace('$', '')
|
||||||
|
urls.append(url)
|
||||||
|
elif isinstance(pattern, URLResolver):
|
||||||
|
urls.extend(get_all_urls(pattern.url_patterns, prefix + str(pattern.pattern)))
|
||||||
|
return urls
|
||||||
|
|
||||||
|
def run_analysis():
|
||||||
|
User = get_user_model()
|
||||||
|
# Try to get an existing admin or any user, or create a temporary one
|
||||||
|
user = User.objects.filter(is_superuser=True).first() or User.objects.first()
|
||||||
|
created_user = False
|
||||||
|
if not user:
|
||||||
|
user = User.objects.create_superuser('test_admin', 'admin@example.com', 'password123')
|
||||||
|
created_user = True
|
||||||
|
|
||||||
|
client = Client()
|
||||||
|
client.force_login(user)
|
||||||
|
|
||||||
|
resolver = get_resolver()
|
||||||
|
urls = get_all_urls(resolver.url_patterns)
|
||||||
|
|
||||||
|
# Filter out admin, media, and tax_api endpoints
|
||||||
|
urls = [u for u in urls if not u.startswith('admin/') and not u.startswith('media/') and 'tax_api' not in u]
|
||||||
|
|
||||||
|
# Prepend / to urls if needed
|
||||||
|
urls = ['/' + u if not u.startswith('/') else u for u in urls]
|
||||||
|
|
||||||
|
results = []
|
||||||
|
|
||||||
|
for url in urls:
|
||||||
|
# Ignore endpoints that aren't strictly GET or testable by simple GET
|
||||||
|
if url == '/logout/':
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = client.get(url)
|
||||||
|
status_code = response.status_code
|
||||||
|
content_type = response.headers.get('Content-Type', '')
|
||||||
|
|
||||||
|
snippet = ""
|
||||||
|
if 'application/json' in content_type:
|
||||||
|
try:
|
||||||
|
data = response.json()
|
||||||
|
snippet = json.dumps(data)[:150] + ("..." if len(json.dumps(data)) > 150 else "")
|
||||||
|
except Exception:
|
||||||
|
snippet = response.content.decode('utf-8', errors='ignore')[:150]
|
||||||
|
else:
|
||||||
|
snippet = response.content.decode('utf-8', errors='ignore')[:150].replace('\n', ' ').strip()
|
||||||
|
if len(response.content) > 150:
|
||||||
|
snippet += "..."
|
||||||
|
|
||||||
|
results.append({
|
||||||
|
'url': url,
|
||||||
|
'status_code': status_code,
|
||||||
|
'snippet': snippet,
|
||||||
|
'content_type': content_type
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
results.append({
|
||||||
|
'url': url,
|
||||||
|
'status_code': 'Error',
|
||||||
|
'snippet': str(e),
|
||||||
|
'content_type': 'N/A'
|
||||||
|
})
|
||||||
|
|
||||||
|
# Cleanup user if we created it
|
||||||
|
if created_user:
|
||||||
|
user.delete()
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print("Starting endpoints analysis...")
|
||||||
|
results = run_analysis()
|
||||||
|
|
||||||
|
artifact_path = "/home/cheerry/.gemini/antigravity/brain/5fe9c8b0-cf58-4038-b407-4851abf7278b/django_endpoints.md"
|
||||||
|
|
||||||
|
with open(artifact_path, "w", encoding="utf-8") as f:
|
||||||
|
f.write("# Django Endpoints Analysis\n\n")
|
||||||
|
f.write("| Endpoint | Status Code | Content Type | Snippet |\n")
|
||||||
|
f.write("|----------|-------------|--------------|---------|\n")
|
||||||
|
for res in results:
|
||||||
|
# Escape pipe symbols for markdown tables
|
||||||
|
snippet = res['snippet'].replace('|', '\\|').replace('\n', ' ')
|
||||||
|
f.write(f"| `{res['url']}` | {res['status_code']} | {res['content_type']} | `{snippet}` |\n")
|
||||||
|
|
||||||
|
print(f"Analysis successfully saved to {artifact_path}")
|
||||||
Executable
+50
@@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================
|
||||||
|
# Script para construir y subir imágenes Docker
|
||||||
|
# al Container Registry de Gitea (ControlMC)
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
REGISTRY="git.controlmc.click"
|
||||||
|
OWNER="ginevra_oa"
|
||||||
|
REPO="erp-efintel-2026"
|
||||||
|
TAG="latest"
|
||||||
|
|
||||||
|
WEB_IMAGE="${REGISTRY}/${OWNER}/${REPO}/web:${TAG}"
|
||||||
|
TAX_IMAGE="${REGISTRY}/${OWNER}/${REPO}/tax-api:${TAG}"
|
||||||
|
|
||||||
|
echo "============================================"
|
||||||
|
echo " ERP Efintel - Build & Push al Registry"
|
||||||
|
echo "============================================"
|
||||||
|
|
||||||
|
# Paso 1: Login en el registry de Gitea
|
||||||
|
echo ""
|
||||||
|
echo "[1/4] Iniciando sesión en el registry de Gitea..."
|
||||||
|
echo " (Usa tu usuario de Gitea y un Access Token como contraseña)"
|
||||||
|
docker login ${REGISTRY}
|
||||||
|
|
||||||
|
# Paso 2: Construir imagen web (Django)
|
||||||
|
echo ""
|
||||||
|
echo "[2/4] Construyendo imagen web (Django + Gunicorn)..."
|
||||||
|
docker build -t ${WEB_IMAGE} .
|
||||||
|
|
||||||
|
# Paso 3: Construir imagen tax-api (FastAPI)
|
||||||
|
echo ""
|
||||||
|
echo "[3/4] Construyendo imagen tax-api (FastAPI)..."
|
||||||
|
docker build -t ${TAX_IMAGE} ./tax_api
|
||||||
|
|
||||||
|
# Paso 4: Subir ambas imágenes al registry
|
||||||
|
echo ""
|
||||||
|
echo "[4/4] Subiendo imágenes al registry de Gitea..."
|
||||||
|
docker push ${WEB_IMAGE}
|
||||||
|
docker push ${TAX_IMAGE}
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " ¡Listo! Imágenes subidas exitosamente."
|
||||||
|
echo " web: ${WEB_IMAGE}"
|
||||||
|
echo " tax-api: ${TAX_IMAGE}"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "Ahora ve a Portainer y haz 'Pull and redeploy'"
|
||||||
Reference in New Issue
Block a user