7 Commits

Author SHA1 Message Date
elordenador c33def1124 Add staticfiles folder to .gitignore 2026-05-04 22:03:05 +02:00
elordenador 52dfa51af2 Remove Static Files 2026-05-04 22:02:19 +02:00
elordenador a02617f8d2 Move MD files and add an AGENTS.md 2026-05-04 22:01:27 +02:00
elordenador 53b4e89347 Fix tasks.py making tests fail 2026-05-04 22:01:12 +02:00
elordenador df0579dd86 Fix GH Issue #68 2026-05-04 21:59:28 +02:00
elordenador 1022a44f12 Fix GH Issue #65 2026-05-04 19:51:49 +02:00
elordenador bb697d92c6 Fix GH Issue #64 2026-05-04 19:45:47 +02:00
27 changed files with 88 additions and 198584 deletions
+1
View File
@@ -8,3 +8,4 @@ __pycache__/
tienda/__pycache__/ tienda/__pycache__/
proyecto/__pycache__/ proyecto/__pycache__/
media media
staticfiles
+61
View File
@@ -0,0 +1,61 @@
# AGENTS.md - Django Tienda Project
## Commands
```bash
# Run tests (runs tienda/tests.py by default)
make test
# Or manually
python manage.py test
# Run dev server
python manage.py runserver
# Run migrations
python manage.py migrate
# Static files (production)
python manage.py collectstatic
```
## Prerequisites
- **Redis**: Must be running on `redis://127.0.0.1:6379/1` for sessions and caching
- Start: `sudo systemctl start redis-server` (Linux) or `brew services start redis` (macOS)
- Verify: `redis-cli ping` → PONG
- **PostgreSQL**: Default database; set `POSTGRES_ENABLED=False` to use SQLite
- **Environment**: Copy `.env.example` to `.env` and configure required vars
## Important Quirks
1. **Migrations**: If `makemigrations` fails with error code 130, **check `tienda/migrations/`** - the file is often created despite the error
2. **Test DB**: Uses SQLite regardless of POSTGRES_ENABLED (hardcoded in settings.py)
3. **App URL**: Not at `/` - access at `http://localhost:8000/tienda/`
4. **Admin**: At `/admin/` (not `/tienda/admin/`)
5. **Custom User Model**: AUTH_USER_MODEL = 'tienda.User' - use for all user-related queries
## Architecture
- `proyecto/` - Django settings, URLs, WSGI/ASGI
- `tienda/` - Main app (models, views, admin, templates)
- `tienda/static/` - CSS, JS, images, fonts
- Templates extend `tienda/templates/tienda/base.html`
## Shipping Restrictions
Only sells to Almería province, Spain (postal codes 04xxx). All addresses saved with country "España".
## External Services
- **Payment**: Stripe + PayPal (configured via .env)
- **Storage**: S3 support - set `S3_ENABLE=True` to enable
- **Email**: SMTP required (see .env.example)
- **Async**: Celery uses Redis broker
## Useful References
- Full developer docs: `.github/copilot-instructions.md`
- Redis setup: `docs/REDIS_SETUP.md`
- PayPal: `docs/PAYPAL_SETUP.md`, `docs/PAYPAL_TROUBLESHOOTING.md`
- View documentation: `docs/views/`
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+6 -1
View File
@@ -33,7 +33,11 @@ def enviar_correo_confirmacion(id: int):
@shared_task @shared_task
def enviar_correo_recuperacion(email: str): def enviar_correo_recuperacion(email: str):
usuario: User | None
try:
usuario = User.objects.get(email=email) usuario = User.objects.get(email=email)
except User.DoesNotExist as e:
usuario = None
if usuario is not None: if usuario is not None:
ver_code = VerificationCode.objects.create( ver_code = VerificationCode.objects.create(
code_mode = VerificationCode.VerificationModes.RESET_PASSWORD, code_mode = VerificationCode.VerificationModes.RESET_PASSWORD,
@@ -53,7 +57,8 @@ def enviar_correo_recuperacion(email: str):
) )
send_hemail(email, "Reset de Contraseña", html_content, "Estas reseteando la contraseña...") send_hemail(email, "Reset de Contraseña", html_content, "Estas reseteando la contraseña...")
else:
print("User does not exist, Cancelling TASK.")
# Purchased items should be a list of dictionary, the dictionary must follow this tags: amount, product name, price (each) # Purchased items should be a list of dictionary, the dictionary must follow this tags: amount, product name, price (each)
@shared_task @shared_task
+18 -1
View File
@@ -240,7 +240,24 @@ def login(request: HttpRequest):
# Autenticar usuario # Autenticar usuario
user = authenticate(request, username=username, password=password) user = authenticate(request, username=username, password=password)
if user is None: # Bug de error 500 en caso de fallar la contra if user is None:
data: str = cache.get(f"tries_login_{username}")
logins: int
if data is None:
logins = int(data)
else:
logins = 0
if logins >= 5:
# Si ha fallado 5 intentos de login...
audit_logger.info(
"LOGIN_FAILED email=%s reason=rate_limited", username
)
messages.error(request, "Has sufrido de Rate Limit por fallar 5 veces la contraseña")
return render(request, "tienda/login.html")
logins+=1
cache.set(f"tries_login_{username}", str(logins), 600)
messages.error(request, "Correo electrónico o contraseña incorrectos.") messages.error(request, "Correo electrónico o contraseña incorrectos.")
return render(request, "tienda/login.html") return render(request, "tienda/login.html")
user = User.objects.get(username=user.username) user = User.objects.get(username=user.username)