dkkd
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
15
cart/cart.py
|
@ -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.
|
||||
|
|
|
@ -5,10 +5,19 @@
|
|||
|
||||
{% 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 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;">
|
||||
|
||||
<div class="cart-container">
|
||||
<h1>Корзина</h1>
|
||||
<table class="cart-table">
|
||||
<thead >
|
||||
<tr>
|
||||
<th>Изображение</th>
|
||||
|
@ -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>
|
||||
|
||||
|
|
BIN
db.sqlite3
5669
django.log
|
@ -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',
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -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
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.3 MiB |
After Width: | Height: | Size: 494 KiB |
After Width: | Height: | Size: 887 KiB |
|
@ -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');
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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); /* Размытие фона за контейнером */
|
||||
|
|
Before Width: | Height: | Size: 3.0 MiB |
After Width: | Height: | Size: 2.8 MiB |
After Width: | Height: | Size: 1.5 MiB |
|
@ -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
|
||||
|
|
After Width: | Height: | Size: 877 KiB |
|
@ -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 %}
|
|
@ -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,7 +22,7 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<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>
|
||||
|
@ -49,79 +50,65 @@
|
|||
<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>
|
||||
{% 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 class="nav-link dropdown-toggle text-white" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Пользователь
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
|
||||
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item text-white" href="{% url 'keycloak_logout' %}">Выйти</a></li>
|
||||
<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>
|
||||
{% 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>
|
||||
</h1>
|
||||
<a href="{% url 'main:about' %}" class="button" id="about-button">
|
||||
<div class="icon-about"></div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<a href="/cart" class="button" id="cart-button">
|
||||
<div class="icon-cart"></div>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Добавление кнопок категорий и формы для фильтрации -->
|
||||
<div class="container">
|
||||
|
||||
|
||||
|
||||
{% 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 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>
|
||||
|
||||
|
||||
{# <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>
|
||||
<script src="{% static "/deps/js/jquery-events.js"%}"></script>
|
||||
|
|
|
@ -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">
|
||||
|
||||
<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 %}
|
||||
|
|
|
@ -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'),
|
||||
|
||||
|
||||
]
|
||||
|
|
|
@ -31,3 +31,8 @@ def product_detail(request, id, slug):
|
|||
'main/product/detail.html',
|
||||
{'product': product,
|
||||
'cart_product_form': cart_product_form})
|
||||
|
||||
|
||||
# Функция для страницы "О нас"
|
||||
def about(request):
|
||||
return render(request, 'main/about.html') # предполагается, что у вас есть шаблон about.html в папке main
|
||||
|
|
After Width: | Height: | Size: 219 KiB |
After Width: | Height: | Size: 439 KiB |
|
@ -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;
|
||||
}
|
|
@ -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">
|
||||
|
|
|
@ -32,4 +32,3 @@ def order_created(request, order_id):
|
|||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
@ -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'),
|
||||
]
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
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'],
|
||||
|
@ -17,73 +24,5 @@ def keycloak_login(request):
|
|||
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')
|
||||
|
|