feat: ctrl+click for new tabs, delete redirect, password change

- Protocol and collection pills now use <a href> instead of <div onclick>,
  so ctrl+click (or cmd+click) opens in a new tab, and middle-click works
- Applied consistently: library, collection detail, user profile
- After deleting a protocol or collection, currentRoute is reset so
  navigation back to the list actually works
- Password change: when viewing your own profile, a 'Change Password'
  button appears. Opens a modal requiring current password + new password
  + confirmation. New API endpoint POST /api/auth/password validates
  the current password and updates the hash.
This commit is contained in:
Protocolbot
2026-07-08 13:50:11 -06:00
parent 6e7c03b373
commit e514e8aca3
4 changed files with 103 additions and 18 deletions
Executable → Regular
+28
View File
@@ -220,4 +220,32 @@ function handle_me(): void {
function handle_sso_callback(): void {
respond(501, ['error' => 'SSO not configured on this instance. Configure SSO in api/auth.php']);
}
function handle_password_change(): void {
$user = current_user();
if (!$user) {
respond(401, ['error' => 'You must be logged in to change your password']);
return;
}
$data = json_decode(file_get_contents('php://input'), true) ?: [];
$current_password = $data['current_password'] ?? '';
$new_password = $data['new_password'] ?? '';
if (strlen($new_password) < 8 || strlen($new_password) > 72) {
respond(400, ['error' => 'New password must be between 8 and 72 characters']);
return;
}
// Verify current password
$row = db_one("SELECT password_hash FROM users WHERE id = ?", [$user['id']]);
if (!$row || !password_verify($current_password, $row['password_hash'] ?? '')) {
respond(401, ['error' => 'Current password is incorrect']);
return;
}
// Update password
db_update('users', ['password_hash' => password_hash($new_password, PASSWORD_DEFAULT)], 'id = ?', [$user['id']]);
respond(200, ['ok' => true]);
}