Add: User can change their password

This commit is contained in:
2021-12-13 16:18:02 +08:00
parent f1e8dcfad4
commit 1b0688e523
8 changed files with 301 additions and 10 deletions

View File

@@ -83,8 +83,11 @@ func NewAPI(config Config) (*API, error) {
apiMux.HandleFunc("/login", api.HandleLogin)
apiMux.HandleFunc("/register", api.HandleRegister)
apiMux.HandleFunc("/logout", api.LoginAsAnonymous)
apiMux.HandleFunc("/get_user_info", api.HandleGetUserInfo)
apiMux.HandleFunc("/get_users", api.HandleGetUsers)
apiMux.HandleFunc("/update_user_active", api.HandleUpdateUserActive)
apiMux.HandleFunc("/update_username", api.HandleUpdateUsername)
apiMux.HandleFunc("/update_user_password", api.HandleUpdateUserPassword)
// tag
apiMux.HandleFunc("/get_tags", api.HandleGetTags)
apiMux.HandleFunc("/get_tag_info", api.HandleGetTagInfo)

View File

@@ -13,6 +13,7 @@ var (
ErrEmpty = errors.New("Empty field detected, please fill in all fields")
ErrAnonymous = errors.New("Anonymous user detected, please login")
ErrNotActive = errors.New("User is not active")
ErrWrongPassword = errors.New("Wrong password")
)
type Error struct {

View File

@@ -245,3 +245,125 @@ func (api *API) HandleUpdateUserActive(w http.ResponseWriter, r *http.Request) {
}
api.HandleOK(w, r)
}
type UpdateUsernameRequest struct {
ID int64 `json:"id"`
Username string `json:"username"`
}
func (api *API) HandleUpdateUsername(w http.ResponseWriter, r *http.Request) {
req := &UpdateUsernameRequest{}
err := json.NewDecoder(r.Body).Decode(req)
if err != nil {
api.HandleError(w, r, err)
return
}
user, err := api.Db.GetUserById(req.ID)
if err != nil {
api.HandleError(w, r, err)
return
}
userID, err := api.GetUserID(w, r)
if err != nil {
api.HandleError(w, r, err)
return
}
if user.ID != userID && user.Role != database.RoleAdmin {
api.HandleError(w, r, ErrNotAdmin)
return
}
err = api.Db.UpdateUsername(req.ID, req.Username)
if err != nil {
api.HandleError(w, r, err)
return
}
api.HandleOK(w, r)
}
type GetUserInfoRequest struct {
ID int64 `json:"id"`
}
type GetUserInfoResponse struct {
User *database.User `json:"user"`
}
func (api *API) HandleGetUserInfo(w http.ResponseWriter, r *http.Request) {
req := &GetUserInfoRequest{}
err := json.NewDecoder(r.Body).Decode(req)
if err != nil {
api.HandleError(w, r, err)
return
}
user, err := api.Db.GetUserById(req.ID)
if err != nil {
api.HandleError(w, r, err)
return
}
ret := &GetUserInfoResponse{
User: user,
}
err = json.NewEncoder(w).Encode(ret)
if err != nil {
api.HandleError(w, r, err)
return
}
}
type UpdateUserPasswordRequest struct {
ID int64 `json:"id"`
OldPassword string `json:"old_password"`
NewPassword string `json:"new_password"`
}
func (api *API) HandleUpdateUserPassword(w http.ResponseWriter, r *http.Request) {
req := &UpdateUserPasswordRequest{}
err := json.NewDecoder(r.Body).Decode(req)
if err != nil {
api.HandleError(w, r, err)
return
}
user, err := api.Db.GetUserById(req.ID)
if err != nil {
api.HandleError(w, r, err)
return
}
userID, err := api.GetUserID(w, r)
if err != nil {
api.HandleError(w, r, err)
return
}
currentUser, err := api.Db.GetUserById(userID)
if err != nil {
api.HandleError(w, r, err)
return
}
if currentUser.Role != database.RoleAdmin {
_, err := api.Db.Login(user.Username, req.OldPassword)
if err != nil {
api.HandleError(w, r, ErrWrongPassword)
return
}
}
err = api.Db.UpdateUserPassword(req.ID, req.NewPassword)
if err != nil {
api.HandleError(w, r, err)
return
}
api.HandleOK(w, r)
}

View File

@@ -92,3 +92,19 @@ func (database *Database) UpdateUserActive(id int64, active bool) error {
}
return nil
}
func (database *Database) UpdateUsername(id int64, username string) error {
_, err := database.stmt.updateUsername.Exec(username, id)
if err != nil {
return err
}
return nil
}
func (database *Database) UpdateUserPassword(id int64, password string) error {
_, err := database.stmt.updateUserPassword.Exec(password, id)
if err != nil {
return err
}
return nil
}

View File

@@ -186,6 +186,10 @@ var getUserByIdQuery = `SELECT id, username, role, active, avatar_id FROM users
var updateUserActiveQuery = `UPDATE users SET active = ? WHERE id = ?;`
var updateUsernameQuery = `UPDATE users SET username = ? WHERE id = ?;`
var updateUserPasswordQuery = `UPDATE users SET password = ? WHERE id = ?;`
var getAnonymousUserQuery = `SELECT id, username, role, avatar_id FROM users WHERE role = 0 LIMIT 1;`
var insertTagQuery = `INSERT INTO tags (name, description, created_by_user_id) VALUES (?, ?, ?);`
@@ -283,6 +287,8 @@ type Stmt struct {
getUsers *sql.Stmt
getUserById *sql.Stmt
updateUserActive *sql.Stmt
updateUsername *sql.Stmt
updateUserPassword *sql.Stmt
getAnonymousUser *sql.Stmt
insertTag *sql.Stmt
getTag *sql.Stmt
@@ -553,6 +559,18 @@ func NewPreparedStatement(sqlConn *sql.DB) (*Stmt, error) {
return nil, err
}
// init updateUsername
stmt.updateUsername, err = sqlConn.Prepare(updateUsernameQuery)
if err != nil {
return nil, err
}
// init updateUserPassword
stmt.updateUserPassword, err = sqlConn.Prepare(updateUserPasswordQuery)
if err != nil {
return nil, err
}
// init getAnonymousUser
stmt.getAnonymousUser, err = sqlConn.Prepare(getAnonymousUserQuery)
if err != nil {

View File

@@ -92,3 +92,8 @@ dialog {
display: flex;
justify-content: space-between;
}
.horizontal {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}

View File

@@ -17,6 +17,15 @@ function Manage(props) {
Login
</button>
)}
{props.user.role !== 0 && (
<button
onClick={() => {
navigate(`/manage/users/${props.user.id}`);
}}
>
Edit
</button>
)}
{props.user.role !== 0 && (
<button
onClick={() => {

View File

@@ -5,13 +5,22 @@ import ReviewEntry from "./ReviewEntry";
function UserProfile(props) {
let params = useParams();
const [reviews, setReviews] = useState([]);
const [oldPassword, setOldPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [newPasswordConfirm, setNewPasswordConfirm] = useState("");
const [user, setUser] = useState({
id: "",
username: "",
role: "",
active: "",
avatar_id: "",
});
function getReviews() {
fetch("/api/v1/get_reviews_by_user", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
id: parseInt(params.id),
@@ -27,24 +36,132 @@ function UserProfile(props) {
});
}
function getUserInfo() {
fetch("/api/v1/get_user_info", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id: parseInt(params.id),
}),
})
.then((response) => response.json())
.then((data) => {
if (data.error) {
alert(data.error);
} else {
setUser(data.user);
}
});
}
useEffect(() => {
getReviews();
getUserInfo();
}, []);
return (
<div className="page">
<h3>User Profile</h3>
<div className="horizontal">
<input
type="text"
value={user.username}
onChange={(e) => {
setUser({
...user,
username: e.target.value,
});
}}
/>
<button
onClick={() => {
fetch("/api/v1/update_username", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id: parseInt(params.id),
username: user.username,
}),
})
.then((response) => response.json())
.then((data) => {
if (data.error) {
alert(data.error);
} else {
props.setUser({
...props.user,
username: user.username,
});
alert("Username updated successfully!");
getUserInfo();
}
});
}}
disabled={props.user.id !== user.id || props.user.role === 1}
>
Save Username
</button>
</div>
<div>
<input
type="password"
value={oldPassword}
placeholder="Old Password"
onChange={(e) => setOldPassword(e.target.value)}
/>
<input
type="password"
value={newPassword}
placeholder="New Password"
onChange={(e) => setNewPassword(e.target.value)}
/>
<input
type="password"
value={newPasswordConfirm}
placeholder="Confirm New Password"
onChange={(e) => setNewPasswordConfirm(e.target.value)}
/>
<button
onClick={() => {
fetch("/api/v1/update_user_password", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id: parseInt(params.id),
old_password: oldPassword,
new_password: newPassword,
new_password_confirm: newPasswordConfirm,
}),
})
.then((response) => response.json())
.then((data) => {
if (data.error) {
alert(data.error);
} else {
alert("Password updated successfully!");
}
});
}}
disabled={
(props.user.id !== user.id ||
props.user.role !== 1) &&
newPassword !== newPasswordConfirm
}
>
Change Password
</button>
</div>
<h4>Reviews</h4>
{reviews.map((review) => (
<ReviewEntry
key={review.id}
review={review}
user={props.user}
/>
<ReviewEntry key={review.id} review={review} user={props.user} />
))}
</div>
</div>
);
}