Release v2.1: GDPR, passkeys, session management, admin redesign
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+87
-2
@@ -7,6 +7,7 @@ from flask import (
|
||||
current_app,
|
||||
flash,
|
||||
jsonify,
|
||||
make_response,
|
||||
redirect,
|
||||
render_template,
|
||||
request,
|
||||
@@ -209,9 +210,44 @@ def index():
|
||||
@cabinet_bp.route("/profile", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def profile():
|
||||
from app.models import User
|
||||
from app.models import User, UserPasskey
|
||||
from app.passkey_service import delete_passkey
|
||||
from app.session_service import (
|
||||
get_current_session_key,
|
||||
list_user_sessions,
|
||||
revoke_all_sessions,
|
||||
revoke_session,
|
||||
)
|
||||
|
||||
if request.method == "POST":
|
||||
action = request.form.get("action", "save")
|
||||
|
||||
if action == "revoke_session":
|
||||
session_id = request.form.get("session_id", type=int)
|
||||
if session_id and revoke_session(session_id, current_user.id):
|
||||
flash("Сессия завершена", "success")
|
||||
return redirect(url_for("cabinet.profile"))
|
||||
|
||||
if action == "revoke_all_sessions":
|
||||
count = revoke_all_sessions(current_user.id, except_current=True)
|
||||
flash(f"Завершено сессий: {count}", "success")
|
||||
return redirect(url_for("cabinet.profile"))
|
||||
|
||||
if action == "delete_passkey":
|
||||
passkey_id = request.form.get("passkey_id", type=int)
|
||||
if passkey_id and delete_passkey(current_user, passkey_id):
|
||||
flash("Passkey удалён", "success")
|
||||
return redirect(url_for("cabinet.profile"))
|
||||
|
||||
if action == "delete_account":
|
||||
password = request.form.get("delete_password", "")
|
||||
if not current_user.check_password(password):
|
||||
flash("Неверный пароль", "error")
|
||||
else:
|
||||
_delete_user_account(current_user)
|
||||
flash("Аккаунт удалён", "success")
|
||||
return redirect(url_for("main.index"))
|
||||
|
||||
email = request.form.get("email", "").strip().lower()
|
||||
current_password = request.form.get("current_password", "")
|
||||
new_password = request.form.get("new_password", "")
|
||||
@@ -234,4 +270,53 @@ def profile():
|
||||
flash("Профиль обновлён", "success")
|
||||
return redirect(url_for("cabinet.profile"))
|
||||
|
||||
return render_template("cabinet/profile.html")
|
||||
sessions = list_user_sessions(current_user.id)
|
||||
passkeys = current_user.passkeys.order_by(UserPasskey.created_at.desc()).all()
|
||||
current_sid = get_current_session_key()
|
||||
return render_template(
|
||||
"cabinet/profile.html",
|
||||
sessions=sessions,
|
||||
passkeys=passkeys,
|
||||
current_sid=current_sid,
|
||||
)
|
||||
|
||||
|
||||
@cabinet_bp.route("/profile/export")
|
||||
@login_required
|
||||
def export_profile():
|
||||
from app.legal import export_user_data
|
||||
import json
|
||||
|
||||
data = export_user_data(current_user)
|
||||
response = make_response(json.dumps(data, ensure_ascii=False, indent=2))
|
||||
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
||||
response.headers["Content-Disposition"] = (
|
||||
f'attachment; filename="photohost-{current_user.username}.json"'
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
def _delete_user_account(user):
|
||||
from app.models import UserPasskey, UserSession
|
||||
from app.session_service import revoke_all_sessions, revoke_current_session
|
||||
from app.storage_service import delete_photo_file
|
||||
from flask_login import logout_user
|
||||
|
||||
revoke_all_sessions(user.id, except_current=False)
|
||||
UserSession.query.filter_by(user_id=user.id).delete()
|
||||
UserPasskey.query.filter_by(user_id=user.id).delete()
|
||||
|
||||
for photo in user.photos.all():
|
||||
delete_photo_file(photo.filename, photo.storage_backend)
|
||||
db.session.delete(photo)
|
||||
|
||||
for folder in user.folders.all():
|
||||
for photo in folder.photos.all():
|
||||
delete_photo_file(photo.filename, photo.storage_backend)
|
||||
db.session.delete(photo)
|
||||
db.session.delete(folder)
|
||||
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
revoke_current_session()
|
||||
logout_user()
|
||||
|
||||
Reference in New Issue
Block a user