Compare commits
	
		
			2 Commits
		
	
	
		
			9814207e0e
			...
			4c198e4113
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						4c198e4113
	
				 | 
					
					
						|||
| 
						
						
							
						
						82a232a20f
	
				 | 
					
					
						
							
								
								
									
										62
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								README.md
									
									
									
									
									
								
							@@ -4,47 +4,51 @@ My own homepage, bocken.org (new.bocken.org for now), built with svelte-kit.
 | 
			
		||||
 | 
			
		||||
## TODO
 | 
			
		||||
### General
 | 
			
		||||
- [] Admin user management
 | 
			
		||||
- [] upload/change pfp
 | 
			
		||||
- [ ] Admin user management
 | 
			
		||||
- [ ] upload/change pfp
 | 
			
		||||
- [x] registration only with minimal permissions
 | 
			
		||||
- [] logout without /logout page
 | 
			
		||||
- [] no DB request for every Layout change if session already got the username once
 | 
			
		||||
- [] preferences page
 | 
			
		||||
- [] change password
 | 
			
		||||
- [] fail2ban integration
 | 
			
		||||
- [ ] logout without /logout page
 | 
			
		||||
- [ ] no DB request for every Layout change if session already got the username once
 | 
			
		||||
- [ ] preferences page
 | 
			
		||||
- [x] change password
 | 
			
		||||
- [ ] fail2ban integration
 | 
			
		||||
- [ ] dark mode 
 | 
			
		||||
 | 
			
		||||
### Rezepte
 | 
			
		||||
- [] nutrition facts
 | 
			
		||||
- [] verify randomize arrays based on day
 | 
			
		||||
- [] notes for next time
 | 
			
		||||
- [ ] nutrition facts
 | 
			
		||||
- [ ] verify randomize arrays based on day
 | 
			
		||||
- [ ] notes for next time
 | 
			
		||||
 | 
			
		||||
### Abrechnungen
 | 
			
		||||
- [] DB setup
 | 
			
		||||
- [] create new entries
 | 
			
		||||
- [] delete entries
 | 
			
		||||
- [] edit entries
 | 
			
		||||
- [] upload img
 | 
			
		||||
- [ ] DB setup
 | 
			
		||||
- [ ] create new entries
 | 
			
		||||
- [ ] delete entries
 | 
			
		||||
- [ ] edit entries
 | 
			
		||||
- [ ] upload img
 | 
			
		||||
 | 
			
		||||
### Flims
 | 
			
		||||
- [] Calendar layout
 | 
			
		||||
- [] DB setup
 | 
			
		||||
- [] create new entries
 | 
			
		||||
- [] delete entries
 | 
			
		||||
- [] edit entries
 | 
			
		||||
- [ ] Calendar layout
 | 
			
		||||
- [ ] DB setup
 | 
			
		||||
- [ ] create new entries
 | 
			
		||||
- [ ] delete entries
 | 
			
		||||
- [ ] edit entries
 | 
			
		||||
 | 
			
		||||
### Glaube
 | 
			
		||||
- [] just keep it as MD rendering for now?
 | 
			
		||||
- [] DB setup
 | 
			
		||||
- [] Google Speech to Text API integration?
 | 
			
		||||
- [] Gebete
 | 
			
		||||
- [ ] just keep it as MD rendering for now?
 | 
			
		||||
- [ ] DB setup
 | 
			
		||||
- [ ] Google Speech to Text API integration?
 | 
			
		||||
- [ ] Gebete
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Outside of this sveltekit project but planned to run on the server as well
 | 
			
		||||
#### E-Mail
 | 
			
		||||
- [] emailwiz setup
 | 
			
		||||
- [] fail2ban
 | 
			
		||||
- [ ] emailwiz setup
 | 
			
		||||
- [ ] fail2ban
 | 
			
		||||
 | 
			
		||||
### Dendrite
 | 
			
		||||
- [] setup dendrite
 | 
			
		||||
- [] OAuth? -> Everything OAuth or OpenID?
 | 
			
		||||
- [] Serve some web-frontend -> Just element?
 | 
			
		||||
- [ ] setup dendrite
 | 
			
		||||
- [ ] OAuth? -> Everything OAuth or OpenID?
 | 
			
		||||
- [ ] Serve some web-frontend -> Just element?
 | 
			
		||||
 | 
			
		||||
### Gitea
 | 
			
		||||
- [ ] consistent theming
 | 
			
		||||
 
 | 
			
		||||
@@ -130,7 +130,7 @@ h2{
 | 
			
		||||
	<div id=options class="speech top" hidden>
 | 
			
		||||
			<h2>{username}</h2>
 | 
			
		||||
			<ul>
 | 
			
		||||
				<!--<li><a href="/settings">Einstellungen</a></li>-->
 | 
			
		||||
				<li><a href="/settings" >Einstellungen</a></li>
 | 
			
		||||
				<li><a href="/logout" >Log Out</a></li>
 | 
			
		||||
			</ul>
 | 
			
		||||
		</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ import "$lib/css/form.css"
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<form action="?/register" method=POST>
 | 
			
		||||
	<h1>Regrieren</h1>
 | 
			
		||||
	<h1>Registrieren</h1>
 | 
			
		||||
	<label>
 | 
			
		||||
		Username
 | 
			
		||||
		<input type="text" name="username" required>
 | 
			
		||||
 
 | 
			
		||||
@@ -10,36 +10,26 @@ export const load: PageServerLoad = async ({ locals }) => {
 | 
			
		||||
 | 
			
		||||
export const actions: Actions = {
 | 
			
		||||
	change_password: async (event) => {
 | 
			
		||||
		const data = await event.fetch.request.formData()
 | 
			
		||||
 | 
			
		||||
	},
 | 
			
		||||
	login: async (event) => {
 | 
			
		||||
    console.log("Changin password")
 | 
			
		||||
		const data = await event.request.formData()
 | 
			
		||||
 		const res = await event.fetch('/api/user/login',
 | 
			
		||||
 		const res = await event.fetch('/api/user/change_pw',
 | 
			
		||||
	    		{method: 'POST',
 | 
			
		||||
			body: JSON.stringify({
 | 
			
		||||
				username: data.get('username'),
 | 
			
		||||
				password: data.get('password'),
 | 
			
		||||
        username:     data.get('username'),
 | 
			
		||||
				new_password: data.get('new_password'),
 | 
			
		||||
				new_password_rep: data.get('new_password_rep'),
 | 
			
		||||
				old_password: data.get('old_password'),
 | 
			
		||||
			}),
 | 
			
		||||
      headers: {
 | 
			
		||||
        credentials: 'include',
 | 
			
		||||
      }
 | 
			
		||||
			})
 | 
			
		||||
			}
 | 
			
		||||
			)
 | 
			
		||||
	    	const jwt = await res.json()
 | 
			
		||||
		if(res.ok){
 | 
			
		||||
		event.cookies.set("UserSession", jwt, {
 | 
			
		||||
			path: "/",
 | 
			
		||||
			httpOnly: true,
 | 
			
		||||
			sameSite: "strict",
 | 
			
		||||
			secure: process.env.NODE_ENV === "production",
 | 
			
		||||
			maxAge: 60 * 60 * 24 * 7, // 1 week
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		throw redirect(303, "/")
 | 
			
		||||
		}
 | 
			
		||||
		else{
 | 
			
		||||
			throw error(401, jwt.message)
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	logout: async () => {
 | 
			
		||||
		throw redirect(303, "/logout")
 | 
			
		||||
	},
 | 
			
		||||
    if(res.ok){
 | 
			
		||||
      console.log("OK response")
 | 
			
		||||
    }
 | 
			
		||||
    else{
 | 
			
		||||
      const item = await res.json()
 | 
			
		||||
      throw error(401, item.message) 
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +1,56 @@
 | 
			
		||||
<script>
 | 
			
		||||
  import {enhance} from '$app/forms';
 | 
			
		||||
	export let data
 | 
			
		||||
  let password;
 | 
			
		||||
	const admin = data.user?.access.includes('admin') ?? false
 | 
			
		||||
  import "$lib/css/form.css"
 | 
			
		||||
</script>
 | 
			
		||||
<style>
 | 
			
		||||
input:invalid + div{
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
input:valid + div{
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  color: green;
 | 
			
		||||
  bottom: 0.25rem;
 | 
			
		||||
  right: -0.25rem;
 | 
			
		||||
  font-size: 1.5rem;
 | 
			
		||||
  width: 1em;
 | 
			
		||||
  height: 1em;
 | 
			
		||||
}
 | 
			
		||||
form label,
 | 
			
		||||
form label input
 | 
			
		||||
{
 | 
			
		||||
  position: relative;
 | 
			
		||||
	display: block;
 | 
			
		||||
}
 | 
			
		||||
input.hide{
 | 
			
		||||
  display:none;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
<section>
 | 
			
		||||
	<h2>Change Profile pictures</h2>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
<section>
 | 
			
		||||
	<h2>Change password</h2>
 | 
			
		||||
	<form>
 | 
			
		||||
  <form action="?/change_password" method=POST use:enhance>
 | 
			
		||||
	<h2>Passwort ändern</h2>
 | 
			
		||||
    <input type="text" bind:value={data.user.username} class=hide name="username" required> 
 | 
			
		||||
		<label>
 | 
			
		||||
		Altes Passwort:
 | 
			
		||||
			<input type="password" >
 | 
			
		||||
			<input type="password" name="old_password" required>
 | 
			
		||||
		</label>
 | 
			
		||||
		<label>
 | 
			
		||||
		Neues Passwort:
 | 
			
		||||
			<input type="password" >
 | 
			
		||||
			<input type="password" name="new_password" required bind:value={password} minlength=10>
 | 
			
		||||
      <div>✔️</div>
 | 
			
		||||
		</label>
 | 
			
		||||
		<label>
 | 
			
		||||
		Neues Passwort wiederholen:
 | 
			
		||||
			<input type="password" >
 | 
			
		||||
			<input type="password" name="new_password_rep" required pattern={password}>
 | 
			
		||||
      <div>✔️</div>
 | 
			
		||||
		</label>
 | 
			
		||||
    <button type="submit">Ändern</button>
 | 
			
		||||
	</form>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
			
		||||
import { error } from '@sveltejs/kit';
 | 
			
		||||
import { hash }  from 'argon2';
 | 
			
		||||
import { verify }  from 'argon2';
 | 
			
		||||
import { hashPassword } from '$lib/js/hashPassword'
 | 
			
		||||
import {randomBytes} from 'crypto'
 | 
			
		||||
 | 
			
		||||
import { PEPPER } from '$env/static/private';
 | 
			
		||||
 | 
			
		||||
@@ -10,31 +12,25 @@ import { dbConnect, dbDisconnect } from '../../../../utils/db';
 | 
			
		||||
// header: use for bearer token for now
 | 
			
		||||
// recipe json in body
 | 
			
		||||
export const POST: RequestHandler = async ({request}) => {
 | 
			
		||||
	const {username, old_password, new_password} = await request.json()
 | 
			
		||||
	const {username, old_password, new_password, new_password_rep} = await request.json()
 | 
			
		||||
  if(new_password != new_password_rep){
 | 
			
		||||
    throw error(400, 'new passwords do not match!')
 | 
			
		||||
  }
 | 
			
		||||
	await dbConnect();
 | 
			
		||||
	const salt = await User.findOne({username: username}, 'salt');
 | 
			
		||||
	const pass_hash =  await hashPassword(old_password + PEPPER, salt)
 | 
			
		||||
	try{
 | 
			
		||||
		await User.updateOne({
 | 
			
		||||
				username: username,
 | 
			
		||||
				pass_hash: pass_hash,
 | 
			
		||||
		})
 | 
			
		||||
	}catch(e){
 | 
			
		||||
		await dbDisconnect();
 | 
			
		||||
		throw error(400, e);
 | 
			
		||||
	}
 | 
			
		||||
	await dbDisconnect();
 | 
			
		||||
	return new Response(JSON.stringify({message: "User added successfully"}),
 | 
			
		||||
			    	{status: 200}
 | 
			
		||||
		);
 | 
			
		||||
	const user = await User.findOne({username: username});
 | 
			
		||||
  console.log("Found user:", user)
 | 
			
		||||
  const isMatch = await verify(user.pass_hash, old_password + PEPPER, {salt: user.salt})
 | 
			
		||||
  console.log("isMatch:", isMatch)
 | 
			
		||||
  if(isMatch){
 | 
			
		||||
		const salt = randomBytes(32).toString('hex'); // Generate a random salt
 | 
			
		||||
    const pass_hash = await hashPassword(new_password + PEPPER, salt)
 | 
			
		||||
    await User.findOneAndUpdate({username: username}, {pass_hash: pass_hash, salt: salt})
 | 
			
		||||
    await dbDisconnect()
 | 
			
		||||
	  return new Response(JSON.stringify({message: "Password updated successfully"}),
 | 
			
		||||
                        {status: 200})
 | 
			
		||||
  }
 | 
			
		||||
  else{
 | 
			
		||||
	  await dbDisconnect();
 | 
			
		||||
    throw error(401, "Wrong old password")
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
async function hashPassword(password, salt) {
 | 
			
		||||
  try {
 | 
			
		||||
    const hashedPassword = await hash(password, salt); // Hash the password with the salt and pepper
 | 
			
		||||
    return hashedPassword;
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    console.error('Error hashing password:', error);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user