Add: User can change their password
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -92,3 +92,8 @@ dialog {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.horizontal {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@@ -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={() => {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user