feat: add PUT /api/0/users/me for user self-service profile updates
Allows any authenticated user to update their own full_name, avatar, notification_channels, and password via the config YAML write path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1173,6 +1173,62 @@ async def start(
|
||||
|
||||
return web.json_response({"ok": True})
|
||||
|
||||
async def api_user_self_put(request):
|
||||
"""PUT /api/0/users/me — update own full_name, avatar, notification_channels, password."""
|
||||
user, err = _require_auth(request)
|
||||
if err:
|
||||
return err
|
||||
if user is None:
|
||||
return web.json_response({"error": "Authentication required"}, status=401)
|
||||
if not _config_path:
|
||||
return web.json_response({"error": "Config path not available"}, status=503)
|
||||
|
||||
try:
|
||||
body = await request.json()
|
||||
except Exception:
|
||||
return web.json_response({"error": "Invalid JSON"}, status=400)
|
||||
|
||||
if not isinstance(body, dict):
|
||||
return web.json_response({"error": "Invalid JSON"}, status=400)
|
||||
|
||||
username = user.username
|
||||
password_change = body.get("password")
|
||||
|
||||
if password_change:
|
||||
current_pw = password_change.get("current", "")
|
||||
new_pw = password_change.get("new", "")
|
||||
if not new_pw:
|
||||
return web.json_response({"error": "New password cannot be empty"}, status=400)
|
||||
if not users_mod.authenticate(username, current_pw):
|
||||
return web.json_response({"error": "Current password incorrect"}, status=403)
|
||||
|
||||
try:
|
||||
data = configio_mod.read_roundtrip(_config_path)
|
||||
if "users" not in data or data["users"] is None:
|
||||
data["users"] = {}
|
||||
user_entry = dict(data["users"].get(username) or {})
|
||||
|
||||
if "full_name" in body:
|
||||
user_entry["full_name"] = str(body["full_name"])
|
||||
if "avatar" in body:
|
||||
user_entry["avatar"] = str(body["avatar"])
|
||||
if "notification_channels" in body:
|
||||
user_entry["notification_channels"] = list(body["notification_channels"])
|
||||
if password_change:
|
||||
user_entry["password"] = users_mod.hash_password(password_change["new"])
|
||||
|
||||
data["users"][username] = user_entry
|
||||
configio_mod.write_config(_config_path, data)
|
||||
except Exception as exc:
|
||||
logger.error("User self-update failed: %s", exc)
|
||||
return web.json_response({"error": str(exc)}, status=500)
|
||||
|
||||
if hasattr(config, "reload"):
|
||||
await config.reload()
|
||||
users_mod.load_users(config)
|
||||
|
||||
return web.json_response({"ok": True})
|
||||
|
||||
app = web.Application()
|
||||
app.add_routes(
|
||||
[
|
||||
@@ -1189,6 +1245,7 @@ async def start(
|
||||
# Users
|
||||
web.get("/api/0/users", api_users),
|
||||
web.get("/api/0/users/me", api_user_self),
|
||||
web.put("/api/0/users/me", api_user_self_put),
|
||||
web.get("/api/0/users/{username}/avatar", api_user_avatar),
|
||||
# Config API (admin)
|
||||
web.get("/api/0/config", api_config_get),
|
||||
|
||||
Reference in New Issue
Block a user