Working signout button

This commit is contained in:
Olaf
2026-08-24 19:56:28 +02:00
parent 28a01175f7
commit 12f3580f60
17 changed files with 252 additions and 10 deletions
+22 -1
View File
@@ -5,7 +5,7 @@ from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
from pydantic import BaseModel
from backend.app.api.dependencies import get_current_user
from backend.app.database import AVATARS_DIR, get_connection
from backend.app.database import AVATARS_DIR, get_connection, hash_password
router = APIRouter()
@@ -15,6 +15,11 @@ class UserConfigUpdate(BaseModel):
bio: str | None = None
class PasswordUpdate(BaseModel):
current_password: str
new_password: str
class UserPluginConfigUpdate(BaseModel):
instance: str | None = None
access_token: str | None = None
@@ -60,6 +65,22 @@ def update_current_user_profile(
return {'status': 'updated'}
@router.put('/password')
def update_password(payload: PasswordUpdate, user: dict = Depends(get_current_user)):
if len(payload.new_password) < 8:
raise HTTPException(status_code=422, detail='New password must be at least 8 characters')
if hash_password(payload.current_password) != user['password_hash']:
raise HTTPException(status_code=400, detail='Current password is incorrect')
with get_connection() as conn:
conn.execute(
'UPDATE users SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
(hash_password(payload.new_password), user['id']),
)
conn.commit()
return {'status': 'password_updated'}
@router.post('/avatar')
async def upload_avatar(
avatar: UploadFile = File(...),
+7 -2
View File
@@ -173,6 +173,8 @@ def test_public_and_admin_pages_render_html():
root_page = client.get('/')
assert 'LinkLog' in root_page.text
assert 'class="login-button" href="/login"' in root_page.text
assert 'id="auth-session" class="auth-session hidden"' in root_page.text
assert 'logout.js?v=3' in root_page.text
assert client.get('/alice').status_code == 200
assert client.get('/alice/').status_code == 200
user_page = client.get('/alice').text
@@ -182,14 +184,17 @@ def test_public_and_admin_pages_render_html():
feed_script = TestClient(app).get('/static/feed.js?v=4').text
assert 'window.location.assign(selectedUser ? `/${encodeURIComponent(selectedUser)}/` : \'/\')' in feed_script
assert client.get('/login').status_code == 200
assert 'Sign in' in client.get('/login').text
login_page = client.get('/login').text
assert 'Sign in' in login_page
assert 'id="auth-session" class="auth-session hidden"' in login_page
assert 'logout.js?v=3' in login_page
assert client.get('/admin').status_code == 200
admin_page = client.get('/admin').text
assert 'Admin' in admin_page
assert 'id="admin-controls" class="hidden"' in admin_page
assert 'id="admin-auth-notice" class="auth-notice hidden"' in admin_page
assert 'id="admin-login-button" class="login-button" href="/login"' in admin_page
assert 'id="logout-button" class="logout-button hidden"' in admin_page
assert 'id="auth-session" class="auth-session hidden"' in admin_page
feed_script = client.get('/static/feed.js?v=5').text
assert 'if (item.is_owner)' in feed_script
+20 -1
View File
@@ -37,7 +37,26 @@ def test_user_config_api_and_profile_page():
assert 'From my #LinkLog: &quot;' in page_response.text
assert 'id="admin-link"' in page_response.text
assert 'class="login-button hidden"' in page_response.text
assert 'id="logout-button" class="logout-button hidden"' in page_response.text
assert 'id="auth-session" class="auth-session hidden"' in page_response.text
bob_login = client.post('/api/auth/login', json={
'username': 'bob',
'password': 'secret123',
}).json()
bob_headers = {'Authorization': f"Bearer {bob_login['access_token']}"}
password_response = client.put('/api/user/password', json={
'current_password': 'secret123',
'new_password': 'new-secret-123',
}, headers=bob_headers)
assert password_response.status_code == 200
assert client.post('/api/auth/login', json={
'username': 'bob',
'password': 'new-secret-123',
}).status_code == 200
assert client.put('/api/user/password', json={
'current_password': 'new-secret-123',
'new_password': 'secret123',
}, headers=bob_headers).status_code == 200
upload_response = client.post(
'/api/user/avatar',