feat: Share-Dialog Fix, User-Sharing, Admin-Benutzerverwaltung, Registrierungs-Toggle
- Fix: Share-Dialog oeffnet sich jetzt auch bei bereits geteilten Dateien - Neu: Dateien/Ordner direkt mit anderen Benutzern teilen (Lesen/Schreiben/Admin) - Neu: Benutzersuche im Share-Dialog, bestehende Freigaben anzeigen/entfernen - Neu: Admin kann Benutzer ueber die Weboberflaeche anlegen - Neu: Admin kann Benutzer bearbeiten (Rolle, Quota, aktiv/inaktiv) und loeschen - Neu: Schieberegler fuer oeffentliche Registrierung in den Admin-Einstellungen - Neu: Register-Link auf Login-Seite nur sichtbar wenn Registrierung erlaubt - Neu: Register-Seite leitet um wenn Registrierung deaktiviert - Neu: AppSettings-Model fuer persistente App-Konfiguration - Neu: /api/users/search Endpunkt fuer Benutzersuche in Share-Dialogen Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
import os
|
||||
|
||||
from flask import request, jsonify
|
||||
|
||||
from app.api import api_bp
|
||||
from app.api.auth import admin_required, token_required
|
||||
from app.extensions import db
|
||||
from app.models.user import User
|
||||
from app.models.settings import AppSettings
|
||||
|
||||
|
||||
@api_bp.route('/users', methods=['GET'])
|
||||
@@ -13,6 +16,51 @@ def list_users():
|
||||
return jsonify([u.to_dict(include_email=True) for u in users]), 200
|
||||
|
||||
|
||||
@api_bp.route('/users', methods=['POST'])
|
||||
@admin_required
|
||||
def create_user():
|
||||
"""Admin creates a new user."""
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({'error': 'Keine Daten gesendet'}), 400
|
||||
|
||||
username = data.get('username', '').strip()
|
||||
password = data.get('password', '')
|
||||
email = data.get('email', '').strip() or None
|
||||
role = data.get('role', 'user')
|
||||
|
||||
if not username or not password:
|
||||
return jsonify({'error': 'Benutzername und Passwort erforderlich'}), 400
|
||||
|
||||
if len(username) < 3:
|
||||
return jsonify({'error': 'Benutzername muss mindestens 3 Zeichen lang sein'}), 400
|
||||
|
||||
if len(password) < 8:
|
||||
return jsonify({'error': 'Passwort muss mindestens 8 Zeichen lang sein'}), 400
|
||||
|
||||
if role not in ('admin', 'user'):
|
||||
return jsonify({'error': 'Rolle muss "admin" oder "user" sein'}), 400
|
||||
|
||||
if User.query.filter_by(username=username).first():
|
||||
return jsonify({'error': 'Benutzername bereits vergeben'}), 409
|
||||
|
||||
if email and User.query.filter_by(email=email).first():
|
||||
return jsonify({'error': 'Email-Adresse bereits vergeben'}), 409
|
||||
|
||||
user = User(
|
||||
username=username,
|
||||
email=email,
|
||||
role=role,
|
||||
master_key_salt=os.urandom(32),
|
||||
storage_quota_mb=data.get('storage_quota_mb', 5120),
|
||||
)
|
||||
user.set_password(password)
|
||||
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
return jsonify(user.to_dict(include_email=True)), 201
|
||||
|
||||
|
||||
@api_bp.route('/users/<int:user_id>', methods=['GET'])
|
||||
@admin_required
|
||||
def get_user(user_id):
|
||||
@@ -41,7 +89,6 @@ def update_user(user_id):
|
||||
user.email = email
|
||||
|
||||
if 'role' in data and data['role'] in ('admin', 'user'):
|
||||
# Prevent removing last admin
|
||||
if user.role == 'admin' and data['role'] == 'user':
|
||||
admin_count = User.query.filter_by(role='admin', is_active=True).count()
|
||||
if admin_count <= 1:
|
||||
@@ -85,6 +132,46 @@ def delete_user(user_id):
|
||||
return jsonify({'message': 'Benutzer geloescht'}), 200
|
||||
|
||||
|
||||
# --- App Settings ---
|
||||
|
||||
@api_bp.route('/settings', methods=['GET'])
|
||||
@admin_required
|
||||
def get_settings():
|
||||
return jsonify({
|
||||
'public_registration': AppSettings.get_bool('public_registration', default=False),
|
||||
}), 200
|
||||
|
||||
|
||||
@api_bp.route('/settings', methods=['PUT'])
|
||||
@admin_required
|
||||
def update_settings():
|
||||
data = request.get_json()
|
||||
if 'public_registration' in data:
|
||||
AppSettings.set('public_registration', str(data['public_registration']).lower())
|
||||
return jsonify({'message': 'Einstellungen gespeichert'}), 200
|
||||
|
||||
|
||||
# --- User search (for sharing dialogs) ---
|
||||
|
||||
@api_bp.route('/users/search', methods=['GET'])
|
||||
@token_required
|
||||
def search_users():
|
||||
"""Search users by username - for sharing dialogs."""
|
||||
query = request.args.get('q', '').strip()
|
||||
if len(query) < 2:
|
||||
return jsonify([]), 200
|
||||
|
||||
users = User.query.filter(
|
||||
User.username.ilike(f'%{query}%'),
|
||||
User.id != request.current_user.id,
|
||||
User.is_active == True,
|
||||
).limit(10).all()
|
||||
|
||||
return jsonify([{'id': u.id, 'username': u.username} for u in users]), 200
|
||||
|
||||
|
||||
# --- Change password (non-admin, own account) ---
|
||||
|
||||
@api_bp.route('/auth/change-password', methods=['POST'])
|
||||
@token_required
|
||||
def change_password():
|
||||
|
||||
Reference in New Issue
Block a user