main
Lowlights 2024-03-05 17:23:18 +06:00
parent 3f86d8d822
commit 1a9f3afec2
44 changed files with 6182 additions and 349 deletions

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@ -9,24 +9,19 @@ class Cart:
Initialize the cart.
"""
self.session = request.session
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)
cart = self.session.get(settings.CART_SESSION_ID)
if not cart:
# save an empty cart in the session
cart = self.session[cart_session_id] = {}
cart = self.session[settings.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:
@ -60,8 +55,6 @@ 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.

View File

@ -5,11 +5,20 @@
{% block content %}
<link rel="stylesheet" href="{% static "/deps/css/cart.css" %}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css">
<div class="cart-container">
<h1>Корзина</h1>
<table class="cart-table">
<thead>
<div class="cart-container position-relative">
<div class="text-center my-4">
<h1 class="display-4" style="color:black;">
<i class="fa fa-shopping-cart" aria-hidden="true"></i> Корзина
</h1>
<p class="lead" style="color: whitesmoke">Проверьте свои товары перед оформлением заказа.</p>
</div>
</div>
<table class="cart-table" style="position: relative; background-color: #1d02269e;">
<thead >
<tr>
<th>Изображение</th>
<th>Продукт</th>
@ -19,7 +28,7 @@
<th>Общая цена</th>
</tr>
</thead>
<tbody>
<tbody >
{% for item in cart %}
{% with product=item.product %}
<tr>
@ -34,13 +43,13 @@
{% csrf_token %}
{{ item.update_quantity_form.quantity }}
{{ item.update_quantity_form.override }}
<input type="submit" value="Update" class="btn-update">
<input type="submit" value="Обновить" class="btn-update">
</form>
</td>
<td>
<form action="{% url 'cart:cart_remove' product.id %}" method="post">
{% csrf_token %}
<input type="submit" value="Remove" class="btn-remove">
<input type="submit" value="Удалить" class="btn-remove">
</form>
</td>
<td class="num">₸{{ item.price }}</td>
@ -48,17 +57,17 @@
</tr>
{% endwith %}
{% endfor %}
<tr class="total">
<tr class="total position-relative">
<td>Итого</td>
<td colspan="4"></td>
<td class="num">₸{{ cart.get_total_price }}</td>
</tr>
</tbody>
</table>
<div class="cart-action-buttons">
<a href="{% url 'main:product_list' %}" class="button light">Продолжить покупки</a>
<div class="d-flex justify-content-between my-3 position-relative">
<a href="{% url 'main:product_list' %}" class="btn btn-light">Продолжить покупки</a>
<!-- Обновите 'your_checkout_url' на реальный URL оформления заказа -->
<a href="{% url "orders:order_create"%}" class="button checkout">Оформить заказ</a>
<a href="{% url "orders:order_create"%}" class="btn btn-light">Оформить заказ</a>
</div>
</div>

Binary file not shown.

5669
django.log

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,11 @@ CART_SESSION_ID = 'cart'
# Application definition
INSTALLED_APPS = [
'django.contrib.sites',
'allauth',
'allauth.account',
'allauth.socialaccount',
'django.contrib.admin',
'django.contrib.auth',
'django_extensions',
@ -45,6 +50,9 @@ INSTALLED_APPS = [
]
SESSION_COOKIE_AGE = 86400 # 24 часа в секундах
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
SITE_ID = 1
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
@ -54,6 +62,8 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'allauth.account.middleware.AccountMiddleware',
]
ROOT_URLCONF = 'djangoProject1.urls'
@ -70,6 +80,7 @@ TEMPLATES = [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
@ -152,9 +163,10 @@ EMAIL_USE_TLS = True
KEYCLOAK_CONFIG = {
'SERVER_URL': 'https://auth.myterior.kz',
'REALM': 'Harmony',
'CLIENT_ID': 'lowlight',
'CLIENT_SECRET': 'u31Kvkj9V2npxdwJUODReO3YJ2w2iMul',
'CLIENT_ID': 'admin-cli',
'CLIENT_SECRET': 'wOVphEiLVBS1AlNKRpaQpD4yQh5Wm3TJ',
'CALLBACK_URL': 'http://127.0.0.1:8000/products/',
}
@ -186,3 +198,21 @@ LOGGING = {
},
},
}
# settings.py
# ... ваш предыдущий код ...
SOCIALACCOUNT_PROVIDERS = {
'openid': {
'SERVERS': [
{
'id': 'keycloak',
'name': 'Keycloak',
'openid_config_url': 'https://auth.myterior.kz/realms/Harmony/.well-known/openid-configuration',
'client_id': 'admin-cli',
'client_secret': 'wOVphEiLVBS1AlNKRpaQpD4yQh5Wm3TJ',
}
]
}
}

View File

@ -2,13 +2,15 @@ from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from main import views
urlpatterns = [
path('admin/', admin.site.urls),
path('cart/', include('cart.urls', namespace='cart')),
path('orders/', include('orders.urls', namespace='orders')),
path('auth/', include('users.urls')),
path('', include('main.urls', namespace='main')),
path('auth/', include('users.urls')), # Подключение URL приложения users
]
if settings.DEBUG:

View File

@ -1,68 +0,0 @@
%PDF-1.3
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
1 0 obj
<<
/F1 2 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/Contents 7 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 6 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
4 0 obj
<<
/PageMode /UseNone /Pages 6 0 R /Type /Catalog
>>
endobj
5 0 obj
<<
/Author (anonymous) /CreationDate (D:20240218225917-06'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20240218225917-06'00') /Producer (ReportLab PDF Library - www.reportlab.com)
/Subject (unspecified) /Title (untitled) /Trapped /False
>>
endobj
6 0 obj
<<
/Count 1 /Kids [ 3 0 R ] /Type /Pages
>>
endobj
7 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 102
>>
stream
GapQh0E=F,0U\H3T\pNYT^QKk?tc>IP,;W#U1^23ihPEM_?CW4KISi90MntRifICK%KE/ee05&l(FoYV9neZ[Kb,ht@Ke@a%%S##~>endstream
endobj
xref
0 8
0000000000 65535 f
0000000073 00000 n
0000000104 00000 n
0000000211 00000 n
0000000414 00000 n
0000000482 00000 n
0000000778 00000 n
0000000837 00000 n
trailer
<<
/ID
[<dfa685a70af9cd44f8a81cfb0b67bc98><dfa685a70af9cd44f8a81cfb0b67bc98>]
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
/Info 5 0 R
/Root 4 0 R
/Size 8
>>
startxref
1029
%%EOF

View File

@ -1,68 +0,0 @@
%PDF-1.3
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
1 0 obj
<<
/F1 2 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/Contents 7 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 6 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
4 0 obj
<<
/PageMode /UseNone /Pages 6 0 R /Type /Catalog
>>
endobj
5 0 obj
<<
/Author (anonymous) /CreationDate (D:20240218230410-06'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20240218230410-06'00') /Producer (ReportLab PDF Library - www.reportlab.com)
/Subject (unspecified) /Title (untitled) /Trapped /False
>>
endobj
6 0 obj
<<
/Count 1 /Kids [ 3 0 R ] /Type /Pages
>>
endobj
7 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 102
>>
stream
GapQh0E=F,0U\H3T\pNYT^QKk?tc>IP,;W#U1^23ihPEM_?CW4KISi90MntRifICK%KE/ee05&l(FoY69neZ[Kb,ht@Ke@a%'("2~>endstream
endobj
xref
0 8
0000000000 65535 f
0000000073 00000 n
0000000104 00000 n
0000000211 00000 n
0000000414 00000 n
0000000482 00000 n
0000000778 00000 n
0000000837 00000 n
trailer
<<
/ID
[<ec49df0e43ea243c0ea148f44a3a082b><ec49df0e43ea243c0ea148f44a3a082b>]
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
/Info 5 0 R
/Root 4 0 R
/Size 8
>>
startxref
1029
%%EOF

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 KiB

BIN
main/static/deps/cart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 887 KiB

View File

@ -0,0 +1,60 @@
.footer-buttons {
display: flex;
justify-content: center;
gap: 20px; /* Space between buttons */
}
.button {
background: #fafaff; /* Background color of the button */
color: white;
border-radius: 50%; /* Makes the button circular */
width: 120px; /* Diameter of the button */
height: 120px; /* Diameter of the button */
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* Shadow for depth */
cursor: pointer;
transition: background 0.3s ease, box-shadow 0.3s ease;
position: relative; /* For absolute positioning of the icon */
overflow: hidden; /* Prevents overflow of the background image */
margin: 10px; /* Creates space between buttons */
/* Border around the button */
border: 3px solid #ffffff; /* White border */
}
.button:hover {
background: #ffffff; /* Lighter color on hover */
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3); /* Larger shadow on hover */
}
.button span {
font-size: 16px; /* Size of the text */
position: absolute;
bottom: 10px; /* Positioning the text below the icon */
text-transform: uppercase; /* Capitalize the text */
}
/* Icon styles */
.icon-shop, .icon-about, .icon-cart {
background-size: cover; /* Ensures the icon covers the area */
background-position: center; /* Center the icon */
background-repeat: no-repeat;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* Center the icon */
width: 100%; /* Full width of the button */
height: 100%; /* Full height of the button */
}
.icon-shop {
background-image: url(" ../shop.png");
}
.icon-cart {
background-image: url('../cart.png');
}
.icon-about {
background-image: url('../aboutus.png');
}

View File

@ -1,6 +1,6 @@
/* Основные стили страницы корзины с улучшенной видимостью картинок */
.cart-page {
background: linear-gradient(135deg, #483D8B 0%, #6A5ACD 100%); /* Эзотерический градиент фона */
background: linear-gradient(135deg, #ffffff 0%, #f5f5f5 100%); /* Эзотерический градиент фона */
color: #FFFFFF;
padding: 20px;
border-radius: 12px;
@ -27,7 +27,7 @@
.cart-table th {
font-size: 1.1rem;
letter-spacing: 1px;
background-color: rgba(255, 255, 255, 0.2); /* Слегка прозрачный фон для заголовков */
background-color: rgb(187, 123, 255); /* Слегка прозрачный фон для заголовков */
backdrop-filter: blur(4px); /* Размытие под заголовками */
}
@ -45,7 +45,7 @@
border-radius: 25px;
border: 2px solid #FFFFFF;
background: transparent;
color: #00ffc4;
color: #db71ff;
letter-spacing: 0.5px;
font-weight: bold;
text-transform: uppercase; /* Буквы в верхнем регистре для акцента */
@ -57,23 +57,23 @@
}
.btn-remove:hover {
background-color: #FF6347; /* Терракот при наведении */
background-color: #cd00ff; /* Терракот при наведении */
}
.btn-update:hover {
background-color: #00bb85; /* Лайм при наведении */
background-color: rgba(184, 98, 255, 0.73); /* Лайм при наведении */
}
.button.light:hover, .button.checkout:hover {
opacity: 0.85;
background-color: #FFFFFF;
color: #235e73;
color: #bb7bff;
}
.cart-container{
background-color: rgba(0, 82, 74, 0.72); /* Прозрачный серый фон */
background-color: rgba(203, 92, 255, 0.62); /* Прозрачный серый фон */
padding: 30px; /* Отступ для создания пространства вокруг .cart-page */
border-radius: 15px; /* Скруглённые углы для согласованности с .cart-page */
box-shadow: 0 6px 12px rgba(0, 107, 85, 0.47); /* Небольшое тень для дополнительной глубины */
box-shadow: 0 6px 12px rgb(136, 5, 255); /* Небольшое тень для дополнительной глубины */
margin: 20px auto; /* Автоматические отступы для центрирования и добавления пространства сверху и снизу */
max-width: calc(100% - 40px); /* Ограничение максимальной ширины с учетом отступов */
}
@ -81,19 +81,19 @@
.total {
font-weight: bold;
color: #FFF;
background: linear-gradient(145deg, #5acdae, #1ba99c);
background: linear-gradient(145deg, #9d27ff, #e2b1ff);
font-size: 1.2rem;
text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.5);
}
.total td {
padding: 15px;
border-top: 3px solid rgba(36, 239, 146, 0.41); /* Добавляем золотую границу для акцента */
border-top: 3px solid rgba(186, 142, 255, 0.9); /* Добавляем золотую границу для акцента */
}
/* Дополнительный стиль для общей цены, чтобы выделить её ещё сильнее */
.total .num {
background-color: rgba(255, 215, 0, 0.2); /* Слегка прозрачный золотой фон */
background-color: rgb(203, 92, 255); /* Слегка прозрачный золотой фон */
border-radius: 5px;
padding: 5px 10px;
margin-right: 5px;
@ -102,7 +102,7 @@
/* Стиль для кнопок действий с использованием эзотерических цветов и эффектов */
.button.light, .button.checkout {
background: linear-gradient(145deg, #2ac296, #00ffb2);
background: linear-gradient(145deg, #8a00a9, #71008f);
color: white;
padding: 10px 20px;
border-radius: 30px;
@ -118,3 +118,7 @@
transform: scale(1.05);
box-shadow: 0px 6px 8px rgba(0, 0, 0, 0.3);
}
h1{
color: #1b003d;
}

View File

@ -1,8 +1,8 @@
body, html {
body {
height: 100%;
margin: 0;
font-family: 'Papyrus', fantasy;
background-image: url("../images/bg-image.png");
font-family: 'Playfair Display', fantasy;
background-size: cover;
}
@ -15,7 +15,7 @@ font-family: 'Papyrus', fantasy;
color: #E0FFFF; /* Цвет текста, напоминающий свет луны */
border-radius: 15px;
padding: 30px;
box-shadow: 0 0 15px 5px rgba(0, 255, 255, 0.2);
box-shadow: 0 0 15px 5px rgba(169, 0, 255, 0.62);
backdrop-filter: blur(10px);
}
@ -79,6 +79,3 @@ font-family: 'Papyrus', fantasy;
}
}
.place-order-btn:focus:not(:hover) {
animation: pulse 2s infinite;
}

View File

@ -24,19 +24,19 @@
/* Стили для заголовков и описания */
.product-detail h1 {
font-size: 2.5rem;
color: #24ffc5; /* Золотой цвет для заголовков */
color: #daa3ff; /* Золотой цвет для заголовков */
text-shadow: 0px 0px 10px rgba(255, 255, 255, 0.7); /* Свечение вокруг текста */
}
.product-detail h2 {
font-size: 1.8rem;
color: #7FFFD4; /* Аквамариновый цвет для подзаголовков */
color: #cc7fff; /* Аквамариновый цвет для подзаголовков */
margin-bottom: 20px;
}
.product-detail .price {
font-size: 2rem;
color: #00FA9A; /* Мягкий зеленый цвет для цены */
color: #4f0088; /* Мягкий зеленый цвет для цены */
font-weight: bold;
margin-bottom: 20px;
}
@ -67,7 +67,7 @@
}
.btn-secondary {
background-color: #4dd0a9; /* Сине-фиолетовый цвет */
background-color: #bf4dd0; /* Сине-фиолетовый цвет */
}
.btn-primary:hover, .btn-secondary:hover {
@ -88,7 +88,7 @@
align-items: center;
overflow: hidden;
border-radius: 15px; /* Скругляем углы контейнера */
box-shadow: 0px 10px 20px rgba(35, 182, 138, 0.2); /* Добавляем тень для эффекта "глубины" */
box-shadow: 0px 10px 20px rgba(203, 92, 255, 0.62); /* Добавляем тень для эффекта "глубины" */
margin-right: 20px; /* Отступ справа */
background: rgba(255, 255, 255, 0.1); /* Немного прозрачности для контейнера */
backdrop-filter: blur(5px); /* Размытие фона за контейнером */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -1,3 +1,8 @@
[LocalizedFileNames]
Снимок экрана 2024-02-08 161455.png=@Снимок экрана 2024-02-08 161455,0
Снимок экрана 2024-02-08 161541.png=@Снимок экрана 2024-02-08 161541,0
Снимок экрана 2024-03-02 000101.png=@Снимок экрана 2024-03-02 000101,0
Снимок экрана 2024-03-03 223840.png=@Снимок экрана 2024-03-03 223840,0
Снимок экрана 2024-03-03 224408.png=@Снимок экрана 2024-03-03 224408,0
Снимок экрана 2024-03-03 225609.png=@Снимок экрана 2024-03-03 225609,0
Снимок экрана 2024-03-03 225802.png=@Снимок экрана 2024-03-03 225802,0

BIN
main/static/deps/shop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 KiB

View File

@ -0,0 +1,33 @@
{% extends "main/base.html" %} <!-- Предполагается, что у вас есть базовый шаблон -->
{% load static %}
{% block title %}О магазине{% endblock %}
{% block content %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
<div class="container py-5">
<h2 class="mb-4">О магазине "Гармония Эзотерики"</h2>
<p>
"Гармония Эзотерики" - это ваш уникальный магазин товаров для практикующих эзотерику и всех, кто интересуется мистическими аспектами жизни. Мы предлагаем широкий ассортимент товаров, включая кристаллы, таро карты, эзотерические книги, амулеты и многое другое.
</p>
<p>
Наш магазин создан для тех, кто ищет гармонию в себе и вокруг. Мы верим, что правильные инструменты и знания могут открыть дверь в мир энергий, интуиции и самопознания.
</p>
<h3 class="mt-5 mb-3">Контакты</h3>
<ul>
<li>Email: info@harmony-esoterics.com</li>
<li>Телефон: +7 (999) 999-99-99</li>
<li>Адрес: Город N, Улица Мистическая, д. 42</li>
</ul>
<h3 class="mt-5 mb-3">Следите за нами в социальных сетях</h3>
<p>Узнавайте о новинках, акциях и мероприятиях первыми!</p>
<div>
<a href="https://instagram.com/yourinstagram" class="btn btn-outline-dark btn-sm mb-2" target="_blank">Instagram <i class="fa fa-instagram"></i></a>
<a href="https://facebook.com/yourfacebookpage" class="btn btn-outline-dark btn-sm mb-2" target="_blank">Facebook <i class="fa fa-facebook"></i></a>
<!-- Добавьте другие социальные сети по аналогии -->
</div>
</div>
{% endblock %}

View File

@ -1,4 +1,3 @@
{% load static %}
<!DOCTYPE html>
<html lang="ru">
@ -9,9 +8,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static "/deps/css/bootstrap/bootstrap.min.css"%}">
<link rel="stylesheet" href="{% static "/deps/css/my_css.css"%}">
<!-- Стиль footer-a для главной страницы -->
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{% static "/deps/css/my_footer_css.css"%}">
<link rel="stylesheet" href="{% static "/deps/css/catal.css"%}">
<link rel="stylesheet" href="{% static "/deps/css/button_icon.css"%}">
<!-- Favicons for different platforms -->
<link rel="apple-touch-icon" sizes="180x180" href="{% static "/deps/favicon/apple-touch-icon.png"%}">
<link rel="icon" type="image/png" sizes="32x32" href="{% static "/deps/favicon/favicon-32x32.png"%}">
@ -21,8 +22,8 @@
</head>
<body>
<header>
<nav class="navbar navbar-expand-lg bg-dark" data-bs-theme="dark">
<header class="sticky-top">
<nav class="navbar navbar-expand-lg bg-dark " data-bs-theme="dark">
<div class="container">
<a class="navbar-brand" href="{% url "main:product_list"%}">Harmony of Esotericism</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
@ -49,81 +50,67 @@
<a class="nav-link text-white" href="{% url 'cart:cart_detail' %}">Корзина</a>
</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 class="nav-item dropdown">
<a class="nav-link dropdown-toggle text-white" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Пользователь
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item text-white" href="#">Личный кабинет</a></li>
<li><a class="dropdown-item text-white" href="{% url 'keycloak_login' %}">Войти</a></li>
<li><a class="dropdown-item text-white" href="#">Выйти</a></li>
</ul>
</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>
</nav>
</header>
<main class="d-flex">
<img alt="backimg" src="{% static '/deps/images/bg-image2.png'%}" class="fixed-top z-0 vw-100 vh-100 object-fit-cover"/>
<div class="container">
<div class="row mt-1">
<div class="col-lg-2">
<!-- Пустой блок на Ваше усмотрение -->
</div>
<div id="header">
<h1 style="color: white; text-align: center; position: relative;">
<a class="navbar-brand" href="{% url 'main:product_list' %}" style="text-decoration: none; color: white; font-family: 'Papyrus', fantasy;">
<!-- Добавляем иконку или символ перед названием магазина -->
<div class="button-container vh-100 d-flex flex-column gap-5 justify-content-center">
<a href="/" class="button" id="shop-button">
<div class="icon-shop"></div>
Гармония Эзотерики
</a>
<a href="{% url 'main:about' %}" class="button" id="about-button">
<div class="icon-about"></div>
</a>
</a>
</h1>
<a href="/cart" class="button" id="cart-button">
<div class="icon-cart"></div>
</div>
</div>
</a>
</div>
<!-- Добавление кнопок категорий и формы для фильтрации -->
{% block content %}
<div class="row mb-3">
<div class="col-12">
<form method="get" action="{% url 'main:product_list' %}">
<button type="submit" class="btn btn-primary">Искать по Категориям</button>
</form>
</div>
</div>
{% endblock %}
<div class="container">
</div>
{# <footer class="py-2 bg-dark">#}
{# <div class="container">#}
{# <div class="row">#}
{# <div class="col-lg-14">#}
{# <p class="m-0 text-center text-white footer-text" style="font-size: 18px;">Harmony of Esotericism 2024</p>#}
{# </div>#}
{# <div class="col-lg-14">#}
{# <div class="text-center mt-3">#}
{# <h5 class="text-white" style="font-size: 14px;">Посетите нашего телеграм бота!</h5>#}
{##}
{# </div>#}
{# </div>#}
{# </div>#}
{# </div>#}
{# </footer>#}
<script src="{% static "/deps/js/jquery/jquery-3.7.0.min.js"%}"></script>
{% block content %}
<div class="row mb-3">
<div class="col-lg-2">
<div class="list-group">
<a href="#" class="list-group-item list-group-item-action active" aria-current="true">Категории</a>
{% for category in categories %}
<a href="{% url 'main:product_list_by_category' category.slug %}" class="list-group-item list-group-item-action">{{ category.name }}</a>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
</div>
</main>
<script src="{% static "/deps/js/jquery/jquery-3.7.0.min.js"%}"></script>
<script src="{% static "/deps/js/jquery-events.js"%}"></script>
<script src="{% static "/deps/js/jquery-ajax.js"%}"></script>
<script src="{% static "/deps/js/bootstrap/bootstrap.bundle.min.js"%}"></script>

View File

@ -1,7 +1,119 @@
{% extends "main/base.html" %}
{% load static %}
{% block content %}
<h1>Личный кабинет</h1>
<p>Привет, {{ username }}!</p>
<div class="row">
<div class="container mt-5">
<div class="row justify-content-center">
<!-- Профиль с данными пользователя -->
<div class="col-md-5">
<div class=" bg-white p-4 mb-4 mx-2 rounded custom-shadow">
<h3 class="text-center mb-4">Профиль пользователя</h3>
<form action="#" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="row">
<div class="col-md-12 mb-3 text-center">
{% endblock %}
<img src="{% static "deps/img/no_image.jpg" %}"
alt="Аватар пользователя" class="img-fluid rounded-circle"
style="max-width: 150px;">
<input type="file" class="form-control mt-3" id="id_image"
name='image'
accept="image/*">
</div>
<div class="col-md-12 mb-3">
<label for="id_first_name" class="form-label">Имя*</label>
<input type="text" class="form-control" id="id_first_name"
name="first_name"
placeholder="Введите ваше имя"
value="{{ userinfo.firstName }}"
required>
</div>
<div class="col-md-12 mb-3">
<label for="id_last_name" class="form-label">Фамилия*</label>
<input type="text" class="form-control" id="id_last_name"
name="last_name"
placeholder="Введите вашу фамилию"
value="{{ userinfo.lastName }}"
required>
</div>
<div class="col-md-12 mb-3">
<label for="id_username" class="form-label">Имя пользователя*</label>
<input type="text" class="form-control" id="id_username"
name="username"
placeholder="Введите ваше имя пользователя"
value="{{ userinfo.username }}"
required>
</div>
<div class="col-md-12 mb-3">
<label for="id_email" class="form-label">Email*</label>
<input type="email" class="form-control" id="id_email"
name="email"
placeholder="Введите ваш email *youremail@example.com"
value="{{ userinfo.email }}"
required>
</div>
</div>
<button type="submit" class="btn btn-dark">Сохранить</button>
</form>
</div>
</div>
<!-- Оформленные заказы -->
<div class="col-md-12">
<div class=" bg-white p-4 mb-4 mx-2 rounded custom-shadow">
<h3 class="text-center mb-4">Мои заказы</h3>
<!-- Разметка заказов -->
<div class="container">
<div class="accordion" id="accordionExample">
{% for order in orders %}
<div class="accordion-item">
<h2 class="accordion-header" id="heading{{ order.id }}">
<button class="accordion-button {% if order != orders.0 %}collapsed{% endif %}" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{ order.id }}" aria-expanded="false" aria-controls="collapse{{ order.id }}">
Заказ № {{ order.id }} - {{ order.created_timestamp }} | Статус: <strong class="mx-2">{{order.requires_delivery}}</strong>
</button>
</h2>
<div id="collapse{{ order.id }}" class="accordion-collapse collapse {% if order == orders.0 %}show{% endif %}" aria-labelledby="heading{{ order.id }}" data-bs-parent="#accordionExample">
<div class="accordion-body">
<table class="table table-dark table-hover">
<thead>
<tr>
<th>Товар</th>
<th>Количество</th>
<th>Цена</th>
<th>Общая стоимость</th>
</tr>
</thead>
<tbody>
{% for item in order.orderitem_set.all %}
<tr>
<td><a class="text-white" href="{% url 'goods:product' item.product.slug %}">{{ item.product.name }}</a></td>
<td>{{ item.quantity }}</td>
<td>{{ item.price }}</td>
<td>{{ item.products_price }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -9,7 +9,7 @@ path('', views.product_list, name='product_list'),
path('<slug:category_slug>/', views.product_list,
name='product_list_by_category'),
path('<int:id>/<slug:slug>/', views.product_detail,
name='product_detail'),
name='product_detail'), path('about/', views.about, name='about'),
]

View File

@ -30,4 +30,9 @@ def product_detail(request, id, slug):
return render(request,
'main/product/detail.html',
{'product': product,
'cart_product_form': cart_product_form})
'cart_product_form': cart_product_form})
# Функция для страницы "О нас"
def about(request):
return render(request, 'main/about.html') # предполагается, что у вас есть шаблон about.html в папке main

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

View File

@ -0,0 +1,35 @@
.profile-container {
background-color: #f4f0ec;
color: #3a3a3c;
padding: 20px;
border-radius: 10px;
max-width: 600px;
margin: auto;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.profile-heading {
color: #8a2be2;
text-align: center;
font-family: 'Merriweather', serif;
}
.user-greeting, .user-info {
text-align: center;
margin-top: 20px;
}
.user-name, .username {
color: #4a47a3;
font-weight: bold;
}
.mystic-decor {
text-align: center;
margin-top: 20px;
}
.mystic-symbol {
width: 100px;
height: auto;
}

View File

@ -5,7 +5,7 @@
{% endblock %}
{% block content %}
<link rel="stylesheet" href="{% static 'thx.css' %}">
<div class="thank-you-page">
<div class="thank-you-page position-relative">
<h1 class="thank-you-title">Спасибо!</h1>
<p class="thank-you-text">Ваш заказ успешно оформлен. Номер вашего заказа: <strong>{{ order.id }}</strong>.</p>
<div class="mystical-decor">

View File

@ -32,4 +32,3 @@ def order_created(request, order_id):

View File

@ -0,0 +1,86 @@
import requests
from djangoProject1 import settings
#Получение access_token
def get_access_token():
keycloack_server = settings.KEYCLOAK_CONFIG['SERVER_URL']
realm = settings.KEYCLOAK_CONFIG['REALM']
url = f'{keycloack_server}/realms/{realm}/protocol/openid-connect/token'
client_id = 'lowlight'
client_secret = settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
username = 'lowlight'
password = ''
payload = {
'grant_type':'password',
'client_id':client_id,
'client_secret':client_secret,
'username':username,
'password':password,
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
response = requests.post(url,data=payload,headers=headers)
if response.ok:
data = response.json()
print('Acces Token:' ,data['access_token'])
print('Refresh Token:', data['refresh_token'])
return data['access_token']
else:
print('Error :',response.text)
def refresh_token():
keycloack_server = settings.KEYCLOAK_CONFIG['SERVER_URL']
realm = settings.KEYCLOAK_CONFIG['REALM']
url = f'{keycloack_server}/realms/{realm}/protocol/openid-connect/token'
client_id = 'lowlight'
refresh_token = 'eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIyMjRmYTgzYy00ZmMzLTQyMTgtYTEyMS00ZTQxZTdiYTVhNzcifQ.eyJleHAiOjE3MDg5NjYxOTcsImlhdCI6MTcwODk2NDM5NywianRpIjoiYWE5N2IwOTMtZjIxMS00N2VhLWFmNGItNjc5OTJiOTdkOGI5IiwiaXNzIjoiaHR0cHM6Ly9hdXRoLm15dGVyaW9yLmt6L3JlYWxtcy9IYXJtb255IiwiYXVkIjoiaHR0cHM6Ly9hdXRoLm15dGVyaW9yLmt6L3JlYWxtcy9IYXJtb255Iiwic3ViIjoiOWE3ZDNiNmUtZTExZi00OGQzLThmYzctMWMyOTZmZWE5MjYyIiwidHlwIjoiUmVmcmVzaCIsImF6cCI6Imxvd2xpZ2h0Iiwic2Vzc2lvbl9zdGF0ZSI6IjExNTJmYTkwLTM0ZmUtNGU1OS1iOTYyLTY5NDA0OWRiMGU2YiIsInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsInNpZCI6IjExNTJmYTkwLTM0ZmUtNGU1OS1iOTYyLTY5NDA0OWRiMGU2YiJ9.-NifrZ0Y-d9-D5l6bxOn6RoTvqm9Ekvp9sTwP4oQoRw'
client_secret = settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
payload = {
'client_id': client_id,
'grant_type': 'refresh_token',
'refresh_token': refresh_token,
'client_secret': client_secret,
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
response = requests.post(url, data=payload, headers=headers)
if response.ok:
data = response.json()
print('Access Token:', data.get('access_token'))
print('Refresh Token:', data.get('refresh_token'))
print('Data:', data)
else:
print('Error:', response.text)
def get_refresh_token():
keycloack_server = settings.KEYCLOAK_CONFIG['SERVER_URL']
realm = settings.KEYCLOAK_CONFIG['REALM']
url = f'{keycloack_server}/realms/{realm}/protocol/openid-connect/token'
client_id = 'lowlight'
client_secret = settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
username = 'lowlight'
password = ''
payload = {
'grant_type': 'password',
'client_id': client_id,
'client_secret': client_secret,
'username': username,
'password': password,
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
response = requests.get(url,data=payload,headers = headers)
if response.ok:
data = response.json()
return ('Refresh Token',data.get('refresh_token'))
else:
print('Error :',response.text)

View File

@ -1,10 +1,9 @@
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'),
# urls.py
from django.urls import path
from . import views
urlpatterns = [
path('test/', views.test_payment, name='test-payment'),
path('login/', views.keycloak_login, name='keycloak_login'),
]

View File

@ -1,89 +1,28 @@
from django.shortcuts import redirect,render
# views.py
from django.shortcuts import redirect
from django.urls import reverse
from keycloak import KeycloakOpenID
from django.http import HttpResponse
from keycloak import KeycloakOpenID, KeycloakGetError
from django.conf import settings
import logging
from django.contrib.auth.decorators import login_required
from djangoProject1 import settings
@login_required
def test_payment(request):
# Assuming the user's username is what you want to return
return HttpResponse(request.user.username)
# Inside your_app/views.py
# Настройка логгера для текущего модуля
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_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'])
# Функция обратного вызова для обработки ответа от 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')
return redirect(auth_url)