mirror of
https://github.com/Damillora/Shioriko.git
synced 2025-02-23 09:23:38 +00:00
feat: separate update profile and password
This commit is contained in:
parent
6581e40f80
commit
c87e76566d
@ -3,6 +3,8 @@ package app
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/Damillora/Shioriko/pkg/database"
|
||||||
|
"github.com/Damillora/Shioriko/pkg/middleware"
|
||||||
"github.com/Damillora/Shioriko/pkg/models"
|
"github.com/Damillora/Shioriko/pkg/models"
|
||||||
"github.com/Damillora/Shioriko/pkg/services"
|
"github.com/Damillora/Shioriko/pkg/services"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -11,6 +13,11 @@ import (
|
|||||||
|
|
||||||
func InitializeAuthRoutes(g *gin.Engine) {
|
func InitializeAuthRoutes(g *gin.Engine) {
|
||||||
g.POST("/api/auth/login", createToken)
|
g.POST("/api/auth/login", createToken)
|
||||||
|
|
||||||
|
protected := g.Group("/api/auth").Use(middleware.AuthMiddleware())
|
||||||
|
{
|
||||||
|
protected.POST("/token", createTokenLoggedIn)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
func createToken(c *gin.Context) {
|
func createToken(c *gin.Context) {
|
||||||
var model models.LoginFormModel
|
var model models.LoginFormModel
|
||||||
@ -50,3 +57,21 @@ func createToken(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createTokenLoggedIn(c *gin.Context) {
|
||||||
|
result, ok := c.Get("user")
|
||||||
|
if ok {
|
||||||
|
user := result.(*database.User)
|
||||||
|
if user != nil {
|
||||||
|
token := services.CreateToken(user)
|
||||||
|
c.JSON(http.StatusOK, models.TokenResponse{
|
||||||
|
Token: token,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.JSON(http.StatusUnauthorized, models.ErrorResponse{
|
||||||
|
Code: http.StatusUnauthorized,
|
||||||
|
Message: "No authorized user",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -18,7 +18,8 @@ func InitializeUserRoutes(g *gin.Engine) {
|
|||||||
protected := g.Group("/api/user").Use(middleware.AuthMiddleware())
|
protected := g.Group("/api/user").Use(middleware.AuthMiddleware())
|
||||||
{
|
{
|
||||||
protected.GET("/profile", userProfile)
|
protected.GET("/profile", userProfile)
|
||||||
protected.POST("/update", userUpdate)
|
protected.PUT("/update", userUpdate)
|
||||||
|
protected.PUT("/update-password", userUpdatePassword)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +115,45 @@ func userUpdate(c *gin.Context) {
|
|||||||
result, ok := c.Get("user")
|
result, ok := c.Get("user")
|
||||||
if ok {
|
if ok {
|
||||||
user := result.(*database.User)
|
user := result.(*database.User)
|
||||||
services.UpdateUser(user.ID, model)
|
services.UpdateUserProfile(user.ID, model)
|
||||||
|
c.JSON(http.StatusOK, nil)
|
||||||
|
} else {
|
||||||
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Message: "User does not exist",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func userUpdatePassword(c *gin.Context) {
|
||||||
|
var model models.UserUpdatePasswordModel
|
||||||
|
|
||||||
|
err := c.ShouldBindJSON(&model)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
validate := validator.New()
|
||||||
|
err = validate.Struct(model)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, ok := c.Get("user")
|
||||||
|
if ok {
|
||||||
|
user := result.(*database.User)
|
||||||
|
services.UpdateUserPassword(user.ID, model)
|
||||||
|
c.JSON(http.StatusOK, nil)
|
||||||
} else {
|
} else {
|
||||||
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
|
@ -7,8 +7,11 @@ type UserCreateModel struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserUpdateModel struct {
|
type UserUpdateModel struct {
|
||||||
Email string `json:"email" validate:"required,email"`
|
Email string `json:"email" validate:"required,email"`
|
||||||
Username string `json:"username" validate:"required"`
|
Username string `json:"username" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserUpdatePasswordModel struct {
|
||||||
OldPassword string `json:"old_password"`
|
OldPassword string `json:"old_password"`
|
||||||
NewPassword string `json:"new_password"`
|
NewPassword string `json:"new_password"`
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ func GetUserFromUsername(username string) *database.User {
|
|||||||
return &user
|
return &user
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateUser(id string, model models.UserUpdateModel) (*database.User, error) {
|
func UpdateUserProfile(id string, model models.UserUpdateModel) (*database.User, error) {
|
||||||
var user database.User
|
var user database.User
|
||||||
result := database.DB.Where("id = ?", id).First(&user)
|
result := database.DB.Where("id = ?", id).First(&user)
|
||||||
|
|
||||||
@ -49,6 +49,19 @@ func UpdateUser(id string, model models.UserUpdateModel) (*database.User, error)
|
|||||||
user.Email = model.Email
|
user.Email = model.Email
|
||||||
user.Username = model.Username
|
user.Username = model.Username
|
||||||
|
|
||||||
|
result = database.DB.Save(&user)
|
||||||
|
if result.Error != nil {
|
||||||
|
return nil, result.Error
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func UpdateUserPassword(id string, model models.UserUpdatePasswordModel) (*database.User, error) {
|
||||||
|
var user database.User
|
||||||
|
result := database.DB.Where("id = ?", id).First(&user)
|
||||||
|
|
||||||
|
|
||||||
if user.Password != "" {
|
if user.Password != "" {
|
||||||
verifyErr := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(model.OldPassword))
|
verifyErr := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(model.OldPassword))
|
||||||
if verifyErr != nil {
|
if verifyErr != nil {
|
||||||
|
@ -37,6 +37,20 @@ export async function register({ email, username, password }) {
|
|||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function updateToken() {
|
||||||
|
const endpoint = url + "/api/auth/token";
|
||||||
|
const response = await axios({
|
||||||
|
url: endpoint,
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + current_token,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
token.set(response.data.token);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getTags() {
|
export async function getTags() {
|
||||||
const endpoint = url + "/api/tag";
|
const endpoint = url + "/api/tag";
|
||||||
const response = await axios.get(endpoint);
|
const response = await axios.get(endpoint);
|
||||||
@ -200,8 +214,8 @@ export async function updateTag(id, { name, tagTypeId }) {
|
|||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateUserProfile({ email, username, oldPassword, newPassword }) {
|
export async function updateUserProfile({ email, username, }) {
|
||||||
const endpoint = url + "/api/tag/" + id;
|
const endpoint = url + "/api/user/update";
|
||||||
const response = await axios({
|
const response = await axios({
|
||||||
url: endpoint,
|
url: endpoint,
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
@ -210,7 +224,22 @@ export async function updateUserProfile({ email, username, oldPassword, newPassw
|
|||||||
},
|
},
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
data: {
|
data: {
|
||||||
email, username, oldPassword, newPassword
|
email, username,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
export async function updateUserPassword({ old_password, new_password }) {
|
||||||
|
const endpoint = url + "/api/user/update-password";
|
||||||
|
const response = await axios({
|
||||||
|
url: endpoint,
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + current_token,
|
||||||
|
},
|
||||||
|
withCredentials: true,
|
||||||
|
data: {
|
||||||
|
old_password, new_password
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return response.data;
|
return response.data;
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="panel is-primary">
|
||||||
|
<div class="panel-heading">User Actions</div>
|
||||||
|
<a href="/user/profile" class="panel-block">Profile</a>
|
||||||
|
<a href="/user/password" class="panel-block">Change Password</a>
|
||||||
|
</div>
|
79
web/app/src/routes/user/password/+page.svelte
Normal file
79
web/app/src/routes/user/password/+page.svelte
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<script>
|
||||||
|
import { updateToken, updateUserPassword, } from "$lib/api";
|
||||||
|
import UserActionsPanel from "$lib/components/panels/UserActionsPanel.svelte";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
let loading = $state(false);
|
||||||
|
let updated = $state(false);
|
||||||
|
let form = $state({
|
||||||
|
old_password: "",
|
||||||
|
new_password: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
loading = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitForm = async () => {
|
||||||
|
updated = false;
|
||||||
|
await updateUserPassword({ old_password: form.old_password, new_password: form.new_password })
|
||||||
|
await updateToken();
|
||||||
|
updated = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
loading = true;
|
||||||
|
getData();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-one-third">
|
||||||
|
<UserActionsPanel />
|
||||||
|
</div>
|
||||||
|
<div class="column is-two-thirds">
|
||||||
|
<div class="box">
|
||||||
|
<h1 class="title">Change Password</h1>
|
||||||
|
{#if updated}
|
||||||
|
<div class="notification is-success">
|
||||||
|
Password updated!
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<form onsubmit={submitForm}>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="old_password">Current Password</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
id="old_password"
|
||||||
|
class="input"
|
||||||
|
class:is-skeleton="{loading}"
|
||||||
|
type="password"
|
||||||
|
placeholder="Current password"
|
||||||
|
bind:value={form.old_password}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="new_password">New Password</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
id="new_password"
|
||||||
|
class="input"
|
||||||
|
class:is-skeleton="{loading}"
|
||||||
|
type="password"
|
||||||
|
placeholder="New password"
|
||||||
|
bind:value={form.new_password}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<button class="button is-primary is-fullwidth is-outlined" type="submit">Change Password</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
@ -1,26 +1,82 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { getUserProfile, updateToken, updateUserProfile } from "$lib/api";
|
||||||
|
import UserActionsPanel from "$lib/components/panels/UserActionsPanel.svelte";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import { getUserProfile } from "$lib/api";
|
|
||||||
import AuthRequired from "$lib/components/checks/AuthRequired.svelte";
|
|
||||||
|
|
||||||
let user = $state();
|
let loading = $state(false);
|
||||||
|
let updated = $state(false);
|
||||||
|
let form = $state({
|
||||||
|
email: "",
|
||||||
|
username: "",
|
||||||
|
});
|
||||||
|
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
user = await getUserProfile();
|
const user = await getUserProfile();
|
||||||
|
form.email = user.email;
|
||||||
|
form.username = user.username;
|
||||||
|
loading = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const submitForm = async () => {
|
||||||
|
updated = false;
|
||||||
|
await updateUserProfile({ email: form.email, username: form.username })
|
||||||
|
await updateToken();
|
||||||
|
updated = true;
|
||||||
|
};
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
loading = true;
|
||||||
getData();
|
getData();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AuthRequired />
|
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{#if user}
|
<div class="columns">
|
||||||
<h1 class="title">Welcome, {user.username}</h1>
|
<div class="column is-one-third">
|
||||||
<p>Email: {user.email}</p>
|
<UserActionsPanel />
|
||||||
<p>Username: {user.username}</p>
|
</div>
|
||||||
{/if}
|
<div class="column is-two-thirds">
|
||||||
|
<div class="box">
|
||||||
|
<h1 class="title">Profile</h1>
|
||||||
|
{#if updated}
|
||||||
|
<div class="notification is-success">
|
||||||
|
Profile updated!
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<form onsubmit={submitForm}>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="email">Email</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
class="input"
|
||||||
|
class:is-skeleton="{loading}"
|
||||||
|
type="text"
|
||||||
|
placeholder="Email"
|
||||||
|
bind:value={form.email}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="username">Username</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
id="username"
|
||||||
|
class="input"
|
||||||
|
class:is-skeleton="{loading}"
|
||||||
|
type="text"
|
||||||
|
placeholder="Username"
|
||||||
|
bind:value={form.username}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<button class="button is-primary is-fullwidth is-outlined" type="submit">Update</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user