Compare commits
55 Commits
022d727394
...
master
Author | SHA1 | Date | |
---|---|---|---|
dd71832b10
|
|||
ce7a542408
|
|||
86225d3237
|
|||
18a5241c1e
|
|||
17a5d6155d
|
|||
15bf4fd922
|
|||
aab1f7da9a
|
|||
367ea7a17e
|
|||
154a8f5efe
|
|||
87768b539f
|
|||
470a74099f
|
|||
1dd47824c7
|
|||
176ffae32c
|
|||
2523ca3d31
|
|||
c82b3334c3
|
|||
b1e05888c9
|
|||
b6e61caa29
|
|||
aeee16078d
|
|||
a1460f5ee3
|
|||
c10f622a78
|
|||
78925d287c
|
|||
a5020be145
|
|||
a1d7420d09
|
|||
8d50e84488
|
|||
632be44fe8
|
|||
dda25edd4b
|
|||
3db9f01e1b
|
|||
b71a7072e7
|
|||
5ee48fa733
|
|||
abddf4b201
|
|||
b4dc4d194f
|
|||
687063f216
|
|||
0bfbb6da10
|
|||
8b5e089792
|
|||
bda44e4647
|
|||
68973dbec7
|
|||
5cf21c7d75
|
|||
c22a7f0e99
|
|||
4fdfacd7be
|
|||
db391bc383
|
|||
2b6499e602
|
|||
c21fbc7f1e
|
|||
4c5826e1b5
|
|||
e3680da1ad
|
|||
cf79f75a5d
|
|||
794817f69d
|
|||
e556e65707
|
|||
2b18176310
|
|||
0bf8d50f1e
|
|||
8e9ca091ef
|
|||
d5228aab60
|
|||
bd5fdbd7c3
|
|||
bb4383f212
|
|||
71aabfb9ba
|
|||
e7944b9aa0
|
33
.gitea/workflows/upload.yml
Normal file
33
.gitea/workflows/upload.yml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
# Controls when the action will run.
|
||||||
|
on:
|
||||||
|
# Triggers the workflow on push to master (including merged PRs)
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||||
|
jobs:
|
||||||
|
# This workflow contains a single job called "build"
|
||||||
|
update:
|
||||||
|
# The type of runner that the job will run on
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||||
|
steps:
|
||||||
|
- name: Updating website.
|
||||||
|
uses: appleboy/ssh-action@master
|
||||||
|
with:
|
||||||
|
host: bocken.org
|
||||||
|
username: homepage
|
||||||
|
key: ${{ secrets.homepage_ssh }}
|
||||||
|
passphrase: ${{ secrets.homepage_pass }}
|
||||||
|
port: 22
|
||||||
|
script: |
|
||||||
|
cd /usr/share/webapps/homepage
|
||||||
|
git pull --force https://Alexander:${{ secrets.homepage_gitea_token }}@git.bocken.org/Alexander/homepage
|
||||||
|
npm run build
|
||||||
|
sudo systemctl restart homepage.service
|
14
README.md
14
README.md
@ -7,7 +7,7 @@ My own homepage, bocken.org, built with svelte-kit.
|
|||||||
- [ ] Admin user management -> move to authentik via oIDC
|
- [ ] Admin user management -> move to authentik via oIDC
|
||||||
- [x] login to authentik
|
- [x] login to authentik
|
||||||
- [x] only let rezepte_users edit recipes -> currently only letting them log in, should be changed
|
- [x] only let rezepte_users edit recipes -> currently only letting them log in, should be changed
|
||||||
- [ ] get user info from authentik (more than email and name)
|
- [x] get user info from authentik (more than email and name)
|
||||||
- [ ] upload pfp
|
- [ ] upload pfp
|
||||||
- [ ] upload/change pfp
|
- [ ] upload/change pfp
|
||||||
- [x] registration only with minimal permissions
|
- [x] registration only with minimal permissions
|
||||||
@ -23,7 +23,7 @@ My own homepage, bocken.org, built with svelte-kit.
|
|||||||
- [x] verify randomize arrays based on day
|
- [x] verify randomize arrays based on day
|
||||||
- [x] notes for next time
|
- [x] notes for next time
|
||||||
- [ ] refactor, like, a lot
|
- [ ] refactor, like, a lot
|
||||||
- [ ] expose json-ld for recipes https://json-ld.org/ https://github.com/flauschtrud/broccoli
|
- [ ] expose json-ld for recipes https://json-ld.org/ https://schema.org/Recipe
|
||||||
- [ ] reference other recipes in recipe
|
- [ ] reference other recipes in recipe
|
||||||
- [ ] add a link to the recipe
|
- [ ] add a link to the recipe
|
||||||
- [ ] add ingredients to the ingredients list
|
- [ ] add ingredients to the ingredients list
|
||||||
@ -63,7 +63,7 @@ My own homepage, bocken.org, built with svelte-kit.
|
|||||||
#### Gitea
|
#### Gitea
|
||||||
- [ ] consistent theming
|
- [ ] consistent theming
|
||||||
- [x] OpenID Connect
|
- [x] OpenID Connect
|
||||||
- [ ] sane landing page
|
- [x] sane landing page
|
||||||
|
|
||||||
#### Jellyfin
|
#### Jellyfin
|
||||||
- [x] connect to LDAP
|
- [x] connect to LDAP
|
||||||
@ -73,7 +73,7 @@ My own homepage, bocken.org, built with svelte-kit.
|
|||||||
- [x] setup Oauth2proxy -> not necessary, authentik has proxy integrated
|
- [x] setup Oauth2proxy -> not necessary, authentik has proxy integrated
|
||||||
- [x] connect to OIDC using Oauth2proxy (using authentik)
|
- [x] connect to OIDC using Oauth2proxy (using authentik)
|
||||||
- [ ] consistent theming
|
- [ ] consistent theming
|
||||||
- [ ] auto-login if not logged in
|
- [x] auto-login if not logged in
|
||||||
|
|
||||||
#### Jitsi
|
#### Jitsi
|
||||||
- [ ] consistent theming
|
- [ ] consistent theming
|
||||||
@ -89,8 +89,8 @@ My own homepage, bocken.org, built with svelte-kit.
|
|||||||
- [ ] OIDC integration (waiting on upstream)
|
- [ ] OIDC integration (waiting on upstream)
|
||||||
|
|
||||||
#### Nextcloud
|
#### Nextcloud
|
||||||
- [ ] consistent theming
|
- [x] consistent theming
|
||||||
- [ ] collabora integration
|
- [x] collabora integration
|
||||||
|
|
||||||
#### Transmission
|
#### Transmission
|
||||||
- [ ] move behind authentik
|
- [x] move behind authentik
|
||||||
|
2211
package-lock.json
generated
2211
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@ -11,24 +11,21 @@
|
|||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^2.1.0",
|
"@sveltejs/adapter-auto": "^3.0.0",
|
||||||
"@sveltejs/kit": "^1.22.3",
|
"@sveltejs/kit": "^2.0.0",
|
||||||
"svelte": "^3.59.2",
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||||
|
"svelte": "^4.0.0",
|
||||||
"svelte-check": "^3.4.6",
|
"svelte-check": "^3.4.6",
|
||||||
"svelte-preprocess-import-assets": "^1.0.1",
|
"svelte-preprocess-import-assets": "^1.0.1",
|
||||||
"tslib": "^2.6.0",
|
"tslib": "^2.6.0",
|
||||||
"typescript": "^5.1.6",
|
"typescript": "^5.1.6",
|
||||||
"vite": "^4.4.4"
|
"vite": "^5.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/sveltekit": "^0.12.3",
|
"@auth/sveltekit": "^0.14.0",
|
||||||
"@sveltejs/adapter-node": "^1.3.1",
|
"@sveltejs/adapter-node": "^2.0.0",
|
||||||
"argon2": "^0.30.3",
|
|
||||||
"cheerio": "1.0.0-rc.12",
|
"cheerio": "1.0.0-rc.12",
|
||||||
"jsonwebtoken": "^9.0.1",
|
|
||||||
"mongoose": "^7.4.0",
|
"mongoose": "^7.4.0",
|
||||||
"sharp": "^0.32.3",
|
"sharp": "^0.32.3"
|
||||||
"svelte-cookie": "^1.0.1",
|
|
||||||
"sveltekit-oidc": "^0.0.8"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { authenticateUser } from "$lib/js/authenticate"
|
|
||||||
import type { Handle } from "@sveltejs/kit"
|
import type { Handle } from "@sveltejs/kit"
|
||||||
import { redirect } from "@sveltejs/kit"
|
import { redirect } from "@sveltejs/kit"
|
||||||
import { error } from "@sveltejs/kit"
|
import { error } from "@sveltejs/kit"
|
||||||
@ -13,13 +12,13 @@ async function authorization({ event, resolve }) {
|
|||||||
if (event.url.pathname.startsWith('/rezepte/edit') || event.url.pathname.startsWith('/rezepte/add')) {
|
if (event.url.pathname.startsWith('/rezepte/edit') || event.url.pathname.startsWith('/rezepte/add')) {
|
||||||
const session = await event.locals.getSession();
|
const session = await event.locals.getSession();
|
||||||
if (!session) {
|
if (!session) {
|
||||||
throw redirect(303, '/auth/signin');
|
redirect(303, '/auth/signin');
|
||||||
}
|
}
|
||||||
else if (! session.user.groups.includes('rezepte_users')) {
|
else if (! session.user.groups.includes('rezepte_users')) {
|
||||||
// strip last dir from url
|
// strip last dir from url
|
||||||
// TODO: give indication of why access failed
|
// TODO: give indication of why access failed
|
||||||
const new_url = event.url.pathname.split('/').slice(0, -1).join('/');
|
const new_url = event.url.pathname.split('/').slice(0, -1).join('/');
|
||||||
throw redirect(303, new_url);
|
redirect(303, new_url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,11 @@ import "$lib/css/nordtheme.css";
|
|||||||
import "$lib/css/shake.css";
|
import "$lib/css/shake.css";
|
||||||
import "$lib/css/icon.css";
|
import "$lib/css/icon.css";
|
||||||
export let do_margin_right = false;
|
export let do_margin_right = false;
|
||||||
|
// to manually override lazy loading for top cards
|
||||||
|
export let loading_strat : "lazy" | "eager" | undefined;
|
||||||
|
if(loading_strat === undefined){
|
||||||
|
loading_strat = "lazy"
|
||||||
|
}
|
||||||
|
|
||||||
if(icon_override){
|
if(icon_override){
|
||||||
current_month = recipe.season[0]
|
current_month = recipe.season[0]
|
||||||
@ -20,6 +25,7 @@ onMount(() => {
|
|||||||
isloaded = document.querySelector("img")?.complete ? true : false
|
isloaded = document.querySelector("img")?.complete ? true : false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const img_name=recipe.short_name + ".webp?v=" + recipe.dateModified
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.card_anchor{
|
.card_anchor{
|
||||||
@ -44,6 +50,9 @@ onMount(() => {
|
|||||||
background-color: var(--blue);
|
background-color: var(--blue);
|
||||||
box-shadow: 0em 0em 2em 0.1em rgba(0, 0, 0, 0.3);
|
box-shadow: 0em 0em 2em 0.1em rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
.icon{
|
||||||
|
font-family: "Noto Color Emoji", emoji, sans-serif;
|
||||||
|
}
|
||||||
#image{
|
#image{
|
||||||
width: 300px;
|
width: 300px;
|
||||||
height: 255px;
|
height: 255px;
|
||||||
@ -176,11 +185,11 @@ onMount(() => {
|
|||||||
<a class=card_anchor href="/rezepte/{recipe.short_name}" class:search_me={search} data-tags=[{recipe.tags}] >
|
<a class=card_anchor href="/rezepte/{recipe.short_name}" class:search_me={search} data-tags=[{recipe.tags}] >
|
||||||
<div class="card" class:margin_right={do_margin_right}>
|
<div class="card" class:margin_right={do_margin_right}>
|
||||||
<div class=div_div_image >
|
<div class=div_div_image >
|
||||||
<div class=div_image style="background-image:url({'https://bocken.org/static/rezepte/placeholder/' + recipe.short_name + '.webp'})">
|
<div class=div_image style="background-image:url(https://bocken.org/static/rezepte/placeholder/{img_name})">
|
||||||
<noscript>
|
<noscript>
|
||||||
<img id=image class="backdrop_blur" src={'https://bocken.org/static/rezepte/thumb/' + recipe.short_name + '.webp'} loading=lazy alt="{recipe.alt}"/>
|
<img id=image class="backdrop_blur" src="https://bocken.org/static/rezepte/thumb/{img_name}" loading={loading_strat} alt="{recipe.alt}"/>
|
||||||
</noscript>
|
</noscript>
|
||||||
<img class:blur={!isloaded} id=image class="backdrop_blur" src={'https://bocken.org/static/rezepte/thumb/' + recipe.short_name + '.webp'} loading=lazy alt="{recipe.alt}" on:load={() => isloaded=true}/>
|
<img class:blur={!isloaded} id=image class="backdrop_blur" src={'https://bocken.org/static/rezepte/thumb/' + recipe.short_name + '.webp'} loading={loading_strat} alt="{recipe.alt}" on:load={() => isloaded=true}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if icon_override || recipe.season.includes(current_month)}
|
{#if icon_override || recipe.season.includes(current_month)}
|
||||||
@ -190,7 +199,7 @@ onMount(() => {
|
|||||||
<a class=category href="/rezepte/category/{recipe.category}" >{recipe.category}</a>
|
<a class=category href="/rezepte/category/{recipe.category}" >{recipe.category}</a>
|
||||||
<div>
|
<div>
|
||||||
<div class=name>{@html recipe.name}</div>
|
<div class=name>{@html recipe.name}</div>
|
||||||
<div class=description>{recipe.description}</div>
|
<div class=description>{@html recipe.description}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class=tags>
|
<div class=tags>
|
||||||
{#each recipe.tags as tag}
|
{#each recipe.tags as tag}
|
||||||
|
@ -3,11 +3,21 @@
|
|||||||
import Cross from '$lib/assets/icons/Cross.svelte'
|
import Cross from '$lib/assets/icons/Cross.svelte'
|
||||||
import "$lib/css/shake.css"
|
import "$lib/css/shake.css"
|
||||||
import "$lib/css/icon.css"
|
import "$lib/css/icon.css"
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
// all data shared with rest of page in card_data
|
// all data shared with rest of page in card_data
|
||||||
export let card_data
|
export let card_data
|
||||||
export let image_preview_url
|
export let image_preview_url
|
||||||
|
|
||||||
|
onMount( () => {
|
||||||
|
fetch(image_preview_url, { method: 'HEAD' })
|
||||||
|
.then(response => {
|
||||||
|
if(response.redirected){
|
||||||
|
image_preview_url = ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
import { img } from '$lib/js/img_store';
|
import { img } from '$lib/js/img_store';
|
||||||
|
|
||||||
if(!card_data.tags){
|
if(!card_data.tags){
|
||||||
|
@ -413,7 +413,7 @@ h3{
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class=list_wrapper>
|
<div class=list_wrapper >
|
||||||
<h4>Portionen:</h4>
|
<h4>Portionen:</h4>
|
||||||
<p contenteditable type="text" bind:innerText={portions_local} on:blur={set_portions}></p>
|
<p contenteditable type="text" bind:innerText={portions_local} on:blur={set_portions}></p>
|
||||||
|
|
||||||
@ -455,9 +455,13 @@ h3{
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div on:click={() => show_modal_edit_ingredient(list_index, ingredient_index)} >{ingredient.amount} {ingredient.unit}</div>
|
<div on:click={() => show_modal_edit_ingredient(list_index, ingredient_index)} >
|
||||||
|
{ingredient.amount} {ingredient.unit}
|
||||||
|
</div>
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class=force_wrap on:click={() => show_modal_edit_ingredient(list_index, ingredient_index)} >{ingredient.name}</div>
|
<div class=force_wrap on:click={() => show_modal_edit_ingredient(list_index, ingredient_index)} >
|
||||||
|
{@html ingredient.name}
|
||||||
|
</div>
|
||||||
<div class=mod_icons><button class="action_button button_subtle" on:click={() => show_modal_edit_ingredient(list_index, ingredient_index)}>
|
<div class=mod_icons><button class="action_button button_subtle" on:click={() => show_modal_edit_ingredient(list_index, ingredient_index)}>
|
||||||
<Pen fill=var(--nord1) height=1em width=1em></Pen></button>
|
<Pen fill=var(--nord1) height=1em width=1em></Pen></button>
|
||||||
<button class="action_button button_subtle" on:click="{() => remove_ingredient(list_index, ingredient_index)}"><Cross fill=var(--nord1) height=1em width=1em></Cross></button></div>
|
<button class="action_button button_subtle" on:click="{() => remove_ingredient(list_index, ingredient_index)}"><Cross fill=var(--nord1) height=1em width=1em></Cross></button></div>
|
||||||
|
@ -509,7 +509,9 @@ h3{
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div on:click={() => show_modal_edit_step(list_index, step_index)}>{step}</div>
|
<div on:click={() => show_modal_edit_step(list_index, step_index)}>
|
||||||
|
{@html step}
|
||||||
|
</div>
|
||||||
<div><button class="action_button button_subtle" on:click={() => show_modal_edit_step(list_index, step_index)}>
|
<div><button class="action_button button_subtle" on:click={() => show_modal_edit_step(list_index, step_index)}>
|
||||||
<Pen fill=var(--nord1)></Pen>
|
<Pen fill=var(--nord1)></Pen>
|
||||||
</button>
|
</button>
|
||||||
|
@ -19,23 +19,6 @@ link_els.forEach((el) => {
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
:global(*){
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
:global(body){
|
|
||||||
margin:0;
|
|
||||||
padding:0;
|
|
||||||
background-color: #fbf9f3;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:global(body){
|
|
||||||
color: white;
|
|
||||||
background-color: var(--background-dark);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nav{
|
nav{
|
||||||
position: sticky;
|
position: sticky;
|
||||||
background-color: var(--nord0);
|
background-color: var(--nord0);
|
||||||
@ -46,6 +29,7 @@ nav{
|
|||||||
justify-content: space-between !important;
|
justify-content: space-between !important;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-shadow: 0 1em 1rem 0rem rgba(0,0,0,0.4);
|
box-shadow: 0 1em 1rem 0rem rgba(0,0,0,0.4);
|
||||||
|
height: 4rem;
|
||||||
}
|
}
|
||||||
nav[hidden]{
|
nav[hidden]{
|
||||||
display:block;
|
display:block;
|
||||||
@ -97,16 +81,16 @@ nav[hidden]{
|
|||||||
padding-inline: 0.5rem;
|
padding-inline: 0.5rem;
|
||||||
}
|
}
|
||||||
:global(svg.symbol){
|
:global(svg.symbol){
|
||||||
height: 3.5rem;
|
height: 4rem;
|
||||||
width: 3.5rem;
|
width: 4rem;
|
||||||
border-radius: 10000px;
|
border-radius: 10000px;
|
||||||
}
|
}
|
||||||
:global(a:has(svg.symbol)){
|
/*:global(a:has(svg.symbol)){
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
width: 3.5rem;
|
width: 4rem;
|
||||||
height: 3.5rem;
|
height: 4rem;
|
||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
}
|
}*/
|
||||||
.wrapper{
|
.wrapper{
|
||||||
display:flex;
|
display:flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -136,8 +120,8 @@ footer{
|
|||||||
background-color: unset;
|
background-color: unset;
|
||||||
display: block;
|
display: block;
|
||||||
fill: white;
|
fill: white;
|
||||||
margin-inline: 1rem;
|
margin-inline: 0.5rem;
|
||||||
width: 2.5rem;
|
width: 2rem;
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
}
|
}
|
||||||
.nav_button svg{
|
.nav_button svg{
|
||||||
@ -197,7 +181,7 @@ footer{
|
|||||||
<div>
|
<div>
|
||||||
<div class=button_wrapper>
|
<div class=button_wrapper>
|
||||||
<a href="/"><Symbol></Symbol></a>
|
<a href="/"><Symbol></Symbol></a>
|
||||||
<button class=nav_button on:click={() => {toggle_sidebar()}}><svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></button>
|
<button class=nav_button on:click={() => {toggle_sidebar()}}><svg xmlns="http://www.w3.org/2000/svg" height="0.5em" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></button>
|
||||||
</div>
|
</div>
|
||||||
<nav hidden class=nav_site>
|
<nav hidden class=nav_site>
|
||||||
<a class=entry href="/"><Symbol></Symbol></a>
|
<a class=entry href="/"><Symbol></Symbol></a>
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
a{
|
a{
|
||||||
|
font-family: "Noto Color Emoji", emoji;
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
a{
|
a{
|
||||||
|
font-family: "Noto Color Emoji", emoji, sans-serif;
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import { onNavigate } from "$app/navigation";
|
||||||
export let data
|
export let data
|
||||||
let multiplier = 1
|
let multiplier;
|
||||||
let custom_mul = "…"
|
let custom_mul = "…"
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
@ -9,6 +10,10 @@ onMount(() => {
|
|||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
multiplier = urlParams.get('multiplier') || 1;
|
multiplier = urlParams.get('multiplier') || 1;
|
||||||
})
|
})
|
||||||
|
onNavigate(() => {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
multiplier = urlParams.get('multiplier') || 1;
|
||||||
|
})
|
||||||
|
|
||||||
function convertFloatsToFractions(inputString) {
|
function convertFloatsToFractions(inputString) {
|
||||||
// Split the input string into individual words
|
// Split the input string into individual words
|
||||||
@ -227,7 +232,7 @@ span
|
|||||||
{/if}
|
{/if}
|
||||||
<div class=ingredients_grid>
|
<div class=ingredients_grid>
|
||||||
{#each list.list as item}
|
{#each list.list as item}
|
||||||
<div class=amount>{@html adjust_amount(item.amount, multiplier)} {item.unit}</div><div class=name>{@html item.name.replace("{{multiplier}}", multiplier)}</div>
|
<div class=amount>{@html adjust_amount(item.amount, multiplier)} {item.unit}</div><div class=name>{@html item.name.replace("{{multiplier}}", multiplier * item.amount)}</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
}
|
}
|
||||||
svg{
|
svg{
|
||||||
transition: 100ms;
|
transition: 100ms;
|
||||||
|
height: 3em;
|
||||||
}
|
}
|
||||||
svg:hover,
|
svg:hover,
|
||||||
svg:focus-visible
|
svg:focus-visible
|
||||||
@ -29,14 +30,9 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
width="calc(45.742325px/ 1.3)"
|
|
||||||
height="calc(80.310539px / 1.3)"
|
|
||||||
viewBox="0 0 45.742326 80.310541"
|
viewBox="0 0 45.742326 80.310541"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<g class=stroke
|
<g class=stroke
|
||||||
id="branches"
|
id="branches"
|
||||||
transform="translate(-42.033271,-37.145192)" >
|
transform="translate(-42.033271,-37.145192)" >
|
||||||
|
@ -2,15 +2,23 @@
|
|||||||
export let src
|
export let src
|
||||||
export let placeholder_src
|
export let placeholder_src
|
||||||
let isloaded=false
|
let isloaded=false
|
||||||
|
let isredirected=false
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const el = document.querySelector("img")
|
const el = document.querySelector("img")
|
||||||
if(el.complete){
|
if(el.complete){
|
||||||
isloaded = true
|
isloaded = true
|
||||||
}
|
}
|
||||||
|
fetch(src, { method: 'HEAD' })
|
||||||
|
.then(response => {
|
||||||
|
isredirected = response.redirected
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function show_dialog_img(){
|
function show_dialog_img(){
|
||||||
|
if(isredirected){
|
||||||
|
return
|
||||||
|
}
|
||||||
if(document.querySelector("img").complete){
|
if(document.querySelector("img").complete){
|
||||||
document.querySelector("#img_carousel").showModal();
|
document.querySelector("#img_carousel").showModal();
|
||||||
}
|
}
|
||||||
@ -160,11 +168,12 @@ dialog button{
|
|||||||
.zoom-in{
|
.zoom-in{
|
||||||
cursor: zoom-in;
|
cursor: zoom-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<figure class="image-container">
|
<figure class="image-container">
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class:zoom-in={isloaded} on:click={show_dialog_img}>
|
<div class:zoom-in={isloaded && !isredirected} on:click={show_dialog_img}>
|
||||||
<div class=placeholder style="background-image:url({placeholder_src})" >
|
<div class=placeholder style="background-image:url({placeholder_src})" >
|
||||||
<div class=placeholder_blur>
|
<div class=placeholder_blur>
|
||||||
<img class:unblur={isloaded} id=image {src} on:load={() => {isloaded=true}} alt=""/>
|
<img class:unblur={isloaded} id=image {src} on:load={() => {isloaded=true}} alt=""/>
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
export let username;
|
export let user;
|
||||||
|
|
||||||
function toggle_options(){
|
function toggle_options(){
|
||||||
const el = document.querySelector("#options")
|
const el = document.querySelector("#options")
|
||||||
el.hidden = !el.hidden
|
el.hidden = !el.hidden
|
||||||
}
|
}
|
||||||
let src="https://bocken.org/static/user/thumb/" + username + ".webp"
|
|
||||||
onMount( () => {
|
onMount( () => {
|
||||||
document.addEventListener("click", (e) => {
|
document.addEventListener("click", (e) => {
|
||||||
const el = document.querySelector("#button")
|
const el = document.querySelector("#button")
|
||||||
@ -101,6 +102,12 @@
|
|||||||
/* (B2) BOTTOM "CALLOUT TAIL" */
|
/* (B2) BOTTOM "CALLOUT TAIL" */
|
||||||
h2{
|
h2{
|
||||||
margin-block: 0;
|
margin-block: 0;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
h2 + p{
|
||||||
|
padding-top: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
@media screen and (max-width: 800px){
|
@media screen and (max-width: 800px){
|
||||||
#options{
|
#options{
|
||||||
@ -125,10 +132,11 @@ h2{
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
{#if username}
|
{#if user}
|
||||||
<button on:click={toggle_options} style="background-image: url({src})" id=button>
|
<button on:click={toggle_options} style="background-image: url(https://bocken.org/static/user/thumb/{user.nickname}.webp)" id=button>
|
||||||
<div id=options class="speech top" hidden>
|
<div id=options class="speech top" hidden>
|
||||||
<h2>{username}</h2>
|
<h2>{user.name}</h2>
|
||||||
|
<p>({user.nickname})</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://sso.bocken.org/if/user/#/settings" >Einstellungen</a></li>
|
<li><a href="https://sso.bocken.org/if/user/#/settings" >Einstellungen</a></li>
|
||||||
<li><a href="/auth/signout" >Log Out</a></li>
|
<li><a href="/auth/signout" >Log Out</a></li>
|
||||||
|
@ -27,3 +27,28 @@
|
|||||||
--background-dark: #21201b;
|
--background-dark: #21201b;
|
||||||
--font-default-dark: #ffffff;
|
--font-default-dark: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a:not(:visited){
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited{
|
||||||
|
color: var(--purple);
|
||||||
|
}
|
||||||
|
|
||||||
|
*{
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: Helvetica, Arial, "Noto Sans", sans-serif
|
||||||
|
}
|
||||||
|
body{
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
background-color: #fbf9f3;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body{
|
||||||
|
color: white;
|
||||||
|
background-color: var(--background-dark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
import type { RequestEvent } from "@sveltejs/kit";
|
|
||||||
import { COOKIE_SECRET } from "$env/static/private";
|
|
||||||
import pkg from 'jsonwebtoken';
|
|
||||||
const { verify } = pkg;
|
|
||||||
import { error } from "@sveltejs/kit";
|
|
||||||
import { dbConnect, dbDisconnect } from "../../utils/db";
|
|
||||||
import { User } from "../../models/User";;
|
|
||||||
|
|
||||||
export async function authenticateUser(cookies){
|
|
||||||
// Set your master secret key (replace with your own secret)
|
|
||||||
const masterSecret = COOKIE_SECRET;
|
|
||||||
const secretKey = masterSecret
|
|
||||||
let decoded
|
|
||||||
try{
|
|
||||||
const cookie : string = cookies.get("UserSession")
|
|
||||||
if(cookie){
|
|
||||||
decoded = await verify(cookie, secretKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch(e){
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if(decoded){
|
|
||||||
await dbConnect()
|
|
||||||
let res = await User.findOne({username: decoded.username}, 'access').lean();
|
|
||||||
await dbDisconnect()
|
|
||||||
if(!res){
|
|
||||||
throw error(404, "User for this Cookie does no longer exist")
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
username: decoded.username,
|
|
||||||
access: res.access,
|
|
||||||
_id: res._id.toString(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
import type { RequestEvent } from "@sveltejs/kit";
|
|
||||||
import { COOKIE_SECRET } from "$env/static/private";
|
|
||||||
import pkg from 'jsonwebtoken';
|
|
||||||
const { verify } = pkg;
|
|
||||||
import { error } from "@sveltejs/kit";
|
|
||||||
import { dbConnect, dbDisconnect } from "../../utils/db";
|
|
||||||
import { User } from "../../models/User";;
|
|
||||||
|
|
||||||
export async function get_username(cookies){
|
|
||||||
// Set your master secret key (replace with your own secret)
|
|
||||||
const masterSecret = COOKIE_SECRET;
|
|
||||||
const secretKey = masterSecret
|
|
||||||
let decoded
|
|
||||||
try{
|
|
||||||
const cookie : string = cookies.get("UserSession")
|
|
||||||
if(cookie){
|
|
||||||
decoded = await verify(cookie, secretKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch(e){
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if(decoded){
|
|
||||||
return {
|
|
||||||
username: decoded.username,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import { hash } from 'argon2'
|
|
||||||
export 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
|||||||
const time = new Date()
|
|
||||||
const MS_PER_DAY = 86400000
|
const MS_PER_DAY = 86400000
|
||||||
let seed = Math.floor(time.getTime()/MS_PER_DAY)
|
|
||||||
function mulberry32(a) {
|
function mulberry32(a) {
|
||||||
return function() {
|
return function() {
|
||||||
var t = a += 0x6D2B79F5;
|
var t = a += 0x6D2B79F5;
|
||||||
@ -11,6 +9,8 @@ function mulberry32(a) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function rand_array(array){
|
export function rand_array(array){
|
||||||
|
let time = new Date()
|
||||||
|
const seed = Math.floor(time.getTime()/MS_PER_DAY)
|
||||||
let rand = mulberry32(seed)
|
let rand = mulberry32(seed)
|
||||||
array.sort((a,b) => 0.5 - rand())
|
array.sort((a,b) => 0.5 - rand())
|
||||||
return array
|
return array
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
import mongoose from 'mongoose';
|
|
||||||
|
|
||||||
const UserSchema = new mongoose.Schema(
|
|
||||||
{
|
|
||||||
username: {type: String, required: true, unique: true},
|
|
||||||
pass_hash: {type: String, required: true},
|
|
||||||
salt : {type: String, required: true},
|
|
||||||
access: [String], //rezepte, flims, abrechnung, ...
|
|
||||||
}, {timestamps: true}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const User = mongoose.model("User", UserSchema);
|
|
@ -1,8 +1,6 @@
|
|||||||
import { get_username } from '$lib/js/get_username';;
|
import type { PageServerLoad } from "./$types"
|
||||||
import type { Actions, PageServerLoad } from "./$types"
|
|
||||||
import { error } from "@sveltejs/kit"
|
|
||||||
|
|
||||||
export const load = (async ({cookies, locals}) => {
|
export const load : PageServerLoad = (async ({locals}) => {
|
||||||
return {
|
return {
|
||||||
session: await locals.auth(),
|
session: await locals.auth(),
|
||||||
}
|
}
|
||||||
|
@ -2,23 +2,15 @@
|
|||||||
import Header from '$lib/components/Header.svelte'
|
import Header from '$lib/components/Header.svelte'
|
||||||
import UserHeader from '$lib/components/UserHeader.svelte';
|
import UserHeader from '$lib/components/UserHeader.svelte';
|
||||||
export let data
|
export let data
|
||||||
let username = ""
|
let user;
|
||||||
if(data.session){
|
if(data.session){
|
||||||
username = data.session.user.name
|
user = data.session.user
|
||||||
}
|
}
|
||||||
console.log(data)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Header>
|
<Header>
|
||||||
<ul class=site_header slot=links>
|
<ul class=site_header slot=links>
|
||||||
<li><a href="/rezepte">Rezepte</a></li>
|
|
||||||
<li><a href="/git">Git</a></li>
|
|
||||||
<li><a href="https://stream.bocken.org">Streaming</a></li>
|
|
||||||
<li><a href="/bilder">Bilder</a></li>
|
|
||||||
<li><a href="https://meet.bocken.org">Jitsi</a></li>
|
|
||||||
<li><a href="https://cloud.bocken.org">Cloud</a></li>
|
|
||||||
<li><a href="/searx">Searx</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<UserHeader {username} slot=right_side></UserHeader>
|
<UserHeader {user} slot=right_side></UserHeader>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</Header>
|
</Header>
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
import "$lib/css/nordtheme.css";
|
import "$lib/css/nordtheme.css";
|
||||||
import LinksGrid from "$lib/components/LinksGrid.svelte";
|
import LinksGrid from "$lib/components/LinksGrid.svelte";
|
||||||
export let data;
|
export let data;
|
||||||
import { SignIn, SignOut } from "@auth/sveltekit/components"
|
|
||||||
import { page } from "$app/stores"
|
|
||||||
/*console.log($page)*/
|
|
||||||
/*console.log($page.daja.session.user)*/
|
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.hero{
|
.hero{
|
||||||
@ -52,11 +48,18 @@ section h2{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<svelte:head>
|
||||||
|
<title>Bocken</title>
|
||||||
|
<meta name="description" content="Die persönliche Website von Alexander Bocken" />
|
||||||
|
<meta property="og:image" content="https://bocken.org/static/favicon.png" />
|
||||||
|
<meta property="og:image:secure_url" content="https://bocken.org/favicon.png" />
|
||||||
|
<meta property="og:image:type" content="image/png" />
|
||||||
|
<meta property="og:image:alt" content="Das Familienwappen simplifiziert" />
|
||||||
|
</svelte:head>
|
||||||
{#if ! data.session}
|
{#if ! data.session}
|
||||||
|
|
||||||
<section class=hero>
|
<section class=hero>
|
||||||
<img src="https://bocken.org/static/user/full/Alexander.webp" alt="Smiling Alexander Bocken">
|
<img src="https://bocken.org/static/user/full/alexander.webp" alt="Smiling Alexander Bocken">
|
||||||
<div>
|
<div>
|
||||||
<h1><q>Willkommen auf bocken.org</q></h1>
|
<h1><q>Willkommen auf bocken.org</q></h1>
|
||||||
<p>
|
<p>
|
||||||
@ -64,7 +67,7 @@ section h2{
|
|||||||
Alles ist selbst gehostet bei mir daheim auf einem kleinen Mini-Server (Arch, btw).
|
Alles ist selbst gehostet bei mir daheim auf einem kleinen Mini-Server (Arch, btw).
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Zu empfeheln ist meine stetig wachsende Rezeptsammlung. Dort findest du viele leckere Rezepte, die ich selbst ausprobiert habe und ständig weiterfeilsche.
|
Zu empfehlen ist meine stetig wachsende Rezeptsammlung. Dort findest du viele leckere Rezepte, die ich selbst ausprobiert habe und ständig weiterfeilsche.
|
||||||
Zudem kannst du gerne meine Suchmaschine oder auch Jitsi-instanz für Videokonferenzen nutzen.
|
Zudem kannst du gerne meine Suchmaschine oder auch Jitsi-instanz für Videokonferenzen nutzen.
|
||||||
Einiges ist hinter einem Login versteckt, anderes ist öffentlich zugänglich.
|
Einiges ist hinter einem Login versteckt, anderes ist öffentlich zugänglich.
|
||||||
Wer sich ein bisschen mit Programmieren auskennt, kann auch gerne in meinen Git-Repositories stöbern.
|
Wer sich ein bisschen mit Programmieren auskennt, kann auch gerne in meinen Git-Repositories stöbern.
|
||||||
@ -74,13 +77,14 @@ section h2{
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
|
||||||
<h2>Seiten</h2>
|
<h2>Seiten</h2>
|
||||||
<LinksGrid>
|
<LinksGrid>
|
||||||
<a href="rezepte">
|
<a href="rezepte">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M240 144A96 96 0 1 0 48 144a96 96 0 1 0 192 0zm44.4 32C269.9 240.1 212.5 288 144 288C64.5 288 0 223.5 0 144S64.5 0 144 0c68.5 0 125.9 47.9 140.4 112h71.8c8.8-9.8 21.6-16 35.8-16H496c26.5 0 48 21.5 48 48s-21.5 48-48 48H392c-14.2 0-27-6.2-35.8-16H284.4zM144 80a64 64 0 1 1 0 128 64 64 0 1 1 0-128zM400 240c13.3 0 24 10.7 24 24v8h96c13.3 0 24 10.7 24 24s-10.7 24-24 24H280c-13.3 0-24-10.7-24-24s10.7-24 24-24h96v-8c0-13.3 10.7-24 24-24zM288 464V352H512V464c0 26.5-21.5 48-48 48H336c-26.5 0-48-21.5-48-48zM48 320h80 16 32c26.5 0 48 21.5 48 48s-21.5 48-48 48H160c0 17.7-14.3 32-32 32H64c-17.7 0-32-14.3-32-32V336c0-8.8 7.2-16 16-16zm128 64c8.8 0 16-7.2 16-16s-7.2-16-16-16H160v32h16zM24 464H200c13.3 0 24 10.7 24 24s-10.7 24-24 24H24c-13.3 0-24-10.7-24-24s10.7-24 24-24z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M240 144A96 96 0 1 0 48 144a96 96 0 1 0 192 0zm44.4 32C269.9 240.1 212.5 288 144 288C64.5 288 0 223.5 0 144S64.5 0 144 0c68.5 0 125.9 47.9 140.4 112h71.8c8.8-9.8 21.6-16 35.8-16H496c26.5 0 48 21.5 48 48s-21.5 48-48 48H392c-14.2 0-27-6.2-35.8-16H284.4zM144 80a64 64 0 1 1 0 128 64 64 0 1 1 0-128zM400 240c13.3 0 24 10.7 24 24v8h96c13.3 0 24 10.7 24 24s-10.7 24-24 24H280c-13.3 0-24-10.7-24-24s10.7-24 24-24h96v-8c0-13.3 10.7-24 24-24zM288 464V352H512V464c0 26.5-21.5 48-48 48H336c-26.5 0-48-21.5-48-48zM48 320h80 16 32c26.5 0 48 21.5 48 48s-21.5 48-48 48H160c0 17.7-14.3 32-32 32H64c-17.7 0-32-14.3-32-32V336c0-8.8 7.2-16 16-16zm128 64c8.8 0 16-7.2 16-16s-7.2-16-16-16H160v32h16zM24 464H200c13.3 0 24 10.7 24 24s-10.7 24-24 24H24c-13.3 0-24-10.7-24-24s10.7-24 24-24z"/></svg>
|
||||||
<h3>Rezepte</h3>
|
<h3>Rezepte</h3>
|
||||||
</a>
|
</a>
|
||||||
<a href=git>
|
<a href=https://git.bocken.org>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M392.8 1.2c-17-4.9-34.7 5-39.6 22l-128 448c-4.9 17 5 34.7 22 39.6s34.7-5 39.6-22l128-448c4.9-17-5-34.7-22-39.6zm80.6 120.1c-12.5 12.5-12.5 32.8 0 45.3L562.7 256l-89.4 89.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l112-112c12.5-12.5 12.5-32.8 0-45.3l-112-112c-12.5-12.5-32.8-12.5-45.3 0zm-306.7 0c-12.5-12.5-32.8-12.5-45.3 0l-112 112c-12.5 12.5-12.5 32.8 0 45.3l112 112c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256l89.4-89.4c12.5-12.5 12.5-32.8 0-45.3z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M392.8 1.2c-17-4.9-34.7 5-39.6 22l-128 448c-4.9 17 5 34.7 22 39.6s34.7-5 39.6-22l128-448c4.9-17-5-34.7-22-39.6zm80.6 120.1c-12.5 12.5-12.5 32.8 0 45.3L562.7 256l-89.4 89.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l112-112c12.5-12.5 12.5-32.8 0-45.3l-112-112c-12.5-12.5-32.8-12.5-45.3 0zm-306.7 0c-12.5-12.5-32.8-12.5-45.3 0l-112 112c-12.5 12.5-12.5 32.8 0 45.3l112 112c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256l89.4-89.4c12.5-12.5 12.5-32.8 0-45.3z"/></svg>
|
||||||
<h3>Git</h3>
|
<h3>Git</h3>
|
||||||
</a>
|
</a>
|
||||||
@ -90,7 +94,7 @@ section h2{
|
|||||||
<h3>Streaming</h3>
|
<h3>Streaming</h3>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="/bilder">
|
<a href="https://bilder.bocken.org">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M0 96C0 60.7 28.7 32 64 32H448c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6h96 32H424c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M0 96C0 60.7 28.7 32 64 32H448c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zM323.8 202.5c-4.5-6.6-11.9-10.5-19.8-10.5s-15.4 3.9-19.8 10.5l-87 127.6L170.7 297c-4.6-5.7-11.5-9-18.7-9s-14.2 3.3-18.7 9l-64 80c-5.8 7.2-6.9 17.1-2.9 25.4s12.4 13.6 21.6 13.6h96 32H424c8.9 0 17.1-4.9 21.2-12.8s3.6-17.4-1.4-24.7l-120-176zM112 192a48 48 0 1 0 0-96 48 48 0 1 0 0 96z"/></svg>
|
||||||
<h3>Familienbilder</h3>
|
<h3>Familienbilder</h3>
|
||||||
</a>
|
</a>
|
||||||
@ -105,7 +109,7 @@ section h2{
|
|||||||
<h3>Videokonferenzen</h3>
|
<h3>Videokonferenzen</h3>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="/searx">
|
<a href="https://searx.bocken.org">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/></svg>
|
||||||
<h3>Suchmaschine</h3>
|
<h3>Suchmaschine</h3>
|
||||||
</a>
|
</a>
|
||||||
@ -127,5 +131,27 @@ section h2{
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32V274.7l-73.4-73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l128 128c12.5 12.5 32.8 12.5 45.3 0l128-128c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L288 274.7V32zM64 352c-35.3 0-64 28.7-64 64v32c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V416c0-35.3-28.7-64-64-64H346.5l-45.3 45.3c-25 25-65.5 25-90.5 0L165.5 352H64zm368 56a24 24 0 1 1 0 48 24 24 0 1 1 0-48z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32V274.7l-73.4-73.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l128 128c12.5 12.5 32.8 12.5 45.3 0l128-128c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L288 274.7V32zM64 352c-35.3 0-64 28.7-64 64v32c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V416c0-35.3-28.7-64-64-64H346.5l-45.3 45.3c-25 25-65.5 25-90.5 0L165.5 352H64zm368 56a24 24 0 1 1 0 48 24 24 0 1 1 0-48z"/></svg>
|
||||||
<h3>Transmission</h3>
|
<h3>Transmission</h3>
|
||||||
</a>
|
</a>
|
||||||
|
<!-- instead of redirect_to_docs(), use a normal link with internal checks for data.session -->
|
||||||
|
{#if !data.session}
|
||||||
|
<a href="/auth/signin">
|
||||||
|
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m106 512h300c24.814 0 45-20.186 45-45v-317h-105c-24.814 0-45-20.186-45-45v-105h-195c-24.814 0-45 20.186-45 45v422c0 24.814 20.186 45 45 45zm60-301h180c8.291 0 15 6.709 15 15s-6.709 15-15 15h-180c-8.291 0-15-6.709-15-15s6.709-15 15-15zm0 60h180c8.291 0 15 6.709 15 15s-6.709 15-15 15h-180c-8.291 0-15-6.709-15-15s6.709-15 15-15zm0 60h180c8.291 0 15 6.709 15 15s-6.709 15-15 15h-180c-8.291 0-15-6.709-15-15s6.709-15 15-15zm0 60h120c8.291 0 15 6.709 15 15s-6.709 15-15 15h-120c-8.291 0-15-6.709-15-15s6.709-15 15-15z"/><path d="m346 120h96.211l-111.211-111.211v96.211c0 8.276 6.724 15 15 15z"/></svg>
|
||||||
|
<h3>Dokumente</h3>
|
||||||
|
</a>
|
||||||
|
{:else if data.session.user.groups.includes("paperless_users")}
|
||||||
|
<a href="https://docs.bocken.org">
|
||||||
|
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m106 512h300c24.814 0 45-20.186 45-45v-317h-105c-24.814 0-45-20.186-45-45v-105h-195c-24.814 0-45 20.186-45 45v422c0 24.814 20.186 45 45 45zm60-301h180c8.291 0 15 6.709 15 15s-6.709 15-15 15h-180c-8.291 0-15-6.709-15-15s6.709-15 15-15zm0 60h180c8.291 0 15 6.709 15 15s-6.709 15-15 15h-180c-8.291 0-15-6.709-15-15s6.709-15 15-15zm0 60h180c8.291 0 15 6.709 15 15s-6.709 15-15 15h-180c-8.291 0-15-6.709-15-15s6.709-15 15-15zm0 60h120c8.291 0 15 6.709 15 15s-6.709 15-15 15h-120c-8.291 0-15-6.709-15-15s6.709-15 15-15z"/><path d="m346 120h96.211l-111.211-111.211v96.211c0 8.276 6.724 15 15 15z"/></svg>
|
||||||
|
<h3>Dokumente</h3>
|
||||||
|
</a>
|
||||||
|
{:else if data.session.user.groups.includes("paperless_eltern_users")}
|
||||||
|
<a href="https://dokumente.bocken.org">
|
||||||
|
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m106 512h300c24.814 0 45-20.186 45-45v-317h-105c-24.814 0-45-20.186-45-45v-105h-195c-24.814 0-45 20.186-45 45v422c0 24.814 20.186 45 45 45zm60-301h180c8.291 0 15 6.709 15 15s-6.709 15-15 15h-180c-8.291 0-15-6.709-15-15s6.709-15 15-15zm0 60h180c8.291 0 15 6.709 15 15s-6.709 15-15 15h-180c-8.291 0-15-6.709-15-15s6.709-15 15-15zm0 60h180c8.291 0 15 6.709 15 15s-6.709 15-15 15h-180c-8.291 0-15-6.709-15-15s6.709-15 15-15zm0 60h120c8.291 0 15 6.709 15 15s-6.709 15-15 15h-120c-8.291 0-15-6.709-15-15s6.709-15 15-15z"/><path d="m346 120h96.211l-111.211-111.211v96.211c0 8.276 6.724 15 15 15z"/></svg>
|
||||||
|
<h3>Dokumente</h3>
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<a href=https://audio.bocken.org>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M256 80C149.9 80 62.4 159.4 49.6 262c9.4-3.8 19.6-6 30.4-6c26.5 0 48 21.5 48 48l0 128c0 26.5-21.5 48-48 48c-44.2 0-80-35.8-80-80l0-16 0-48 0-48C0 146.6 114.6 32 256 32s256 114.6 256 256l0 48 0 48 0 16c0 44.2-35.8 80-80 80c-26.5 0-48-21.5-48-48l0-128c0-26.5 21.5-48 48-48c10.8 0 21 2.1 30.4 6C449.6 159.4 362.1 80 256 80z"/></svg>
|
||||||
|
<h3>Hörbücher & Podcasts</h3>
|
||||||
|
</a>
|
||||||
</LinksGrid>
|
</LinksGrid>
|
||||||
</section>
|
</section>
|
||||||
|
@ -6,7 +6,7 @@ import { rand_array } from '$lib/js/randomize';
|
|||||||
|
|
||||||
export const GET: RequestHandler = async ({params}) => {
|
export const GET: RequestHandler = async ({params}) => {
|
||||||
await dbConnect();
|
await dbConnect();
|
||||||
let found_brief = rand_array(await Recipe.find({}, 'name short_name tags category icon description season').lean()) as BriefRecipeType[];
|
let found_brief = rand_array(await Recipe.find({}, 'name short_name tags category icon description season dateModified').lean()) as BriefRecipeType[];
|
||||||
await dbDisconnect();
|
await dbDisconnect();
|
||||||
return json(JSON.parse(JSON.stringify(found_brief)));
|
return json(JSON.parse(JSON.stringify(found_brief)));
|
||||||
};
|
};
|
||||||
|
@ -6,7 +6,7 @@ import { rand_array } from '$lib/js/randomize';
|
|||||||
|
|
||||||
export const GET: RequestHandler = async ({params}) => {
|
export const GET: RequestHandler = async ({params}) => {
|
||||||
await dbConnect();
|
await dbConnect();
|
||||||
let recipes = rand_array(await Recipe.find({category: params.category}, 'name short_name images tags category icon description season').lean()) as BriefRecipeType[];
|
let recipes = rand_array(await Recipe.find({category: params.category}, 'name short_name images tags category icon description season dateModified').lean()) as BriefRecipeType[];
|
||||||
await dbDisconnect();
|
await dbDisconnect();
|
||||||
|
|
||||||
recipes = JSON.parse(JSON.stringify(recipes));
|
recipes = JSON.parse(JSON.stringify(recipes));
|
||||||
|
@ -6,7 +6,7 @@ import { rand_array } from '$lib/js/randomize';
|
|||||||
|
|
||||||
export const GET: RequestHandler = async ({params}) => {
|
export const GET: RequestHandler = async ({params}) => {
|
||||||
await dbConnect();
|
await dbConnect();
|
||||||
let recipes = rand_array(await Recipe.find({icon: params.icon}, 'name short_name images tags category icon description season').lean()) as BriefRecipeType[];
|
let recipes = rand_array(await Recipe.find({icon: params.icon}, 'name short_name images tags category icon description season dateModified').lean()) as BriefRecipeType[];
|
||||||
await dbDisconnect();
|
await dbDisconnect();
|
||||||
|
|
||||||
recipes = JSON.parse(JSON.stringify(recipes));
|
recipes = JSON.parse(JSON.stringify(recipes));
|
||||||
|
@ -6,7 +6,7 @@ import { rand_array } from '$lib/js/randomize';
|
|||||||
|
|
||||||
export const GET: RequestHandler = async ({params}) => {
|
export const GET: RequestHandler = async ({params}) => {
|
||||||
await dbConnect();
|
await dbConnect();
|
||||||
let found_in_season = rand_array(await Recipe.find({season: params.month, icon: {$ne: "🍽️"}}, 'name short_name images tags category icon description season').lean());
|
let found_in_season = rand_array(await Recipe.find({season: params.month, icon: {$ne: "🍽️"}}, 'name short_name images tags category icon description season dateModified').lean());
|
||||||
await dbDisconnect();
|
await dbDisconnect();
|
||||||
found_in_season = JSON.parse(JSON.stringify(found_in_season));
|
found_in_season = JSON.parse(JSON.stringify(found_in_season));
|
||||||
return json(found_in_season);
|
return json(found_in_season);
|
||||||
|
@ -6,7 +6,7 @@ import { rand_array } from '$lib/js/randomize';
|
|||||||
|
|
||||||
export const GET: RequestHandler = async ({params}) => {
|
export const GET: RequestHandler = async ({params}) => {
|
||||||
await dbConnect();
|
await dbConnect();
|
||||||
let recipes = rand_array(await Recipe.find({tags: params.tag}, 'name short_name images tags category icon description season').lean()) as BriefRecipeType[];
|
let recipes = rand_array(await Recipe.find({tags: params.tag}, 'name short_name images tags category icon description season dateModified').lean()) as BriefRecipeType[];
|
||||||
await dbDisconnect();
|
await dbDisconnect();
|
||||||
|
|
||||||
recipes = JSON.parse(JSON.stringify(recipes));
|
recipes = JSON.parse(JSON.stringify(recipes));
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
|
||||||
import { error } from '@sveltejs/kit';
|
|
||||||
|
|
||||||
import { dbConnect, dbDisconnect } from '../../../../../utils/db';
|
|
||||||
import { User } from '../../../../../models/User';
|
|
||||||
import { get_username } from '$lib/js/get_username';
|
|
||||||
|
|
||||||
// header: use for bearer token for now
|
|
||||||
// recipe json in body
|
|
||||||
export const POST: RequestHandler = async ({cookies}) => {
|
|
||||||
const requesting_user = await get_username(cookies)
|
|
||||||
await dbConnect()
|
|
||||||
let res = await User.findOne({username: requesting_user}, 'access').lean()
|
|
||||||
if(!res.access.contains("admin")){
|
|
||||||
await dbDisconnect()
|
|
||||||
throw error(401, {message: "Your user does not have the permissions to do this"})
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
let res = await User.find({}, 'username access').lean()
|
|
||||||
await dbDisconnect()
|
|
||||||
return { res }
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,36 +0,0 @@
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
|
||||||
import { error } from '@sveltejs/kit';
|
|
||||||
import { verify } from 'argon2';
|
|
||||||
import { hashPassword } from '$lib/js/hashPassword'
|
|
||||||
import {randomBytes} from 'crypto'
|
|
||||||
|
|
||||||
import { PEPPER } from '$env/static/private';
|
|
||||||
|
|
||||||
import { User } from '../../../../models/User';
|
|
||||||
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, new_password_rep} = await request.json()
|
|
||||||
if(new_password != new_password_rep){
|
|
||||||
throw error(400, 'new passwords do not match!')
|
|
||||||
}
|
|
||||||
await dbConnect();
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,46 +0,0 @@
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
|
||||||
import { error } from '@sveltejs/kit';
|
|
||||||
import pkg from 'jsonwebtoken';
|
|
||||||
const { sign } = pkg;
|
|
||||||
import { verify} from 'argon2';
|
|
||||||
import { COOKIE_SECRET } from '$env/static/private'
|
|
||||||
import { PEPPER } from '$env/static/private'
|
|
||||||
|
|
||||||
import { dbConnect, dbDisconnect } from '../../../../utils/db';
|
|
||||||
import { User } from '../../../../models/User';
|
|
||||||
|
|
||||||
// header: use for bearer token for now
|
|
||||||
// recipe json in body
|
|
||||||
export const POST: RequestHandler = async ({request}) => {
|
|
||||||
const {username, password} = await request.json()
|
|
||||||
await dbConnect()
|
|
||||||
let res = await User.findOne({username: username}, 'pass_hash salt').lean()
|
|
||||||
await dbDisconnect()
|
|
||||||
if(!res){
|
|
||||||
console.log("NOT FOUND")
|
|
||||||
throw error(401, {message: "wrong password or user does not exist"})
|
|
||||||
}
|
|
||||||
|
|
||||||
const stored_pw = res.pass_hash
|
|
||||||
const salt = res.salt
|
|
||||||
|
|
||||||
const isMatch = await verify(stored_pw, password + PEPPER, {salt})
|
|
||||||
if(!isMatch){
|
|
||||||
throw error(401, {message: "wrong password or user does not exist"})
|
|
||||||
}
|
|
||||||
|
|
||||||
res = await createJWT(username)
|
|
||||||
return new Response(JSON.stringify(res))
|
|
||||||
};
|
|
||||||
|
|
||||||
async function createJWT(username) {
|
|
||||||
const payload = {
|
|
||||||
username: username,
|
|
||||||
};
|
|
||||||
|
|
||||||
const masterSecret = COOKIE_SECRET;
|
|
||||||
const secretKey = masterSecret;
|
|
||||||
const jwt = sign(payload, secretKey);
|
|
||||||
console.log(jwt)
|
|
||||||
return jwt
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
import type { RequestHandler } from '@sveltejs/kit';
|
|
||||||
import { error } from '@sveltejs/kit';
|
|
||||||
import { randomBytes } from 'crypto';
|
|
||||||
import { ALLOW_REGISTRATION } from '$env/static/private';
|
|
||||||
import { PEPPER } from '$env/static/private';
|
|
||||||
import {hashPassword} from '$lib/js/hashPassword'
|
|
||||||
|
|
||||||
import { User } from '../../../../models/User';
|
|
||||||
import { dbConnect, dbDisconnect } from '../../../../utils/db';
|
|
||||||
|
|
||||||
// header: use for bearer token for now
|
|
||||||
// recipe json in body
|
|
||||||
export const POST: RequestHandler = async ({request}) => {
|
|
||||||
if(ALLOW_REGISTRATION){
|
|
||||||
const {username, password} = await request.json()
|
|
||||||
const salt = randomBytes(32).toString('hex'); // Generate a random salt
|
|
||||||
|
|
||||||
const pass_hash = await hashPassword(password + PEPPER, salt)
|
|
||||||
await dbConnect();
|
|
||||||
try{
|
|
||||||
await User.create({
|
|
||||||
username: username,
|
|
||||||
pass_hash: pass_hash,
|
|
||||||
salt: salt,
|
|
||||||
access: [],
|
|
||||||
})
|
|
||||||
}catch(e){
|
|
||||||
await dbDisconnect();
|
|
||||||
throw error(400, e);
|
|
||||||
}
|
|
||||||
await dbDisconnect();
|
|
||||||
return new Response(JSON.stringify({message: "User added successfully"}),
|
|
||||||
{status: 200}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
throw error(401, "user registration currently closed")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
@ -1,7 +1,7 @@
|
|||||||
import { get_username } from '$lib/js/get_username';;
|
import type { PageServerLoad } from "./$types"
|
||||||
import type { Actions, PageServerLoad } from "./$types"
|
|
||||||
import { error } from "@sveltejs/kit"
|
|
||||||
|
|
||||||
export const load = (async ({cookies}) => {
|
export const load : PageServerLoad = (async ({locals}) => {
|
||||||
return { user: await get_username(cookies) }
|
return {
|
||||||
|
session: await locals.auth(),
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@ -70,6 +70,6 @@
|
|||||||
<path d="m377.943 50.035v-35.035c0-8.284-6.716-15-15-15h-76.926c-11.523 0-22.046 4.357-30.017 11.505-7.971-7.148-18.494-11.505-30.018-11.505h-76.926c-8.284 0-15 6.716-15 15v35.035z"/>
|
<path d="m377.943 50.035v-35.035c0-8.284-6.716-15-15-15h-76.926c-11.523 0-22.046 4.357-30.017 11.505-7.971-7.148-18.494-11.505-30.018-11.505h-76.926c-8.284 0-15 6.716-15 15v35.035z"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
<h3>Predigten<h3>
|
<h3>Predigten</h3>
|
||||||
</a>
|
</a>
|
||||||
</LinksGrid>
|
</LinksGrid>
|
||||||
|
@ -463,19 +463,19 @@ Der unten abgebildete Rosenkranz zeigt die aktuellen Gehmeinisse des Tages nach
|
|||||||
</g>
|
</g>
|
||||||
<g id=lbead5>
|
<g id=lbead5>
|
||||||
<circle class=lbead />
|
<circle class=lbead />
|
||||||
<circle class=hitbox onclick="" />
|
<circle class=hitbox onclick="{true}" />
|
||||||
</g>
|
</g>
|
||||||
<g id=lbead4>
|
<g id=lbead4>
|
||||||
<circle class=lbead />
|
<circle class=lbead />
|
||||||
<circle class=hitbox onclick="" />
|
<circle class=hitbox onclick="{true}" />
|
||||||
</g>
|
</g>
|
||||||
<g id=lbead3>
|
<g id=lbead3>
|
||||||
<circle class=lbead />
|
<circle class=lbead />
|
||||||
<circle class=hitbox onclick="" />
|
<circle class=hitbox onclick="{true}" />
|
||||||
</g>
|
</g>
|
||||||
<g id=lbead6>
|
<g id=lbead6>
|
||||||
<circle class=lbead />
|
<circle class=lbead />
|
||||||
<circle class=hitbox onclick="" />
|
<circle class=hitbox onclick="{true}" />
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<g class=beforedecades>
|
<g class=beforedecades>
|
||||||
@ -490,11 +490,11 @@ Der unten abgebildete Rosenkranz zeigt die aktuellen Gehmeinisse des Tages nach
|
|||||||
</g>
|
</g>
|
||||||
<g id=lbead1>
|
<g id=lbead1>
|
||||||
<circle class=lbead />
|
<circle class=lbead />
|
||||||
<circle class=hitbox onclick="" />
|
<circle class=hitbox onclick="{true}" />
|
||||||
</g>
|
</g>
|
||||||
<g id=lbead2>
|
<g id=lbead2>
|
||||||
<circle class=lbead />
|
<circle class=lbead />
|
||||||
<circle class=hitbox onclick="" />
|
<circle class=hitbox onclick="{true}" />
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
@ -511,7 +511,7 @@ Dieser Plan ist wie folgt:
|
|||||||
<div class=table >
|
<div class=table >
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<thead>
|
<tr>
|
||||||
<td>Mo</td>
|
<td>Mo</td>
|
||||||
<td>Di</td>
|
<td>Di</td>
|
||||||
<td>Mi</td>
|
<td>Mi</td>
|
||||||
@ -519,7 +519,7 @@ Dieser Plan ist wie folgt:
|
|||||||
<td>Fr</td>
|
<td>Fr</td>
|
||||||
<td>Sa</td>
|
<td>Sa</td>
|
||||||
<td>So</td>
|
<td>So</td>
|
||||||
</thead>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>freudenreich</td>
|
<td>freudenreich</td>
|
||||||
<td>schmerzhaft</td>
|
<td>schmerzhaft</td>
|
||||||
@ -546,7 +546,7 @@ Der Plan ohne lichtreiche Geheimnisse ist wie folgt:
|
|||||||
<div class=table>
|
<div class=table>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<thead>
|
<tr>
|
||||||
<td>Mo</td>
|
<td>Mo</td>
|
||||||
<td>Di</td>
|
<td>Di</td>
|
||||||
<td>Mi</td>
|
<td>Mi</td>
|
||||||
@ -554,7 +554,7 @@ Der Plan ohne lichtreiche Geheimnisse ist wie folgt:
|
|||||||
<td>Fr</td>
|
<td>Fr</td>
|
||||||
<td>Sa</td>
|
<td>Sa</td>
|
||||||
<td>So</td>
|
<td>So</td>
|
||||||
</thead>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>freudenreich</td>
|
<td>freudenreich</td>
|
||||||
<td>schmerzhaft</td>
|
<td>schmerzhaft</td>
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import type { Actions, PageServerLoad } from "./$types"
|
import type { PageServerLoad } from "./$types"
|
||||||
import { error } from "@sveltejs/kit"
|
|
||||||
|
|
||||||
export const load = (async ({cookies, locals}) => {
|
export const load : PageServerLoad = async ({locals}) => {
|
||||||
return {
|
return {
|
||||||
session: await locals.getSession()
|
session: await locals.auth()
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
import Header from '$lib/components/Header.svelte'
|
import Header from '$lib/components/Header.svelte'
|
||||||
import UserHeader from '$lib/components/UserHeader.svelte';
|
import UserHeader from '$lib/components/UserHeader.svelte';
|
||||||
export let data
|
export let data
|
||||||
let username = ""
|
let user;
|
||||||
if(data.session){
|
if(data.session){
|
||||||
username = data.session.user.name
|
user = data.session.user
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -15,7 +15,8 @@ if(data.session){
|
|||||||
<li><a href="/rezepte/category">Kategorie</a></li>
|
<li><a href="/rezepte/category">Kategorie</a></li>
|
||||||
<li><a href="/rezepte/icon">Icon</a></li>
|
<li><a href="/rezepte/icon">Icon</a></li>
|
||||||
<li><a href="/rezepte/tag">Stichwörter</a></li>
|
<li><a href="/rezepte/tag">Stichwörter</a></li>
|
||||||
|
<li><a href="/rezepte/tips-and-tricks">Tipps</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<UserHeader slot=right_side {username}></UserHeader>
|
<UserHeader slot=right_side {user}></UserHeader>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</Header>
|
</Header>
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
import Search from '$lib/components/Search.svelte';
|
import Search from '$lib/components/Search.svelte';
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
export let current_month = new Date().getMonth() + 1
|
export let current_month = new Date().getMonth() + 1
|
||||||
const categories = ["Hauptspeise", "Nudel", "Brot", "Dessert", "Suppe", "Beilage", "Salat", "Kuchen", "Frühstück", "Sauce", "Zutat", "Getränk", "Aufstrich", "Guetzli", "Unterwegs"]
|
const categories = ["Hauptspeise", "Nudel", "Brot", "Dessert", "Suppe", "Beilage", "Salat", "Kuchen", "Frühstück", "Sauce", "Zutat", "Getränk", "Aufstrich", "Guetzli", "Snack"]
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
h1{
|
h1{
|
||||||
@ -14,6 +14,11 @@ h1{
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
font-size: 4rem;
|
font-size: 4rem;
|
||||||
}
|
}
|
||||||
|
.subheading{
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Bocken Rezepte</title>
|
<title>Bocken Rezepte</title>
|
||||||
@ -25,12 +30,13 @@ h1{
|
|||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<h1>Rezepte</h1>
|
<h1>Rezepte</h1>
|
||||||
|
<p class=subheading>{data.all_brief.length} Rezepte und stetig wachsend...</p>
|
||||||
|
|
||||||
<Search></Search>
|
<Search></Search>
|
||||||
|
|
||||||
<MediaScroller title="In Saison">
|
<MediaScroller title="In Saison">
|
||||||
{#each data.season as recipe}
|
{#each data.season as recipe}
|
||||||
<Card {recipe} {current_month} do_margin_right={true}></Card>
|
<Card {recipe} {current_month} loading_strat={"eager"} do_margin_right={true}></Card>
|
||||||
{/each}
|
{/each}
|
||||||
</MediaScroller>
|
</MediaScroller>
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
let hero_img_src = "https://bocken.org/static/rezepte/full/" + data.short_name + ".webp"
|
let hero_img_src = "https://bocken.org/static/rezepte/full/" + data.short_name + ".webp?v=" + data.dateModified
|
||||||
let placeholder_src = "https://bocken.org/static/rezepte/placeholder/" + data.short_name + ".webp"
|
let placeholder_src = "https://bocken.org/static/rezepte/placeholder/" + data.short_name + ".webp?v=" + data.dateModified
|
||||||
export let months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]
|
export let months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]
|
||||||
function season_intervals() {
|
function season_intervals() {
|
||||||
let interval_arr = []
|
let interval_arr = []
|
||||||
@ -185,6 +185,7 @@ h1{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon{
|
.icon{
|
||||||
|
font-family: "Noto Color Emoji", emoji;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -1em;
|
top: -1em;
|
||||||
right: -0.75em;
|
right: -0.75em;
|
||||||
@ -292,7 +293,9 @@ h4{
|
|||||||
<h4>Saison:</h4>
|
<h4>Saison:</h4>
|
||||||
{#each season_iv as season}
|
{#each season_iv as season}
|
||||||
<a class=tag href="/rezepte/season/{season[0]}">
|
<a class=tag href="/rezepte/season/{season[0]}">
|
||||||
|
{#if season[0]}
|
||||||
{months[season[0] - 1]}
|
{months[season[0] - 1]}
|
||||||
|
{/if}
|
||||||
{#if season[1]}
|
{#if season[1]}
|
||||||
- {months[season[1] - 1]}
|
- {months[season[1] - 1]}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
console.log(img_local)
|
console.log(img_local)
|
||||||
const data = {
|
const data = {
|
||||||
image: img_local,
|
image: img_local,
|
||||||
name: short_name,
|
name: short_name.trim(),
|
||||||
}
|
}
|
||||||
await fetch(`/api/rezepte/img/add`, {
|
await fetch(`/api/rezepte/img/add`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -108,9 +108,9 @@
|
|||||||
recipe: {
|
recipe: {
|
||||||
...card_data,
|
...card_data,
|
||||||
...add_info,
|
...add_info,
|
||||||
images: {mediapath: short_name + '.webp', alt: "", caption: ""}, // TODO
|
images: {mediapath: short_name.trim() + '.webp', alt: "", caption: ""}, // TODO
|
||||||
season: season_local,
|
season: season_local,
|
||||||
short_name,
|
short_name : short_name.trim(),
|
||||||
portions: portions_local,
|
portions: portions_local,
|
||||||
datecreated,
|
datecreated,
|
||||||
datemodified,
|
datemodified,
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
let preamble = data.recipe.preamble
|
let preamble = data.recipe.preamble
|
||||||
let addendum = data.recipe.addendum
|
let addendum = data.recipe.addendum
|
||||||
let image_preview_url="https://bocken.org/static/rezepte/thumb/" + data.recipe.short_name + ".webp"
|
let image_preview_url="https://bocken.org/static/rezepte/thumb/" + data.recipe.short_name + ".webp?v=" + data.recipe.dateModified;
|
||||||
let note = data.recipe.note
|
let note = data.recipe.note
|
||||||
|
|
||||||
import { season } from '$lib/js/season_store';
|
import { season } from '$lib/js/season_store';
|
||||||
@ -160,7 +160,7 @@
|
|||||||
async function upload_img(){
|
async function upload_img(){
|
||||||
const data = {
|
const data = {
|
||||||
image: img_local,
|
image: img_local,
|
||||||
name: short_name,
|
name: short_name.trim(),
|
||||||
}
|
}
|
||||||
const res = await fetch(`/api/rezepte/img/add`, {
|
const res = await fetch(`/api/rezepte/img/add`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -191,7 +191,7 @@
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
old_name: old_short_name,
|
old_name: old_short_name,
|
||||||
new_name: short_name,
|
new_name: short_name.trim(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
if(!res_img.ok){
|
if(!res_img.ok){
|
||||||
@ -208,7 +208,7 @@
|
|||||||
...add_info,
|
...add_info,
|
||||||
images, // TODO
|
images, // TODO
|
||||||
season: season_local,
|
season: season_local,
|
||||||
short_name,
|
short_name: short_name.trim(),
|
||||||
datecreated,
|
datecreated,
|
||||||
portions: portions_local,
|
portions: portions_local,
|
||||||
datemodified,
|
datemodified,
|
||||||
@ -228,7 +228,7 @@
|
|||||||
if(res.ok){
|
if(res.ok){
|
||||||
const url = location.href.split('/');
|
const url = location.href.split('/');
|
||||||
url.splice(url.length -2, 2);
|
url.splice(url.length -2, 2);
|
||||||
url.push(short_name);
|
url.push(short_name.trim());
|
||||||
location.assign(url.join('/'))
|
location.assign(url.join('/'))
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
a{
|
a{
|
||||||
|
font-family: "Noto Color Emoji", emoji, sans-serif;
|
||||||
--padding: 0.5em;
|
--padding: 0.5em;
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
62
src/routes/rezepte/tips-and-tricks/+page.svelte
Normal file
62
src/routes/rezepte/tips-and-tricks/+page.svelte
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { PageData } from './$types';
|
||||||
|
import AddButton from '$lib/components/AddButton.svelte';
|
||||||
|
import Converter from './Converter.svelte';
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
h1{
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
.subheading{
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
.content{
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: var(--nord0);
|
||||||
|
padding: 1rem;
|
||||||
|
margin-block: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<svelte:head>
|
||||||
|
<title>Bocken Rezepte</title>
|
||||||
|
<meta name="description" content="Eine stetig wachsende Ansammlung an Rezepten aus der Bockenschen Küche." />
|
||||||
|
<meta property="og:image" content="https://bocken.org/static/rezepte/thumb/ragu_aus_rindsrippen.webp" />
|
||||||
|
<meta property="og:image:secure_url" content="https://bocken.org/static/rezepte/thumb/ragu_aus_rindsrippen.webp" />
|
||||||
|
<meta property="og:image:type" content="image/webp" />
|
||||||
|
<meta property="og:image:alt" content="Pasta al Ragu mit Linguine" />
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<h1>Tipps & Tricks</h1>
|
||||||
|
|
||||||
|
<div class=content>
|
||||||
|
<h2>Trockenhefe vs. Frischhefe</h2>
|
||||||
|
<Converter></Converter>
|
||||||
|
<p>
|
||||||
|
Frischhefe ist mit Trockenhefe ersetzbar, jedoch muss man ein paar Kleinigkeiten beachten:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
<li>Nur ein Drittel der Menge verwenden.</li>
|
||||||
|
<li>Falls ein kalter Teig zubereitet wird, die Trockenhefe umbedingt zuerst zur Gärprobe in warmer Flüssigkeit (je nach Rezept z.B. Milch oder Wasser) mit einem TL Zucker für ~10 Minuten <q>aufwachen</q> lassen.</li>
|
||||||
|
<li>Generell ist die Meinung das Trockenhefe etwas <q>energischer</q> ist am Anfang der Gärung und etwas langsamer am Ende der Gare. Dementsprechend eventuell die Stock- und Stückgare verkürzen bzw. verlängern.</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=content>
|
||||||
|
<h2>Fensterprobe</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Die Fensterprobe ist eine Methode um den optimalen Knetzustand eines Teiges zu bestimmen.
|
||||||
|
Dazu wird ein kleines, ca. Walnussgrosses Stück Teig zwischen den Fingern auseinandergezogen. Ist der Teig elastisch und reißt nicht bis der Teig so dünn ist, dass man leicht licht durchsehen kann, so ist der Teig optimal verknetet.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Teig lässt sich leichter verkneten wenn er noch trockener ist. Daher lohnt es sich zunächst etwa 10% der Flüssigkeit zurückzuhalten und erst nach und nach zuzugeben nachdem der Teig bereits für einige Minuten geknetet wurde.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<AddButton></AddButton>
|
68
src/routes/rezepte/tips-and-tricks/Converter.svelte
Normal file
68
src/routes/rezepte/tips-and-tricks/Converter.svelte
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<script>
|
||||||
|
class HefeConverter {
|
||||||
|
constructor(trockenhefe = 1) {
|
||||||
|
this._trockenhefe = trockenhefe;
|
||||||
|
this._frischhefe = this._trockenhefe * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
get trockenhefe() {
|
||||||
|
return Math.round(this._trockenhefe * 100) / 100 + "g";
|
||||||
|
}
|
||||||
|
|
||||||
|
set trockenhefe(value) {
|
||||||
|
this._trockenhefe = value.replace(/\D/g, '');
|
||||||
|
this._frischhefe = this._trockenhefe * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
get frischhefe() {
|
||||||
|
return this._frischhefe+"g";
|
||||||
|
}
|
||||||
|
|
||||||
|
set frischhefe(value) {
|
||||||
|
this._frischhefe = value.replace(/\D/g, '');
|
||||||
|
this._trockenhefe = this._frischhefe / 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hefeConverter = new HefeConverter();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.converter_container {
|
||||||
|
width: fit-content;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 2rem;
|
||||||
|
background-color: var(--blue);
|
||||||
|
padding: 2rem;
|
||||||
|
margin-inline: auto;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
width: 5rem;
|
||||||
|
height: 2rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.flex_column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class=converter_container>
|
||||||
|
<div class="flex_column">
|
||||||
|
<label for="trockenhefe">Trockenhefe</label>
|
||||||
|
<input type="text" bind:value={hefeConverter.trockenhefe} min="0" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
=
|
||||||
|
</div>
|
||||||
|
<div class="flex_column">
|
||||||
|
<label for="frischhefe">Frischhefe</label>
|
||||||
|
<input type="text" bind:value={hefeConverter.frischhefe} min="0"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -3,12 +3,6 @@ This file styles jellyfin and can be imported by adding:
|
|||||||
@import url("https://bocken.org/other/jellyfin.css");
|
@import url("https://bocken.org/other/jellyfin.css");
|
||||||
under Server -> General -> Custom CSS Code
|
under Server -> General -> Custom CSS Code
|
||||||
*/
|
*/
|
||||||
/* jellyscrub default: width: 15vw, annoying for smaller screen sizes */
|
|
||||||
.chapterThumbContainer{
|
|
||||||
width: min(80vw, 500px, 50vh);
|
|
||||||
}
|
|
||||||
/* Attempt to change accent color */
|
|
||||||
|
|
||||||
:root{
|
:root{
|
||||||
--nord0: #2E3440;
|
--nord0: #2E3440;
|
||||||
--blue: #5E81AC;
|
--blue: #5E81AC;
|
||||||
@ -86,8 +80,10 @@ border-color: var(--blue);
|
|||||||
.defaultCardBackground3,
|
.defaultCardBackground3,
|
||||||
.defaultCardBackground4,
|
.defaultCardBackground4,
|
||||||
.defaultCardBackground5,
|
.defaultCardBackground5,
|
||||||
.emby-checkbox:checked + span + .checkboxOutline, .itemProgressBarForeground,
|
.emby-checkbox:checked + span + .checkboxOutline,
|
||||||
.taskProgressInner
|
.itemProgressBarForeground,
|
||||||
|
.taskProgressInner,
|
||||||
|
.sliderMarker.watched
|
||||||
{
|
{
|
||||||
background-color: var(--blue) !important;
|
background-color: var(--blue) !important;
|
||||||
}
|
}
|
||||||
@ -131,6 +127,9 @@ scale: 1.05;
|
|||||||
font-size: initial !important;
|
font-size: initial !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.emby-scroller{
|
.backgroundContainer{
|
||||||
overflow: visible !important;
|
background-color: #21201b;
|
||||||
|
}
|
||||||
|
.pageTitleWithDefaultLogo {
|
||||||
|
background-image: url(https://bocken.org/static/css/logos/logo_text_light.png);
|
||||||
}
|
}
|
||||||
|
5
static/robots.txt
Normal file
5
static/robots.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
User-agent: GPTBot
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
User-agent: *
|
||||||
|
Disallow: /static/
|
@ -1,5 +1,5 @@
|
|||||||
import adapter from '@sveltejs/adapter-node';
|
import adapter from '@sveltejs/adapter-node';
|
||||||
import { vitePreprocess } from '@sveltejs/kit/vite';
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
const config = {
|
const config = {
|
||||||
|
Reference in New Issue
Block a user