fjf
parent
a387b64af9
commit
3f86d8d822
Binary file not shown.
15
cart/cart.py
15
cart/cart.py
|
@ -9,19 +9,24 @@ class Cart:
|
|||
Initialize the cart.
|
||||
"""
|
||||
self.session = request.session
|
||||
cart = self.session.get(settings.CART_SESSION_ID)
|
||||
user_id = self.session.get('keycloak_user_id') # Используйте идентификатор пользователя из сессии
|
||||
if user_id:
|
||||
cart_session_id = f"{settings.CART_SESSION_ID}_{user_id}" # Создайте уникальный ключ сессии для корзины
|
||||
else:
|
||||
cart_session_id = settings.CART_SESSION_ID
|
||||
cart = self.session.get(cart_session_id)
|
||||
if not cart:
|
||||
# save an empty cart in the session
|
||||
cart = self.session[settings.CART_SESSION_ID] = {}
|
||||
cart = self.session[cart_session_id] = {}
|
||||
self.cart = cart
|
||||
self.cart_session_id = cart_session_id
|
||||
|
||||
def __iter__(self):
|
||||
"""
|
||||
Iterate over the items in the cart and get the products
|
||||
from the database.
|
||||
"""
|
||||
product_ids = self.cart.keys()
|
||||
# get the product objects and add them to the cart
|
||||
product_ids = self.cart.keys() # get the product objects and add them to the cart
|
||||
products = Product.objects.filter(id__in=product_ids)
|
||||
cart = self.cart.copy()
|
||||
for product in products:
|
||||
|
@ -55,6 +60,8 @@ class Cart:
|
|||
# mark the session as "modified" to make sure it gets saved
|
||||
self.session.modified = True
|
||||
|
||||
self.session[self.cart_session_id] = self.cart
|
||||
|
||||
def remove(self, product):
|
||||
"""
|
||||
Remove a product from the cart.
|
||||
|
|
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
|
@ -0,0 +1,15 @@
|
|||
INFO 2024-02-23 13:45:46,251 autoreload Watching for file changes with StatReloader
|
||||
INFO 2024-02-23 13:46:13,396 views Redirecting to Keycloak for authentication.
|
||||
INFO 2024-02-23 13:46:43,771 views Redirecting to Keycloak for authentication.
|
||||
INFO 2024-02-23 13:46:48,385 views Redirecting to Keycloak for authentication.
|
||||
INFO 2024-02-23 13:46:48,633 views Redirecting to Keycloak for authentication.
|
||||
INFO 2024-02-23 13:46:48,830 views Redirecting to Keycloak for authentication.
|
||||
INFO 2024-02-23 13:48:22,691 autoreload Watching for file changes with StatReloader
|
||||
INFO 2024-02-23 13:48:32,058 views Redirecting to Keycloak for authentication.
|
||||
INFO 2024-02-23 13:48:37,176 views Redirecting to Keycloak for authentication.
|
||||
INFO 2024-02-23 13:48:37,585 views Redirecting to Keycloak for authentication.
|
||||
INFO 2024-02-23 13:48:44,312 views Redirecting to Keycloak for authentication.
|
||||
INFO 2024-02-23 13:48:51,761 views Redirecting to Keycloak for authentication.
|
||||
INFO 2024-02-23 21:15:16,575 autoreload Watching for file changes with StatReloader
|
||||
INFO 2024-02-23 21:15:27,243 views Redirecting to Keycloak for authentication.
|
||||
INFO 2024-02-23 21:15:32,818 views Redirecting to Keycloak for authentication.
|
Binary file not shown.
Binary file not shown.
|
@ -25,7 +25,7 @@ SECRET_KEY = 'django-insecure-)p-ed99i+^)@_t33f65!y8eqn23!&kt)zc(ht$8h7e8#1k(o$f
|
|||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
ALLOWED_HOSTS = ['127.0.0.1'] # Пример для разработки и продакшена
|
||||
|
||||
CART_SESSION_ID = 'cart'
|
||||
# Application definition
|
||||
|
@ -33,6 +33,7 @@ CART_SESSION_ID = 'cart'
|
|||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django_extensions',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
|
@ -40,6 +41,9 @@ INSTALLED_APPS = [
|
|||
'main.apps.MainConfig',
|
||||
'cart.apps.CartConfig',
|
||||
'orders.apps.OrdersConfig',
|
||||
'users.apps.UsersConfig',
|
||||
|
||||
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -143,3 +147,42 @@ EMAIL_HOST_USER = 'fhjj3590@gmail.com'
|
|||
EMAIL_HOST_PASSWORD = 'uzmd nmit yxwc sgox' # Предполагается, что это сгенерированный пароль для приложений
|
||||
EMAIL_PORT = 587
|
||||
EMAIL_USE_TLS = True
|
||||
|
||||
|
||||
KEYCLOAK_CONFIG = {
|
||||
'SERVER_URL': 'https://auth.myterior.kz',
|
||||
'REALM': 'Harmony',
|
||||
'CLIENT_ID': 'lowlight',
|
||||
'CLIENT_SECRET': 'u31Kvkj9V2npxdwJUODReO3YJ2w2iMul',
|
||||
'CALLBACK_URL': 'http://127.0.0.1:8000/products/',
|
||||
}
|
||||
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '{levelname} {asctime} {module} {message}',
|
||||
'style': '{',
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'console': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
'file': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging.FileHandler',
|
||||
'filename': 'django.log',
|
||||
'formatter': 'verbose',
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'': { # 'root' logger
|
||||
'handlers': ['console', 'file'],
|
||||
'level': 'INFO',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,32 +1,15 @@
|
|||
"""
|
||||
URL configuration for Harmony project.
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/5.0/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path,include
|
||||
from django.urls import path, include
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('cart/', include('cart.urls', namespace='cart')),
|
||||
path('cart/', include('cart.urls', namespace='cart')),
|
||||
path('orders/', include('orders.urls', namespace='orders')),
|
||||
|
||||
path('', include('main.urls', namespace='main')),
|
||||
|
||||
|
||||
path('auth/', include('users.urls')), # Подключение URL приложения users
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.MEDIA_URL,
|
||||
document_root=settings.MEDIA_ROOT)
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 2.0 MiB |
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.0.2 on 2024-02-22 09:03
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('main', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='product',
|
||||
name='price',
|
||||
field=models.IntegerField(max_length=19),
|
||||
),
|
||||
]
|
Binary file not shown.
|
@ -27,8 +27,7 @@ class Product(models.Model):
|
|||
image = models.ImageField(upload_to='products/%Y/%m/%d',
|
||||
blank=True)
|
||||
description = models.TextField(blank=True)
|
||||
price = models.DecimalField(max_digits=10,
|
||||
decimal_places=2)
|
||||
price = models.IntegerField(max_length=19)
|
||||
available = models.BooleanField(default=True)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
updated = models.DateTimeField(auto_now=True)
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-facebook" viewBox="0 0 16 16">
|
||||
<path d="M16 8.049c0-4.446-3.582-8.05-8-8.05C3.58 0-.002 3.603-.002 8.05c0 4.017 2.926 7.347 6.75 7.951v-5.625h-2.03V8.05H6.75V6.275c0-2.017 1.195-3.131 3.022-3.131.876 0 1.791.157 1.791.157v1.98h-1.009c-.993 0-1.303.621-1.303 1.258v1.51h2.218l-.354 2.326H9.25V16c3.824-.604 6.75-3.934 6.75-7.951z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 429 B |
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-google" viewBox="0 0 16 16">
|
||||
<path d="M15.545 6.558a9.42 9.42 0 0 1 .139 1.626c0 2.434-.87 4.492-2.384 5.885h.002C11.978 15.292 10.158 16 8 16A8 8 0 1 1 8 0a7.689 7.689 0 0 1 5.352 2.082l-2.284 2.284A4.347 4.347 0 0 0 8 3.166c-2.087 0-3.86 1.408-4.492 3.304a4.792 4.792 0 0 0 0 3.063h.003c.635 1.893 2.405 3.301 4.492 3.301 1.078 0 2.004-.276 2.722-.764h-.003a3.702 3.702 0 0 0 1.599-2.431H8v-3.08h7.545z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 505 B |
|
@ -48,22 +48,28 @@
|
|||
<li class="nav-item">
|
||||
<a class="nav-link text-white" href="{% url 'cart:cart_detail' %}">Корзина</a>
|
||||
</li>
|
||||
{# <li class="nav-item dropdown ">#}
|
||||
{# <a class="nav-link dropdown-toggle text-white" href="#" role="button"#}
|
||||
{# data-bs-toggle="dropdown" aria-expanded="false">#}
|
||||
{# Мой профиль#}
|
||||
{# </a>#}
|
||||
{# <ul class="dropdown-menu">#}
|
||||
{# #}
|
||||
{# <li><a class="dropdown-item text-white" href="#">Личный кабинет</a>#}
|
||||
{# </li>#}
|
||||
{##}
|
||||
{# <li>#}
|
||||
{# <hr class="dropdown-divider">#}
|
||||
{# </li>#}
|
||||
{##}
|
||||
{# </ul>#}
|
||||
{# </li>#}
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-white" href="{% url 'keycloak_login' %}">Войти</a>
|
||||
<li><a class="dropdown-item text-white" href="{% url 'profile' %}">Личный кабинет</a></li>
|
||||
</li>
|
||||
{% if request.session.keycloak_user_id %}
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle text-white" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Мой профиль
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item text-white" href="{% url 'keycloak_logout' %}">Выйти</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "main/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Личный кабинет</h1>
|
||||
<p>Привет, {{ username }}!</p>
|
||||
|
||||
{% endblock %}
|
Binary file not shown.
After Width: | Height: | Size: 137 KiB |
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.0.2 on 2024-02-22 09:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('orders', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='orderitem',
|
||||
name='price',
|
||||
field=models.IntegerField(max_length=19),
|
||||
),
|
||||
]
|
Binary file not shown.
|
@ -29,7 +29,7 @@ class Order(models.Model):
|
|||
class OrderItem(models.Model):
|
||||
order = models.ForeignKey(Order, related_name='items', on_delete=models.CASCADE)
|
||||
product = models.ForeignKey(Product, related_name='order_items', on_delete=models.CASCADE)
|
||||
price = models.DecimalField(max_digits=10, decimal_places=2)
|
||||
price = models.IntegerField(max_length=19)
|
||||
quantity = models.PositiveIntegerField(default=1)
|
||||
|
||||
def __str__(self):
|
||||
|
|
|
@ -3,6 +3,7 @@ from .models import OrderItem, Order
|
|||
from .forms import OrderCreateForm
|
||||
from cart.cart import Cart
|
||||
from django.conf import settings
|
||||
from decimal import Decimal
|
||||
|
||||
from .utils import send_invoice_via_email
|
||||
|
||||
|
@ -27,4 +28,8 @@ def order_create(request):
|
|||
|
||||
def order_created(request, order_id):
|
||||
order = get_object_or_404(Order, id=order_id)
|
||||
return render(request, 'orders/order/created.html', {'order': order})
|
||||
return render(request, 'orders/order/created.html', {'order': order})
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class UsersConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'users'
|
Binary file not shown.
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -0,0 +1,10 @@
|
|||
from django.urls import path
|
||||
from .views import keycloak_login, keycloak_callback, keycloak_logout
|
||||
from .views import profile
|
||||
urlpatterns = [
|
||||
path('login/', keycloak_login, name='keycloak_login'),
|
||||
path('callback/', keycloak_callback, name='keycloak_callback'),
|
||||
path('logout/', keycloak_logout, name='keycloak_logout'),
|
||||
path('profile/', profile, name='profile'),
|
||||
|
||||
]
|
|
@ -0,0 +1,89 @@
|
|||
from django.shortcuts import redirect,render
|
||||
from django.http import HttpResponse
|
||||
from keycloak import KeycloakOpenID, KeycloakGetError
|
||||
from django.conf import settings
|
||||
import logging
|
||||
|
||||
# Настройка логгера для текущего модуля
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Функция для начала процесса аутентификации с Keycloak
|
||||
def keycloak_login(request):
|
||||
try:
|
||||
keycloak_openid = KeycloakOpenID(
|
||||
server_url=settings.KEYCLOAK_CONFIG['SERVER_URL'],
|
||||
client_id=settings.KEYCLOAK_CONFIG['CLIENT_ID'],
|
||||
realm_name=settings.KEYCLOAK_CONFIG['REALM'],
|
||||
client_secret_key=settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
|
||||
)
|
||||
auth_url = keycloak_openid.auth_url(redirect_uri=settings.KEYCLOAK_CONFIG['CALLBACK_URL'])
|
||||
logger.info("Redirecting to Keycloak for authentication.")
|
||||
return redirect(auth_url)
|
||||
except Exception as e:
|
||||
logger.error(f"Error during Keycloak login: {e}")
|
||||
return HttpResponse("Ошибка при попытке аутентификации через Keycloak.")
|
||||
|
||||
# Функция обратного вызова для обработки ответа от Keycloak
|
||||
def keycloak_callback(request):
|
||||
code = request.GET.get('code')
|
||||
if code:
|
||||
try:
|
||||
keycloak_openid = KeycloakOpenID(
|
||||
server_url=settings.KEYCLOAK_CONFIG['SERVER_URL'],
|
||||
client_id=settings.KEYCLOAK_CONFIG['CLIENT_ID'],
|
||||
realm_name=settings.KEYCLOAK_CONFIG['REALM'],
|
||||
client_secret_key=settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
|
||||
)
|
||||
|
||||
token = keycloak_openid.token(
|
||||
grant_type=['authorization_code'],
|
||||
code=code,
|
||||
redirect_uri=settings.KEYCLOAK_CONFIG['CALLBACK_URL']
|
||||
)
|
||||
|
||||
userinfo = keycloak_openid.userinfo(token['access_token'])
|
||||
logger.info(f"User authenticated with Keycloak: {userinfo}")
|
||||
|
||||
request.session['keycloak_user_id'] = userinfo['sub']
|
||||
request.session['username'] = userinfo.get('preferred_username', 'Guest')
|
||||
|
||||
return redirect('http://127.0.0.1:8000/products/')
|
||||
except KeycloakGetError as e:
|
||||
logger.error(f"Keycloak authentication error: {e}")
|
||||
return HttpResponse("Ошибка аутентификации.")
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error during Keycloak callback: {e}")
|
||||
return HttpResponse("Неожиданная ошибка.")
|
||||
else:
|
||||
logger.warning("Authentication code not provided.")
|
||||
return HttpResponse("Код аутентификации не предоставлен.")
|
||||
|
||||
# Функция для выхода из системы
|
||||
def keycloak_logout(request):
|
||||
try:
|
||||
request.session.flush()
|
||||
logger.info("User logged out.")
|
||||
return redirect('http://127.0.0.1:8000/')
|
||||
except Exception as e:
|
||||
logger.error(f"Error during logout: {e}")
|
||||
return HttpResponse("Ошибка при выходе из системы.")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def profile(request):
|
||||
# Проверяем, аутентифицирован ли пользователь
|
||||
if 'keycloak_user_id' in request.session:
|
||||
# Извлекаем данные пользователя из сессии
|
||||
user_id = request.session['keycloak_user_id']
|
||||
username = request.session.get('username', 'Гость')
|
||||
logger.info(f"Keycloak user ID: {request.session['keycloak_user_id']}, Username: {request.session['username']},{username}")
|
||||
|
||||
|
||||
|
||||
# Отображаем шаблон, передавая в него данные пользователя
|
||||
return render(request, 'main/product/profile.html', {'username': username})
|
||||
else:
|
||||
# Если пользователь не аутентифицирован, перенаправляем на страницу входа
|
||||
return redirect('keycloak_login')
|
Loading…
Reference in New Issue