# server.py import re import json from datetime import datetime from pathlib import Path from fastapi import FastAPI, Request, Form from fastapi.responses import HTMLResponse, JSONResponse from fastapi.staticfiles import StaticFiles from typing import Optional # ==================== КОНФИГУРАЦИЯ ==================== HOST = "0.0.0.0" PORT = 8000 DEBUG = True BASE_DIR = Path(__file__).parent TEMPLATES_DIR = BASE_DIR / "templates" STATIC_DIR = BASE_DIR / "static" DATA_DIR = BASE_DIR / "data" # Создаем необходимые папки TEMPLATES_DIR.mkdir(exist_ok=True) STATIC_DIR.mkdir(exist_ok=True) DATA_DIR.mkdir(exist_ok=True) # Создаем подпапки для статики (STATIC_DIR / "css").mkdir(exist_ok=True) (STATIC_DIR / "js").mkdir(exist_ok=True) # ==================== ИНИЦИАЛИЗАЦИЯ FASTAPI ==================== app = FastAPI(title="БС Патриот - Регистрация на круглый стол", debug=DEBUG) # Подключаем статические файлы app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static") # ==================== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ==================== def save_registration_to_file(data: dict) -> dict: """Сохраняет заявку в JSON-файл""" filepath = DATA_DIR / "registrations.json" registrations = [] if filepath.exists(): try: with open(filepath, 'r', encoding='utf-8') as f: registrations = json.load(f) except (json.JSONDecodeError, IOError): registrations = [] new_record = { "id": len(registrations) + 1, "created_at": datetime.now().isoformat(), "fullname": data.get("fullname"), "email": data.get("email"), "phone": data.get("phone"), "company": data.get("company"), "businessSize": data.get("businessSize", ""), "message": data.get("message", ""), "status": "pending" } registrations.append(new_record) with open(filepath, 'w', encoding='utf-8') as f: json.dump(registrations, f, ensure_ascii=False, indent=2) return new_record def get_html_content() -> str: """Читает HTML файл или возвращает fallback""" html_path = TEMPLATES_DIR / "index.html" if html_path.exists(): with open(html_path, 'r', encoding='utf-8') as f: return f.read() else: return get_fallback_html() def get_fallback_html() -> str: """Fallback HTML если файл шаблона не найден""" return """ Круглый стол | БС «Патриот»
Закрытый клуб предпринимателей

Круглый стол с бизнес-сообществом «Патриот»

26 апреля 2026, начало в 11:00

Вход по рекомендации

557+

участников

125+

выездов в зону СВО

30+

комитетов

Регистрация на мероприятие

""" # ==================== МАРШРУТЫ ==================== @app.get("/", response_class=HTMLResponse) async def get_landing_page(): """Главная страница - посадочный лендинг мероприятия""" return HTMLResponse(content=get_html_content(), status_code=200) @app.post("/api/register") async def register( fullname: str = Form(...), email: str = Form(...), phone: str = Form(...), company: str = Form(...), businessSize: Optional[str] = Form(None), message: Optional[str] = Form(None), agree: Optional[str] = Form(None) ): """API-эндпоинт для регистрации участников""" print(f"Received data: fullname={fullname}, email={email}, phone={phone}, company={company}, agree={agree}") # Валидация if not agree or agree != "true": return JSONResponse( status_code=400, content={"success": False, "message": "Необходимо согласие с условиями"} ) if not fullname or len(fullname.strip()) < 2: return JSONResponse( status_code=400, content={"success": False, "message": "Введите корректное ФИО"} ) email_pattern = r'^[^\s@]+@([^\s@.,]+\.)+[^\s@.,]{2,}$' if not re.match(email_pattern, email): return JSONResponse( status_code=400, content={"success": False, "message": "Введите корректный email"} ) digits_only = re.sub(r'\D', '', phone) if len(digits_only) < 10: return JSONResponse( status_code=400, content={"success": False, "message": "Введите корректный номер телефона (минимум 10 цифр)"} ) if not company or len(company.strip()) < 1: return JSONResponse( status_code=400, content={"success": False, "message": "Укажите название компании"} ) data = { "fullname": fullname.strip(), "email": email.strip().lower(), "phone": phone.strip(), "company": company.strip(), "businessSize": businessSize if businessSize else "", "message": message.strip() if message else "", } try: saved = save_registration_to_file(data) print(f"Saved registration: {saved}") return JSONResponse(content={ "success": True, "message": "Заявка успешно отправлена! Менеджер сообщества свяжется с вами в ближайшее время." }) except Exception as e: print(f"Error saving registration: {e}") return JSONResponse( status_code=500, content={"success": False, "message": f"Ошибка сервера: {str(e)}"} ) @app.get("/admin/registrations", response_class=HTMLResponse) async def admin_registrations(): """Админка для просмотра заявок""" filepath = DATA_DIR / "registrations.json" registrations = [] if filepath.exists(): try: with open(filepath, 'r', encoding='utf-8') as f: registrations = json.load(f) except: registrations = [] registrations.sort(key=lambda x: x.get("created_at", ""), reverse=True) # Используем обычное форматирование строки без .format() html = f""" Админка | Заявки на круглый стол

📋 Заявки на круглый стол «Патриот»

📊 Всего заявок: {len(registrations)}
""" for reg in registrations: created = reg.get("created_at", "")[:16].replace("T", " ") status_class = "status-pending" if reg.get('status') == 'pending' else "" message_text = reg.get('message', '—')[:100] if len(reg.get('message', '')) > 100: message_text += "..." html += f""" """ html += """
ID Дата ФИО Email Телефон Компания Оборот Сообщение Статус
{reg.get('id', '—')} {created} {reg.get('fullname', '—')} {reg.get('email', '—')} {reg.get('phone', '—')} {reg.get('company', '—')} {reg.get('businessSize', '—')} {message_text} {reg.get('status', 'pending')}
← Вернуться на главную
""" return HTMLResponse(content=html, status_code=200) @app.get("/health") async def health_check(): """Health check для хостинга""" return {"status": "ok", "timestamp": datetime.now().isoformat()} # ==================== ЗАПУСК ==================== if __name__ == "__main__": import uvicorn print("=" * 60) print("🚀 Сервер для посадочной страницы БС «Патриот»") print(f"📁 Шаблоны: {TEMPLATES_DIR}") print(f"📁 Статика: {STATIC_DIR}") print(f"📁 Данные: {DATA_DIR}") print("=" * 60) print(f"✨ Главная страница: http://localhost:{PORT}") print(f"🔧 Админка: http://localhost:{PORT}/admin/registrations") print("=" * 60) print("💡 Для остановки сервера нажмите Ctrl+C") print("=" * 60) uvicorn.run( app, host=HOST, port=PORT, log_level="info" )