refactor: Extract messages + notifications blueprint (Phase 4)
- Create blueprints/messages/ with 11 routes: - messages_inbox, messages_sent, messages_new, messages_send - messages_view, messages_reply, api_unread_count - api_notifications, api_notification_mark_read - api_notifications_mark_all_read, api_notifications_unread_count - Register messages blueprint with backward-compatible aliases - Remove dead code from app.py (-340 lines) - app.py: 13,398 → 13,058 lines (-2.5%) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
ad2262388b
commit
0f482cc4aa
340
app.py
340
app.py
@ -2219,346 +2219,6 @@ def admin_calendar_delete(event_id):
|
||||
db.close()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# PRIVATE MESSAGES ROUTES
|
||||
# ============================================================
|
||||
|
||||
@app.route('/wiadomosci')
|
||||
@login_required
|
||||
def messages_inbox():
|
||||
"""Skrzynka odbiorcza"""
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = 20
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
query = db.query(PrivateMessage).filter(
|
||||
PrivateMessage.recipient_id == current_user.id
|
||||
).order_by(PrivateMessage.created_at.desc())
|
||||
|
||||
total = query.count()
|
||||
messages = query.limit(per_page).offset((page - 1) * per_page).all()
|
||||
|
||||
unread_count = db.query(PrivateMessage).filter(
|
||||
PrivateMessage.recipient_id == current_user.id,
|
||||
PrivateMessage.is_read == False
|
||||
).count()
|
||||
|
||||
return render_template('messages/inbox.html',
|
||||
messages=messages,
|
||||
page=page,
|
||||
total_pages=(total + per_page - 1) // per_page,
|
||||
unread_count=unread_count
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/wiadomosci/wyslane')
|
||||
@login_required
|
||||
def messages_sent():
|
||||
"""Wysłane wiadomości"""
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = 20
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
query = db.query(PrivateMessage).filter(
|
||||
PrivateMessage.sender_id == current_user.id
|
||||
).order_by(PrivateMessage.created_at.desc())
|
||||
|
||||
total = query.count()
|
||||
messages = query.limit(per_page).offset((page - 1) * per_page).all()
|
||||
|
||||
return render_template('messages/sent.html',
|
||||
messages=messages,
|
||||
page=page,
|
||||
total_pages=(total + per_page - 1) // per_page
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/wiadomosci/nowa')
|
||||
@login_required
|
||||
def messages_new():
|
||||
"""Formularz nowej wiadomości"""
|
||||
recipient_id = request.args.get('to', type=int)
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Lista użytkowników do wyboru
|
||||
users = db.query(User).filter(
|
||||
User.is_active == True,
|
||||
User.is_verified == True,
|
||||
User.id != current_user.id
|
||||
).order_by(User.name).all()
|
||||
|
||||
recipient = None
|
||||
if recipient_id:
|
||||
recipient = db.query(User).filter(User.id == recipient_id).first()
|
||||
|
||||
return render_template('messages/compose.html',
|
||||
users=users,
|
||||
recipient=recipient
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/wiadomosci/wyslij', methods=['POST'])
|
||||
@login_required
|
||||
def messages_send():
|
||||
"""Wyślij wiadomość"""
|
||||
recipient_id = request.form.get('recipient_id', type=int)
|
||||
subject = sanitize_input(request.form.get('subject', ''), 255)
|
||||
content = request.form.get('content', '').strip()
|
||||
|
||||
if not recipient_id or not content:
|
||||
flash('Odbiorca i treść są wymagane.', 'error')
|
||||
return redirect(url_for('messages_new'))
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
recipient = db.query(User).filter(User.id == recipient_id).first()
|
||||
if not recipient:
|
||||
flash('Odbiorca nie istnieje.', 'error')
|
||||
return redirect(url_for('messages_new'))
|
||||
|
||||
# Check if either user has blocked the other
|
||||
block_exists = db.query(UserBlock).filter(
|
||||
((UserBlock.user_id == current_user.id) & (UserBlock.blocked_user_id == recipient_id)) |
|
||||
((UserBlock.user_id == recipient_id) & (UserBlock.blocked_user_id == current_user.id))
|
||||
).first()
|
||||
if block_exists:
|
||||
flash('Nie można wysłać wiadomości do tego użytkownika.', 'error')
|
||||
return redirect(url_for('messages_new'))
|
||||
|
||||
message = PrivateMessage(
|
||||
sender_id=current_user.id,
|
||||
recipient_id=recipient_id,
|
||||
subject=subject,
|
||||
content=content
|
||||
)
|
||||
db.add(message)
|
||||
db.commit()
|
||||
|
||||
flash('Wiadomość wysłana.', 'success')
|
||||
return redirect(url_for('messages_sent'))
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/wiadomosci/<int:message_id>')
|
||||
@login_required
|
||||
def messages_view(message_id):
|
||||
"""Czytaj wiadomość"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
message = db.query(PrivateMessage).filter(
|
||||
PrivateMessage.id == message_id
|
||||
).first()
|
||||
|
||||
if not message:
|
||||
flash('Wiadomość nie istnieje.', 'error')
|
||||
return redirect(url_for('messages_inbox'))
|
||||
|
||||
# Sprawdź dostęp
|
||||
if message.recipient_id != current_user.id and message.sender_id != current_user.id:
|
||||
flash('Brak dostępu do tej wiadomości.', 'error')
|
||||
return redirect(url_for('messages_inbox'))
|
||||
|
||||
# Oznacz jako przeczytaną
|
||||
if message.recipient_id == current_user.id and not message.is_read:
|
||||
message.is_read = True
|
||||
message.read_at = datetime.now()
|
||||
db.commit()
|
||||
|
||||
return render_template('messages/view.html', message=message)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/wiadomosci/<int:message_id>/odpowiedz', methods=['POST'])
|
||||
@login_required
|
||||
def messages_reply(message_id):
|
||||
"""Odpowiedz na wiadomość"""
|
||||
content = request.form.get('content', '').strip()
|
||||
|
||||
if not content:
|
||||
flash('Treść jest wymagana.', 'error')
|
||||
return redirect(url_for('messages_view', message_id=message_id))
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
original = db.query(PrivateMessage).filter(
|
||||
PrivateMessage.id == message_id
|
||||
).first()
|
||||
|
||||
if not original:
|
||||
flash('Wiadomość nie istnieje.', 'error')
|
||||
return redirect(url_for('messages_inbox'))
|
||||
|
||||
# Odpowiedz do nadawcy oryginalnej wiadomości
|
||||
recipient_id = original.sender_id if original.sender_id != current_user.id else original.recipient_id
|
||||
|
||||
# Check if either user has blocked the other
|
||||
block_exists = db.query(UserBlock).filter(
|
||||
((UserBlock.user_id == current_user.id) & (UserBlock.blocked_user_id == recipient_id)) |
|
||||
((UserBlock.user_id == recipient_id) & (UserBlock.blocked_user_id == current_user.id))
|
||||
).first()
|
||||
if block_exists:
|
||||
flash('Nie można wysłać wiadomości do tego użytkownika.', 'error')
|
||||
return redirect(url_for('messages_inbox'))
|
||||
|
||||
reply = PrivateMessage(
|
||||
sender_id=current_user.id,
|
||||
recipient_id=recipient_id,
|
||||
subject=f"Re: {original.subject}" if original.subject else None,
|
||||
content=content,
|
||||
parent_id=message_id
|
||||
)
|
||||
db.add(reply)
|
||||
db.commit()
|
||||
|
||||
flash('Odpowiedź wysłana.', 'success')
|
||||
return redirect(url_for('messages_view', message_id=message_id))
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/api/messages/unread-count')
|
||||
@login_required
|
||||
def api_unread_count():
|
||||
"""API: Liczba nieprzeczytanych wiadomości"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
count = db.query(PrivateMessage).filter(
|
||||
PrivateMessage.recipient_id == current_user.id,
|
||||
PrivateMessage.is_read == False
|
||||
).count()
|
||||
return jsonify({'count': count})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# NOTIFICATIONS API ROUTES
|
||||
# ============================================================
|
||||
|
||||
@app.route('/api/notifications')
|
||||
@login_required
|
||||
def api_notifications():
|
||||
"""API: Get user notifications"""
|
||||
limit = request.args.get('limit', 20, type=int)
|
||||
offset = request.args.get('offset', 0, type=int)
|
||||
unread_only = request.args.get('unread_only', 'false').lower() == 'true'
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
query = db.query(UserNotification).filter(
|
||||
UserNotification.user_id == current_user.id
|
||||
)
|
||||
|
||||
if unread_only:
|
||||
query = query.filter(UserNotification.is_read == False)
|
||||
|
||||
# Order by most recent first
|
||||
query = query.order_by(UserNotification.created_at.desc())
|
||||
|
||||
total = query.count()
|
||||
notifications = query.limit(limit).offset(offset).all()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'notifications': [
|
||||
{
|
||||
'id': n.id,
|
||||
'title': n.title,
|
||||
'message': n.message,
|
||||
'notification_type': n.notification_type,
|
||||
'related_type': n.related_type,
|
||||
'related_id': n.related_id,
|
||||
'action_url': n.action_url,
|
||||
'is_read': n.is_read,
|
||||
'created_at': n.created_at.isoformat() if n.created_at else None
|
||||
}
|
||||
for n in notifications
|
||||
],
|
||||
'total': total,
|
||||
'unread_count': db.query(UserNotification).filter(
|
||||
UserNotification.user_id == current_user.id,
|
||||
UserNotification.is_read == False
|
||||
).count()
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/api/notifications/<int:notification_id>/read', methods=['POST'])
|
||||
@login_required
|
||||
def api_notification_mark_read(notification_id):
|
||||
"""API: Mark notification as read"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
notification = db.query(UserNotification).filter(
|
||||
UserNotification.id == notification_id,
|
||||
UserNotification.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if not notification:
|
||||
return jsonify({'success': False, 'error': 'Powiadomienie nie znalezione'}), 404
|
||||
|
||||
notification.mark_as_read()
|
||||
db.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'Oznaczono jako przeczytane'
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/api/notifications/read-all', methods=['POST'])
|
||||
@login_required
|
||||
def api_notifications_mark_all_read():
|
||||
"""API: Mark all notifications as read"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
updated = db.query(UserNotification).filter(
|
||||
UserNotification.user_id == current_user.id,
|
||||
UserNotification.is_read == False
|
||||
).update({
|
||||
UserNotification.is_read: True,
|
||||
UserNotification.read_at: datetime.now()
|
||||
})
|
||||
db.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': f'Oznaczono {updated} powiadomien jako przeczytane',
|
||||
'count': updated
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/api/notifications/unread-count')
|
||||
@login_required
|
||||
def api_notifications_unread_count():
|
||||
"""API: Get unread notifications count"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
count = db.query(UserNotification).filter(
|
||||
UserNotification.user_id == current_user.id,
|
||||
UserNotification.is_read == False
|
||||
).count()
|
||||
return jsonify({'count': count})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# USER ANALYTICS API ROUTES
|
||||
# ============================================================
|
||||
|
||||
@ -145,7 +145,33 @@ def register_blueprints(app):
|
||||
except Exception as e:
|
||||
logger.error(f"Error registering forum blueprint: {e}")
|
||||
|
||||
# Phase 4-10: Future blueprints will be added here
|
||||
# Phase 4: Messages + Notifications blueprint
|
||||
try:
|
||||
from blueprints.messages import bp as messages_bp
|
||||
app.register_blueprint(messages_bp)
|
||||
logger.info("Registered blueprint: messages")
|
||||
|
||||
# Create aliases for backward compatibility
|
||||
_create_endpoint_aliases(app, messages_bp, {
|
||||
'messages_inbox': 'messages.messages_inbox',
|
||||
'messages_sent': 'messages.messages_sent',
|
||||
'messages_new': 'messages.messages_new',
|
||||
'messages_send': 'messages.messages_send',
|
||||
'messages_view': 'messages.messages_view',
|
||||
'messages_reply': 'messages.messages_reply',
|
||||
'api_unread_count': 'messages.api_unread_count',
|
||||
'api_notifications': 'messages.api_notifications',
|
||||
'api_notification_mark_read': 'messages.api_notification_mark_read',
|
||||
'api_notifications_mark_all_read': 'messages.api_notifications_mark_all_read',
|
||||
'api_notifications_unread_count': 'messages.api_notifications_unread_count',
|
||||
})
|
||||
logger.info("Created messages endpoint aliases")
|
||||
except ImportError as e:
|
||||
logger.debug(f"Blueprint messages not yet available: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error registering messages blueprint: {e}")
|
||||
|
||||
# Phase 5-10: Future blueprints will be added here
|
||||
|
||||
|
||||
def _create_endpoint_aliases(app, blueprint, aliases):
|
||||
|
||||
12
blueprints/messages/__init__.py
Normal file
12
blueprints/messages/__init__.py
Normal file
@ -0,0 +1,12 @@
|
||||
"""
|
||||
Messages Blueprint
|
||||
==================
|
||||
|
||||
Private messages and notifications routes.
|
||||
"""
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
bp = Blueprint('messages', __name__)
|
||||
|
||||
from . import routes # noqa: E402, F401
|
||||
355
blueprints/messages/routes.py
Normal file
355
blueprints/messages/routes.py
Normal file
@ -0,0 +1,355 @@
|
||||
"""
|
||||
Messages Routes
|
||||
===============
|
||||
|
||||
Private messages and notifications API.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from flask import render_template, request, redirect, url_for, flash, jsonify
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
from . import bp
|
||||
from database import SessionLocal, User, PrivateMessage, UserNotification, UserBlock
|
||||
from utils.helpers import sanitize_input
|
||||
|
||||
|
||||
# ============================================================
|
||||
# PRIVATE MESSAGES ROUTES
|
||||
# ============================================================
|
||||
|
||||
@bp.route('/wiadomosci')
|
||||
@login_required
|
||||
def messages_inbox():
|
||||
"""Skrzynka odbiorcza"""
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = 20
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
query = db.query(PrivateMessage).filter(
|
||||
PrivateMessage.recipient_id == current_user.id
|
||||
).order_by(PrivateMessage.created_at.desc())
|
||||
|
||||
total = query.count()
|
||||
messages = query.limit(per_page).offset((page - 1) * per_page).all()
|
||||
|
||||
unread_count = db.query(PrivateMessage).filter(
|
||||
PrivateMessage.recipient_id == current_user.id,
|
||||
PrivateMessage.is_read == False
|
||||
).count()
|
||||
|
||||
return render_template('messages/inbox.html',
|
||||
messages=messages,
|
||||
page=page,
|
||||
total_pages=(total + per_page - 1) // per_page,
|
||||
unread_count=unread_count
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/wiadomosci/wyslane')
|
||||
@login_required
|
||||
def messages_sent():
|
||||
"""Wysłane wiadomości"""
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = 20
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
query = db.query(PrivateMessage).filter(
|
||||
PrivateMessage.sender_id == current_user.id
|
||||
).order_by(PrivateMessage.created_at.desc())
|
||||
|
||||
total = query.count()
|
||||
messages = query.limit(per_page).offset((page - 1) * per_page).all()
|
||||
|
||||
return render_template('messages/sent.html',
|
||||
messages=messages,
|
||||
page=page,
|
||||
total_pages=(total + per_page - 1) // per_page
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/wiadomosci/nowa')
|
||||
@login_required
|
||||
def messages_new():
|
||||
"""Formularz nowej wiadomości"""
|
||||
recipient_id = request.args.get('to', type=int)
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Lista użytkowników do wyboru
|
||||
users = db.query(User).filter(
|
||||
User.is_active == True,
|
||||
User.is_verified == True,
|
||||
User.id != current_user.id
|
||||
).order_by(User.name).all()
|
||||
|
||||
recipient = None
|
||||
if recipient_id:
|
||||
recipient = db.query(User).filter(User.id == recipient_id).first()
|
||||
|
||||
return render_template('messages/compose.html',
|
||||
users=users,
|
||||
recipient=recipient
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/wiadomosci/wyslij', methods=['POST'])
|
||||
@login_required
|
||||
def messages_send():
|
||||
"""Wyślij wiadomość"""
|
||||
recipient_id = request.form.get('recipient_id', type=int)
|
||||
subject = sanitize_input(request.form.get('subject', ''), 255)
|
||||
content = request.form.get('content', '').strip()
|
||||
|
||||
if not recipient_id or not content:
|
||||
flash('Odbiorca i treść są wymagane.', 'error')
|
||||
return redirect(url_for('.messages_new'))
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
recipient = db.query(User).filter(User.id == recipient_id).first()
|
||||
if not recipient:
|
||||
flash('Odbiorca nie istnieje.', 'error')
|
||||
return redirect(url_for('.messages_new'))
|
||||
|
||||
# Check if either user has blocked the other
|
||||
block_exists = db.query(UserBlock).filter(
|
||||
((UserBlock.user_id == current_user.id) & (UserBlock.blocked_user_id == recipient_id)) |
|
||||
((UserBlock.user_id == recipient_id) & (UserBlock.blocked_user_id == current_user.id))
|
||||
).first()
|
||||
if block_exists:
|
||||
flash('Nie można wysłać wiadomości do tego użytkownika.', 'error')
|
||||
return redirect(url_for('.messages_new'))
|
||||
|
||||
message = PrivateMessage(
|
||||
sender_id=current_user.id,
|
||||
recipient_id=recipient_id,
|
||||
subject=subject,
|
||||
content=content
|
||||
)
|
||||
db.add(message)
|
||||
db.commit()
|
||||
|
||||
flash('Wiadomość wysłana.', 'success')
|
||||
return redirect(url_for('.messages_sent'))
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/wiadomosci/<int:message_id>')
|
||||
@login_required
|
||||
def messages_view(message_id):
|
||||
"""Czytaj wiadomość"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
message = db.query(PrivateMessage).filter(
|
||||
PrivateMessage.id == message_id
|
||||
).first()
|
||||
|
||||
if not message:
|
||||
flash('Wiadomość nie istnieje.', 'error')
|
||||
return redirect(url_for('.messages_inbox'))
|
||||
|
||||
# Sprawdź dostęp
|
||||
if message.recipient_id != current_user.id and message.sender_id != current_user.id:
|
||||
flash('Brak dostępu do tej wiadomości.', 'error')
|
||||
return redirect(url_for('.messages_inbox'))
|
||||
|
||||
# Oznacz jako przeczytaną
|
||||
if message.recipient_id == current_user.id and not message.is_read:
|
||||
message.is_read = True
|
||||
message.read_at = datetime.now()
|
||||
db.commit()
|
||||
|
||||
return render_template('messages/view.html', message=message)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/wiadomosci/<int:message_id>/odpowiedz', methods=['POST'])
|
||||
@login_required
|
||||
def messages_reply(message_id):
|
||||
"""Odpowiedz na wiadomość"""
|
||||
content = request.form.get('content', '').strip()
|
||||
|
||||
if not content:
|
||||
flash('Treść jest wymagana.', 'error')
|
||||
return redirect(url_for('.messages_view', message_id=message_id))
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
original = db.query(PrivateMessage).filter(
|
||||
PrivateMessage.id == message_id
|
||||
).first()
|
||||
|
||||
if not original:
|
||||
flash('Wiadomość nie istnieje.', 'error')
|
||||
return redirect(url_for('.messages_inbox'))
|
||||
|
||||
# Odpowiedz do nadawcy oryginalnej wiadomości
|
||||
recipient_id = original.sender_id if original.sender_id != current_user.id else original.recipient_id
|
||||
|
||||
# Check if either user has blocked the other
|
||||
block_exists = db.query(UserBlock).filter(
|
||||
((UserBlock.user_id == current_user.id) & (UserBlock.blocked_user_id == recipient_id)) |
|
||||
((UserBlock.user_id == recipient_id) & (UserBlock.blocked_user_id == current_user.id))
|
||||
).first()
|
||||
if block_exists:
|
||||
flash('Nie można wysłać wiadomości do tego użytkownika.', 'error')
|
||||
return redirect(url_for('.messages_inbox'))
|
||||
|
||||
reply = PrivateMessage(
|
||||
sender_id=current_user.id,
|
||||
recipient_id=recipient_id,
|
||||
subject=f"Re: {original.subject}" if original.subject else None,
|
||||
content=content,
|
||||
parent_id=message_id
|
||||
)
|
||||
db.add(reply)
|
||||
db.commit()
|
||||
|
||||
flash('Odpowiedź wysłana.', 'success')
|
||||
return redirect(url_for('.messages_view', message_id=message_id))
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/api/messages/unread-count')
|
||||
@login_required
|
||||
def api_unread_count():
|
||||
"""API: Liczba nieprzeczytanych wiadomości"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
count = db.query(PrivateMessage).filter(
|
||||
PrivateMessage.recipient_id == current_user.id,
|
||||
PrivateMessage.is_read == False
|
||||
).count()
|
||||
return jsonify({'count': count})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# NOTIFICATIONS API ROUTES
|
||||
# ============================================================
|
||||
|
||||
@bp.route('/api/notifications')
|
||||
@login_required
|
||||
def api_notifications():
|
||||
"""API: Get user notifications"""
|
||||
limit = request.args.get('limit', 20, type=int)
|
||||
offset = request.args.get('offset', 0, type=int)
|
||||
unread_only = request.args.get('unread_only', 'false').lower() == 'true'
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
query = db.query(UserNotification).filter(
|
||||
UserNotification.user_id == current_user.id
|
||||
)
|
||||
|
||||
if unread_only:
|
||||
query = query.filter(UserNotification.is_read == False)
|
||||
|
||||
# Order by most recent first
|
||||
query = query.order_by(UserNotification.created_at.desc())
|
||||
|
||||
total = query.count()
|
||||
notifications = query.limit(limit).offset(offset).all()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'notifications': [
|
||||
{
|
||||
'id': n.id,
|
||||
'title': n.title,
|
||||
'message': n.message,
|
||||
'notification_type': n.notification_type,
|
||||
'related_type': n.related_type,
|
||||
'related_id': n.related_id,
|
||||
'action_url': n.action_url,
|
||||
'is_read': n.is_read,
|
||||
'created_at': n.created_at.isoformat() if n.created_at else None
|
||||
}
|
||||
for n in notifications
|
||||
],
|
||||
'total': total,
|
||||
'unread_count': db.query(UserNotification).filter(
|
||||
UserNotification.user_id == current_user.id,
|
||||
UserNotification.is_read == False
|
||||
).count()
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/api/notifications/<int:notification_id>/read', methods=['POST'])
|
||||
@login_required
|
||||
def api_notification_mark_read(notification_id):
|
||||
"""API: Mark notification as read"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
notification = db.query(UserNotification).filter(
|
||||
UserNotification.id == notification_id,
|
||||
UserNotification.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if not notification:
|
||||
return jsonify({'success': False, 'error': 'Powiadomienie nie znalezione'}), 404
|
||||
|
||||
notification.mark_as_read()
|
||||
db.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'Oznaczono jako przeczytane'
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/api/notifications/read-all', methods=['POST'])
|
||||
@login_required
|
||||
def api_notifications_mark_all_read():
|
||||
"""API: Mark all notifications as read"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
updated = db.query(UserNotification).filter(
|
||||
UserNotification.user_id == current_user.id,
|
||||
UserNotification.is_read == False
|
||||
).update({
|
||||
UserNotification.is_read: True,
|
||||
UserNotification.read_at: datetime.now()
|
||||
})
|
||||
db.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': f'Oznaczono {updated} powiadomien jako przeczytane',
|
||||
'count': updated
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/api/notifications/unread-count')
|
||||
@login_required
|
||||
def api_notifications_unread_count():
|
||||
"""API: Get unread notifications count"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
count = db.query(UserNotification).filter(
|
||||
UserNotification.user_id == current_user.id,
|
||||
UserNotification.is_read == False
|
||||
).count()
|
||||
return jsonify({'count': count})
|
||||
finally:
|
||||
db.close()
|
||||
@ -129,7 +129,7 @@ Usuń funkcje z prefiksem `_old_` z app.py.
|
||||
| **1** | reports, community, education | 19 | ✅ WDROŻONA |
|
||||
| **2a** | auth + public + cleanup | 31 | ✅ WDROŻONA |
|
||||
| **3** | forum (10 routes) | 10 | ✅ WDROŻONA |
|
||||
| **4** | messages, notifications | ~10 | ⏳ |
|
||||
| **4** | messages + notifications (11 routes) | 11 | ✅ WDROŻONA |
|
||||
| **5** | chat | ~8 | ⏳ |
|
||||
| **6** | admin (8 modułów) | ~60 | ⏳ |
|
||||
| **7** | audits (6 modułów) | ~35 | ⏳ |
|
||||
@ -165,6 +165,7 @@ Usuń funkcje z prefiksem `_old_` z app.py.
|
||||
- Po Fazie 1: 13,699 linii (-12.0%)
|
||||
- Po Fazie 2a: 13,820 linii (-11.2% od startu)
|
||||
- Po Fazie 3: 13,398 linii (-13.9% od startu)
|
||||
- Po Fazie 4: 13,058 linii (-16.1% od startu)
|
||||
- **Cel końcowy: ~500 linii**
|
||||
|
||||
---
|
||||
|
||||
Loading…
Reference in New Issue
Block a user