feat: System-Email-Benachrichtigungen bei Freigaben und Benutzer-Erstellung
Automatische E-Mail-Benachrichtigungen ueber die konfigurierte System-Email bei folgenden Ereignissen: - Datei/Ordner mit Benutzer geteilt -> Empfaenger wird benachrichtigt - Share-Link heruntergeladen -> Ersteller wird benachrichtigt (mit IP) - Kalender mit Benutzer geteilt -> Empfaenger wird benachrichtigt - Adressbuch mit Benutzer geteilt -> Empfaenger wird benachrichtigt - Passwort-Eintrag/-Ordner geteilt -> Empfaenger wird benachrichtigt - Admin erstellt neuen Benutzer -> Neuer Benutzer wird benachrichtigt Alle Benachrichtigungen sind fail-safe (try/except), damit die eigentliche Aktion nie durch Email-Fehler blockiert wird. Emails werden nur gesendet wenn System-SMTP konfiguriert ist UND der Empfaenger eine Email-Adresse hat. Neuer Service: app/services/system_mail.py mit zentralem Helper Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
61ce2ec244
commit
e811210977
|
|
@ -269,6 +269,7 @@ def share_calendar(cal_id):
|
|||
existing = CalendarShare.query.filter_by(
|
||||
calendar_id=cal_id, shared_with_id=target.id
|
||||
).first()
|
||||
is_new = not existing
|
||||
if existing:
|
||||
existing.permission = permission
|
||||
else:
|
||||
|
|
@ -278,6 +279,14 @@ def share_calendar(cal_id):
|
|||
db.session.add(share)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
if is_new:
|
||||
try:
|
||||
from app.services.system_mail import notify_calendar_shared
|
||||
notify_calendar_shared(cal.name, user.username, target, permission)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return jsonify({'message': f'Kalender mit {username} geteilt'}), 200
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -246,6 +246,7 @@ def share_addressbook(book_id):
|
|||
existing = AddressBookShare.query.filter_by(
|
||||
address_book_id=book_id, shared_with_id=target.id
|
||||
).first()
|
||||
is_new = not existing
|
||||
if existing:
|
||||
existing.permission = permission
|
||||
else:
|
||||
|
|
@ -255,6 +256,14 @@ def share_addressbook(book_id):
|
|||
db.session.add(share)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
if is_new:
|
||||
try:
|
||||
from app.services.system_mail import notify_contacts_shared
|
||||
notify_contacts_shared(book.name, user.username, target, permission)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return jsonify({'message': f'Adressbuch mit {username} geteilt'}), 200
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -368,6 +368,7 @@ def set_permission(file_id):
|
|||
existing = FilePermission.query.filter_by(
|
||||
file_id=file_id, user_id=target_user_id
|
||||
).first()
|
||||
is_new = not existing
|
||||
if existing:
|
||||
existing.permission = permission
|
||||
else:
|
||||
|
|
@ -375,6 +376,15 @@ def set_permission(file_id):
|
|||
db.session.add(perm)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Notify user via email
|
||||
if is_new:
|
||||
try:
|
||||
from app.services.system_mail import notify_file_shared_with_user
|
||||
notify_file_shared_with_user(f.name, user.username, target)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return jsonify({'message': 'Berechtigung gesetzt'}), 200
|
||||
|
||||
|
||||
|
|
@ -536,6 +546,13 @@ def share_download(token):
|
|||
link.download_count += 1
|
||||
db.session.commit()
|
||||
|
||||
# Notify creator about download
|
||||
try:
|
||||
from app.services.system_mail import notify_share_link_accessed
|
||||
notify_share_link_accessed(link, f.name, request.remote_addr)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return send_file(str(filepath), mimetype=f.mime_type, as_attachment=True,
|
||||
download_name=f.name)
|
||||
|
||||
|
|
|
|||
|
|
@ -240,6 +240,7 @@ def share_password():
|
|||
shareable_type=shareable_type, shareable_id=shareable_id,
|
||||
shared_with_id=target.id
|
||||
).first()
|
||||
is_new = not existing
|
||||
if existing:
|
||||
existing.permission = permission
|
||||
else:
|
||||
|
|
@ -254,6 +255,14 @@ def share_password():
|
|||
db.session.add(share)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
if is_new:
|
||||
try:
|
||||
from app.services.system_mail import notify_password_shared
|
||||
notify_password_shared(shareable_type, user.username, target, permission)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return jsonify({'message': f'Mit {username} geteilt'}), 200
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,14 @@ def create_user():
|
|||
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
# Notify new user via email
|
||||
try:
|
||||
from app.services.system_mail import notify_user_created
|
||||
notify_user_created(user, request.current_user.username)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return jsonify(user.to_dict(include_email=True)), 201
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,152 @@
|
|||
"""System email sending helper. Uses the admin-configured SMTP settings."""
|
||||
import smtplib
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
|
||||
|
||||
def send_system_email(to, subject, body_text, body_html=None):
|
||||
"""Send an email via the system SMTP configuration.
|
||||
|
||||
Returns (success: bool, error: str|None)
|
||||
"""
|
||||
from app.models.settings import AppSettings
|
||||
|
||||
host = AppSettings.get('system_smtp_host', '')
|
||||
port = int(AppSettings.get('system_smtp_port', '587'))
|
||||
ssl = AppSettings.get_bool('system_smtp_ssl', default=True)
|
||||
username = AppSettings.get('system_smtp_username', '')
|
||||
password = AppSettings.get('system_smtp_password', '')
|
||||
from_addr = AppSettings.get('system_email_from', '') or username
|
||||
|
||||
if not host or not username or not password:
|
||||
return False, 'System-Email nicht konfiguriert'
|
||||
|
||||
try:
|
||||
if body_html:
|
||||
msg = MIMEMultipart('alternative')
|
||||
msg.attach(MIMEText(body_text, 'plain', 'utf-8'))
|
||||
msg.attach(MIMEText(body_html, 'html', 'utf-8'))
|
||||
else:
|
||||
msg = MIMEText(body_text, 'plain', 'utf-8')
|
||||
|
||||
msg['From'] = from_addr
|
||||
msg['To'] = to
|
||||
msg['Subject'] = subject
|
||||
|
||||
if ssl and port == 465:
|
||||
server = smtplib.SMTP_SSL(host, port, timeout=10)
|
||||
else:
|
||||
server = smtplib.SMTP(host, port, timeout=10)
|
||||
server.starttls()
|
||||
|
||||
server.login(username, password)
|
||||
server.sendmail(from_addr, [to], msg.as_string())
|
||||
server.quit()
|
||||
return True, None
|
||||
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
|
||||
|
||||
def notify_share_link_accessed(share_link, file_name, accessor_ip):
|
||||
"""Notify the share link creator that someone accessed their shared file."""
|
||||
from app.models.user import User
|
||||
|
||||
creator = User.query.get(share_link.created_by)
|
||||
if not creator or not creator.email:
|
||||
return
|
||||
|
||||
subject = f'Mini-Cloud: Zugriff auf geteilte Datei "{file_name}"'
|
||||
body = (
|
||||
f'Hallo {creator.username},\n\n'
|
||||
f'Jemand hat auf deine geteilte Datei zugegriffen:\n\n'
|
||||
f' Datei: {file_name}\n'
|
||||
f' IP-Adresse: {accessor_ip}\n'
|
||||
f' Downloads bisher: {share_link.download_count}\n\n'
|
||||
f'Falls du diesen Zugriff nicht erwartet hast, kannst du den '
|
||||
f'Freigabe-Link in deiner Mini-Cloud loeschen.\n\n'
|
||||
f'Deine Mini-Cloud'
|
||||
)
|
||||
send_system_email(creator.email, subject, body)
|
||||
|
||||
|
||||
def notify_file_shared_with_user(file_name, owner_username, target_user):
|
||||
"""Notify a user that a file/folder was shared with them."""
|
||||
if not target_user.email:
|
||||
return
|
||||
|
||||
subject = f'Mini-Cloud: "{file_name}" wurde mit dir geteilt'
|
||||
body = (
|
||||
f'Hallo {target_user.username},\n\n'
|
||||
f'{owner_username} hat "{file_name}" mit dir geteilt.\n\n'
|
||||
f'Melde dich in deiner Mini-Cloud an, um die Datei zu sehen.\n\n'
|
||||
f'Deine Mini-Cloud'
|
||||
)
|
||||
send_system_email(target_user.email, subject, body)
|
||||
|
||||
|
||||
def notify_calendar_shared(calendar_name, owner_username, target_user, permission):
|
||||
"""Notify a user that a calendar was shared with them."""
|
||||
if not target_user.email:
|
||||
return
|
||||
|
||||
perm_text = 'Lesen' if permission == 'read' else 'Lesen und Schreiben'
|
||||
subject = f'Mini-Cloud: Kalender "{calendar_name}" wurde mit dir geteilt'
|
||||
body = (
|
||||
f'Hallo {target_user.username},\n\n'
|
||||
f'{owner_username} hat den Kalender "{calendar_name}" mit dir geteilt.\n'
|
||||
f'Berechtigung: {perm_text}\n\n'
|
||||
f'Melde dich in deiner Mini-Cloud an, um den Kalender zu sehen.\n\n'
|
||||
f'Deine Mini-Cloud'
|
||||
)
|
||||
send_system_email(target_user.email, subject, body)
|
||||
|
||||
|
||||
def notify_contacts_shared(addressbook_name, owner_username, target_user, permission):
|
||||
"""Notify a user that an address book was shared with them."""
|
||||
if not target_user.email:
|
||||
return
|
||||
|
||||
perm_text = 'Lesen' if permission == 'read' else 'Lesen und Schreiben'
|
||||
subject = f'Mini-Cloud: Adressbuch "{addressbook_name}" wurde mit dir geteilt'
|
||||
body = (
|
||||
f'Hallo {target_user.username},\n\n'
|
||||
f'{owner_username} hat das Adressbuch "{addressbook_name}" mit dir geteilt.\n'
|
||||
f'Berechtigung: {perm_text}\n\n'
|
||||
f'Melde dich in deiner Mini-Cloud an.\n\n'
|
||||
f'Deine Mini-Cloud'
|
||||
)
|
||||
send_system_email(target_user.email, subject, body)
|
||||
|
||||
|
||||
def notify_password_shared(shareable_type, owner_username, target_user, permission):
|
||||
"""Notify a user that passwords were shared with them."""
|
||||
if not target_user.email:
|
||||
return
|
||||
|
||||
type_text = 'Ein Passwort-Ordner' if shareable_type == 'folder' else 'Ein Passwort-Eintrag'
|
||||
subject = f'Mini-Cloud: {type_text} wurde mit dir geteilt'
|
||||
body = (
|
||||
f'Hallo {target_user.username},\n\n'
|
||||
f'{owner_username} hat {type_text.lower()} mit dir geteilt.\n'
|
||||
f'Berechtigung: {permission}\n\n'
|
||||
f'Melde dich in deiner Mini-Cloud an, um die Passwoerter zu sehen.\n\n'
|
||||
f'Deine Mini-Cloud'
|
||||
)
|
||||
send_system_email(target_user.email, subject, body)
|
||||
|
||||
|
||||
def notify_user_created(user, created_by_username):
|
||||
"""Notify a newly created user about their account."""
|
||||
if not user.email:
|
||||
return
|
||||
|
||||
subject = 'Mini-Cloud: Dein Konto wurde erstellt'
|
||||
body = (
|
||||
f'Hallo {user.username},\n\n'
|
||||
f'{created_by_username} hat ein Konto fuer dich in der Mini-Cloud erstellt.\n\n'
|
||||
f'Benutzername: {user.username}\n\n'
|
||||
f'Bitte melde dich an und aendere dein Passwort.\n\n'
|
||||
f'Deine Mini-Cloud'
|
||||
)
|
||||
send_system_email(user.email, subject, body)
|
||||
Loading…
Reference in New Issue