Add forms for changing username, email and password
Signed-off-by: Aelita4 <kontakt@mikorosa.pl>
This commit is contained in:
		
							parent
							
								
									eafea0c1f5
								
							
						
					
					
						commit
						2945828935
					
				| 
						 | 
				
			
			@ -2,8 +2,21 @@
 | 
			
		|||
    "Header": {
 | 
			
		||||
        "user": "user {}"
 | 
			
		||||
    },
 | 
			
		||||
    "Label": {
 | 
			
		||||
        "userCreationDate": "Account created at {}",
 | 
			
		||||
        "newUsernamePlaceholder": "new username",
 | 
			
		||||
        "newEmailPlaceholder": "new email",
 | 
			
		||||
        "newPasswordPlaceholder": "new password",
 | 
			
		||||
        "newPasswordVerifyPlaceholder": "verify",
 | 
			
		||||
        "passwordPlaceholder": "password",
 | 
			
		||||
        "oldPasswordPlaceholder": "old password"
 | 
			
		||||
    },
 | 
			
		||||
    "Link": {
 | 
			
		||||
        "logout": "Log out",
 | 
			
		||||
        "build": "[build]"
 | 
			
		||||
        "build": "[build]",
 | 
			
		||||
        
 | 
			
		||||
        "changeUsername": "Change username",
 | 
			
		||||
        "changeEmail": "Change email",
 | 
			
		||||
        "changePassword": "Change password",
 | 
			
		||||
        "logout": "[log out]"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,116 @@
 | 
			
		|||
---
 | 
			
		||||
import type { ObjectId } from "mongodb";
 | 
			
		||||
import { Users } from "../../../../lib/db/mongodb";
 | 
			
		||||
import { getUserById, getUserByNickOrEmail } from "../../../../lib/db/users";
 | 
			
		||||
import validateAccessToken from "../../../../lib/utils/validateAccessToken";
 | 
			
		||||
import { hash, compare } from "bcrypt";
 | 
			
		||||
 | 
			
		||||
if(Astro.request.method === "PATCH") {
 | 
			
		||||
    const response = await validateAccessToken(Astro.request);
 | 
			
		||||
    if(response instanceof Response) return response;
 | 
			
		||||
    
 | 
			
		||||
    const { path } = Astro.params;
 | 
			
		||||
 | 
			
		||||
    const body = await Astro.request.json();
 | 
			
		||||
 | 
			
		||||
    if(!body || !body['password']) return new Response(
 | 
			
		||||
        JSON.stringify({
 | 
			
		||||
            code: 400,
 | 
			
		||||
            message: "Bad Request"
 | 
			
		||||
        }), { status: 400 }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const user = await getUserById(response.user as ObjectId);
 | 
			
		||||
    if(!user) return new Response(
 | 
			
		||||
        JSON.stringify({
 | 
			
		||||
            code: 404,
 | 
			
		||||
            message: "Not Found"
 | 
			
		||||
        }), { status: 404 }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if(!(await compare(body['password'], user.password))) return new Response(
 | 
			
		||||
        JSON.stringify({
 | 
			
		||||
            code: 401,
 | 
			
		||||
            message: "Unauthorized"
 | 
			
		||||
        }), { status: 401 }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    switch(path) {
 | 
			
		||||
        case 'username':
 | 
			
		||||
            if(!body['newUsername']) return new Response(
 | 
			
		||||
                JSON.stringify({
 | 
			
		||||
                    code: 400,
 | 
			
		||||
                    message: "Bad Request"
 | 
			
		||||
                }), { status: 400 }
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            if(await getUserByNickOrEmail(body['newUsername'])) return new Response(
 | 
			
		||||
                JSON.stringify({
 | 
			
		||||
                    code: 409,
 | 
			
		||||
                    message: "Conflict"
 | 
			
		||||
                }), { status: 409 }
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            (await Users()).updateOne({ _id: user._id }, { $set: { username: body['newUsername'] } })
 | 
			
		||||
            return new Response(
 | 
			
		||||
                JSON.stringify({
 | 
			
		||||
                    code: 200,
 | 
			
		||||
                    message: "OK"
 | 
			
		||||
                })
 | 
			
		||||
            );
 | 
			
		||||
        case 'email':
 | 
			
		||||
            if(!body['newEmail']) return new Response(
 | 
			
		||||
                JSON.stringify({
 | 
			
		||||
                    code: 400,
 | 
			
		||||
                    message: "Bad Request"
 | 
			
		||||
                }), { status: 400 }
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            if(await getUserByNickOrEmail(body['newEmail'])) return new Response(
 | 
			
		||||
                JSON.stringify({
 | 
			
		||||
                    code: 409,
 | 
			
		||||
                    message: "Conflict"
 | 
			
		||||
                }), { status: 409 }
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            (await Users()).updateOne({ _id: user._id }, { $set: { email: body['newEmail'] } })
 | 
			
		||||
            return new Response(
 | 
			
		||||
                JSON.stringify({
 | 
			
		||||
                    code: 200,
 | 
			
		||||
                    message: "OK"
 | 
			
		||||
                })
 | 
			
		||||
            );
 | 
			
		||||
        case 'password':
 | 
			
		||||
            if(!body['newPassword']) return new Response(
 | 
			
		||||
                JSON.stringify({
 | 
			
		||||
                    code: 400,
 | 
			
		||||
                    message: "Bad Request"
 | 
			
		||||
                }), { status: 400 }
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            const newPassword = await hash(body['newPassword'], 10);
 | 
			
		||||
 | 
			
		||||
            (await Users()).updateOne({ _id: user._id }, { $set: { password: newPassword } }) 
 | 
			
		||||
            return new Response(
 | 
			
		||||
                JSON.stringify({
 | 
			
		||||
                    code: 200,
 | 
			
		||||
                    message: "OK"
 | 
			
		||||
                })
 | 
			
		||||
            );
 | 
			
		||||
        default:
 | 
			
		||||
            return new Response(
 | 
			
		||||
                JSON.stringify({
 | 
			
		||||
                    code: 400,
 | 
			
		||||
                    message: "Bad Request"
 | 
			
		||||
                }), { status: 400 }
 | 
			
		||||
            );
 | 
			
		||||
    }
 | 
			
		||||
} else {
 | 
			
		||||
    return new Response(
 | 
			
		||||
        JSON.stringify({
 | 
			
		||||
            code: 405,
 | 
			
		||||
            message: "Method Not Allowed"
 | 
			
		||||
        }), { status: 405 }
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
---
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,230 @@
 | 
			
		|||
---
 | 
			
		||||
import Layout from '../../layouts/Layout.astro';
 | 
			
		||||
import NavBar from '../../components/NavBar.astro';
 | 
			
		||||
import { getUserByNickOrEmail, getUserResources, updateUserResources } from '../../lib/db/users';
 | 
			
		||||
import { getHighestWeightedLanguage, getLocales } from '../../lib/lang/langDriver';
 | 
			
		||||
import ResourceBar from '../../components/ResourceBar.astro';
 | 
			
		||||
import format from '../../lib/utils/format';
 | 
			
		||||
 | 
			
		||||
const loggedToken = Astro.cookies.get('sessionToken')?.value ?? null;
 | 
			
		||||
const username = Astro.cookies.get('username')?.value ?? "";
 | 
			
		||||
 | 
			
		||||
if(loggedToken === null || username === "") return Astro.redirect('/');
 | 
			
		||||
 | 
			
		||||
const locale = getHighestWeightedLanguage(Astro.request.headers.get('accept-language'));
 | 
			
		||||
 | 
			
		||||
const user = await getUserByNickOrEmail(username);
 | 
			
		||||
 | 
			
		||||
const langGame = await getLocales(locale, 'game');
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<Layout title="Profile">
 | 
			
		||||
	<NavBar loggedIn="true" active="profile" />
 | 
			
		||||
	<ResourceBar loggedIn="true" />
 | 
			
		||||
 | 
			
		||||
    <div class="wrapper">
 | 
			
		||||
        <h3>{format(langGame['Label_userCreationDate'], user?.createdAt.toISOString().slice(0, 19).replace(/-/g, "/").replace("T", " ").toString() ?? "")}</h3>
 | 
			
		||||
        <a href="/logout" class="a-button">{langGame['Link_logout']}</a>
 | 
			
		||||
    </div>
 | 
			
		||||
    <form id="changeUsernameForm" class="data-form">
 | 
			
		||||
        <input class="data-form-input" type="text" name="username" placeholder={langGame['Label_newUsernamePlaceholder']} />
 | 
			
		||||
        <input class="data-form-input" type="password" name="password" placeholder={langGame['Label_passwordPlaceholder']} />
 | 
			
		||||
        <input class="data-form-button" type="button" value={langGame['Link_changeUsername']} />
 | 
			
		||||
    </form>
 | 
			
		||||
    <form id="changeEmailForm" class="data-form">
 | 
			
		||||
        <input class="data-form-input" type="email" name="email" placeholder={langGame['Label_newEmailPlaceholder']} />
 | 
			
		||||
        <input class="data-form-input" type="password" name="password" placeholder={langGame['Label_passwordPlaceholder']} />
 | 
			
		||||
        <input class="data-form-button" type="button" value={langGame['Link_changeEmail']} />
 | 
			
		||||
    </form>
 | 
			
		||||
    <form id="changePasswordForm" class="data-form">
 | 
			
		||||
        <input class="data-form-input" type="password" name="oldPassword" placeholder={langGame['Label_oldPasswordPlaceholder']} />
 | 
			
		||||
        <input class="data-form-input" type="password" name="password1" placeholder={langGame['Label_newPasswordPlaceholder']} />
 | 
			
		||||
        <input class="data-form-input" type="password" name="password2" placeholder={langGame['Label_newPasswordVerifyPlaceholder']} />
 | 
			
		||||
        <input class="data-form-button" type="button" value={langGame['Link_changePassword']} />
 | 
			
		||||
    </form>
 | 
			
		||||
</Layout>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
	* {
 | 
			
		||||
		color: white;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    .data-form {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        justify-content: center;
 | 
			
		||||
        margin-top: 2rem;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .data-form-input {
 | 
			
		||||
        color: black;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .data-form-button {
 | 
			
		||||
        color: red;
 | 
			
		||||
        font-weight: 700;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .wrapper {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        justify-content: center;
 | 
			
		||||
        margin-top: 2rem;
 | 
			
		||||
        color: white;
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
	main {
 | 
			
		||||
		margin: auto;
 | 
			
		||||
		padding: 1rem;
 | 
			
		||||
		width: 800px;
 | 
			
		||||
		max-width: calc(100% - 2rem);
 | 
			
		||||
		color: white;
 | 
			
		||||
		font-size: 20px;
 | 
			
		||||
		line-height: 1.6;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.astro-a {
 | 
			
		||||
		position: absolute;
 | 
			
		||||
		top: -32px;
 | 
			
		||||
		left: 50%;
 | 
			
		||||
		transform: translatex(-50%);
 | 
			
		||||
		width: 220px;
 | 
			
		||||
		height: auto;
 | 
			
		||||
		z-index: -1;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	h3 {
 | 
			
		||||
		font-size: 2rem;
 | 
			
		||||
		font-weight: 700;
 | 
			
		||||
		line-height: 1;
 | 
			
		||||
		text-align: center;
 | 
			
		||||
		margin-bottom: 1em;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	.text-gradient {
 | 
			
		||||
		background-image: var(--accent-gradient);
 | 
			
		||||
		-webkit-background-clip: text;
 | 
			
		||||
		-webkit-text-fill-color: transparent;
 | 
			
		||||
		background-size: 400%;
 | 
			
		||||
		background-position: 0%;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	.instructions {
 | 
			
		||||
		margin-bottom: 2rem;
 | 
			
		||||
		border: 1px solid rgba(var(--accent-light), 25%);
 | 
			
		||||
		background: linear-gradient(rgba(var(--accent-dark), 66%), rgba(var(--accent-dark), 33%));
 | 
			
		||||
		padding: 1.5rem;
 | 
			
		||||
		border-radius: 8px;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	.instructions code {
 | 
			
		||||
		font-size: 0.8em;
 | 
			
		||||
		font-weight: bold;
 | 
			
		||||
		background: rgba(var(--accent-light), 12%);
 | 
			
		||||
		color: rgb(var(--accent-light));
 | 
			
		||||
		border-radius: 4px;
 | 
			
		||||
		padding: 0.3em 0.4em;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	.instructions strong {
 | 
			
		||||
		color: rgb(var(--accent-light));
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	.link-card-grid {
 | 
			
		||||
		display: grid;
 | 
			
		||||
		grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
 | 
			
		||||
		gap: 2rem;
 | 
			
		||||
		padding: 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    .a-button {
 | 
			
		||||
		text-decoration: none;
 | 
			
		||||
    	color: green;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.a-button:hover {
 | 
			
		||||
		color: lime;
 | 
			
		||||
	}
 | 
			
		||||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
    const changeUsernameForm = document.getElementById('changeUsernameForm');
 | 
			
		||||
    if(changeUsernameForm === null) console.error("changeUsernameForm is null");
 | 
			
		||||
    else {
 | 
			
		||||
        changeUsernameForm.querySelector('input[type="button"]')?.addEventListener("click", async () => {
 | 
			
		||||
            const newUsername = (changeUsernameForm.querySelector('input[name="username"]') as HTMLInputElement).value;
 | 
			
		||||
            const password = (changeUsernameForm.querySelector('input[name="password"]') as HTMLInputElement).value;
 | 
			
		||||
    
 | 
			
		||||
            const request = await fetch('/api/auth/changeUserData/username', {
 | 
			
		||||
                method: 'PATCH',
 | 
			
		||||
                headers: {
 | 
			
		||||
                    'Content-Type': 'application/json'
 | 
			
		||||
                },
 | 
			
		||||
                body: JSON.stringify({
 | 
			
		||||
                    newUsername,
 | 
			
		||||
                    password
 | 
			
		||||
                })
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            const response = await request.json();
 | 
			
		||||
            console.log(response);
 | 
			
		||||
 | 
			
		||||
            window.location.reload();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const changeEmailForm = document.getElementById('changeEmailForm');
 | 
			
		||||
    if(changeEmailForm === null) console.error("changeEmailForm is null");
 | 
			
		||||
    else {
 | 
			
		||||
        changeEmailForm.querySelector('input[type="button"]')?.addEventListener("click", async () => {
 | 
			
		||||
            const newEmail = (changeEmailForm.querySelector('input[name="email"]') as HTMLInputElement).value;
 | 
			
		||||
            const password = (changeEmailForm.querySelector('input[name="password"]') as HTMLInputElement).value;
 | 
			
		||||
    
 | 
			
		||||
            const request = await fetch('/api/auth/changeUserData/email', {
 | 
			
		||||
                method: 'PATCH',
 | 
			
		||||
                headers: {
 | 
			
		||||
                    'Content-Type': 'application/json'
 | 
			
		||||
                },
 | 
			
		||||
                body: JSON.stringify({
 | 
			
		||||
                    newEmail,
 | 
			
		||||
                    password
 | 
			
		||||
                })
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            const response = await request.json();
 | 
			
		||||
            console.log(response);
 | 
			
		||||
 | 
			
		||||
            window.location.reload();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const changePasswordForm = document.getElementById('changePasswordForm');
 | 
			
		||||
    if(changePasswordForm === null) console.error("changePasswordForm is null");
 | 
			
		||||
    else {
 | 
			
		||||
        changePasswordForm.querySelector('input[type="button"]')?.addEventListener("click", async () => {
 | 
			
		||||
            const oldPassword = (changePasswordForm.querySelector('input[name="oldPassword"]') as HTMLInputElement).value;
 | 
			
		||||
            const password1 = (changePasswordForm.querySelector('input[name="password1"]') as HTMLInputElement).value;
 | 
			
		||||
            const password2 = (changePasswordForm.querySelector('input[name="password2"]') as HTMLInputElement).value;
 | 
			
		||||
 | 
			
		||||
            if(password1 !== password2) return alert("Passwords don't match");
 | 
			
		||||
            else {
 | 
			
		||||
                const request = await fetch('/api/auth/changeUserData/password', {
 | 
			
		||||
                    method: 'PATCH',
 | 
			
		||||
                    headers: {
 | 
			
		||||
                        'Content-Type': 'application/json'
 | 
			
		||||
                    },
 | 
			
		||||
                    body: JSON.stringify({
 | 
			
		||||
                        password: oldPassword,
 | 
			
		||||
                        newPassword: password1
 | 
			
		||||
                    })
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                const response = await request.json();
 | 
			
		||||
                console.log(response);
 | 
			
		||||
 | 
			
		||||
                window.location.reload();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
</script>
 | 
			
		||||
		Loading…
	
		Reference in New Issue