fix
This commit is contained in:
parent
a21e6f47f8
commit
60aa58844c
@ -28,7 +28,7 @@ REFRESH_TOKEN_EXPIRE_DAYS = 170
|
||||
TOKEN_TTL_DAYS = 7
|
||||
ACCESS_COOKIE = "access_token"
|
||||
REFRESH_COOKIE = "refresh_token"
|
||||
RABBITMQ_HOST = "rabbitmq-0.rabbitmq.kultmost.svc.cluster.local" if PROD else "rabbit.spiritofweb.ru:31023"
|
||||
RABBITMQ_HOST = "rabbit.spiritofweb.ru:31023"
|
||||
EMAIL_SUBJECT = 'Культмост'
|
||||
PROTECTED_FIELDS_USER = {"_id", "img", 'original_img', "hashed_password", "password", "roles"}
|
||||
VK_CLIENT_ID = os.getenv("VK_CLIENT_ID")
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# ignore\n",
|
||||
"#ignore\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"def load_env(file_path):\n",
|
||||
|
||||
@ -1,3 +1,35 @@
|
||||
#ignore
|
||||
import os
|
||||
|
||||
def load_env(file_path):
|
||||
"""Загружает переменные окружения из файла .env с поддержкой булевых значений."""
|
||||
with open(file_path) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
|
||||
try:
|
||||
key, value = line.split('=', 1)
|
||||
key = key.strip()
|
||||
value = value.strip('"\' ')
|
||||
# Преобразование строковых значений в булевые
|
||||
if value.lower() in ['true', '1', 'yes']:
|
||||
value = True
|
||||
elif value.lower() in ['false', '0', 'no']:
|
||||
value = False
|
||||
|
||||
# Проверка, что ключ не пустой
|
||||
if key:
|
||||
os.environ[key] = str(value) # Храним как строку
|
||||
except ValueError:
|
||||
print(f"Ошибка: неверный формат в строке '{line}'")
|
||||
|
||||
try:
|
||||
load_env('env')
|
||||
except:
|
||||
load_env('env_example')
|
||||
|
||||
import smtplib
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
|
||||
17
main.ipynb
17
main.ipynb
@ -2701,8 +2701,9 @@
|
||||
" user: Dict = Security(get_current_user, scopes=[\"create:super_items\"])\n",
|
||||
"): \n",
|
||||
" competition = await competitions_col.find_one({'_id': ObjectId(competition_id)})\n",
|
||||
" data = json.dumps(competition, cls=MongoJSONEncoder, ensure_ascii=False)\n",
|
||||
" return templates.TemplateResponse(\n",
|
||||
" request=request, name=\"form/competition_edit.html\", context={\"competition\": competition, \"user\": user}\n",
|
||||
" request=request, name=\"form/competition_edit.html\", context={\"competition\": str(data), \"user\": user}\n",
|
||||
" )\n"
|
||||
]
|
||||
},
|
||||
@ -2815,17 +2816,6 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"from datetime import datetime, date\n",
|
||||
"from bson import ObjectId\n",
|
||||
"\n",
|
||||
"class MongoJSONEncoder(json.JSONEncoder):\n",
|
||||
" def default(self, o):\n",
|
||||
" if isinstance(o, ObjectId):\n",
|
||||
" return str(o)\n",
|
||||
" if isinstance(o, (datetime, date)):\n",
|
||||
" return o.isoformat()\n",
|
||||
" return super().default(o)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@app.get(\"/form/exhibition/{exhibition_id}\", response_class=HTMLResponse)\n",
|
||||
@ -2835,8 +2825,7 @@
|
||||
" user: Dict = Security(get_current_user)\n",
|
||||
"):\n",
|
||||
" exhibition = await exhibition_collection.find_one({'_id': ObjectId(exhibition_id)},{'posts':0,'vk':0,'tg':0,'fb':0,'inst':0,'max':0})\n",
|
||||
" data= json.dumps(exhibition, cls=MongoJSONEncoder, ensure_ascii=False)\n",
|
||||
" print(data)\n",
|
||||
" data = json.dumps(exhibition, cls=MongoJSONEncoder, ensure_ascii=False)\n",
|
||||
" return templates.TemplateResponse(\n",
|
||||
" request=request, name=\"form/exhibition_edit.html\", context={\"id\": 1, \"user\": user, \"exhibition\": str(data)}\n",
|
||||
" )"
|
||||
|
||||
17
main.py
17
main.py
@ -2028,8 +2028,9 @@ async def form_(
|
||||
user: Dict = Security(get_current_user, scopes=["create:super_items"])
|
||||
):
|
||||
competition = await competitions_col.find_one({'_id': ObjectId(competition_id)})
|
||||
data = json.dumps(competition, cls=MongoJSONEncoder, ensure_ascii=False)
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="form/competition_edit.html", context={"competition": competition, "user": user}
|
||||
request=request, name="form/competition_edit.html", context={"competition": str(data), "user": user}
|
||||
)
|
||||
|
||||
|
||||
@ -2104,17 +2105,6 @@ async def form_exhibition(
|
||||
request=request, name="form/exhibition.html", context={"id": 1, "user": user}
|
||||
)
|
||||
|
||||
import json
|
||||
from datetime import datetime, date
|
||||
from bson import ObjectId
|
||||
|
||||
class MongoJSONEncoder(json.JSONEncoder):
|
||||
def default(self, o):
|
||||
if isinstance(o, ObjectId):
|
||||
return str(o)
|
||||
if isinstance(o, (datetime, date)):
|
||||
return o.isoformat()
|
||||
return super().default(o)
|
||||
|
||||
|
||||
@app.get("/form/exhibition/{exhibition_id}", response_class=HTMLResponse)
|
||||
@ -2124,8 +2114,7 @@ async def form_exhibition(
|
||||
user: Dict = Security(get_current_user)
|
||||
):
|
||||
exhibition = await exhibition_collection.find_one({'_id': ObjectId(exhibition_id)},{'posts':0,'vk':0,'tg':0,'fb':0,'inst':0,'max':0})
|
||||
data= json.dumps(exhibition, cls=MongoJSONEncoder, ensure_ascii=False)
|
||||
print(data)
|
||||
data = json.dumps(exhibition, cls=MongoJSONEncoder, ensure_ascii=False)
|
||||
return templates.TemplateResponse(
|
||||
request=request, name="form/exhibition_edit.html", context={"id": 1, "user": user, "exhibition": str(data)}
|
||||
)
|
||||
|
||||
@ -2,24 +2,6 @@
|
||||
{% import "components/checkbox.html" as checkbox %}
|
||||
|
||||
{% macro section(user, competition) -%}
|
||||
<script>
|
||||
var type = '{{competition.type_}}';
|
||||
|
||||
function toggleType(clickedId) {
|
||||
const artist = document.getElementById('artistCheck');
|
||||
const paint = document.getElementById('paintCheck');
|
||||
if (clickedId === 'artistCheck') {
|
||||
artist.checked = clickedId === 'artistCheck';
|
||||
paint.checked = clickedId === 'paintCheck';
|
||||
type = 'artist';
|
||||
} else if (clickedId === 'paintCheck') {
|
||||
artist.checked = clickedId === 'artistCheck';
|
||||
paint.checked = clickedId === 'paintCheck';
|
||||
type = 'paint';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<form class="c_sbrxk">
|
||||
<div class="c_dtjoh">
|
||||
<h3>Выбор</h3>
|
||||
@ -28,7 +10,6 @@
|
||||
<input type="checkbox"
|
||||
id="artistCheck"
|
||||
class="offer-checkbox"
|
||||
{% if competition.type_ == 'artist'%}checked{%endif%}
|
||||
onchange="toggleType('artistCheck')">
|
||||
<span class="offer-text">Художников</span>
|
||||
</label>
|
||||
@ -37,7 +18,6 @@
|
||||
<input type="checkbox"
|
||||
id="paintCheck"
|
||||
class="offer-checkbox"
|
||||
{% if competition.type_ == 'paint'%}checked{%endif%}
|
||||
onchange="toggleType('paintCheck')">
|
||||
<span class="offer-text">Картин</span>
|
||||
</label>
|
||||
@ -168,26 +148,24 @@
|
||||
<input type="number" id="max_val" name="max_paintings" class="c_l8kil" value="10" min="1" max="99">
|
||||
<!-- <input type="range" id="select_val" name="selected_count" class="c_5qh93" min="1" max="10" value="3"> -->
|
||||
</div>
|
||||
<!-- {% if competition.get('publish') is none %}
|
||||
<!--
|
||||
<div class="fl">
|
||||
<span class="c_2fr39 b_tfnz2">Публикация запланирована на</span>
|
||||
</div>
|
||||
{{ (competition.datetime_start.strftime('%Y.%m.%d %H:%M' )) if competition.datetime_start else ''}}
|
||||
|
||||
<label class="offer-label">
|
||||
<input type="checkbox" id="emailsNotify" class="offer-checkbox" {% if competition.emailsNotify %}checked{% endif %}>
|
||||
<input type="checkbox" id="emailsNotify" class="offer-checkbox">
|
||||
<span class="offer-text">Рассылка художникам приглашения на почту</span>
|
||||
</label>
|
||||
<label class="offer-label">
|
||||
<input type="checkbox" id="serviceSocial" class="offer-checkbox" {% if competition.serviceSocial %}checked{% endif %}>
|
||||
<input type="checkbox" id="serviceSocial" class="offer-checkbox">
|
||||
<span class="offer-text">Публикация в соц. сетях сервиса</span>
|
||||
</label>
|
||||
<label class="offer-label">
|
||||
<input type="checkbox" id="userSocial" class="offer-checkbox" {% if competition.userSocial %}checked{% endif %}>
|
||||
<input type="checkbox" id="userSocial" class="offer-checkbox">
|
||||
<span class="offer-text">Публикация в личных соц. сетях</span>
|
||||
</label>
|
||||
|
||||
{% endif %} -->
|
||||
-->
|
||||
<button class="c_aq9et" id="createBtn">
|
||||
<span class="c_som9s">Сохранить изменения</span>
|
||||
</button>
|
||||
@ -202,7 +180,7 @@
|
||||
style="position:absolute;inset:0;filter:blur(3px)">
|
||||
</div>
|
||||
<picture>
|
||||
<img id="preview_poster" src="/b/competition/{{competition.poster_file_id}}" crossorigin="anonymous" fetchpriority="high" loading="eager" alt="Загрузите афишу" class="b_ornr9">
|
||||
<img id="preview_poster" crossorigin="anonymous" fetchpriority="high" loading="eager" alt="Загрузите афишу" class="b_ornr9">
|
||||
</picture>
|
||||
</div>
|
||||
<div class="c_z3dpx b_73p10">
|
||||
@ -210,9 +188,9 @@
|
||||
<span class="c_95csi b_0jst1">Формируется</span>
|
||||
</div>
|
||||
<header class="c_m876y b_9xtz9">
|
||||
<h3 class="c_hysjg b_8f3uh" id="preview_title">{{competition.title or ""}}</h3>
|
||||
<h3 class="c_hysjg b_8f3uh" id="preview_title"></h3>
|
||||
</header>
|
||||
<p class="c_2fr39 b_tfnz2" id="preview_topic">{{competition.topic or ""}}</p>
|
||||
<p class="c_2fr39 b_tfnz2" id="preview_topic"></p>
|
||||
<div class="c_dbbe4 b_otttb">
|
||||
{% if user %}
|
||||
<img crossorigin="anonymous" fetchpriority="high" loading="eager" class="c_pm80w b_a68fq" src="/a/{{user._id}}" alt="{{ user.username | e }}">
|
||||
@ -221,7 +199,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p class="c_sihck b_s6ool" id="preview_desc">{{competition.short_desc or ""}}</p>
|
||||
<p class="c_sihck b_s6ool" id="preview_desc"></p>
|
||||
<p class="c_2fr39 b_tfnz2">Подробнее</p>
|
||||
<p class="c_drpqj b_mp1wb">Дата создания</p>
|
||||
</div>
|
||||
@ -365,51 +343,109 @@ function bindLinkValidation({
|
||||
|
||||
</script>
|
||||
<script defer>
|
||||
document.getElementById('title').value = `{{competition.title or ""}}`;
|
||||
document.getElementById('short_desc').value = `{{competition.short_desc or ""}}`;
|
||||
document.getElementById('address').value = `{{competition.address or ""}}`;
|
||||
document.getElementById('topic').value = `{{competition.topic or ""}}`;
|
||||
document.getElementById('rules_link').value = `{{competition.get('rules_link') or ""}}`;
|
||||
{% if competition.get('rules_link') %}
|
||||
document.getElementById('file-block').style.display = 'none';
|
||||
const competition = JSON.parse({{competition|tojson}})
|
||||
const competitionId = competition._id
|
||||
|
||||
document.getElementById('title').value = competition.title;
|
||||
document.getElementById('preview_title').textContent = competition.title;
|
||||
|
||||
function setDateOrDateTimeLocal(elId, isoString) {
|
||||
if (!isoString) return;
|
||||
|
||||
const s = isoString.replace(/^"|"$/g, ""); // на случай двойных кавычек
|
||||
const [datePart, timePart = "00:00:00"] = s.split("T");
|
||||
|
||||
const [y, m, d] = datePart.split("-").map(Number);
|
||||
const [hh, mm, ss = "0"] = timePart.split(":");
|
||||
|
||||
// Date constructor: month is 0-based
|
||||
const dt = new Date(y, m - 1, d, Number(hh), Number(mm), Number(ss));
|
||||
|
||||
// Подготовим строку для input (локальное время)
|
||||
const pad = (n) => String(n).padStart(2, "0");
|
||||
const value = `${dt.getFullYear()}-${pad(dt.getMonth() + 1)}-${pad(dt.getDate())}` +
|
||||
`T${pad(dt.getHours())}:${pad(dt.getMinutes())}:${pad(dt.getSeconds())}`;
|
||||
|
||||
document.getElementById(elId).value = value;
|
||||
}
|
||||
|
||||
function setDateOnly(elId, isoString) {
|
||||
if (!isoString) return;
|
||||
const s = isoString.replace(/^"|"$/g, "");
|
||||
const datePart = s.split("T")[0];
|
||||
document.getElementById(elId).value = datePart; // YYYY-MM-DD
|
||||
}
|
||||
|
||||
setDateOnly("date_finish", competition.date_finish);
|
||||
setDateOrDateTimeLocal("datetime_start", competition.datetime_start);
|
||||
|
||||
document.getElementById('short_desc').value = competition.short_desc ;
|
||||
document.getElementById('preview_desc').textContent = competition.short_desc ;
|
||||
|
||||
document.getElementById('address').value = competition.address;
|
||||
document.getElementById('topic').value = competition.topic;
|
||||
document.getElementById('preview_topic').textContent = competition.topic;
|
||||
|
||||
if (competition.rules_link){
|
||||
document.getElementById('rules_link').value = competition.rules_link;
|
||||
document.getElementById('file-block').style.display = 'none';
|
||||
document.getElementById('link-block').style.display = 'flex';
|
||||
const select = document.querySelectorAll('.toggle-option')
|
||||
select.forEach(opt => opt.classList.remove('active'));
|
||||
select[1].classList.add('active');
|
||||
select[0].querySelector('input').checked=false
|
||||
select[1].querySelector('input').checked=true
|
||||
{% endif %}
|
||||
document.getElementById('nominations').dataset.value = `{{competition.nominations | safe or []}}`;
|
||||
document.getElementById('nominations').dataset.list = `{{competition.nominations | safe or []}}`;
|
||||
}
|
||||
if (competition.type_ == 'artist'){
|
||||
document.getElementById('artistCheck').checked = true
|
||||
}
|
||||
if (competition.type_ == 'paint'){
|
||||
document.getElementById('paintCheck').checked = true
|
||||
}
|
||||
|
||||
|
||||
const inputEl = document.getElementById('nominations');
|
||||
|
||||
const raw = inputEl.getAttribute('data-list') || '[]';
|
||||
const fixed = raw.replace(/'/g, '"');
|
||||
const list = JSON.parse(fixed);
|
||||
const list = competition.nominations;
|
||||
|
||||
inputEl.dataset.list = fixed
|
||||
inputEl.dataset.value = fixed
|
||||
inputEl.dataset.list = list
|
||||
inputEl.dataset.value = list
|
||||
|
||||
document.getElementById('nominations').value = list.join(', ');
|
||||
|
||||
document.getElementById('min_val').value = `{{competition.min_paintings or ""}}`;
|
||||
document.getElementById('max_val').value = `{{competition.max_paintings or ""}}`;
|
||||
document.getElementById('date_finish').value = `{{competition.date_finish or ""}}`;
|
||||
document.getElementById('datetime_start').value = `{{competition.datetime_start or ""}}`;
|
||||
document.getElementById('preview_poster').src = '/b/competition/{{competition.poster_file_id}}'
|
||||
const competitionId = '{{competition._id}}'
|
||||
const input = document.getElementById('datetime_start');
|
||||
const date_finish = document.getElementById('date_finish');
|
||||
const date_finish_date = new Date(`{{competition.date_finish or ""}}`);
|
||||
const datetime_start_date = new Date('{{competition.datetime_start or ""}}');
|
||||
const MOSCOW_OFFSET_MS = 3 * 60 * 60 * 1000;
|
||||
const moscowInstant = new Date(datetime_start_date.getTime() + MOSCOW_OFFSET_MS);
|
||||
document.getElementById('min_val').value = competition.min_paintings;
|
||||
document.getElementById('max_val').value = competition.max_paintings;
|
||||
|
||||
input.value = moscowInstant.toISOString().slice(0, 16);
|
||||
|
||||
date_finish.value = date_finish_date.toISOString().slice(0, 10);
|
||||
document.getElementById('preview_poster').src = `/b/competition/${competition.poster_file_id}`
|
||||
|
||||
// const input = document.getElementById('datetime_start');
|
||||
// const date_finish = document.getElementById('date_finish');
|
||||
// const date_finish_date = new Date(competition.date_finish);
|
||||
// const datetime_start_date = new Date(competition.datetime_start);
|
||||
// const MOSCOW_OFFSET_MS = 3 * 60 * 60 * 1000;
|
||||
// const moscowInstant = new Date(datetime_start_date.getTime() + MOSCOW_OFFSET_MS);
|
||||
|
||||
// input.value = moscowInstant.toISOString().slice(0, 16);
|
||||
|
||||
// date_finish.value = date_finish_date.toISOString().slice(0, 10);
|
||||
</script>
|
||||
<script>
|
||||
var type = competition.type_;
|
||||
|
||||
function toggleType(clickedId) {
|
||||
const artist = document.getElementById('artistCheck');
|
||||
const paint = document.getElementById('paintCheck');
|
||||
if (clickedId === 'artistCheck') {
|
||||
artist.checked = clickedId === 'artistCheck';
|
||||
paint.checked = clickedId === 'paintCheck';
|
||||
type = 'artist';
|
||||
} else if (clickedId === 'paintCheck') {
|
||||
artist.checked = clickedId === 'artistCheck';
|
||||
paint.checked = clickedId === 'paintCheck';
|
||||
type = 'paint';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script defer>
|
||||
const minInput = document.getElementById('min_val');
|
||||
|
||||
13
utils.py
13
utils.py
@ -2,7 +2,7 @@ from fastapi import HTTPException, Request
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from passlib.context import CryptContext
|
||||
import jwt
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from datetime import datetime, timedelta, timezone, date
|
||||
import hashlib
|
||||
from typing import List, Dict, Optional, Set, Any
|
||||
from datetime import timedelta
|
||||
@ -17,6 +17,8 @@ import httpx
|
||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||
import os
|
||||
import base64
|
||||
import json
|
||||
|
||||
|
||||
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
|
||||
@ -25,6 +27,15 @@ OP_MAP = {"like_in": "like"}
|
||||
ENC_KEY = base64.b64decode(ENC_KEY_B64)
|
||||
|
||||
|
||||
class MongoJSONEncoder(json.JSONEncoder):
|
||||
def default(self, o):
|
||||
if isinstance(o, ObjectId):
|
||||
return str(o)
|
||||
if isinstance(o, (datetime, date)):
|
||||
return o.isoformat()
|
||||
return super().default(o)
|
||||
|
||||
|
||||
def encrypt(plaintext: bytes) -> str:
|
||||
aesgcm = AESGCM(ENC_KEY)
|
||||
|
||||
|
||||
4
vk.ipynb
4
vk.ipynb
@ -15,11 +15,11 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# ignore\n",
|
||||
"#ignore\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"def load_env(file_path):\n",
|
||||
" \"\"\"Загружает переменные окружения из файла .env с поддержкой булевых значений.\"\"\"\n",
|
||||
" \"\"\"Загружает переменные окружения из файла env с поддержкой булевых значений.\"\"\"\n",
|
||||
" with open(file_path) as f:\n",
|
||||
" for line in f:\n",
|
||||
" line = line.strip()\n",
|
||||
|
||||
34
vk.py
34
vk.py
@ -1,5 +1,39 @@
|
||||
# VK
|
||||
|
||||
#ignore
|
||||
import os
|
||||
|
||||
def load_env(file_path):
|
||||
"""Загружает переменные окружения из файла env с поддержкой булевых значений."""
|
||||
with open(file_path) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
|
||||
try:
|
||||
key, value = line.split('=', 1)
|
||||
key = key.strip()
|
||||
value = value.strip('"\' ')
|
||||
|
||||
# Преобразование строковых значений в булевые
|
||||
if value.lower() in ['true', '1', 'yes']:
|
||||
value = True
|
||||
elif value.lower() in ['false', '0', 'no']:
|
||||
value = False
|
||||
|
||||
# Проверка, что ключ не пустой
|
||||
if key:
|
||||
os.environ[key] = str(value) # Храним как строку
|
||||
|
||||
except ValueError:
|
||||
print(f"Ошибка: неверный формат в строке '{line}'")
|
||||
|
||||
try:
|
||||
load_env('env')
|
||||
except:
|
||||
load_env('env_example')
|
||||
|
||||
## Class
|
||||
|
||||
import httpx
|
||||
|
||||
Loading…
Reference in New Issue
Block a user