Compare commits
3 Commits
848a49c92d
...
a61664a46e
| Author | SHA1 | Date | |
|---|---|---|---|
| a61664a46e | |||
| 1a73a9e373 | |||
| 4877e859bd |
+11
-1
@@ -6,4 +6,14 @@ venv
|
|||||||
.venv
|
.venv
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
static
|
static
|
||||||
media
|
media
|
||||||
|
docs
|
||||||
|
logs
|
||||||
|
staticfiles
|
||||||
|
.gitignore
|
||||||
|
AGENTS.md
|
||||||
|
Dockerfile
|
||||||
|
Makefile
|
||||||
|
nginx.conf
|
||||||
|
Procfile
|
||||||
|
uv.lock
|
||||||
@@ -14,13 +14,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout del código
|
- name: Checkout del código
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
- name: Configurar Python
|
- name: Configurar Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.14'
|
python-version: '3.14'
|
||||||
- name: Configurar uv
|
- name: Configurar uv
|
||||||
uses: astral-sh/setup-uv@v6
|
uses: astral-sh/setup-uv@d0d8abe699bfb85fec6de9f7adb5ae17292296ff # v6
|
||||||
- name: Instalar dependencias
|
- name: Instalar dependencias
|
||||||
run: |
|
run: |
|
||||||
uv sync --no-dev --no-install-project
|
uv sync --no-dev --no-install-project
|
||||||
@@ -38,13 +38,13 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout del código
|
- name: Checkout del código
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
|
||||||
- name: Configurar Docker Buildx
|
- name: Configurar Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v4
|
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
|
||||||
|
|
||||||
- name: Build (sin push)
|
- name: Build (sin push)
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: false
|
push: false
|
||||||
|
|||||||
+19
-3
@@ -4,18 +4,34 @@ ENV PYTHONDONTWRITEBYTECODE=1
|
|||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY pyproject.toml uv.lock /app/
|
COPY pyproject.toml uv.lock /app/
|
||||||
|
|
||||||
RUN apk --no-cache update && apk --no-cache upgrade \
|
RUN apk --no-cache update \
|
||||||
|
&& apk --no-cache upgrade \
|
||||||
|
&& apk --no-cache add \
|
||||||
|
build-base \
|
||||||
|
freetype-dev \
|
||||||
|
jpeg-dev \
|
||||||
|
zlib-dev \
|
||||||
&& pip install --no-cache-dir uv \
|
&& pip install --no-cache-dir uv \
|
||||||
&& uv sync --no-dev --no-install-project # Install only dependencies, not the local project package
|
&& uv sync --no-dev --no-install-project # Install only dependencies, not the local project package
|
||||||
|
|
||||||
COPY . /app/
|
COPY ./entrypoint.sh /app/entrypoint.sh
|
||||||
RUN chmod +x /app/entrypoint.sh
|
RUN chmod +x /app/entrypoint.sh
|
||||||
|
|
||||||
|
COPY ./proyecto /app/proyecto
|
||||||
|
COPY ./tienda /app/tienda
|
||||||
|
COPY ./manage.py /app/manage.py
|
||||||
|
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
RUN mkdir -pv /fonts
|
RUN mkdir -pv /fonts
|
||||||
COPY tienda/static/fonts/ /fonts/
|
COPY tienda/static/fonts/ /fonts/
|
||||||
|
|
||||||
|
RUN addgroup -S app \
|
||||||
|
&& adduser -S app -G app \
|
||||||
|
&& chown -R app:app /app /fonts
|
||||||
|
|
||||||
|
USER app
|
||||||
|
|
||||||
ENTRYPOINT ["/bin/sh", "/app/entrypoint.sh"]
|
ENTRYPOINT ["/bin/sh", "/app/entrypoint.sh"]
|
||||||
|
|||||||
+3
-2
@@ -6,7 +6,8 @@ from django.contrib.auth.models import User, AbstractUser
|
|||||||
from django.core.validators import MaxValueValidator
|
from django.core.validators import MaxValueValidator
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string
|
||||||
from .vars import VAT_RATE, TRANSACTION_CODE_PREFIX, TRANSACTION_CODE_LENGTH, TRANSACTION_CODE_ALPHABET
|
from .vars import VAT_RATE, TRANSACTION_CODE_PREFIX, TRANSACTION_CODE_LENGTH, TRANSACTION_CODE_ALPHABET
|
||||||
import random, string
|
import secrets
|
||||||
|
import string
|
||||||
|
|
||||||
MAX_QUANTITY = 9999
|
MAX_QUANTITY = 9999
|
||||||
|
|
||||||
@@ -76,7 +77,7 @@ class VerificationCode(models.Model):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def generate(user: User, code_mode: str) -> VerificationCode:
|
def generate(user: User, code_mode: str) -> VerificationCode:
|
||||||
while True:
|
while True:
|
||||||
code = "".join(random.choices(string.ascii_letters+string.digits, k=64))
|
code = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(64))
|
||||||
if not VerificationCode.objects.filter(code=code).exists():
|
if not VerificationCode.objects.filter(code=code).exists():
|
||||||
return VerificationCode.objects.create(
|
return VerificationCode.objects.create(
|
||||||
code = code,
|
code = code,
|
||||||
|
|||||||
+4
-3
@@ -4,7 +4,8 @@ from django.template.loader import render_to_string
|
|||||||
from django.core.mail import EmailMessage
|
from django.core.mail import EmailMessage
|
||||||
from .utilities import send_email, send_hemail
|
from .utilities import send_email, send_hemail
|
||||||
from .vars import login_message, verify_message
|
from .vars import login_message, verify_message
|
||||||
import random, string
|
import secrets
|
||||||
|
import string
|
||||||
from . import pdf
|
from . import pdf
|
||||||
|
|
||||||
from .models import User, VerificationCode
|
from .models import User, VerificationCode
|
||||||
@@ -43,7 +44,7 @@ def enviar_correo_confirmacion(id: int):
|
|||||||
code = VerificationCode.objects.create(
|
code = VerificationCode.objects.create(
|
||||||
user = usuario,
|
user = usuario,
|
||||||
code_mode = VerificationCode.VerificationModes.VERIFY_ACCOUNT,
|
code_mode = VerificationCode.VerificationModes.VERIFY_ACCOUNT,
|
||||||
code = ''.join(random.choices(string.digits, k=12))
|
code = ''.join(secrets.choice(string.digits) for _ in range(12))
|
||||||
)
|
)
|
||||||
|
|
||||||
message = verify_message.format(name = usuario.get_full_name(), protocol = settings.PROTOCOL, domain = settings.DOMAIN, code = code.code)
|
message = verify_message.format(name = usuario.get_full_name(), protocol = settings.PROTOCOL, domain = settings.DOMAIN, code = code.code)
|
||||||
@@ -60,7 +61,7 @@ def enviar_correo_recuperacion(email: str):
|
|||||||
ver_code = VerificationCode.objects.create(
|
ver_code = VerificationCode.objects.create(
|
||||||
code_mode = VerificationCode.VerificationModes.RESET_PASSWORD,
|
code_mode = VerificationCode.VerificationModes.RESET_PASSWORD,
|
||||||
user = usuario,
|
user = usuario,
|
||||||
code = ''.join(random.choices(string.digits, k=12))
|
code = ''.join(secrets.choice(string.digits) for _ in range(12))
|
||||||
)
|
)
|
||||||
ver_code.save()
|
ver_code.save()
|
||||||
html_content = render_to_string(
|
html_content = render_to_string(
|
||||||
|
|||||||
+5
-4
@@ -16,6 +16,7 @@ from .models import (
|
|||||||
)
|
)
|
||||||
from .forms import UserRegisterForm, UserLoginForm, EditProfileForm, ChangePasswordForm, ShippingAddressForm, ResetPasswordForm, ResetPasswordPhase2Form
|
from .forms import UserRegisterForm, UserLoginForm, EditProfileForm, ChangePasswordForm, ShippingAddressForm, ResetPasswordForm, ResetPasswordPhase2Form
|
||||||
from .vars import VAT_RATE, TRANSACTION_CODE_PREFIX
|
from .vars import VAT_RATE, TRANSACTION_CODE_PREFIX
|
||||||
|
import secrets
|
||||||
import string
|
import string
|
||||||
import random
|
import random
|
||||||
|
|
||||||
@@ -335,7 +336,7 @@ class VerificationCodeModelTests(TestCase):
|
|||||||
"""50 códigos pueden crearse sin conflictos."""
|
"""50 códigos pueden crearse sin conflictos."""
|
||||||
codes = []
|
codes = []
|
||||||
for i in range(50):
|
for i in range(50):
|
||||||
mode = random.choice([
|
mode = secrets.choice([
|
||||||
VerificationCode.VerificationModes.VERIFY_ACCOUNT,
|
VerificationCode.VerificationModes.VERIFY_ACCOUNT,
|
||||||
VerificationCode.VerificationModes.RESET_PASSWORD
|
VerificationCode.VerificationModes.RESET_PASSWORD
|
||||||
])
|
])
|
||||||
@@ -377,7 +378,7 @@ class CategoryModelTests(TestCase):
|
|||||||
"""100 categorías pueden crearse sin problemas."""
|
"""100 categorías pueden crearse sin problemas."""
|
||||||
categories = []
|
categories = []
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
cat = Category.objects.create(name=f"Category_{i}_{random.randint(1000, 9999)}")
|
cat = Category.objects.create(name=f"Category_{i}_{1000 + secrets.randbelow(9000)}")
|
||||||
categories.append(cat)
|
categories.append(cat)
|
||||||
|
|
||||||
self.assertEqual(len(categories), 100)
|
self.assertEqual(len(categories), 100)
|
||||||
@@ -1786,7 +1787,7 @@ class EndpointViewTests(TestCase):
|
|||||||
self.assertTrue(OrderMessage.objects.filter(order_item=item, sender=self.seller).exists())
|
self.assertTrue(OrderMessage.objects.filter(order_item=item, sender=self.seller).exists())
|
||||||
|
|
||||||
delete_get = self.client.get(reverse("borrar_producto", args=[created.id]))
|
delete_get = self.client.get(reverse("borrar_producto", args=[created.id]))
|
||||||
self.assertEqual(delete_get.status_code, 302)
|
self.assertEqual(delete_get.status_code, 405)
|
||||||
delete_post = self.client.post(reverse("borrar_producto", args=[created.id]))
|
delete_post = self.client.post(reverse("borrar_producto", args=[created.id]))
|
||||||
self.assertEqual(delete_post.status_code, 302)
|
self.assertEqual(delete_post.status_code, 302)
|
||||||
self.assertFalse(Product.objects.filter(id=created.id).exists())
|
self.assertFalse(Product.objects.filter(id=created.id).exists())
|
||||||
@@ -2068,7 +2069,7 @@ class EndpointViewTests(TestCase):
|
|||||||
self.assertEqual(new_address.full_name, "Comprador Dos Editado")
|
self.assertEqual(new_address.full_name, "Comprador Dos Editado")
|
||||||
|
|
||||||
delete_get = self.client.get(reverse("eliminar_direccion", args=[new_address.id]))
|
delete_get = self.client.get(reverse("eliminar_direccion", args=[new_address.id]))
|
||||||
self.assertEqual(delete_get.status_code, 302)
|
self.assertEqual(delete_get.status_code, 405)
|
||||||
delete_post = self.client.post(reverse("eliminar_direccion", args=[new_address.id]))
|
delete_post = self.client.post(reverse("eliminar_direccion", args=[new_address.id]))
|
||||||
self.assertEqual(delete_post.status_code, 302)
|
self.assertEqual(delete_post.status_code, 302)
|
||||||
self.assertFalse(ShippingAddress.objects.filter(id=new_address.id).exists())
|
self.assertFalse(ShippingAddress.objects.filter(id=new_address.id).exists())
|
||||||
|
|||||||
+2
-5
@@ -1030,7 +1030,7 @@ def editar_producto(request: HttpRequest, id: int):
|
|||||||
})
|
})
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@require_http_methods(["GET", "POST"])
|
@require_POST
|
||||||
def borrar_producto(request: HttpRequest, id: int):
|
def borrar_producto(request: HttpRequest, id: int):
|
||||||
"""Borra un producto del usuario autenticado"""
|
"""Borra un producto del usuario autenticado"""
|
||||||
|
|
||||||
@@ -2191,12 +2191,9 @@ def editar_direccion(request: HttpRequest, id: int):
|
|||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@require_http_methods(["GET", "POST"])
|
@require_POST
|
||||||
def eliminar_direccion(request: HttpRequest, id: int):
|
def eliminar_direccion(request: HttpRequest, id: int):
|
||||||
"""Elimina una dirección de entrega"""
|
"""Elimina una dirección de entrega"""
|
||||||
if request.method != "POST":
|
|
||||||
messages.error(request, "Acción no permitida.")
|
|
||||||
return redirect("direcciones_usuario")
|
|
||||||
|
|
||||||
direccion = get_object_or_404(ShippingAddress, id=id, user=request.user)
|
direccion = get_object_or_404(ShippingAddress, id=id, user=request.user)
|
||||||
direccion.delete()
|
direccion.delete()
|
||||||
|
|||||||
Reference in New Issue
Block a user