1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2025-04-05 18:26:56 +00:00

implement user profile update and password change feature

This commit is contained in:
webplusai 2024-10-11 18:08:22 +00:00
parent 81f73de87d
commit 851a7ab4ae
4 changed files with 192 additions and 15 deletions

View File

@ -0,0 +1,69 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/routes/handlers/change-password.js
type: application/javascript
module-type: mws-route
POST /change-user-password
\*/
(function () {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var authenticator = require("$:/plugins/tiddlywiki/multiwikiserver/auth/authentication.js").Authenticator;
exports.method = "POST";
exports.path = /^\/change-user-password\/?$/;
exports.bodyFormat = "www-form-urlencoded";
exports.csrfDisable = true;
exports.handler = function (request, response, state) {
if(!state.authenticatedUser) {
response.writeHead(401, "Unauthorized", { "Content-Type": "text/plain" });
response.end("Unauthorized");
return;
}
var auth = authenticator($tw);
var userId = state.authenticatedUser.user_id;
var currentPassword = state.data.currentPassword;
var newPassword = state.data.newPassword;
var confirmPassword = state.data.confirmPassword;
if(newPassword !== confirmPassword) {
response.setHeader("Set-Cookie", "flashMessage=New passwords do not match; Path=/; HttpOnly; Max-Age=5");
response.writeHead(302, { "Location": "/admin/users/" + userId });
response.end();
return;
}
var userData = state.server.sqlTiddlerDatabase.getUser(userId);
if(!userData) {
response.setHeader("Set-Cookie", "flashMessage=User not found; Path=/; HttpOnly; Max-Age=5");
response.writeHead(302, { "Location": "/admin/users/" + userId });
response.end();
return;
}
var isCurrentPasswordValid = auth.verifyPassword(currentPassword, userData.password);
if(!isCurrentPasswordValid) {
response.setHeader("Set-Cookie", "flashMessage=Current password is incorrect; Path=/; HttpOnly; Max-Age=5");
response.writeHead(302, { "Location": "/admin/users/" + userId });
response.end();
return;
}
var newHash = auth.hashPassword(newPassword);
var result = state.server.sqlTiddlerDatabase.updateUserPassword(userId, newHash);
response.setHeader("Set-Cookie", `flashMessage=${result.message}; Path=/; HttpOnly; Max-Age=5`);
response.writeHead(302, { "Location": "/admin/users/" + userId });
response.end();
};
}());

View File

@ -0,0 +1,46 @@
/*\
title: $:/plugins/tiddlywiki/multiwikiserver/routes/handlers/update-profile.js
type: application/javascript
module-type: mws-route
POST /update-user-profile
\*/
(function () {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "POST";
exports.path = /^\/update-user-profile\/?$/;
exports.bodyFormat = "www-form-urlencoded";
exports.csrfDisable = true;
exports.handler = function (request,response,state) {
if(!state.authenticatedUser) {
response.writeHead(401, "Unauthorized", { "Content-Type": "text/plain" });
response.end("Unauthorized");
return;
}
var userId = state.authenticatedUser.user_id;
var username = state.data.username;
var email = state.data.email;
var result = state.server.sqlTiddlerDatabase.updateUser(userId, username, email);
if(result.success) {
response.setHeader("Set-Cookie", "flashMessage="+result.mesasge+"; Path=/; HttpOnly; Max-Age=5");
response.writeHead(302, { "Location": "/admin/users/" + userId });
} else {
response.setHeader("Set-Cookie", "flashMessage="+result.mesasge+"; Path=/; HttpOnly; Max-Age=5");
response.writeHead(302, { "Location": "/admin/users/" + userId });
}
response.end();
};
}());

View File

@ -781,16 +781,66 @@ SqlTiddlerDatabase.prototype.getUserByUsername = function(username) {
});
};
SqlTiddlerDatabase.prototype.updateUser = function(userId, username, email) {
this.engine.runStatement(`
UPDATE users
SET username = $username, email = $email
WHERE user_id = $userId
`, {
SqlTiddlerDatabase.prototype.updateUser = function (userId, username, email) {
const existingUser = this.engine.runStatement(`
SELECT user_id FROM users
WHERE email = $email AND user_id != $userId
`, {
$email: email,
$userId: userId
});
if (existingUser.length > 0) {
return {
success: false,
message: "Email address already in use by another user."
};
}
try {
this.engine.runStatement(`
UPDATE users
SET username = $username, email = $email
WHERE user_id = $userId
`, {
$userId: userId,
$username: username,
$email: email
});
});
return {
success: true,
message: "User profile updated successfully."
};
} catch (error) {
return {
success: false,
message: "Failed to update user profile: " + error.message
};
}
};
SqlTiddlerDatabase.prototype.updateUserPassword = function (userId, newHash) {
try {
this.engine.runStatement(`
UPDATE users
SET password = $newHash
WHERE user_id = $userId
`, {
$userId: userId,
$newHash: newHash,
});
return {
success: true,
message: "Password updated successfully."
};
} catch (error) {
return {
success: false,
message: "Failed to update password: " + error.message
};
}
};
SqlTiddlerDatabase.prototype.deleteUser = function(userId) {

View File

@ -42,36 +42,48 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/manage-user
</div>
</div>
<!-- <$reveal type="match" state="is-current-user-profile" text="yes"> -->
<div class="user-profile-management">
<h2>Manage Your Account</h2>
<form class="user-profile-form">
<form class="user-profile-form" action="/update-user-profile" method="POST">
<div class="form-group">
<label for="username">Username:</label>
<input type="text" id="username" name="username" value={{{ [<user>jsonget[username]] }}} />
<input type="text" id="username" name="username" value={{{ [<user>jsonget[username]] }}} required />
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" id="email" name="email" value={{{ [<user>jsonget[email]] }}} />
<input type="email" id="email" name="email" value={{{ [<user>jsonget[email]] }}} required />
</div>
<button type="submit" class="update-profile-btn">Update Profile</button>
</form>
<hr />
<h2>Danger Zone</h2>
<form class="user-profile-form">
<h2>Change Password</h2>
<form class="user-profile-form" action="/change-user-password" method="POST">
<div class="form-group">
<label for="current-password">Current Password:</label>
<input type="password" id="current-password" name="currentPassword" required />
</div>
<div class="form-group">
<label for="new-password">New Password:</label>
<input type="password" id="new-password" name="new-password" required />
<input type="password" id="new-password" name="newPassword" required />
</div>
<div class="form-group">
<label for="confirm-password">Confirm New Password:</label>
<input type="password" id="confirm-password" name="confirm-password" required />
<input type="password" id="confirm-password" name="confirmPassword" required />
</div>
<button type="submit" class="update-password-btn">Change Password</button>
</form>
</div>
<!-- </$reveal> -->
<$let flash-message={{{ [[$:/state/mws/flash-message]get[text]] }}}>
<$reveal type="nomatch" state="$:/state/mws/flash-message" text="">
<div class="flash-message">
<$text text=<<flash-message>>/>
</div>
<$action-setfield $tiddler="$:/state/mws/flash-message" text=""/>
</$reveal>
</$let>
</div>
<style>