diff --git a/main.py b/main.py index 3fd22d8..9101096 100644 --- a/main.py +++ b/main.py @@ -226,6 +226,23 @@ async def change_own_password(request: Request): conn.close() return {"status": "ok"} +@app.put("/api/users/me/username", dependencies=[Depends(is_authenticated)]) +async def change_own_username(request: Request): + data = await request.json() + new_username = data.get("username") + if not new_username: + raise HTTPException(status_code=400, detail="New username required") + user_id = request.session.get("user_id") + try: + conn = sqlite3.connect(DB_PATH) + c = conn.cursor() + c.execute('UPDATE users SET username = ? WHERE id = ?', (new_username, user_id)) + conn.commit() + conn.close() + except sqlite3.IntegrityError: + raise HTTPException(status_code=400, detail="Username already exists") + return {"status": "ok"} + class InstanceCreate(BaseModel): name: str ip: str diff --git a/static/app.js b/static/app.js index 04e7963..7fa7e0f 100644 --- a/static/app.js +++ b/static/app.js @@ -14,6 +14,7 @@ document.addEventListener('DOMContentLoaded', () => { const usersList = document.getElementById('users-list'); const createUserForm = document.getElementById('create-user-form'); const changePasswordForm = document.getElementById('change-password-form'); + const changeUsernameForm = document.getElementById('change-username-form'); const profileSuccess = document.getElementById('profile-success'); const helpBtn = document.getElementById('help-btn'); const helpModal = document.getElementById('help-modal'); @@ -265,6 +266,7 @@ document.addEventListener('DOMContentLoaded', () => { } else { navAdmin.style.display = 'none'; } + document.getElementById('profile-username').value = currentUser.username; }; logoutBtn.addEventListener('click', async () => { @@ -277,6 +279,28 @@ document.addEventListener('DOMContentLoaded', () => { } }); + changeUsernameForm.addEventListener('submit', async (e) => { + e.preventDefault(); + const username = document.getElementById('profile-username').value; + try { + const res = await fetch('/api/users/me/username', { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username }) + }); + if (res.ok) { + currentUser.username = username; + profileSuccess.style.display = 'block'; + setTimeout(() => profileSuccess.style.display = 'none', 3000); + } else { + const data = await res.json(); + alert(data.detail || 'Failed to update username'); + } + } catch (e) { + console.error('Error changing username', e); + } + }); + createUserForm.addEventListener('submit', async (e) => { e.preventDefault(); const username = document.getElementById('new-username').value; diff --git a/static/index.html b/static/index.html index 8104271..0a0dfd9 100644 --- a/static/index.html +++ b/static/index.html @@ -84,15 +84,26 @@

My Profile

-

Change your account password below.

-
+

Update your account details below.

+ +
- + + +
+ +
+ +
+ +
+
+
- +
diff --git a/static/style.css b/static/style.css index 428de72..2a9345f 100644 --- a/static/style.css +++ b/static/style.css @@ -197,8 +197,11 @@ input:focus { .btn-primary { background-color: var(--primary); color: white; +} + +.login-card .btn-primary { width: 100%; - margin-top: 0.5rem; + margin-top: 1rem; } .btn-primary:hover { @@ -440,6 +443,23 @@ input:focus { color: var(--text-muted); } +.profile-form { + display: flex; + flex-direction: column; + gap: 1.25rem; + max-width: 400px; +} + +.profile-form .btn-primary { + align-self: flex-start; +} + +.divider { + height: 1px; + background-color: var(--border-color); + margin: 2rem 0; +} + .success-msg { color: var(--status-online); text-align: center;