Files
evo-sync/web-python/routes/profile.py
mguschin 854c912a88 Migrate web app from Python/FastAPI to Node.js/TypeScript
Replace the entire Python/FastAPI backend with a Node.js/TypeScript stack:
- Framework: Hono + @hono/node-server
- Templates: Nunjucks (.njk) replacing Jinja2 (.html)
- ORM: Drizzle ORM with mysql2 (same MariaDB schema, no migrations needed)
- Sessions: hono-sessions with CookieStore
- CSS: Pico CSS v2 replacing Bootstrap 5 (Bootstrap Icons CDN kept)
- Dev: tsx watch; Prod: tsc + node dist/index.js

Original Python app preserved in web-python/ as backup.
Updated Dockerfile.web and docker-compose.yml for Node.js deployment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 19:33:32 +03:00

145 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from fastapi import APIRouter, Request, Depends
from fastapi.responses import RedirectResponse
from web.templates_env import templates
from sqlalchemy.orm import Session
from web.auth import get_current_user, verify_password, hash_password
from web.database import get_db
from web.models import User
from web.schemas import validate_profile, validate_reset_password
router = APIRouter()
# VIEW PROFILE
@router.get("/profile")
def profile_view(request: Request, user: User | None = Depends(get_current_user)):
if not user:
return RedirectResponse("/login", 303)
return templates.TemplateResponse("profile_view.html", {"request": request, "user": user})
# EDIT PROFILE
@router.get("/profile/edit")
def profile_edit_form(request: Request, user: User | None = Depends(get_current_user)):
if not user:
return RedirectResponse("/login", 303)
return templates.TemplateResponse("profile_edit.html", {"request": request, "user": user})
@router.post("/profile/edit")
async def profile_edit_submit(
request: Request,
db: Session = Depends(get_db),
user: User | None = Depends(get_current_user),
):
if not user:
return RedirectResponse("/login", 303)
form = await request.form()
data = dict(form)
errors = validate_profile(data)
if not errors:
existing = db.query(User).filter(
User.phone == data["phone"].strip(), User.id != user.id
).first()
if existing:
errors.append("Пользователь с таким телефоном уже существует")
if errors:
return templates.TemplateResponse("profile_edit.html", {
"request": request, "user": user, "errors": errors, "form": data,
})
user.first_name = data["first_name"].strip()
user.last_name = data["last_name"].strip()
user.phone = data["phone"].strip()
db.commit()
return templates.TemplateResponse("profile_edit.html", {
"request": request, "user": user, "success": "Профиль обновлен",
})
# CHANGE PASSWORD
@router.get("/profile/change-password")
def change_password_form(request: Request, user: User | None = Depends(get_current_user)):
if not user:
return RedirectResponse("/login", 303)
return templates.TemplateResponse("profile_change_password.html", {"request": request, "user": user})
@router.post("/profile/change-password")
async def change_password_submit(
request: Request,
db: Session = Depends(get_db),
user: User | None = Depends(get_current_user),
):
if not user:
return RedirectResponse("/login", 303)
form = await request.form()
data = dict(form)
errors = []
current_password = data.get("current_password", "")
if not current_password:
errors.append("Введите текущий пароль")
elif not verify_password(current_password, user.password_hash):
errors.append("Неверный текущий пароль")
password_errors = validate_reset_password(data)
errors.extend(password_errors)
if errors:
return templates.TemplateResponse("profile_change_password.html", {
"request": request, "user": user, "errors": errors,
})
user.password_hash = hash_password(data["password"])
db.commit()
return templates.TemplateResponse("profile_change_password.html", {
"request": request, "user": user, "success": "Пароль изменен",
})
# DELETE ACCOUNT
@router.get("/profile/delete")
def delete_account_form(request: Request, user: User | None = Depends(get_current_user)):
if not user:
return RedirectResponse("/login", 303)
return templates.TemplateResponse("profile_delete.html", {"request": request, "user": user})
@router.post("/profile/delete")
async def delete_account_submit(
request: Request,
db: Session = Depends(get_db),
user: User | None = Depends(get_current_user),
):
if not user:
return RedirectResponse("/login", 303)
form = await request.form()
data = dict(form)
password = data.get("password", "")
if not password:
return templates.TemplateResponse("profile_delete.html", {
"request": request, "user": user, "errors": ["Введите пароль для подтверждения"],
})
if not verify_password(password, user.password_hash):
return templates.TemplateResponse("profile_delete.html", {
"request": request, "user": user, "errors": ["Неверный пароль"],
})
db.delete(user)
db.commit()
request.session.clear()
return RedirectResponse("/", 303)