Compare commits
88 Commits
a3d2e66b5b
...
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
|
|||
022d727394
|
|||
650a6ce1fc
|
|||
3a684a5d5a
|
|||
a781be8d00
|
|||
1929189187
|
|||
d6f8ab9a17
|
|||
4c92c1c43d
|
|||
ee1008eeea
|
|||
6ccdfd51de
|
|||
29893f931d
|
|||
08cc4091b1
|
|||
9d6e160ec8
|
|||
51381c3f3d
|
|||
6baaefcfe8
|
|||
5c009d74fe
|
|||
9ddafaacca
|
|||
1222fe7487
|
|||
61488a8ce9
|
|||
ef78686432
|
|||
177e2c8fca
|
|||
7ec94246f0
|
|||
0f45145119
|
|||
6fc2755d87
|
|||
b047034731
|
|||
18e26790ce
|
|||
27c643ef2b
|
|||
735ce5aecc
|
|||
37d2265a3b
|
|||
b5878390ad
|
|||
0d180cc4f9
|
|||
c4c72bd8f0
|
|||
36e0abb26d
|
|||
65049e49ec
|
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
|
65
README.md
65
README.md
@ -5,12 +5,15 @@ My own homepage, bocken.org, built with svelte-kit.
|
||||
## TODO
|
||||
### General
|
||||
- [ ] Admin user management -> move to authentik via oIDC
|
||||
- [x] login to authentik
|
||||
- [x] only let rezepte_users edit recipes -> currently only letting them log in, should be changed
|
||||
- [x] get user info from authentik (more than email and name)
|
||||
- [ ] upload pfp
|
||||
- [ ] upload/change pfp
|
||||
- [x] registration only with minimal permissions
|
||||
- [ ] logout without /logout page
|
||||
- [ ] preferences page
|
||||
- [x] change password
|
||||
- [ ] fail2ban integration
|
||||
- [x] css dark mode `@media (prefers-color-scheme: dark) {}`
|
||||
- [ ] dark mode toggle
|
||||
|
||||
@ -20,15 +23,30 @@ My own homepage, bocken.org, built with svelte-kit.
|
||||
- [x] verify randomize arrays based on day
|
||||
- [x] notes for next time
|
||||
- [ ] 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
|
||||
- [ ] add a link to the recipe
|
||||
- [ ] add ingredients to the ingredients list
|
||||
- [ ] include steps?
|
||||
- [ ] add favoriting ability when logged in
|
||||
- [ ] favorite button on recipe
|
||||
- [ ] store favorites in DB -> add to user object
|
||||
- [ ] favorite API endpoint (requires auth of user)
|
||||
- [ ] set
|
||||
- [ ] retrieve
|
||||
- [ ] favorite page/MediaScroller
|
||||
- [ ] graceful degradation for JS-less browsers
|
||||
- [ ] use js-only class with display:none and remove it with JS
|
||||
- [ ] disable search -> use form action instead on submit?
|
||||
- [x] do not blur images without js
|
||||
- [x] correct Recipe Card rendering
|
||||
|
||||
|
||||
### Glaube
|
||||
- [ ] just keep it as MD rendering for now?
|
||||
- [ ] DB setup
|
||||
- [ ] just keep it md rendered
|
||||
- [ ] Google Speech to Text API integration?
|
||||
- [ ] Gebete
|
||||
|
||||
|
||||
### Outside of this sveltekit project but planned to run on the server as well
|
||||
- [x] create LDAP and OpenID
|
||||
|
||||
@ -37,15 +55,42 @@ My own homepage, bocken.org, built with svelte-kit.
|
||||
- [x] fail2ban
|
||||
- [ ] LDAP?
|
||||
|
||||
### Dendrite
|
||||
#### Dendrite
|
||||
- [x] setup dendrite
|
||||
- [ ] Connect to LDAP
|
||||
- [ ] Connect to LDAP/OIDC (waiting on upstream)
|
||||
- [x] Serve some web-frontend -> Just element?
|
||||
|
||||
### Gitea
|
||||
#### Gitea
|
||||
- [ ] consistent theming
|
||||
- [ ] LDAP
|
||||
- [x] OpenID Connect
|
||||
- [x] sane landing page
|
||||
|
||||
### Jellyfin
|
||||
#### Jellyfin
|
||||
- [x] connect to LDAP
|
||||
- [x] consitent theming
|
||||
|
||||
#### Webtrees
|
||||
- [x] setup Oauth2proxy -> not necessary, authentik has proxy integrated
|
||||
- [x] connect to OIDC using Oauth2proxy (using authentik)
|
||||
- [ ] consistent theming
|
||||
- [x] auto-login if not logged in
|
||||
|
||||
#### Jitsi
|
||||
- [ ] consistent theming
|
||||
- [ ] move away from docker
|
||||
- [ ] find a way to improve max video quality without jitsi becoming unreliable
|
||||
|
||||
#### Searx
|
||||
- [x] investigate SearxNG as more reliable alternative
|
||||
- [ ] consistent theming
|
||||
|
||||
#### Photoprism
|
||||
- [ ] consistent theming
|
||||
- [ ] OIDC integration (waiting on upstream)
|
||||
|
||||
#### Nextcloud
|
||||
- [x] consistent theming
|
||||
- [x] collabora integration
|
||||
|
||||
#### Transmission
|
||||
- [x] move behind authentik
|
||||
|
2571
package-lock.json
generated
2571
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@ -11,22 +11,21 @@
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^2.1.0",
|
||||
"@sveltejs/kit": "^1.22.3",
|
||||
"svelte": "^3.59.2",
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"svelte": "^4.0.0",
|
||||
"svelte-check": "^3.4.6",
|
||||
"svelte-preprocess-import-assets": "^1.0.1",
|
||||
"tslib": "^2.6.0",
|
||||
"typescript": "^5.1.6",
|
||||
"vite": "^4.4.4"
|
||||
"vite": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sveltejs/adapter-node": "^1.3.1",
|
||||
"argon2": "^0.30.3",
|
||||
"@auth/sveltekit": "^0.14.0",
|
||||
"@sveltejs/adapter-node": "^2.0.0",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"jsonwebtoken": "^9.0.1",
|
||||
"mongoose": "^7.4.0",
|
||||
"sharp": "^0.32.3",
|
||||
"svelte-cookie": "^1.0.1"
|
||||
"sharp": "^0.32.3"
|
||||
}
|
||||
}
|
||||
|
29
src/auth.ts
Normal file
29
src/auth.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { SvelteKitAuth } from "@auth/sveltekit"
|
||||
import Authentik from "@auth/core/providers/authentik"
|
||||
import { AUTHENTIK_ID, AUTHENTIK_SECRET, AUTHENTIK_ISSUER } from "$env/static/private";
|
||||
|
||||
export const { handle, signIn, signOut } = SvelteKitAuth({
|
||||
providers: [
|
||||
Authentik({
|
||||
clientId: AUTHENTIK_ID,
|
||||
clientSecret: AUTHENTIK_SECRET,
|
||||
issuer: AUTHENTIK_ISSUER,
|
||||
})],
|
||||
callbacks: {
|
||||
// this feels like an extremely hacky way to get nickname and groups into the session object
|
||||
// TODO: investigate if there's a better way to do this
|
||||
jwt: async ({token, profile}) => {
|
||||
if(profile){
|
||||
token.nickname = profile.nickname;
|
||||
token.groups = profile.groups;
|
||||
}
|
||||
return token;
|
||||
},
|
||||
session: async ({session, token}) => {
|
||||
session.user.nickname = token.nickname;
|
||||
session.user.groups = token.groups;
|
||||
return session;
|
||||
},
|
||||
|
||||
}
|
||||
})
|
@ -1,28 +1,32 @@
|
||||
import { authenticateUser } from "$lib/js/authenticate"
|
||||
import type { Handle } from "@sveltejs/kit"
|
||||
import { redirect } from "@sveltejs/kit"
|
||||
import { error } from "@sveltejs/kit"
|
||||
import { SvelteKitAuth } from "@auth/sveltekit"
|
||||
import Authentik from "@auth/core/providers/authentik"
|
||||
import { AUTHENTIK_ID, AUTHENTIK_SECRET, AUTHENTIK_ISSUER } from "$env/static/private";
|
||||
import { sequence } from "@sveltejs/kit/hooks"
|
||||
import * as auth from "./auth"
|
||||
|
||||
export const handle : Handle = async({event, resolve}) => {
|
||||
if(event.url.pathname.startsWith('/rezepte/edit') || event.url.pathname.startsWith('/rezepte/add')){
|
||||
event.locals.user = await authenticateUser(event.cookies)
|
||||
if(!event.locals.user){
|
||||
throw redirect(303, "/login")
|
||||
async function authorization({ event, resolve }) {
|
||||
// Protect any routes under /authenticated
|
||||
if (event.url.pathname.startsWith('/rezepte/edit') || event.url.pathname.startsWith('/rezepte/add')) {
|
||||
const session = await event.locals.getSession();
|
||||
if (!session) {
|
||||
redirect(303, '/auth/signin');
|
||||
}
|
||||
else if(!event.locals.user.access.includes("rezepte")){
|
||||
throw error(401, "Your user does not have access to this page")
|
||||
}
|
||||
}
|
||||
else if(event.url.pathname.startsWith('/abrechnung')){
|
||||
event.locals.user = await authenticateUser(event.cookies)
|
||||
if(!event.locals.user){
|
||||
throw redirect(303, "/login")
|
||||
}
|
||||
else if(!event.locals.user.access.includes("abrechnung")){
|
||||
throw error(401, "Your User does not have access to this page")
|
||||
else if (! session.user.groups.includes('rezepte_users')) {
|
||||
// strip last dir from url
|
||||
// TODO: give indication of why access failed
|
||||
const new_url = event.url.pathname.split('/').slice(0, -1).join('/');
|
||||
redirect(303, new_url);
|
||||
}
|
||||
}
|
||||
|
||||
const response = await resolve(event)
|
||||
return response
|
||||
// If the request is still here, just proceed as normally
|
||||
return resolve(event);
|
||||
}
|
||||
|
||||
export const handle: Handle = sequence(
|
||||
auth.handle,
|
||||
authorization
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang='ts'>
|
||||
export let href
|
||||
import "$lib/components/nordtheme.css"
|
||||
import "$lib/css/nordtheme.css"
|
||||
import "$lib/css/action_button.css"
|
||||
</script>
|
||||
|
||||
|
@ -2,9 +2,16 @@
|
||||
export let recipe
|
||||
export let current_month
|
||||
export let icon_override = false;
|
||||
export let search = "search_me"
|
||||
export let search = true;
|
||||
import "$lib/css/nordtheme.css";
|
||||
import "$lib/css/shake.css";
|
||||
import "$lib/css/icon.css";
|
||||
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){
|
||||
current_month = recipe.season[0]
|
||||
@ -13,25 +20,28 @@ if(icon_override){
|
||||
let isloaded = false
|
||||
|
||||
import { onMount } from "svelte";
|
||||
onMount(() => {
|
||||
const el = document.querySelector("img")
|
||||
if(el.complete){
|
||||
isloaded = true
|
||||
}
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
isloaded = document.querySelector("img")?.complete ? true : false
|
||||
})
|
||||
|
||||
const img_name=recipe.short_name + ".webp?v=" + recipe.dateModified
|
||||
</script>
|
||||
<style>
|
||||
.card_anchor{
|
||||
border-radius: 20px;
|
||||
}
|
||||
.card{
|
||||
--card-width: 300px;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
transition: 200ms;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
font-family: sans-serif;
|
||||
cursor: pointer;
|
||||
height: calc(7/4 * var(--card-width)); /* otherwise card is not initialized at correct size and readjusts when populated*/
|
||||
width: var(--card-width);
|
||||
height: 525px;
|
||||
width: 300px;
|
||||
border-radius: 20px;
|
||||
background-size: contain;
|
||||
display: flex;
|
||||
@ -40,19 +50,24 @@ import { onMount } from "svelte";
|
||||
background-color: var(--blue);
|
||||
box-shadow: 0em 0em 2em 0.1em rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.card #image{
|
||||
width: var(--card-width);
|
||||
height: calc(var(--card-width)*0.85);
|
||||
.icon{
|
||||
font-family: "Noto Color Emoji", emoji, sans-serif;
|
||||
}
|
||||
#image{
|
||||
width: 300px;
|
||||
height: 255px;
|
||||
object-fit: cover;
|
||||
transition: 200ms;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.blur{
|
||||
filter: blur(10px);
|
||||
}
|
||||
.unblur{
|
||||
filter: blur(0px) !important;
|
||||
.backdrop_blur{
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
div:has(#image){
|
||||
width: var(--card-width);
|
||||
.div_image,
|
||||
.div_div_image{
|
||||
width: 300px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
@ -60,33 +75,12 @@ div:has(#image){
|
||||
border-top-left-radius: inherit;
|
||||
border-top-right-radius: inherit;
|
||||
}
|
||||
div:has(div #image){
|
||||
height: calc(var(--card-width)*0.85);
|
||||
.div_div_image{
|
||||
height: 255px;
|
||||
position: absolute;
|
||||
width: var(--card-width);
|
||||
width: 300px;
|
||||
top: 0;
|
||||
}
|
||||
@supports(-moz-appearance:none){
|
||||
#image{
|
||||
}
|
||||
#image, #div_image{
|
||||
border-top-left-radius: 20px;
|
||||
border-top-right-radius: 20px;
|
||||
height: 100%;
|
||||
}
|
||||
#div_image{
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
|
||||
}
|
||||
#div_div_image{
|
||||
height: calc(var(--card-width)*0.85);
|
||||
position: absolute;
|
||||
width: var(--card-width);
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.card:hover,
|
||||
.card:focus-within{
|
||||
@ -97,12 +91,12 @@ div:has(div #image){
|
||||
.card:focus{
|
||||
scale: 0.95 0.95;
|
||||
}
|
||||
.card .title {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
.card_title {
|
||||
position: absolute;
|
||||
padding-top: 0.5em;
|
||||
height: 50%;
|
||||
width: 100% ;
|
||||
height: 262.5px;
|
||||
width: 300px;
|
||||
top: 262.5px;
|
||||
border-bottom-left-radius: inherit;
|
||||
border-bottom-right-radius: inherit;
|
||||
display: flex;
|
||||
@ -110,17 +104,17 @@ div:has(div #image){
|
||||
justify-content: space-between;
|
||||
transition: 100ms;
|
||||
}
|
||||
.card .name{
|
||||
.name{
|
||||
font-size: 2em;
|
||||
color: white;
|
||||
padding-inline: 0.5em;
|
||||
padding-block: 0.2em;
|
||||
}
|
||||
.card .description{
|
||||
.description{
|
||||
padding-inline: 1em;
|
||||
color: var(--nord4);
|
||||
}
|
||||
.card .tags{
|
||||
.tags{
|
||||
display: flex;
|
||||
flex-wrap: wrap-reverse;
|
||||
overflow: hidden;
|
||||
@ -130,7 +124,7 @@ div:has(div #image){
|
||||
margin-bottom:0.5em;
|
||||
flex-grow: 0;
|
||||
}
|
||||
.card .tag{
|
||||
.tag{
|
||||
cursor: pointer;
|
||||
text-decoration: unset;
|
||||
background-color: var(--nord4);
|
||||
@ -142,18 +136,18 @@ div:has(div #image){
|
||||
transition: 100ms;
|
||||
box-shadow: 0em 0em 0.2em 0.05em rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.card .tag:hover,
|
||||
.card .tag:focus-visible
|
||||
.tag:hover,
|
||||
.tag:focus-visible
|
||||
{
|
||||
transform: scale(1.04, 1.04);
|
||||
background-color: var(--orange);
|
||||
box-shadow: 0.2em 0.2em 0.2em 0.1em rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.card .tag:focus{
|
||||
.tag:focus{
|
||||
transition: 100ms;
|
||||
scale: 0.9;
|
||||
}
|
||||
.card .title .category{
|
||||
.card_title .category{
|
||||
position: absolute;
|
||||
box-shadow: 0em 0em 1em 0.1em rgba(0, 0, 0, 0.6);
|
||||
text-decoration: none;
|
||||
@ -167,14 +161,14 @@ div:has(div #image){
|
||||
transition: 100ms;
|
||||
|
||||
}
|
||||
.card .title .category:hover,
|
||||
.card .title .category:focus-within
|
||||
.card_title .category:hover,
|
||||
.card_title .category:focus-within
|
||||
{
|
||||
box-shadow: -0.2em 0.2em 1em 0.1em rgba(0, 0, 0, 0.6);
|
||||
background-color: var(--nord3);
|
||||
transform: scale(1.05, 1.05)
|
||||
}
|
||||
.card .category:focus{
|
||||
.category:focus{
|
||||
scale: 0.9 0.9;
|
||||
}
|
||||
|
||||
@ -183,23 +177,29 @@ div:has(div #image){
|
||||
{
|
||||
animation: shake 0.6s;
|
||||
}
|
||||
.margin_right{
|
||||
margin-right: 2em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<a class="card {search}" href="/rezepte/{recipe.short_name}" data-tags=[{recipe.tags}]>
|
||||
<div id=div_div_image >
|
||||
<div id=div_image style="background-image:url({'https://bocken.org/static/rezepte/placeholder/' + recipe.short_name + '.webp'})">
|
||||
<img class:unblur={isloaded} id=image src={'https://bocken.org/static/rezepte/thumb/' + recipe.short_name + '.webp'} loading=lazy alt="{recipe.alt}" on:load={() => isloaded=true}/>
|
||||
</div>
|
||||
<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=div_div_image >
|
||||
<div class=div_image style="background-image:url(https://bocken.org/static/rezepte/placeholder/{img_name})">
|
||||
<noscript>
|
||||
<img id=image class="backdrop_blur" src="https://bocken.org/static/rezepte/thumb/{img_name}" loading={loading_strat} alt="{recipe.alt}"/>
|
||||
</noscript>
|
||||
<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>
|
||||
{#if icon_override || recipe.season.includes(current_month)}
|
||||
<a class=icon href="/rezepte/icon/{recipe.icon}">{recipe.icon}</a>
|
||||
{/if}
|
||||
|
||||
<div class=title>
|
||||
<a class=icon href="/rezepte/icon/{recipe.icon}">{recipe.icon}</a>
|
||||
{/if}
|
||||
<div class="card_title">
|
||||
<a class=category href="/rezepte/category/{recipe.category}" >{recipe.category}</a>
|
||||
<div>
|
||||
<div class=name>{@html recipe.name}</div>
|
||||
<div class=description>{recipe.description}</div>
|
||||
<div class=description>{@html recipe.description}</div>
|
||||
</div>
|
||||
<div class=tags>
|
||||
{#each recipe.tags as tag}
|
||||
@ -207,4 +207,5 @@ div:has(div #image){
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
@ -3,11 +3,21 @@
|
||||
import Cross from '$lib/assets/icons/Cross.svelte'
|
||||
import "$lib/css/shake.css"
|
||||
import "$lib/css/icon.css"
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
// all data shared with rest of page in card_data
|
||||
export let card_data
|
||||
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';
|
||||
|
||||
if(!card_data.tags){
|
||||
|
@ -413,7 +413,7 @@ h3{
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class=list_wrapper>
|
||||
<div class=list_wrapper >
|
||||
<h4>Portionen:</h4>
|
||||
<p contenteditable type="text" bind:innerText={portions_local} on:blur={set_portions}></p>
|
||||
|
||||
@ -455,9 +455,13 @@ h3{
|
||||
</button>
|
||||
</div>
|
||||
<!-- 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 -->
|
||||
<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)}>
|
||||
<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>
|
||||
|
@ -5,7 +5,7 @@ import Cross from '$lib/assets/icons/Cross.svelte'
|
||||
import Plus from '$lib/assets/icons/Plus.svelte'
|
||||
import Check from '$lib/assets/icons/Check.svelte'
|
||||
|
||||
import '$lib/components/nordtheme.css'
|
||||
import '$lib/css/nordtheme.css'
|
||||
import "$lib/css/action_button.css"
|
||||
|
||||
import { do_on_key } from '$lib/components/do_on_key.js'
|
||||
@ -509,7 +509,9 @@ h3{
|
||||
</button>
|
||||
</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)}>
|
||||
<Pen fill=var(--nord1)></Pen>
|
||||
</button>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import "$lib/components/nordtheme.css"
|
||||
import "$lib/css/nordtheme.css"
|
||||
import { onMount } from "svelte";
|
||||
import Symbol from "./Symbol.svelte"
|
||||
|
||||
@ -19,23 +19,6 @@ link_els.forEach((el) => {
|
||||
|
||||
</script>
|
||||
<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{
|
||||
position: sticky;
|
||||
background-color: var(--nord0);
|
||||
@ -45,6 +28,8 @@ nav{
|
||||
flex-direction: row;
|
||||
justify-content: space-between !important;
|
||||
align-items: center;
|
||||
box-shadow: 0 1em 1rem 0rem rgba(0,0,0,0.4);
|
||||
height: 4rem;
|
||||
}
|
||||
nav[hidden]{
|
||||
display:block;
|
||||
@ -96,18 +81,30 @@ nav[hidden]{
|
||||
padding-inline: 0.5rem;
|
||||
}
|
||||
:global(svg.symbol){
|
||||
height: 3.5rem;
|
||||
width: 3.5rem;
|
||||
height: 4rem;
|
||||
width: 4rem;
|
||||
border-radius: 10000px;
|
||||
}
|
||||
:global(a:has(svg.symbol)){
|
||||
/*:global(a:has(svg.symbol)){
|
||||
padding: 0 !important;
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
margin-left: 1rem;
|
||||
}*/
|
||||
.wrapper{
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
min-height: 100svh;
|
||||
}
|
||||
footer{
|
||||
padding-block: 1rem;
|
||||
text-align: center;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.button_wrapper{
|
||||
box-shadow: 0 1em 1rem 0rem rgba(0,0,0,0.4);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@ -123,8 +120,8 @@ nav[hidden]{
|
||||
background-color: unset;
|
||||
display: block;
|
||||
fill: white;
|
||||
margin-inline: 1rem;
|
||||
width: 2.5rem;
|
||||
margin-inline: 0.5rem;
|
||||
width: 2rem;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
.nav_button svg{
|
||||
@ -136,7 +133,7 @@ nav[hidden]{
|
||||
fill: var(--red);
|
||||
scale: 0.9;
|
||||
}
|
||||
nav{
|
||||
.nav_site{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
@ -151,16 +148,16 @@ nav[hidden]{
|
||||
justify-content: space-between!important;
|
||||
padding-inline: 0.5rem;
|
||||
}
|
||||
:global(nav ul){
|
||||
:global(.nav_site ul){
|
||||
width: 100% ;
|
||||
}
|
||||
nav :first-child{
|
||||
.nav_site :first-child{
|
||||
display:none;
|
||||
}
|
||||
nav[hidden]{
|
||||
.nav_site[hidden]{
|
||||
transform: translateX(100%);
|
||||
}
|
||||
nav a:last-child{
|
||||
:global(.nav_site a:last-child){
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
@ -179,24 +176,14 @@ nav[hidden]{
|
||||
transform: unset;
|
||||
}
|
||||
}
|
||||
.wrapper{
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
min-height: 100svh;
|
||||
}
|
||||
footer{
|
||||
padding-block: 1rem;
|
||||
text-align: center;
|
||||
margin-top: auto;
|
||||
}
|
||||
</style>
|
||||
<div class=wrapper lang=de>
|
||||
<div>
|
||||
<div class=button_wrapper>
|
||||
<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>
|
||||
<nav hidden>
|
||||
<nav hidden class=nav_site>
|
||||
<a class=entry href="/"><Symbol></Symbol></a>
|
||||
<slot name=links></slot>
|
||||
<slot name=right_side></slot>
|
||||
|
@ -5,6 +5,7 @@
|
||||
</script>
|
||||
<style>
|
||||
a{
|
||||
font-family: "Noto Color Emoji", emoji;
|
||||
font-size: 2rem;
|
||||
text-decoration: none;
|
||||
padding: 0.5em;
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import '$lib/components/nordtheme.css';
|
||||
import '$lib/css/nordtheme.css';
|
||||
import Recipes from '$lib/components/Recipes.svelte';
|
||||
import Search from './Search.svelte';
|
||||
export let icons
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
<style>
|
||||
a{
|
||||
font-family: "Noto Color Emoji", emoji, sans-serif;
|
||||
font-size: 2rem;
|
||||
text-decoration: none;
|
||||
padding: 0.5em;
|
||||
|
@ -1,7 +1,8 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { onNavigate } from "$app/navigation";
|
||||
export let data
|
||||
let multiplier = 1
|
||||
let multiplier;
|
||||
let custom_mul = "…"
|
||||
|
||||
onMount(() => {
|
||||
@ -9,6 +10,10 @@ onMount(() => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
multiplier = urlParams.get('multiplier') || 1;
|
||||
})
|
||||
onNavigate(() => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
multiplier = urlParams.get('multiplier') || 1;
|
||||
})
|
||||
|
||||
function convertFloatsToFractions(inputString) {
|
||||
// Split the input string into individual words
|
||||
@ -227,7 +232,7 @@ span
|
||||
{/if}
|
||||
<div class=ingredients_grid>
|
||||
{#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}
|
||||
</div>
|
||||
{/each}
|
||||
|
91
src/lib/components/LinksGrid.svelte
Normal file
91
src/lib/components/LinksGrid.svelte
Normal file
@ -0,0 +1,91 @@
|
||||
<style>
|
||||
|
||||
:global(.links_grid a:nth-child(4n)),
|
||||
:global(.links_grid a:nth-child(4n) svg){
|
||||
background-color: var(--nord4);
|
||||
fill: var(--nord11);
|
||||
}
|
||||
:global(.links_grid a:nth-child(4n+1)),
|
||||
:global(.links_grid a:nth-child(4n+1) svg){
|
||||
background-color: var(--nord6);
|
||||
fill: var(--nord10);
|
||||
}
|
||||
:global(.links_grid a:nth-child(4n+2)){
|
||||
background-color: var(--nord5);
|
||||
}
|
||||
:global(.links_grid a:nth-child(4n+3)){
|
||||
background-color: var(--nord5);
|
||||
}
|
||||
|
||||
:global(a){
|
||||
text-decoration: unset;
|
||||
color: var(--nord0);
|
||||
transition: 200ms;
|
||||
}
|
||||
:global(.links_grid a:hover){
|
||||
box-shadow: 1em 1em 2em 1em rgba(0,0,0, 0.3);
|
||||
}
|
||||
:global(.links_grid a){
|
||||
box-shadow: 0.2em 0.2em 1em 1em rgba(0,0,0, 0.1);
|
||||
}
|
||||
.links_grid{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 2rem;
|
||||
max-width: 1000px;
|
||||
margin-inline: auto;
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
:global(.links_grid a){
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: unset;
|
||||
color: var(--nord0);
|
||||
transition: 200ms;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
}
|
||||
:global(.links_grid a:hover){
|
||||
scale: 1.02;
|
||||
}
|
||||
:global(.links_grid a :is(svg, img)){
|
||||
height: 120px;
|
||||
fill: var(--nord0);
|
||||
}
|
||||
:global(.links_grid h3){
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
:global(.links_grid h3){
|
||||
color: white;
|
||||
}
|
||||
:global(.links_grid a:nth-child(4n)),
|
||||
:global(.links_grid a:nth-child(4n) svg){
|
||||
background-color: var(--nord6-dark);
|
||||
fill: var(--nord11);
|
||||
}
|
||||
:global(.links_grid a:nth-child(4n+1)),
|
||||
:global(.links_grid a:nth-child(4n+1) svg){
|
||||
background-color: var(--accent-dark);
|
||||
fill: var(--nord9);
|
||||
}
|
||||
:global(.links_grid a:nth-child(4n+2)),
|
||||
:global(.links_grid a:nth-child(4n+2) svg){
|
||||
background-color: var(--nord1);
|
||||
fill: var(--nord8);
|
||||
|
||||
}
|
||||
:global(.links_grid a:nth-child(4n+3)),
|
||||
:global(.links_grid a:nth-child(4n+3) svg){
|
||||
background-color: var(--background-dark);
|
||||
fill: var(--nord7);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class=links_grid>
|
||||
<slot></slot>
|
||||
</div>
|
@ -8,7 +8,7 @@ export let title
|
||||
flex-direction: row;
|
||||
flex-wrap:nowrap;
|
||||
overflow-x: auto;
|
||||
gap: 2rem;
|
||||
/*gap: 2rem;*/ /*messes up if js disabled as anchor tag is inserted twice...*/
|
||||
padding: 3rem;
|
||||
}
|
||||
.media_scroller_wrapper{
|
||||
|
@ -36,9 +36,12 @@ onMount(() => {
|
||||
scroller.parentNode.style.display= 'none'
|
||||
})
|
||||
scroll
|
||||
let items = document.querySelectorAll(".matched-recipe");
|
||||
items = [...new Set(items)] // make unique as seasonal mediascroller can lead to duplicates
|
||||
// if only one result and click_only_result is true, click it
|
||||
if(click_only_result && scrollers_with_results.length == 1 && scrollers_with_results[0].querySelector(".matched-recipe").length == 1){
|
||||
scrollers_with_results[0].querySelector(".matched-recipe").click()
|
||||
if(click_only_result && scrollers_with_results.length == 1 && items.length == 1){
|
||||
// add '/rezepte' to history to not force-redirect back to recipe if going back
|
||||
items[0].click();
|
||||
}
|
||||
// if scrollers with results are presenet scroll first result into view
|
||||
/*if(scrollers_with_results.length > 0){
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import '$lib/components/nordtheme.css';
|
||||
import '$lib/css/nordtheme.css';
|
||||
import Recipes from '$lib/components/Recipes.svelte';
|
||||
import Search from './Search.svelte';
|
||||
let months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang=ts>
|
||||
import "$lib/components/nordtheme.css"
|
||||
import "$lib/css/nordtheme.css"
|
||||
import { season } from '$lib/js/season_store.js'
|
||||
import {onMount} from "svelte";
|
||||
import {do_on_key} from "./do_on_key";
|
||||
|
@ -1,9 +1,13 @@
|
||||
<script>
|
||||
import "$lib/css/nordtheme.css";
|
||||
</script>
|
||||
<style>
|
||||
:root{
|
||||
--icon_fill: var(--nord4);
|
||||
}
|
||||
svg{
|
||||
transition: 100ms;
|
||||
height: 3em;
|
||||
}
|
||||
svg:hover,
|
||||
svg:focus-visible
|
||||
@ -26,14 +30,9 @@
|
||||
</style>
|
||||
|
||||
<svg
|
||||
width="calc(45.742325px/ 1.3)"
|
||||
height="calc(80.310539px / 1.3)"
|
||||
viewBox="0 0 45.742326 80.310541"
|
||||
version="1.1"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
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">
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g class=stroke
|
||||
id="branches"
|
||||
transform="translate(-42.033271,-37.145192)" >
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
export let tag : string;
|
||||
export let ref: string;
|
||||
import '$lib/components/nordtheme.css'
|
||||
import '$lib/css/nordtheme.css'
|
||||
</script>
|
||||
<style>
|
||||
a{
|
||||
|
@ -2,15 +2,23 @@
|
||||
export let src
|
||||
export let placeholder_src
|
||||
let isloaded=false
|
||||
let isredirected=false
|
||||
import { onMount } from "svelte";
|
||||
onMount(() => {
|
||||
const el = document.querySelector("img")
|
||||
if(el.complete){
|
||||
isloaded = true
|
||||
}
|
||||
fetch(src, { method: 'HEAD' })
|
||||
.then(response => {
|
||||
isredirected = response.redirected
|
||||
})
|
||||
})
|
||||
|
||||
function show_dialog_img(){
|
||||
if(isredirected){
|
||||
return
|
||||
}
|
||||
if(document.querySelector("img").complete){
|
||||
document.querySelector("#img_carousel").showModal();
|
||||
}
|
||||
@ -160,16 +168,22 @@ dialog button{
|
||||
.zoom-in{
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
</style>
|
||||
<section class="section">
|
||||
<figure class="image-container">
|
||||
<!-- 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_blur>
|
||||
<img class:unblur={isloaded} id=image {src} on:load={() => {isloaded=true}} alt=""/>
|
||||
</div>
|
||||
</div>
|
||||
<noscript>
|
||||
<div class=placeholder style="background-image:url({placeholder_src})" >
|
||||
<img class="unblur" id=image {src} on:load={() => {isloaded=true}} alt=""/>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
</figure>
|
||||
<div class=content><slot></slot></div>
|
||||
|
@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
export let username;
|
||||
export let user;
|
||||
|
||||
function toggle_options(){
|
||||
const el = document.querySelector("#options")
|
||||
el.hidden = !el.hidden
|
||||
}
|
||||
let src="https://bocken.org/static/user/thumb/" + username + ".webp"
|
||||
|
||||
onMount( () => {
|
||||
document.addEventListener("click", (e) => {
|
||||
const el = document.querySelector("#button")
|
||||
@ -101,6 +102,12 @@
|
||||
/* (B2) BOTTOM "CALLOUT TAIL" */
|
||||
h2{
|
||||
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){
|
||||
#options{
|
||||
@ -125,16 +132,17 @@ h2{
|
||||
}
|
||||
</style>
|
||||
|
||||
{#if username}
|
||||
<button on:click={toggle_options} style="background-image: url({src})" id=button>
|
||||
{#if user}
|
||||
<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>
|
||||
<h2>{username}</h2>
|
||||
<h2>{user.name}</h2>
|
||||
<p>({user.nickname})</p>
|
||||
<ul>
|
||||
<li><a href="/settings" >Einstellungen</a></li>
|
||||
<li><a href="/logout" >Log Out</a></li>
|
||||
<li><a href="https://sso.bocken.org/if/user/#/settings" >Einstellungen</a></li>
|
||||
<li><a href="/auth/signout" >Log Out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</button>
|
||||
{:else}
|
||||
<a class=entry href=/login>Log In</a>
|
||||
<a class=entry href=/auth/signin>Log In</a>
|
||||
{/if}
|
||||
|
@ -1,25 +0,0 @@
|
||||
:root{
|
||||
--nord0: #2E3440;
|
||||
--nord1: #3B4252;
|
||||
--nord2: #434C5E;
|
||||
--nord3: #4C566A;
|
||||
--nord4: #D8DEE9;
|
||||
--nord5: #E5E9F0;
|
||||
--nord6: #ECEFF4;
|
||||
--nord7: #8FBCBB;
|
||||
--nord8: #88C0D0;
|
||||
--nord9: #81A1C1;
|
||||
--nord10: #5E81AC;
|
||||
--nord11: #BF616A;
|
||||
--nord12: #D08770;
|
||||
--nord13: #EBCB8B;
|
||||
--nord14: #A3BE8C;
|
||||
--nord15: #B48EAD;
|
||||
--lightblue: var(--nord9);
|
||||
--blue: var(--nord10);
|
||||
--red: var(--nord11);
|
||||
--orange: var(--nord12);
|
||||
--yellow: var(--nord13);
|
||||
--green: var(--nord14);
|
||||
--purple: var(--nord15);
|
||||
}
|
32
src/lib/css/christ.css
Normal file
32
src/lib/css/christ.css
Normal file
@ -0,0 +1,32 @@
|
||||
div.gebet{
|
||||
text-align: center;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
ul {
|
||||
font-size: 120%;
|
||||
}
|
||||
.gebet v{
|
||||
margin:0;
|
||||
}
|
||||
.gebet v:lang(la) {
|
||||
color: var(--nord6);
|
||||
}
|
||||
.gebet.bilingue v:lang(de){
|
||||
color: grey;
|
||||
}
|
||||
|
||||
i{
|
||||
font-style: normal;
|
||||
color: var(--nord11);
|
||||
font-weight: 900;
|
||||
}
|
||||
i.txt {
|
||||
font-size: 70%;
|
||||
font-weight: normal;
|
||||
}
|
||||
v{
|
||||
display: block;
|
||||
}
|
||||
.mobile audio{
|
||||
width:70%;
|
||||
}
|
@ -27,3 +27,28 @@
|
||||
--background-dark: #21201b;
|
||||
--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);
|
||||
}
|
||||
}
|
||||
|
65
src/lib/css/predigten.css
Normal file
65
src/lib/css/predigten.css
Normal file
@ -0,0 +1,65 @@
|
||||
@font-face {
|
||||
font-family: 'UnifrakturMaguntia';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('/fonts/UnifrakturMaguntia20.ttf');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
.bibel{
|
||||
font-family: 'UnifrakturMaguntia', cursive;
|
||||
-moz-font-feature-settings: "cv11";
|
||||
-webkit-font-feature-settings: "cv11";
|
||||
-ms-font-feature-settings: "cv11";
|
||||
font-feature-settings: "cv11";
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
li::marker, i, li:lang(la){
|
||||
font-family: serif;
|
||||
}
|
||||
ol{
|
||||
list-style-position: inside;
|
||||
}
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
h1{
|
||||
font-size: 4rem;
|
||||
}
|
||||
h2{
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
h3{
|
||||
font-size: 2rem;
|
||||
}
|
||||
h4{
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.quote p.title{
|
||||
font-weight: bold;
|
||||
}
|
||||
/*.quote .bibel{
|
||||
margin-bottom: 1em;
|
||||
}*/
|
||||
.quote q{
|
||||
display: block;
|
||||
width: 90%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.tod, .grund{
|
||||
font-size: 1.5rem;
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
margin-top: -1rem;
|
||||
}
|
||||
.schott q{
|
||||
quotes: "«" "»";
|
||||
}
|
||||
.predigt video {
|
||||
display: block;
|
||||
width: 80%;
|
||||
margin: auto;
|
||||
}
|
204
src/lib/css/rosenkranz.css
Normal file
204
src/lib/css/rosenkranz.css
Normal file
@ -0,0 +1,204 @@
|
||||
@font-face {
|
||||
font-family: 'crosses';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/fonts/crosses.ttf);
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'LibertineMinimal';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/fonts/LinLibertine_minimal.ttf);
|
||||
}
|
||||
.sbeads{
|
||||
fill: var(--nord10);
|
||||
}
|
||||
|
||||
.chain{
|
||||
stroke:black;
|
||||
stroke-width: 0.7;
|
||||
stroke-miterlimit: 4;
|
||||
stroke: gray;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.sbeads circle.hitbox{
|
||||
r: 3.2px;
|
||||
stroke-width:0;
|
||||
}
|
||||
|
||||
#start1 circle{
|
||||
cx:15.559271px;
|
||||
cy: 20.881956px;
|
||||
}
|
||||
#start2 circle{
|
||||
cx:21.633902px;
|
||||
cy:20.367514px;
|
||||
}
|
||||
#start3 circle{
|
||||
cx:27.96961px;
|
||||
cy:21.178484px;
|
||||
}
|
||||
#lbead5 circle{
|
||||
cx:118.50725px;
|
||||
cy:59.477211px;
|
||||
}
|
||||
#lbead4 circle{
|
||||
cx:126.81134px;
|
||||
cy:15.751753px;
|
||||
}
|
||||
#lbead1 circle{
|
||||
cx:7.6719489px;
|
||||
cy:25.364584px;
|
||||
}
|
||||
#lbead2 circle{
|
||||
cx:36.798512px;
|
||||
cy:23.486462px;
|
||||
}
|
||||
#lbead3 circle{
|
||||
cx:84.105789px;
|
||||
cy:3.0456686px;
|
||||
}
|
||||
#lbead6 circle{
|
||||
cx:72.185097px;
|
||||
cy:64.006859px;
|
||||
}
|
||||
#start1:hover .msg,
|
||||
#start2:hover .msg,
|
||||
#start3:hover .msg,
|
||||
#secret1:hover .msg,
|
||||
#secret2:hover .msg,
|
||||
#secret3:hover .msg,
|
||||
#secret4:hover .msg,
|
||||
#secret5:hover .msg,
|
||||
#lbeads .beforedecades:hover .msg,
|
||||
#lbeads .afterdecade:hover .msg,
|
||||
#cross:hover .msg
|
||||
{
|
||||
display:block;
|
||||
}
|
||||
|
||||
#start1:hover .sbeads circle:not(.hitbox),
|
||||
#start2:hover .sbeads circle:not(.hitbox),
|
||||
#start3:hover .sbeads circle:not(.hitbox),
|
||||
#secret1:hover .sbeads circle,
|
||||
#secret2:hover .sbeads circle,
|
||||
#secret3:hover .sbeads circle,
|
||||
#secret4:hover .sbeads circle,
|
||||
#secret5:hover .sbeads circle
|
||||
{
|
||||
fill: var(--nord11);
|
||||
r: 1.5px;
|
||||
}
|
||||
#lbead1:hover .lbead,
|
||||
#lbead2:hover .lbead,
|
||||
#lbead3:hover .lbead,
|
||||
#lbead4:hover .lbead,
|
||||
#lbead5:hover .lbead,
|
||||
#lbead6:hover .lbead{
|
||||
r: 2.8px;
|
||||
fill: var(--nord11);
|
||||
}
|
||||
#cross:hover .symbol{
|
||||
fill: var(--nord11);
|
||||
stroke: var(--nord11);
|
||||
stroke-width: 0.25;
|
||||
}
|
||||
#lbeads.msg{
|
||||
display:block;
|
||||
}
|
||||
.sbeads circle{
|
||||
r: 1.25px;
|
||||
}
|
||||
.msg .diff{
|
||||
fill: var(--nord11);
|
||||
}
|
||||
.msg .b{
|
||||
font-family: crosses;
|
||||
font-weight: bold;
|
||||
}
|
||||
.msg .title{
|
||||
fill: var(--nord10);
|
||||
font-weight: bold;
|
||||
font-size: 5px;
|
||||
}
|
||||
.msg{
|
||||
font-size: 4px;
|
||||
stroke: none;
|
||||
fill: var(--nord4);
|
||||
display:none;
|
||||
}
|
||||
text{
|
||||
font-family: LibertineMinimal;
|
||||
}
|
||||
#lbeads circle.hitbox{
|
||||
r:5px;
|
||||
stroke:none;
|
||||
stroke-width:0;
|
||||
}
|
||||
.lbead{
|
||||
fill: var(--nord12);
|
||||
r: 2.65px;
|
||||
}
|
||||
|
||||
.hitbox{
|
||||
opacity:0;
|
||||
stroke-width: 2;
|
||||
fill: red;
|
||||
stroke: red;
|
||||
}
|
||||
#coin circle{
|
||||
r: 2.7px;
|
||||
fill:darkgray;
|
||||
}
|
||||
|
||||
#coin text{
|
||||
fill:var(--nord0);
|
||||
font-size: 4.259px;
|
||||
line-height:1.25;
|
||||
font-family: crosses;
|
||||
}
|
||||
|
||||
#cross .symbol{
|
||||
font-family: crosses;
|
||||
fill: var(--nord4);
|
||||
font-size: 17.3637px;
|
||||
line-height: 1.25;
|
||||
stroke-width:0.434093
|
||||
}
|
||||
|
||||
table{
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
td{
|
||||
text-align:center;
|
||||
border-left: 1px solid;
|
||||
border-right: 1px solid;
|
||||
border-color: var(--nord2);
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
tr :last-child{
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
tr :first-child{
|
||||
border-left: 0px solid;
|
||||
}
|
||||
|
||||
thead td{
|
||||
color: var(--nord4);
|
||||
border-bottom-width: 3px;
|
||||
border-bottom-color: var(--nord10);
|
||||
border-bottom-style: dotted;
|
||||
font-size: 110%;
|
||||
font-weight: bold;
|
||||
}
|
||||
.table{
|
||||
width:100%;
|
||||
overflow-x: auto;
|
||||
}
|
@ -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
|
||||
let seed = Math.floor(time.getTime()/MS_PER_DAY)
|
||||
function mulberry32(a) {
|
||||
return function() {
|
||||
var t = a += 0x6D2B79F5;
|
||||
@ -11,6 +9,8 @@ function mulberry32(a) {
|
||||
}
|
||||
|
||||
export function rand_array(array){
|
||||
let time = new Date()
|
||||
const seed = Math.floor(time.getTime()/MS_PER_DAY)
|
||||
let rand = mulberry32(seed)
|
||||
array.sort((a,b) => 0.5 - rand())
|
||||
return array
|
||||
|
@ -1,15 +0,0 @@
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
const PaymentSchema= new mongoose.Schema(
|
||||
{
|
||||
payee: {type: String, required: true},
|
||||
amount: {type: Number, required: true},
|
||||
for_self: {type: Number},
|
||||
for_other: {type: Number},
|
||||
description: {type: String},
|
||||
added_by: {type: String},
|
||||
date: {type: Date, required: true, default: Date.now},
|
||||
}, {timestamps: true}
|
||||
);
|
||||
|
||||
export const Payment = mongoose.model("Payment", PaymentSchema);
|
@ -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,7 +1,7 @@
|
||||
import { get_username } from '$lib/js/get_username';;
|
||||
import type { Actions, PageServerLoad } from "./$types"
|
||||
import { error } from "@sveltejs/kit"
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export const load = (async ({cookies}) => {
|
||||
return { user: await get_username(cookies) }
|
||||
export const load : PageServerLoad = (async ({locals}) => {
|
||||
return {
|
||||
session: await locals.auth(),
|
||||
}
|
||||
});
|
||||
|
@ -2,22 +2,15 @@
|
||||
import Header from '$lib/components/Header.svelte'
|
||||
import UserHeader from '$lib/components/UserHeader.svelte';
|
||||
export let data
|
||||
let username = ""
|
||||
if(data.user){
|
||||
username = data.user.username
|
||||
let user;
|
||||
if(data.session){
|
||||
user = data.session.user
|
||||
}
|
||||
</script>
|
||||
|
||||
<Header>
|
||||
<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>
|
||||
<UserHeader {username} slot=right_side></UserHeader>
|
||||
<UserHeader {user} slot=right_side></UserHeader>
|
||||
<slot></slot>
|
||||
</Header>
|
||||
|
@ -1,164 +1,90 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import "$lib/css/nordtheme.css";
|
||||
import LinksGrid from "$lib/components/LinksGrid.svelte";
|
||||
export let data;
|
||||
</script>
|
||||
<style>
|
||||
|
||||
section{
|
||||
padding: 2rem 1rem;
|
||||
min-height: 350px;
|
||||
overflow: hidden;
|
||||
transition: 200ms;
|
||||
}
|
||||
section:nth-child(3n),
|
||||
.grid_section a:nth-child(4n),
|
||||
.grid_section a:nth-child(4n) svg{
|
||||
background-color: var(--nord4);
|
||||
fill: var(--nord11);
|
||||
}
|
||||
section:nth-child(4n+1),
|
||||
.grid_section a:nth-child(4n+1),
|
||||
.grid_section a:nth-child(4n+1) svg{
|
||||
background-color: var(--nord6);
|
||||
fill: var(--nord10);
|
||||
}
|
||||
section:nth-child(3n+2),
|
||||
.grid_section a:nth-child(4n+2){
|
||||
background-color: var(--nord5);
|
||||
}
|
||||
.grid_section a:nth-child(4n+3){
|
||||
background-color: var(--nord5);
|
||||
}
|
||||
|
||||
section:nth-child(2n) .flex{
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
section:nth-child(2n) img{
|
||||
object-position: right;
|
||||
left: -350px;
|
||||
}
|
||||
.flex{
|
||||
.hero{
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
max-width: 1000px;
|
||||
margin-inline: auto;
|
||||
min-height: 350px;
|
||||
}
|
||||
.flex > *:first-child{
|
||||
width: 66%;
|
||||
}
|
||||
.flex > *:last-child{
|
||||
position: relative;
|
||||
max-width: 34%;
|
||||
}
|
||||
.flex > *:last-child img{
|
||||
position:absolute;
|
||||
}
|
||||
img{
|
||||
height: 350px;
|
||||
object-fit: cover;
|
||||
}
|
||||
h2{
|
||||
font-size: 3rem;
|
||||
}
|
||||
a{
|
||||
text-decoration: unset;
|
||||
color: var(--nord0);
|
||||
transition: 200ms;
|
||||
}
|
||||
section:hover{
|
||||
scale: 1.02;
|
||||
}
|
||||
section:has(a:hover){
|
||||
box-shadow: 1em 1em 1em 1em rgba(0,0,0, 0.3);
|
||||
}
|
||||
.grid_section a:hover{
|
||||
box-shadow: 1em 1em 2em 1em rgba(0,0,0, 0.3);
|
||||
}
|
||||
.grid_section a{
|
||||
box-shadow: 0.2em 0.2em 1em 1em rgba(0,0,0, 0.1);
|
||||
}
|
||||
.grid_section{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 2rem;
|
||||
max-width: 1000px;
|
||||
margin-inline: auto;
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
.grid_section a{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: unset;
|
||||
color: var(--nord0);
|
||||
transition: 200ms;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
max-width: 1400px;
|
||||
margin-inline: auto;
|
||||
gap: 2rem;
|
||||
}
|
||||
.grid_section a:hover{
|
||||
scale: 1.02;
|
||||
.hero img{
|
||||
border-radius: 1000px;
|
||||
margin: 1rem;
|
||||
width: clamp(100px, 300px, 50vw);
|
||||
object-fit: cover;
|
||||
background-color: var(--nord4);
|
||||
box-shadow: 0.2em 0.2em 1em 1em rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.grid_section a :is(svg, img){
|
||||
height: 120px;
|
||||
fill: var(--nord0);
|
||||
}
|
||||
.grid_section h3{
|
||||
font-size: 1.5rem;
|
||||
.hero div{
|
||||
margin-inline: 1rem;
|
||||
}
|
||||
|
||||
section h2{
|
||||
font-size: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
@media (prefers-color-scheme: dark){
|
||||
*{
|
||||
color: white;
|
||||
}
|
||||
section:nth-child(3n),
|
||||
.grid_section a:nth-child(4n),
|
||||
.grid_section a:nth-child(4n) svg{
|
||||
background-color: var(--nord6-dark);
|
||||
fill: var(--nord11);
|
||||
}
|
||||
section:nth-child(4n+1),
|
||||
.grid_section a:nth-child(4n+1),
|
||||
.grid_section a:nth-child(4n+1) svg{
|
||||
background-color: var(--accent-dark);
|
||||
fill: var(--nord9);
|
||||
}
|
||||
section:nth-child(3n+2),
|
||||
.grid_section a:nth-child(4n+2),
|
||||
.grid_section a:nth-child(4n+2) svg{
|
||||
background-color: var(--nord1);
|
||||
fill: var(--nord8);
|
||||
|
||||
}
|
||||
.grid_section a:nth-child(4n+3),
|
||||
.grid_section a:nth-child(4n+3) svg{
|
||||
background-color: var(--background-dark);
|
||||
fill: var(--nord7);
|
||||
.hero img{
|
||||
box-shadow: 0.1em 0.1em 2em 0.5em rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
@media (max-width: 600px){
|
||||
.hero{
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
.hero img{
|
||||
width: clamp(100px, 200px, 80vw);
|
||||
}
|
||||
.hero h1{
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
</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}
|
||||
|
||||
<section class=hero>
|
||||
<img src="https://bocken.org/static/user/full/alexander.webp" alt="Smiling Alexander Bocken">
|
||||
<div>
|
||||
<h1><q>Willkommen auf bocken.org</q></h1>
|
||||
<p>
|
||||
Hallo, ich bin Alexander Bocken. Auf dieser Seite findest du einige Softwareprojekte für Freunde, Familie und mich.
|
||||
Alles ist selbst gehostet bei mir daheim auf einem kleinen Mini-Server (Arch, btw).
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
{/if}
|
||||
|
||||
<section>
|
||||
<a href="/rezepte" class=flex>
|
||||
<div class=text>
|
||||
<h2>Re­zep­te</h2>
|
||||
<p>Die eigenen Rezepte für das ganze Jahr kann man hier finden. Von traditionell Kärntner Küche zu Schweizer Klassikern oder auch das ein oder andere Hipsterrezept findet man für das ganze Jahr Rezepte. An den Rezepten wird kontinuirlich gefeilscht. </p>
|
||||
</div>
|
||||
<div>
|
||||
<img src="https://bocken.org/static/rezepte/thumb/ragu_aus_rindsrippen.webp" alt="">
|
||||
</div>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<div class=grid_section>
|
||||
<h2>Seiten</h2>
|
||||
<LinksGrid>
|
||||
<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>
|
||||
<h3>Rezepte</h3>
|
||||
</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>
|
||||
<h3>Git</h3>
|
||||
</a>
|
||||
@ -168,7 +94,7 @@ section:has(a:hover){
|
||||
<h3>Streaming</h3>
|
||||
</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>
|
||||
<h3>Familienbilder</h3>
|
||||
</a>
|
||||
@ -183,19 +109,49 @@ section:has(a:hover){
|
||||
<h3>Videokonferenzen</h3>
|
||||
</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>
|
||||
<h3>Suchmaschine</h3>
|
||||
</a>
|
||||
|
||||
<a href="/transmission">
|
||||
<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>
|
||||
|
||||
<a href="https://cloud.bocken.org/apps/cospend/">
|
||||
<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="M0 24C0 10.7 10.7 0 24 0H69.5c22 0 41.5 12.8 50.6 32h411c26.3 0 45.5 25 38.6 50.4l-41 152.3c-8.5 31.4-37 53.3-69.5 53.3H170.7l5.4 28.5c2.2 11.3 12.1 19.5 23.6 19.5H488c13.3 0 24 10.7 24 24s-10.7 24-24 24H199.7c-34.6 0-64.3-24.6-70.7-58.5L77.4 54.5c-.7-3.8-4-6.5-7.9-6.5H24C10.7 48 0 37.3 0 24zM128 464a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zm336-48a48 48 0 1 1 0 96 48 48 0 1 1 0-96z"/></svg>
|
||||
<h3>Einkauf</h3>
|
||||
</a>
|
||||
|
||||
<a href="https://tree.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="M335.5 4l288 160c15.4 8.6 21 28.1 12.4 43.5s-28.1 21-43.5 12.4L320 68.6 47.5 220c-15.4 8.6-34.9 3-43.5-12.4s-3-34.9 12.4-43.5L304.5 4c9.7-5.4 21.4-5.4 31.1 0zM320 160a40 40 0 1 1 0 80 40 40 0 1 1 0-80zM144 256a40 40 0 1 1 0 80 40 40 0 1 1 0-80zm312 40a40 40 0 1 1 80 0 40 40 0 1 1 -80 0zM226.9 491.4L200 441.5V480c0 17.7-14.3 32-32 32H120c-17.7 0-32-14.3-32-32V441.5L61.1 491.4c-6.3 11.7-20.8 16-32.5 9.8s-16-20.8-9.8-32.5l37.9-70.3c15.3-28.5 45.1-46.3 77.5-46.3h19.5c16.3 0 31.9 4.5 45.4 12.6l33.6-62.3c15.3-28.5 45.1-46.3 77.5-46.3h19.5c32.4 0 62.1 17.8 77.5 46.3l33.6 62.3c13.5-8.1 29.1-12.6 45.4-12.6h19.5c32.4 0 62.1 17.8 77.5 46.3l37.9 70.3c6.3 11.7 1.9 26.2-9.8 32.5s-26.2 1.9-32.5-9.8L552 441.5V480c0 17.7-14.3 32-32 32H472c-17.7 0-32-14.3-32-32V441.5l-26.9 49.9c-6.3 11.7-20.8 16-32.5 9.8s-16-20.8-9.8-32.5l36.3-67.5c-1.7-1.7-3.2-3.6-4.3-5.8L376 345.5V400c0 17.7-14.3 32-32 32H296c-17.7 0-32-14.3-32-32V345.5l-26.9 49.9c-1.2 2.2-2.6 4.1-4.3 5.8l36.3 67.5c6.3 11.7 1.9 26.2-9.8 32.5s-26.2 1.9-32.5-9.8z"/></svg>
|
||||
<h3>Stammbaum</h3>
|
||||
</a>
|
||||
</div>
|
||||
<a href=glaube>
|
||||
<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="M351.2 4.8c3.2-2 6.6-3.3 10-4.1c4.7-1 9.6-.9 14.1 .1c7.7 1.8 14.8 6.5 19.4 13.6L514.6 194.2c8.8 13.1 13.4 28.6 13.4 44.4v73.5c0 6.9 4.4 13 10.9 15.2l79.2 26.4C631.2 358 640 370.2 640 384v96c0 9.9-4.6 19.3-12.5 25.4s-18.1 8.1-27.7 5.5L431 465.9c-56-14.9-95-65.7-95-123.7V224c0-17.7 14.3-32 32-32s32 14.3 32 32v80c0 8.8 7.2 16 16 16s16-7.2 16-16V219.1c0-7-1.8-13.8-5.3-19.8L340.3 48.1c-1.7-3-2.9-6.1-3.6-9.3c-1-4.7-1-9.6 .1-14.1c1.9-8 6.8-15.2 14.3-19.9zm-62.4 0c7.5 4.6 12.4 11.9 14.3 19.9c1.1 4.6 1.2 9.4 .1 14.1c-.7 3.2-1.9 6.3-3.6 9.3L213.3 199.3c-3.5 6-5.3 12.9-5.3 19.8V304c0 8.8 7.2 16 16 16s16-7.2 16-16V224c0-17.7 14.3-32 32-32s32 14.3 32 32V342.3c0 58-39 108.7-95 123.7l-168.7 45c-9.6 2.6-19.9 .5-27.7-5.5S0 490 0 480V384c0-13.8 8.8-26 21.9-30.4l79.2-26.4c6.5-2.2 10.9-8.3 10.9-15.2V238.5c0-15.8 4.7-31.2 13.4-44.4L245.2 14.5c4.6-7.1 11.7-11.8 19.4-13.6c4.6-1.1 9.4-1.2 14.1-.1c3.5 .8 6.9 2.1 10 4.1z"/></svg>
|
||||
<h3>Glaube</h3>
|
||||
</a>
|
||||
<a href=https://transmission.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="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>
|
||||
</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>
|
||||
</section>
|
||||
|
@ -1,35 +0,0 @@
|
||||
import { redirect } from "@sveltejs/kit"
|
||||
import type { Actions, PageServerLoad } from "./$types"
|
||||
import { error } from "@sveltejs/kit"
|
||||
|
||||
export const actions: Actions = {
|
||||
login: async (event) => {
|
||||
const data = await event.request.formData()
|
||||
const res = await event.fetch('/api/user/login',
|
||||
{method: 'POST',
|
||||
body: JSON.stringify({
|
||||
username: data.get('username'),
|
||||
password: data.get('password'),
|
||||
})
|
||||
}
|
||||
)
|
||||
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")
|
||||
},
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
<script>
|
||||
import "$lib/css/form.css"
|
||||
import "$lib/css/nordtheme.css"
|
||||
</script>
|
||||
<form action="?/login" method=POST>
|
||||
<h1>Log In</h1>
|
||||
<label>
|
||||
Benutzername
|
||||
<input type="text" name="username" required>
|
||||
</label>
|
||||
<label>
|
||||
Passwort
|
||||
<input name="password" type="password" required>
|
||||
</label>
|
||||
|
||||
<button type="submit">Log In</button>
|
||||
<p>Noch keinen Account? <a href=/register>Hier registrieren</a>.</p>
|
||||
</form>
|
@ -1 +0,0 @@
|
||||
{"terminal": "nvimterm"}
|
@ -1,8 +0,0 @@
|
||||
import redirect from "@sveltejs/kit"
|
||||
import type { Actions } from './$types';
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async ({cookies}) => {
|
||||
cookies.delete("UserSession")
|
||||
}
|
||||
} satisfies Actions;
|
@ -1,7 +0,0 @@
|
||||
<script>
|
||||
import "$lib/css/form.css"
|
||||
</script>
|
||||
<form method='POST'>
|
||||
<h1>Log out</h1>
|
||||
<button type='submit'>Log Out</button>
|
||||
</form>
|
@ -1,5 +0,0 @@
|
||||
import { get_username } from '$lib/js/get_username';;
|
||||
|
||||
export const load = (async ({cookies}) => {
|
||||
return { user: await get_username(cookies) }
|
||||
});
|
@ -1,61 +0,0 @@
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { Payment } from '../../../../models/Payment';
|
||||
import { dbConnect, dbDisconnect } from '../../../../utils/db';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { authenticateUser } from '$lib/js/authenticate';;
|
||||
import sharp from 'sharp';
|
||||
import path from 'path';
|
||||
import {IMAGE_DIR} from '$env/static/private';
|
||||
|
||||
export const POST: RequestHandler = async ({request, cookies}) => {
|
||||
const user = await authenticateUser(cookies)
|
||||
if(!user){
|
||||
throw error(401, "Not logged in")
|
||||
}
|
||||
if(!user.access.includes("abrechnung")){
|
||||
throw error(401, "This user does not have permissions to add payments")
|
||||
}
|
||||
else{
|
||||
const formData = await request.formData();
|
||||
const json = {
|
||||
amount: formData.get("amount"),
|
||||
for_self: formData.get("for_self"),
|
||||
for_other: formData.get("for_other"),
|
||||
payee: formData.get("payee"),
|
||||
added_by: user._id
|
||||
}
|
||||
await dbConnect();
|
||||
let id;
|
||||
try{
|
||||
id = (await Payment.create(json))._id.toString();
|
||||
} catch(e){
|
||||
await dbDisconnect();
|
||||
throw error(400, e)
|
||||
}
|
||||
|
||||
await dbDisconnect();
|
||||
const img = formData.get("file")
|
||||
if(img){
|
||||
//this feels stupid, is there a smarter way directly to Buffer?
|
||||
const full_res = Buffer.from(await img.arrayBuffer())
|
||||
|
||||
await sharp(full_res)
|
||||
.toFormat('webp')
|
||||
.toFile(path.join(IMAGE_DIR,
|
||||
"abrechnung",
|
||||
"full",
|
||||
id + '.webp'))
|
||||
|
||||
await sharp(full_res)
|
||||
.resize({width: 20})
|
||||
.toFormat('webp')
|
||||
.toFile(path.join(IMAGE_DIR,
|
||||
"abrechnung",
|
||||
"placeholder",
|
||||
id + '.webp'))
|
||||
}
|
||||
return new Response(JSON.stringify({message: "Added payment successfully"}),{
|
||||
status: 200,
|
||||
});
|
||||
}
|
||||
};
|
@ -1,24 +0,0 @@
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { Payment } from '../../../../models/Payment';
|
||||
import { dbConnect, dbDisconnect } from '../../../../utils/db';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { authenticateUser } from '$lib/js/authenticate';
|
||||
// header: use for bearer token for now
|
||||
// recipe json in body
|
||||
export const POST: RequestHandler = async ({request, cookies}) => {
|
||||
let json = await request.json()
|
||||
|
||||
const user = await authenticateUser(cookies)
|
||||
if(!user) throw error(401, "Need to be logged in")
|
||||
if(!user.access.includes("abrechnung")){
|
||||
throw error(401, "Insufficient permissions")
|
||||
}
|
||||
else{
|
||||
await dbConnect();
|
||||
await Payment.findOneAndDelete({_id: json.id});
|
||||
await dbDisconnect();
|
||||
return new Response(JSON.stringify({msg: "Deleted payment successfully"}),{
|
||||
status: 200,
|
||||
});
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { Payment } from '../../../../models/Payment';
|
||||
import { dbConnect, dbDisconnect } from '../../../../utils/db';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { authenticateUser } from '$lib/js/authenticate';
|
||||
// header: use for bearer token for now
|
||||
// recipe json in body
|
||||
export const POST: RequestHandler = async ({request, cookies}) => {
|
||||
let message = await request.json()
|
||||
const json = message.payment
|
||||
const user = await authenticateUser(cookies)
|
||||
if(!user){
|
||||
throw error(403, "Not logged in")
|
||||
}
|
||||
else if(!user.access.includes("abrechnung")){
|
||||
throw error(403, "This user does not have edit permissions for payments")
|
||||
}
|
||||
else{
|
||||
await dbConnect();
|
||||
await Payment.findOneAndUpdate({_id: json.id}, json);
|
||||
await dbDisconnect();
|
||||
return new Response(JSON.stringify({msg: "Edited payment successfully"}),{
|
||||
status: 200,
|
||||
});
|
||||
|
||||
}
|
||||
};
|
@ -1,27 +0,0 @@
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { Payment } from '../../../../../models/Payment';
|
||||
import { dbConnect, dbDisconnect } from '../../../../../utils/db';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { authenticateUser } from '$lib/js/authenticate';
|
||||
// header: use for bearer token for now
|
||||
// recipe json in body
|
||||
export const POST: RequestHandler = async ({request, cookies}) => {
|
||||
let message = await request.json()
|
||||
const json = message.payment
|
||||
const user = await authenticateUser(cookies)
|
||||
if(!user){
|
||||
throw error(403, "Not logged in")
|
||||
}
|
||||
else if(!user.access.includes("abrechnung")){
|
||||
throw error(403, "This user does not have edit permissions for payments")
|
||||
}
|
||||
else{
|
||||
await dbConnect();
|
||||
const payment = await Payment.findOne({_id: json.id}).lean();
|
||||
await dbDisconnect();
|
||||
return new Response(JSON.stringify({payment}),{
|
||||
status: 200,
|
||||
});
|
||||
|
||||
}
|
||||
};
|
@ -1,28 +0,0 @@
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { Payment } from '../../../../../models/Payment';
|
||||
import { dbConnect, dbDisconnect } from '../../../../../utils/db';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { authenticateUser } from '$lib/js/authenticate';
|
||||
// header: use for bearer token for now
|
||||
// recipe json in body
|
||||
export const POST: RequestHandler = async ({request, cookies, params}) => {
|
||||
let message = await request.json()
|
||||
const n = params.range
|
||||
const start = message?.start ?? 0;
|
||||
const user = await authenticateUser(cookies)
|
||||
if(!user){
|
||||
throw error(403, "Not logged in")
|
||||
}
|
||||
else if(!user.access.includes("abrechnung")){
|
||||
throw error(403, "This user does not have viewing permissions for payments")
|
||||
}
|
||||
else{
|
||||
await dbConnect();
|
||||
const payments = await Payment.find({}).sort({ date: -1 }).skip(start).limit(n).lean()
|
||||
await dbDisconnect();
|
||||
return new Response(JSON.stringify({payments}),{
|
||||
status: 200,
|
||||
});
|
||||
|
||||
}
|
||||
};
|
@ -1,26 +0,0 @@
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { Payment } from '../../../../models/Payment';
|
||||
import { dbConnect, dbDisconnect } from '../../../../utils/db';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { authenticateUser } from '$lib/js/authenticate';
|
||||
import { User } from '../../../../models/User';
|
||||
// header: use for bearer token for now
|
||||
// recipe json in body
|
||||
export const GET: RequestHandler = async ({request, cookies}) => {
|
||||
const user = await authenticateUser(cookies)
|
||||
if(!user){
|
||||
throw error(403, "Not logged in")
|
||||
}
|
||||
else if(!user.access.includes("abrechnung")){
|
||||
throw error(403, "This user does not have edit permissions for payments")
|
||||
}
|
||||
else{
|
||||
await dbConnect();
|
||||
const users = await User.find({access: "abrechnung"}, 'username').lean()
|
||||
await dbDisconnect();
|
||||
return new Response(JSON.stringify({users}),{
|
||||
status: 200,
|
||||
});
|
||||
|
||||
}
|
||||
};
|
@ -2,29 +2,28 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { Recipe } from '../../../../models/Recipe';
|
||||
import { dbConnect, dbDisconnect } from '../../../../utils/db';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { authenticateUser } from '$lib/js/authenticate';;
|
||||
// header: use for bearer token for now
|
||||
// recipe json in body
|
||||
export const POST: RequestHandler = async ({request, cookies}) => {
|
||||
export const POST: RequestHandler = async ({request, cookies, locals}) => {
|
||||
let message = await request.json()
|
||||
const recipe_json = message.recipe
|
||||
const user = await authenticateUser(cookies)
|
||||
if(!user){
|
||||
let auth = await locals.auth();
|
||||
/*const user = session.user;*/
|
||||
console.log(auth)
|
||||
if(!auth){
|
||||
throw error(401, "Not logged in")
|
||||
}
|
||||
if(!user.access.includes("rezepte")){
|
||||
/*if(!user.access.includes("rezepte")){
|
||||
throw error(401, "This user does not have permissions to add recipes")
|
||||
}
|
||||
else{
|
||||
await dbConnect();
|
||||
try{
|
||||
await Recipe.create(recipe_json);
|
||||
} catch(e){
|
||||
throw error(400, e)
|
||||
}
|
||||
await dbDisconnect();
|
||||
return new Response(JSON.stringify({msg: "Added recipe successfully"}),{
|
||||
status: 200,
|
||||
});
|
||||
}
|
||||
}*/
|
||||
await dbConnect();
|
||||
try{
|
||||
await Recipe.create(recipe_json);
|
||||
} catch(e){
|
||||
throw error(400, e)
|
||||
}
|
||||
await dbDisconnect();
|
||||
return new Response(JSON.stringify({msg: "Added recipe successfully"}),{
|
||||
status: 200,
|
||||
});
|
||||
};
|
||||
|
@ -3,15 +3,13 @@ import { Recipe } from '../../../../models/Recipe';
|
||||
import { dbConnect, dbDisconnect } from '../../../../utils/db';
|
||||
import type {RecipeModelType} from '../../../../types/types';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { authenticateUser } from '$lib/js/authenticate';
|
||||
// header: use for bearer token for now
|
||||
// recipe json in body
|
||||
export const POST: RequestHandler = async ({request, cookies}) => {
|
||||
export const POST: RequestHandler = async ({request, locals}) => {
|
||||
let message = await request.json()
|
||||
|
||||
const user = await authenticateUser(cookies)
|
||||
if(!user) throw error(401, "Need to be logged in")
|
||||
if(!user.access.includes("rezepte")) throw error(401, "Insufficient permissions")
|
||||
const auth = await locals.auth();
|
||||
if(!auth) throw error(401, "Need to be logged in")
|
||||
|
||||
const short_name = message.old_short_name
|
||||
await dbConnect();
|
||||
|
@ -3,20 +3,15 @@ import { Recipe } from '../../../../models/Recipe';
|
||||
import { dbConnect, dbDisconnect } from '../../../../utils/db';
|
||||
import type {RecipeModelType} from '../../../../types/types';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { authenticateUser } from '$lib/js/authenticate';
|
||||
// header: use for bearer token for now
|
||||
// recipe json in body
|
||||
export const POST: RequestHandler = async ({request, cookies}) => {
|
||||
export const POST: RequestHandler = async ({request, locals}) => {
|
||||
let message = await request.json()
|
||||
const recipe_json = message.recipe
|
||||
const user = await authenticateUser(cookies)
|
||||
console.log(user)
|
||||
if(!user){
|
||||
const auth = await locals.auth();
|
||||
if(!auth){
|
||||
throw error(403, "Not logged in")
|
||||
}
|
||||
else if(!user.access.includes("rezepte")){
|
||||
throw error(403, "This user does not have edit permissions for recipes")
|
||||
}
|
||||
else{
|
||||
await dbConnect();
|
||||
await Recipe.findOneAndUpdate({short_name: message.old_short_name }, recipe_json);
|
||||
|
@ -3,13 +3,11 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { IMAGE_DIR } from '$env/static/private'
|
||||
import sharp from 'sharp';
|
||||
import { authenticateUser } from '$lib/js/authenticate';
|
||||
|
||||
export const POST = (async ({ request, cookies }) => {
|
||||
export const POST = (async ({ request, locals}) => {
|
||||
const data = await request.json();
|
||||
const user = await authenticateUser(cookies)
|
||||
if (!user) throw error(401, "Need to be logged in")
|
||||
if (!user.access.includes("rezepte")) throw error(401, "You don't have sufficient permissions for this")
|
||||
const auth = await locals.auth();
|
||||
if (!auth) throw error(401, "Need to be logged in")
|
||||
let full_res = new Buffer.from(data.image, 'base64')
|
||||
// reduce image size if over 500KB
|
||||
const MAX_SIZE_KB = 500
|
||||
|
@ -3,13 +3,12 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { IMAGE_DIR } from '$env/static/private'
|
||||
import { unlink } from 'node:fs';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { authenticateUser } from '$lib/js/authenticate';;
|
||||
|
||||
export const POST = (async ({ request, cookies }) => {
|
||||
export const POST = (async ({ request, locals}) => {
|
||||
const data = await request.json();
|
||||
const user = await authenticateUser(cookies)
|
||||
if(!user) throw error(401, "You need to be logged in")
|
||||
if(!user.access.includes("rezepte")) throw error(401, "Your don't have the required permission for this")
|
||||
const auth = await locals.auth()
|
||||
if(!auth) throw error(401, "You need to be logged in")
|
||||
|
||||
[ "full", "thumb", "placeholder"].forEach((folder) => {
|
||||
unlink(path.join(IMAGE_DIR, "rezepte", folder, data.name + ".webp"), (e) => {
|
||||
if(e) error(404, "could not delete: " + folder + "/" + data.name + ".webp" + e)
|
||||
|
@ -3,13 +3,11 @@ import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { IMAGE_DIR } from '$env/static/private'
|
||||
import { rename } from 'node:fs';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { authenticateUser } from '$lib/js/authenticate';
|
||||
|
||||
export const POST = (async ({ request, cookies }) => {
|
||||
export const POST = (async ({ request, locals}) => {
|
||||
const data = await request.json();
|
||||
const user = await authenticateUser(cookies)
|
||||
if(!user) throw error(401, "need to be logged in")
|
||||
if(!user.access.includes("rezepte")) throw error(401, "You don't have the required permission to do this")
|
||||
const auth = await locals.auth();
|
||||
if(!auth ) throw error(401, "need to be logged in")
|
||||
|
||||
[ "full", "thumb", "placeholder"].forEach((folder) => {
|
||||
const old_path = path.join(IMAGE_DIR, "rezepte", folder, data.old_name + ".webp")
|
||||
|
@ -6,7 +6,7 @@ import { rand_array } from '$lib/js/randomize';
|
||||
|
||||
export const GET: RequestHandler = async ({params}) => {
|
||||
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();
|
||||
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}) => {
|
||||
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();
|
||||
|
||||
recipes = JSON.parse(JSON.stringify(recipes));
|
||||
|
@ -6,7 +6,7 @@ import { rand_array } from '$lib/js/randomize';
|
||||
|
||||
export const GET: RequestHandler = async ({params}) => {
|
||||
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();
|
||||
|
||||
recipes = JSON.parse(JSON.stringify(recipes));
|
||||
|
@ -6,7 +6,7 @@ import { rand_array } from '$lib/js/randomize';
|
||||
|
||||
export const GET: RequestHandler = async ({params}) => {
|
||||
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();
|
||||
found_in_season = JSON.parse(JSON.stringify(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}) => {
|
||||
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();
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
};
|
7
src/routes/glaube/+layout.server.ts
Normal file
7
src/routes/glaube/+layout.server.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export const load : PageServerLoad = (async ({locals}) => {
|
||||
return {
|
||||
session: await locals.auth(),
|
||||
}
|
||||
});
|
18
src/routes/glaube/+layout.svelte
Normal file
18
src/routes/glaube/+layout.svelte
Normal file
@ -0,0 +1,18 @@
|
||||
<script>
|
||||
import Header from '$lib/components/Header.svelte'
|
||||
import UserHeader from '$lib/components/UserHeader.svelte';
|
||||
export let data
|
||||
let username = ""
|
||||
if(data.user){
|
||||
username = data.user.username
|
||||
}
|
||||
</script>
|
||||
<Header>
|
||||
<ul class=site_header slot=links>
|
||||
<li><a href="/glaube/gebete">Gebete</a></li>
|
||||
<li><a href="/glaube/rosenkranz">Rosenkranz</a></li>
|
||||
<li><a href="/glaube/predigten">Predigten</a></li>
|
||||
</ul>
|
||||
<UserHeader {username} slot=right_side></UserHeader>
|
||||
<slot></slot>
|
||||
</Header>
|
75
src/routes/glaube/+page.svelte
Normal file
75
src/routes/glaube/+page.svelte
Normal file
@ -0,0 +1,75 @@
|
||||
<script>
|
||||
import LinksGrid from '$lib/components/LinksGrid.svelte';
|
||||
</script>
|
||||
<style>
|
||||
h1{
|
||||
text-align: center;
|
||||
font-size: 3em;
|
||||
}
|
||||
p{
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<h1>Glaube</h1>
|
||||
<p>
|
||||
Hier findet man einige Gebete, den Rosenkranz und aufgearbeitete Predigten zum katholischen Glauben.
|
||||
Ein Fokus auf Latein und den tridentinischen Ritus wird zu bemerken sein.
|
||||
Diese Seiten sind noch im Aufbau und werden nach und nach erweitert.
|
||||
Bis jetzt sind nur die Gebete in einem guten Stand.
|
||||
</p>
|
||||
|
||||
<LinksGrid>
|
||||
<a href="/glaube/gebete">
|
||||
<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="M351.2 4.8c3.2-2 6.6-3.3 10-4.1c4.7-1 9.6-.9 14.1 .1c7.7 1.8 14.8 6.5 19.4 13.6L514.6 194.2c8.8 13.1 13.4 28.6 13.4 44.4v73.5c0 6.9 4.4 13 10.9 15.2l79.2 26.4C631.2 358 640 370.2 640 384v96c0 9.9-4.6 19.3-12.5 25.4s-18.1 8.1-27.7 5.5L431 465.9c-56-14.9-95-65.7-95-123.7V224c0-17.7 14.3-32 32-32s32 14.3 32 32v80c0 8.8 7.2 16 16 16s16-7.2 16-16V219.1c0-7-1.8-13.8-5.3-19.8L340.3 48.1c-1.7-3-2.9-6.1-3.6-9.3c-1-4.7-1-9.6 .1-14.1c1.9-8 6.8-15.2 14.3-19.9zm-62.4 0c7.5 4.6 12.4 11.9 14.3 19.9c1.1 4.6 1.2 9.4 .1 14.1c-.7 3.2-1.9 6.3-3.6 9.3L213.3 199.3c-3.5 6-5.3 12.9-5.3 19.8V304c0 8.8 7.2 16 16 16s16-7.2 16-16V224c0-17.7 14.3-32 32-32s32 14.3 32 32V342.3c0 58-39 108.7-95 123.7l-168.7 45c-9.6 2.6-19.9 .5-27.7-5.5S0 490 0 480V384c0-13.8 8.8-26 21.9-30.4l79.2-26.4c6.5-2.2 10.9-8.3 10.9-15.2V238.5c0-15.8 4.7-31.2 13.4-44.4L245.2 14.5c4.6-7.1 11.7-11.8 19.4-13.6c4.6-1.1 9.4-1.2 14.1-.1c3.5 .8 6.9 2.1 10 4.1z"/></svg>
|
||||
<h3>Gebete</h3>
|
||||
</a>
|
||||
<a href=/glaube/rosenkranz >
|
||||
<svg viewBox="0 0 512 512">
|
||||
<g>
|
||||
<path class="st0" d="M241.251,145.056c-39.203-17.423-91.472,17.423-104.54,60.982c-13.068,43.558,8.712,117.608,65.337,143.742
|
||||
c56.626,26.135,108.896-8.712,87.117-39.202c-74.049-8.712-121.963-87.117-100.184-126.319S280.453,162.479,241.251,145.056z"/>
|
||||
<path class="st0" d="M337.079,271.375c47.914-39.202,21.779-126.319-17.423-135.031c-39.202-8.712-56.626,13.068-26.135,39.202
|
||||
c39.203,30.491-8.712,91.472-39.202,87.117C254.318,262.663,289.165,310.577,337.079,271.375z"/>
|
||||
<path class="st0" d="M254.318,119.788c43.558-17.423,74.049-9.579,100.184,16.556c13.068-39.202-30.491-104.54-108.896-113.252
|
||||
S93.153,118.921,127.999,171.191C136.711,153.767,188.981,106.721,254.318,119.788z"/>
|
||||
<path class="st0" d="M110.576,245.24C36.527,262.663,28.87,335.248,45.239,380.27c17.423,47.914,4.356,82.761,26.135,91.472
|
||||
c20.622,8.253,91.472,13.068,152.454,17.423c60.982,4.356,108.896-47.914,91.472-108.896
|
||||
C141.067,410.761,110.576,284.442,110.576,245.24z"/>
|
||||
<path class="st0" d="M93.883,235.796c0,0,2.178-28.313,10.89-43.558c-4.356-4.356-8.712-21.779-8.712-21.779
|
||||
s-4.356-19.601-4.356-34.846c-32.669-6.534-89.295,34.846-91.472,41.38c-2.178,6.534,10.889,80.583,39.202,82.761
|
||||
C69.927,235.796,93.883,235.796,93.883,235.796z"/>
|
||||
<path class="st0" d="M489.533,175.546c-39.202-82.761-113.252-65.337-113.252-65.337s4.356,21.779-4.356,34.846
|
||||
c43.558,47.914,13.067,146.643-24.681,158.265c130.675,56.626,159.712-58.081,164.068-75.504
|
||||
C515.668,210.393,498.245,197.326,489.533,175.546z"/>
|
||||
<path class="st0" d="M454.108,332.076c-22.359,15.841-85.663,11.613-121.964-7.265c1.446,14.514-13.067,37.756-20.325,39.202
|
||||
c27.59,11.621,53.725,62.436,7.265,116.161c18.878,18.87,95.828,4.356,140.842-24.689c7.325-4.722,18.869-52.27,21.779-79.851
|
||||
C485.56,339.103,488.963,307.387,454.108,332.076z"/>
|
||||
<path class="st0" d="M257.227,213.294c-18.928,5.164-30.439-6.27-23.234-18.869c5.811-10.167,5.266-20.69-8.712-13.068
|
||||
c-29.044,17.423-11.612,66.784,24.689,62.428c49.36-17.423,27.581-62.428,14.514-60.982
|
||||
C251.417,184.249,273.196,208.938,257.227,213.294z"/>
|
||||
</g>
|
||||
</svg>
|
||||
<h3>Rosenkranz</h3>
|
||||
</a>
|
||||
<a href="/glaube/predigten">
|
||||
<svg
|
||||
enable-background="new 0 0 512 512"
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<path
|
||||
d="m134.057 149.979v-69.942h-74.41c-8.284 0-15 6.716-15 15v39.94c0 8.284 6.716 15 15 15z"/>
|
||||
<path d="m437.947 391.026v-211.047h-60.004v150.343c0 4.694-2.197 9.118-5.938 11.954-2.637 1.999-5.826 3.046-9.062 3.046-1.354 0-2.717-.184-4.051-.558l-102.892-28.865-102.892 28.865c-4.521 1.267-9.374.346-13.113-2.489-3.741-2.836-5.938-7.259-5.938-11.954v-150.342h-60.004v211.047z"/>
|
||||
<path d="m377.943 149.979 74.409-.002c8.284 0 15-6.716 15-15v-39.94c0-8.284-6.716-15-15-15h-74.409z"/>
|
||||
<path d="m164.057 310.535 87.892-24.657c1.325-.372 2.688-.558 4.052-.558s2.727.186 4.052.558l87.892 24.657v-230.5h-183.888zm106.943-175.06v17.394h15.34c8.284 0 15 6.716 15 15s-6.716 15-15 15h-15.34v57.793c0 8.284-6.716 15-15 15s-15-6.716-15-15v-57.793h-15.34c-8.284 0-15-6.716-15-15s6.716-15 15-15h15.34v-17.394c0-8.284 6.716-15 15-15s15 6.715 15 15z"/>
|
||||
<path d="m497 482h-18.397v-35.972c0-13.785-11.215-25-25-25h-395.206c-13.785 0-25 11.215-25 25v35.972h-18.397c-8.284 0-15 6.716-15 15s6.716 15 15 15h482c8.284 0 15-6.716 15-15s-6.716-15-15-15z"/>
|
||||
<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>
|
||||
</svg>
|
||||
<h3>Predigten</h3>
|
||||
</a>
|
||||
</LinksGrid>
|
338
src/routes/glaube/gebete/+page.svelte
Normal file
338
src/routes/glaube/gebete/+page.svelte
Normal file
@ -0,0 +1,338 @@
|
||||
<script>
|
||||
import "$lib/css/christ.css";
|
||||
import "$lib/css/nordtheme.css";
|
||||
import Gebet from "./Gebet.svelte";
|
||||
</script>
|
||||
<style>
|
||||
.ccontainer{
|
||||
margin: auto;
|
||||
overflow-x: visible;
|
||||
max-width: 1000px;
|
||||
}
|
||||
.container{
|
||||
column-count: 2;
|
||||
column-gap: 1em;
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
.container{
|
||||
column-count: 1;
|
||||
padding-left: calc((100% - 600px ) * 0.5); /* ugly*/
|
||||
}
|
||||
}
|
||||
:global(.container > *){
|
||||
break-inside: avoid-column; /* Prevent children from splitting across columns */
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
h1{
|
||||
text-align: center;
|
||||
font-size: 3rem;
|
||||
}
|
||||
</style>
|
||||
<h1>Gebete</h1>
|
||||
|
||||
|
||||
<div class="ccontainer">
|
||||
<div class=container>
|
||||
|
||||
<Gebet name={"Das heilige Kreuzzeichen"} is_bilingue={true} >
|
||||
<p>
|
||||
<v lang=la>In nómine <i>♱</i> Patris, et Fílii, et Spíritus Sancti. Amen.</v>
|
||||
<v lang=de>Im Namen des <i>♱</i> Vaters und des Sohnes und des Heiligen Geistes. Amen.</v>
|
||||
</p>
|
||||
</Gebet>
|
||||
|
||||
<Gebet name={"Glória Patri"} is_bilingue={true}>
|
||||
<p>
|
||||
<v lang=la>Glória Patri, et Fílio, et Spirítui Sancto.</v>
|
||||
<v lang=de>Ehre sei dem Vater und dem Sohne und dem Hl. Geiste.</v>
|
||||
<v lang=la>Sicute erat in princípio, et nunc, et semper:</v>
|
||||
<v lang=de>Wie es war am Anfang, so auch jetzt und allezeit</v>
|
||||
<v lang=la>et in sǽcula sæculórum. Amen.</v>
|
||||
<v lang=de>und in Ewigkeit. Amen.</v>
|
||||
</p>
|
||||
</Gebet>
|
||||
|
||||
<Gebet name={"Paternoster"} is_bilingue={true} >
|
||||
<p>
|
||||
<v lang=la>Pater noster, qui es in cælis</v>
|
||||
<v lang=de>Vater unser, der Du bist im Himmel,</v>
|
||||
<v lang=la>Sanctificétur nomen tuum</v>
|
||||
<v lang=de>geheiligt werde Dein Name;</v>
|
||||
<v lang=la>Advéniat regnum tuum</v>
|
||||
<v lang=de>zu uns komme Dein Reich;</v>
|
||||
<v lang=la>Fiat volúntas tua, sicut in cælo, et in terra.</v>
|
||||
<v lang=de>Dein Wille geschehe, wie im Himmel, also auch auf Erden!</v>
|
||||
<v lang=la>Panem nostrum quotidiánum da nobis hódie.</v>
|
||||
<v lang=de>Unser tägliches Brot gib uns heute;</v>
|
||||
<v lang=la>Et dimítte nobis debíta nostra,</v>
|
||||
<v lang=de>und vergib uns unsere Schulden,</v>
|
||||
<v lang=la>sicut et nos dimíttimus debitóribus nostris.</v>
|
||||
<v lang=de>wie auch wir vergeben unsern Schuldigern;</v>
|
||||
<v lang=la>Et ne nos indúcas in tentatiónem.</v>
|
||||
<v lang=de>und führe uns nicht in Versuchung.</v>
|
||||
<v lang=la>Sed líbera nos a malo. Amen.</v>
|
||||
<v lang=de>Sondern erlöse uns von dem Übel. Amen.</v>
|
||||
</p>
|
||||
</Gebet>
|
||||
|
||||
<Gebet name={"Credo"} is_bilingue={true}>
|
||||
<p>
|
||||
<v lang=la>Credo in unum <i><sup>⚬</sup></i> Deum, Patrem omnipoténtem,</v>
|
||||
<v lang=de>Ich glaub an den einen <i><sup>⚬</sup></i> Gott. Den allmächtigen Vater,</v>
|
||||
<v lang=la>factórem cæli et terræ,</v>
|
||||
<v lang=de>Schöpfer des Himmels und der Erde,</v>
|
||||
<v lang=la>visibílium ómnium et invisibílium.</v>
|
||||
<v lang=de>aller sichtbaren und unsichtbaren Dinge.</v>
|
||||
<v lang=la>Et in unum Dóminum <i><sup>⚬</sup></i> Jesum Christum,</v>
|
||||
<v lang=de>Und an den einen Herrn <i><sup>⚬</sup></i> Jesus Christus,</v>
|
||||
<v lang=la>Fílium Dei unigénitum.</v>
|
||||
<v lang=de>Gottes eingeborenen Sohn.</v>
|
||||
<v lang=la>Et ex Patre natum ante ómnia sǽcula.</v>
|
||||
<v lang=de>Er ist aus dem Vater geboren vor aller Zeit.</v>
|
||||
<v lang=la>Deum de Deo,</v>
|
||||
<v lang=de>Gott von Gott,</v>
|
||||
<v lang=la>lumen de lúmine,</v>
|
||||
<v lang=de>Licht vom Lichte,</v>
|
||||
<v lang=la>Deum verum de Deo vero.</v>
|
||||
<v lang=de>wahrer Gott vom wahren Gott;</v>
|
||||
<v lang=la>Génitum, non factum,</v>
|
||||
<v lang=de>Gezeugt, nicht geschaffen,</v>
|
||||
<v lang=la>consubstantiálem Patri:</v>
|
||||
<v lang=de>eines Wesens mit dem Vater;</v>
|
||||
<v lang=la>per quem ómnia facta sunt.</v>
|
||||
<v lang=de>durch Ihn ist alles geschaffen.</v>
|
||||
<v lang=la>Qui propter nos hómines</v>
|
||||
<v lang=de>Für uns Menschen</v>
|
||||
<v lang=la>et propter nostram salútem</v>
|
||||
<v lang=de>und um unsres Heiles willen</v>
|
||||
<v lang=la>descéndit de cælis.</v>
|
||||
<v lang=de>ist Er vom Himmel herabgestiegen.</v>
|
||||
</p>
|
||||
<p>
|
||||
<v lang=la>Et incarnátus est de Spíritu Sancto</v>
|
||||
<v lang=de>Er hat Fleisch angenommen durch den Hl. Geist</v>
|
||||
<v lang=la>ex <i><sup>⚬</sup></i> María Vírgine:</v>
|
||||
<v lang=de>aus <i><sup>⚬</sup></i> Maria, der Jungfrau</v>
|
||||
<v lang=la>Et homo factus est.</v>
|
||||
<v lang=de>und ist Mensch geworden.</v>
|
||||
<v lang=la>Crucifíxus étiam pro nobis:</v>
|
||||
<v lang=de>Gekreuzigt wurde Er sogar für uns;</v>
|
||||
<v lang=la>sub Póntio Piláto passus, et sepúltus est.</v>
|
||||
<v lang=de>unter Pontius Pilatus hat Er den Tod erlitten</v>
|
||||
<v lang=de>und ist begraben worden</v>
|
||||
</p>
|
||||
<p>
|
||||
<v lang=la>Et resurréxit tértia die,</v>
|
||||
<v lang=de>Er ist auferstanden am dritten Tage,</v>
|
||||
<v lang=la>secúndum Scriptúras.</v>
|
||||
<v lang=de>gemäß der Schrift;</v>
|
||||
<v lang=la>Et ascéndit in cáelum:</v>
|
||||
<v lang=de>Er ist aufgefahren in den Himmel</v>
|
||||
<v lang=la>sedet ad déxteram Patris.</v>
|
||||
<v lang=de>und sitzet zur Rechten des Vaters.</v>
|
||||
</p>
|
||||
<p>
|
||||
<v lang=la>Et íterum ventúrus est cum glória</v>
|
||||
<v lang=de>Er wird wiederkommen in Herrlichkeit,</v>
|
||||
<v lang=la>judicáre vivos et mórtuos:</v>
|
||||
<v lang=de>Gericht zu halten über Lebende und Tote:</v>
|
||||
<v lang=la>cujus regni non erit finis.</v>
|
||||
<v lang=de>und Seines Reiches wird kein Endes sein.</v>
|
||||
</p>
|
||||
<p>
|
||||
<v lang=la>Et in Spíritum Sanctum,</v>
|
||||
<v lang=de>Ich glaube an den Heiligen Geist,</v>
|
||||
<v lang=la>Dóminum et vivificántem:</v>
|
||||
<v lang=de>den Herrn und Lebensspender,</v>
|
||||
<v lang=la>qui ex Patre Filióque procédit.</v>
|
||||
<v lang=de>der vom Vater und vom Sohne ausgeht.</v>
|
||||
<v lang=la>Qui cum Patre et Fílio simul <i><sup></sup></i> adorátur et conglorificátur:</v>
|
||||
<v lang=de>zugleich <i><sup></sup></i> angebetet und verherrlicht;</v>
|
||||
<v lang=la>qui locútus est per Prophétas.</v>
|
||||
<v lang=de>Er hat gesprochen durch die Propheten.</v>
|
||||
<v lang=la>Et unam sanctam cathólicam et apostólicam Ecclésiam.</v>
|
||||
<v lang=de>Ich glaube an die eine, heilige, katholische und apostolische Kirche.</v>
|
||||
<v lang=la>Confíteor unum baptísma</v>
|
||||
<v lang=de>Ich bekenne die eine Taufe</v>
|
||||
<v lang=la>in remissiónem peccatórum.</v>
|
||||
<v lang=de>zur Vergebung der Sünden.</v>
|
||||
<v lang=la>Et exspécto resurrectiónem mortuórum.</v>
|
||||
<v lang=de>Ich erwarte die Auferstehung der Toten.</v>
|
||||
<v lang=la><i>♱</i> Et vitam ventúri sǽculi. Amen.</v>
|
||||
<v lang=de><i>♱</i> Und das Leben der zukünftigen Welt. Amen.</v>
|
||||
</p>
|
||||
</Gebet>
|
||||
|
||||
<Gebet name={"Ave Maria"} is_bilingue={true}>
|
||||
<p>
|
||||
<v lang=la>Ave María, grátia plena. Dóminus tecum,</v>
|
||||
<v lang=de>Gegrüsset seist du Maria, voll der Gnade; der Herr ist mit dir;</v>
|
||||
<v lang=la>benedícta tu in muliéribus,</v>
|
||||
<v lang=de>du bist gebenedeit unter den Weibern,</v>
|
||||
<v lang=la>et benedíctus fructus ventris tui, Jesus.</v>
|
||||
<v lang=de>und gebenedeit ist die Frucht deines Leibes, Jesus.</v>
|
||||
</p>
|
||||
<p>
|
||||
<v lang=la>Sancta María, mater Dei, ora pro nobis peccatóribus,</v>
|
||||
<v lang=de>Heilige Maria, Mutter Gottes, bitte für uns Sünder</v>
|
||||
<v lang=la>nunc, et in hora mortis nostræ. Amen.</v>
|
||||
<v lang=de>jetzt und in der Stunde unseres Todes! Amen.</v>
|
||||
</p>
|
||||
</Gebet>
|
||||
|
||||
<Gebet name={"Salve Regina"} is_bilingue={true}>
|
||||
<p>
|
||||
<v lang=la>Salve, Regína,</v>
|
||||
<v lang=de>Sei gegrüßt, o Königin,</v>
|
||||
<v lang=la>máter misericórdiae;</v>
|
||||
<v lang=de>Mutter der Barmherzigkeit,</v>
|
||||
<v lang=la>Víta, dulcédo et spes nóstra, sálve.</v>
|
||||
<v lang=de>unser Leben, unsre Wonne</v>
|
||||
<v lang=de>und unsere Hoffnung, sei gegrüßt!</v>
|
||||
</p>
|
||||
<p>
|
||||
<v lang=la>Ad te clamámus, éxsules fílii Hévae.</v>
|
||||
<v lang=de>Zu dir rufen wir verbannte Kinder Evas;</v>
|
||||
<v lang=la>Ad te suspirámus,</v>
|
||||
<v lang=de>zu dir seufzen wir</v>
|
||||
<v lang=la>geméntes et fléntes in hac lacrimárum válle.</v>
|
||||
<v lang=de>trauernd und weinend in diesem Tal der Tränen.</v>
|
||||
<v lang=la>Eia ergo, Advocáta nóstra,</v>
|
||||
<v lang=de>Wohlan denn, unsre Fürsprecherin,</v>
|
||||
<v lang=la>íllos túos misericórdes óculos ad nos convérte.</v>
|
||||
<v lang=de>deine barmherzigen Augen wende zu uns</v>
|
||||
<v lang=la>Et Jésum, benedíctum frúctum véntris túi,</v>
|
||||
<v lang=de>und nach diesem Elend zeige uns Jesus,</v>
|
||||
<v lang=la>nóbis post hoc exsílíum osténde.</v>
|
||||
<v lang=de>die gebenedeite Frucht deines Leibes.</v>
|
||||
<v lang=la>O clémens, o pía, o dúlcis Vírgo María.</v>
|
||||
<v lang=de>O gütige, o milde, o süße Jungfrau Maria.</v>
|
||||
</p>
|
||||
</Gebet>
|
||||
|
||||
<Gebet name={"Das Fatimagebet"} is_bilingue={true}>
|
||||
<v lang=la>Ó mí Jésú, dímitte nóbís débita nostra,</v>
|
||||
<v lang=de>O mein Jesus, verzeih' uns unsere Sünden,</v>
|
||||
<v lang=la>líberá nós ab igne ínferní,</v>
|
||||
<v lang=de>bewahre uns vor den Feuern der Hölle</v>
|
||||
<v lang=la>condúc in cælum omnés animás, </v>
|
||||
<v lang=de>und führe alle Seelen in den Himmel,</v>
|
||||
<v lang=la>præsertim illás,</v>
|
||||
<v lang=de>besonders jene,</v>
|
||||
<v lang=la>quæ maximé indigent misericordiá tuá. Amen.</v>
|
||||
<v lang=de>die Deiner Barmherzigkeit am meisten bedürfen. Amen.</v>
|
||||
</Gebet>
|
||||
|
||||
<Gebet name={"Glória"} is_bilingue={true}>
|
||||
<p slot="intro">Der uralte Gesang beginnt mit den Worten, mit denen die Engelscharen den neugeborenen Welterlöser feierten. Er preist zunächst Gott Vater, dann Gott Sohn; er schließt mit einer Huldigung an die Heiligste Dreifaltigkeit, wobei man sich mit dem großen Kreuze bezeichnet.</p>
|
||||
<p>
|
||||
<v lang=la>Glória in excélsis <i><sup>⚬</sup></i> Deo.</v>
|
||||
<v lang=de>Ehre sei <i><sup>⚬</sup></i> Gott in der Höhe.</v>
|
||||
<v lang=la>Et in terra pax homínibus</v>
|
||||
<v lang=de>Und auf Erden Friede den Mesnchen,</v>
|
||||
<v lang=la>bonæ voluntátis.</v>
|
||||
<v lang=de>die guten Willens sind.</v>
|
||||
<v lang=la>Laudámus te.</v>
|
||||
<v lang=de>Wir loben Dich.</v>
|
||||
<v lang=la>Benedícimus te.</v>
|
||||
<v lang=de>Wir preisen Dich.</v>
|
||||
<v lang=la><i><sup>⚬</sup></i> Adorámus te.</v>
|
||||
<v lang=de><i><sup>⚬</sup></i> Wir beten Dich an.</v>
|
||||
<v lang=la>Glorificámus te.</v>
|
||||
<v lang=de>Wir verherrlichen Dich.</v>
|
||||
<v lang=la><i><sup>⚬</sup></i> Grátias ágimus tibi</v>
|
||||
<v lang=de><i><sup>⚬</sup></i> Wir sagen Dir Dank</v>
|
||||
<v lang=la>propter magnam glóriam tuam.</v>
|
||||
<v lang=de>ob Deiner großen Herrlichkeit.</v>
|
||||
<v lang=la>Dómine Deus, Rex cæléstis,</v>
|
||||
<v lang=de>Herr und Gott, König des Himmels,</v>
|
||||
<v lang=la>Deus Pater omnípotens.</v>
|
||||
<v lang=de>Gott allmächtiger Vater!</v>
|
||||
<v lang=la>Dómine Fili unigénite, <i><sup>⚬</sup></i> Jesu Christe.</v>
|
||||
<v lang=de>Herr <i><sup>⚬</sup></i> Jesus Christus, eingeborener Sohn!</v>
|
||||
<v lang=la>Dómine Deus, Agnus Dei,</v>
|
||||
<v lang=de>Herr und Gott, Lamm Gottes,</v>
|
||||
<v lang=la>Fílius Patris.</v>
|
||||
<v lang=de>Sohn des Vaters!</v>
|
||||
<v lang=la>Qui tollis peccáta mundi,</v>
|
||||
<v lang=de>Du nimmst hinweg die Sünden der Welt:</v>
|
||||
<v lang=la>miserére nobis.</v>
|
||||
<v lang=de>erbarme Dich unser.</v>
|
||||
<v lang=la>Qui tollis peccáta mundi,</v>
|
||||
<v lang=de>Du nimmst hinwerg die Sünden der Welt.</v>
|
||||
<v lang=la><i><sup>⚬</sup></i> súscipe depreciatiónem nostram.</v>
|
||||
<v lang=de><i><sup>⚬</sup></i> nimm unser Flehen gnädig auf.</v>
|
||||
<v lang=la>Qui sedes ad déxteram Patris,</v>
|
||||
<v lang=de>Du sitzt zur Rechten des Vaters:</v>
|
||||
<v lang=la>miserére nobis.</v>
|
||||
<v lang=de>erbarme Dich unser.</v>
|
||||
<v lang=la>Quóniam tu solus Sanctus.</v>
|
||||
<v lang=de>Denn Du allein bist der Heilige.</v>
|
||||
<v lang=la>Tu solus Altíssimus,</v>
|
||||
<v lang=de>Du allein der Höchste,</v>
|
||||
<v lang=la><i><sup>⚬</sup></i> Jesu Christe.</v>
|
||||
<v lang=de><i><sup>⚬</sup></i> Jesus Christus,</v>
|
||||
<v lang=la>Cum Sancto Spíritu</v>
|
||||
<v lang=de>Mit dem Hl. Geiste,</v>
|
||||
<v lang=la><i>♱</i> in glória Dei Patris. Amen.</v>
|
||||
<v lang=de><i>♱</i> in der Herrlichkeit Gottes des Vaters. Amen.</v>
|
||||
</p>
|
||||
</Gebet>
|
||||
|
||||
<Gebet name={"Gebet zum hl. Erzengel Michael"} is_bilingue={true}>
|
||||
<p>
|
||||
<v lang=la>Sáncte Míchael Archángele,</v>
|
||||
<v lang=de>Heiliger Erzengel Michael,</v>
|
||||
<v lang=la>defénde nos in proélio,</v>
|
||||
<v lang=de>verteidige uns im Kampfe!</v>
|
||||
<v lang=la>cóntra nequítam et insídias</v>
|
||||
<v lang=de>Gegen die Bosheit und Nachstellungen</v>
|
||||
<v lang=la>diáboli ésto præsídium.</v>
|
||||
<v lang=de>des Teufels sei unser Schutz. </v>
|
||||
<v lang=la>Ímperet ílli Déus, súpplices deprecámur:</v>
|
||||
<v lang=de>»Gott gebiete ihm!«, so bitten wir flehentlich.</v>
|
||||
<v lang=la>tuque, Prínceps milítæ cæléstis,</v>
|
||||
<v lang=de>Du aber, Fürst der himmlischen Heerscharen,</v>
|
||||
<v lang=la>Sátanam aliósque spíritus malígnos,</v>
|
||||
<v lang=de>stoße den Satan und die anderen bösen Geister,</v>
|
||||
<v lang=la>qui ad perditiónem animárum</v>
|
||||
<v lang=la>pervagántur in múndo,</v>
|
||||
<v lang=de>die in der Welt umhergehen,</v>
|
||||
<v lang=de>um die Seelen zu verderben,</v>
|
||||
<v lang=la>divína virtúte, in inférnum detrúde. Amen.</v>
|
||||
<v lang=de>durch die Kraft Gottes in die Hölle. Amen.</v>
|
||||
</p>
|
||||
</Gebet>
|
||||
|
||||
<Gebet name={"Bruder Klaus Gebet"} is_bilingue={false}>
|
||||
<p>
|
||||
<v lang=de>Mein Herr und mein Gott,</v>
|
||||
<v lang=de>nimm alles von mir,</v>
|
||||
<v lang=de>was mich hindert zu Dir.</v>
|
||||
</p>
|
||||
<p>
|
||||
<v lang=de>Mein Herr und mein Gott,</v>
|
||||
<v lang=de>gib alles mir,</v>
|
||||
<v lang=de>was mich führet zu Dir.</v>
|
||||
</p>
|
||||
<p>
|
||||
<v lang=de>Mein Herr und mein Gott,</v>
|
||||
<v lang=de>nimm mich mir</v>
|
||||
<v lang=de>und gib mich ganz zu eigen Dir.</v>
|
||||
</p>
|
||||
</Gebet>
|
||||
|
||||
<Gebet name={"Josephgebet des hl. Papst Pius X"} is_bilingue={false}>
|
||||
<p slot="intro">Wenn man mehr zum hl. Joseph als <q>Patrone Morientium</q> wissen möchte kann man <a href="predigten/20220319-hl._joseph">hier</a> die Predigt zum Festtag des hl. Joseph nachlesen.</p>
|
||||
|
||||
<p>
|
||||
<v>Jungfräulicher Vater Jesu,</v>
|
||||
<v>Reinster Bräutigam Mariä,</v>
|
||||
<v>Sankt Joseph, bitte Tag für Tag bei Jesus, dem Sohn Gottes.</v>
|
||||
<v>Seine Kraft und Gnade soll uns stärken,</v>
|
||||
<v>dass wir siegreich streiten im Leben</v>
|
||||
<v>und die Krone von Ihm erhalten im Sterben.</v>
|
||||
</p>
|
||||
</Gebet>
|
||||
</div>
|
||||
</div>
|
52
src/routes/glaube/gebete/Gebet.svelte
Normal file
52
src/routes/glaube/gebete/Gebet.svelte
Normal file
@ -0,0 +1,52 @@
|
||||
<script>
|
||||
export let is_bilingue;
|
||||
export let name;
|
||||
</script>
|
||||
<style>
|
||||
div.gebet{
|
||||
text-align: center;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
:global(.gebet v){
|
||||
margin:0;
|
||||
display: block;
|
||||
}
|
||||
:global(.gebet v:lang(la)) {
|
||||
color: var(--nord6);
|
||||
}
|
||||
:global(.bilingue v:lang(de)){
|
||||
color: grey;
|
||||
}
|
||||
.gebet_wrapper h2{
|
||||
text-align: center;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
:global(.gebet i){
|
||||
font-style: normal;
|
||||
color: var(--nord11);
|
||||
font-weight: 900;
|
||||
}
|
||||
.gebet_wrapper{
|
||||
padding: 1em;
|
||||
background-color: var(--accent-dark);
|
||||
box-shadow: 0 0 1em black;
|
||||
max-width: 600px;
|
||||
}
|
||||
@media(prefers-color-scheme: light){
|
||||
.gebet_wrapper{
|
||||
background-color: var(--accent-light);
|
||||
}
|
||||
:global(.gebet v:lang(la)){
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="gebet_wrapper">
|
||||
<h2>{name}</h2>
|
||||
<slot name="intro"></slot>
|
||||
<div class="gebet" class:bilingue={is_bilingue}>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
314
src/routes/glaube/predigten/+page.svelte
Normal file
314
src/routes/glaube/predigten/+page.svelte
Normal file
@ -0,0 +1,314 @@
|
||||
<script>
|
||||
import "$lib/css/nordtheme.css"
|
||||
import "$lib/css/christ.css"
|
||||
</script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'UnifrakturMaguntia';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('/fonts/UnifrakturMaguntia20.ttf');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
.bibel{
|
||||
font-family: 'UnifrakturMaguntia', cursive;
|
||||
-moz-font-feature-settings: "cv11";
|
||||
-webkit-font-feature-settings: "cv11";
|
||||
-ms-font-feature-settings: "cv11";
|
||||
font-feature-settings: "cv11";
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
li::marker, i, li:lang(la){
|
||||
font-family: serif;
|
||||
}
|
||||
ol{
|
||||
list-style-position: inside;
|
||||
}
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
h1{
|
||||
font-size: 4rem;
|
||||
}
|
||||
h2{
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
h3{
|
||||
font-size: 2rem;
|
||||
}
|
||||
h4{
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.quote p.title{
|
||||
font-weight: bold;
|
||||
}
|
||||
/*.quote .bibel{
|
||||
margin-bottom: 1em;
|
||||
}*/
|
||||
.quote q{
|
||||
display: block;
|
||||
width: 90%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.tod, .grund{
|
||||
font-size: 1.5rem;
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
margin-top: -1rem;
|
||||
}
|
||||
.schott q{
|
||||
quotes: "«" "»";
|
||||
}
|
||||
.predigt video {
|
||||
display: block;
|
||||
width: 80%;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
<h1>13. Februar 2022 - Septuagesima</h1>
|
||||
<p>Das ganze hier ist noch ein Platzhalter</p>
|
||||
<h2>Der Osterfestkreis</h2>
|
||||
<div class=schott>
|
||||
<p>
|
||||
Der Weihnachtsfestkreis schließt mit der Woche vor Septuagesima.
|
||||
Der ersehnte Erlöser ist gekommen und hat in seiner ersten Ankunft zugleich seine zweite, die am Gerichtstage erfolgen wird, begründet und begonnen.
|
||||
</p>
|
||||
<p>
|
||||
Jetzt ist die Zeit des anstrengten Kampfes gegen Sünde, Welt und Fleisch gekommen, die Zeit der mühevollen Aussaat, des sturmumtobten Wachsens.
|
||||
Durch Kampf zum Sieg, durch Sterben zum Leben, zur Auferstehung, zur Vollherrschaft Christi und schließlich zur Verklärung im Osterlichte!
|
||||
Christus soll in uns den Thron seiner Herrschaft errichten, einer Herrschaft, die uns nicht erdrückt, sondern erhöht; nicht beraubt, sondern bereichert; nicht einschränkt, sondern innerlich weitet und uns einmal mitherrschen läßt im ewigen Ostern des Himmels.
|
||||
</p>
|
||||
<p>
|
||||
Der Osterfestkreis umfaßt drei Abschnitte:
|
||||
die Zeit der Vorbereitung: Vorfasten- und Fastenzeit;
|
||||
die eigentliche Festzeit: Oster- und Pfingstfest;
|
||||
endlich die Zeit nach Pfingsten
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2> I. Die Zeit der Vorbereitung </h2>
|
||||
<h3> 1. Die Vorfastenzeit</h3>
|
||||
<div class=schott >
|
||||
<p>
|
||||
Sie umfaßt die Sonntage Septuagesima, Sexagesima und Quinquagesima.
|
||||
Diese Namen bezeichnen nicht die genauen Abstände bis zum Osterfest, sonder deuten auf die rund berechnete 70tägige, 60tägige, 50tägige Vorbereitungzeit auf Ostern.
|
||||
</p>
|
||||
<p>
|
||||
Der Name Septuagesima weckt die Erinnerung an die 70 Jahre der Gefangenschaft, welche die Juden zur Strafe für ihre Untreue fern von Jerusalem, zu Babylon, verbringen mußten, bevor sie wieder ins Gelobte Land zurückkerhen durften.
|
||||
So mahnt uns diese Zeit an unsre eigene Pilgerschaft aus der Fremde, aus der gottfernen Welt (Babylon), zum wahren Vaterland (Jerusalem).
|
||||
Diese Pilgerschaft ist für uns eine beständiger Kampf gegen die Feinde unsres Heiles.
|
||||
Für den göttlichen Heiland bedeutet das öffentliche Wirken Mühsal und Leiden und schließlich den Tod;
|
||||
so muß sich auch unser Leben, soll es dem seinen nachgebildet werden, auf Kämpfe, selbst auf ein geistiges Sterben gefaßt machen;
|
||||
erst dann wird es mit dem Heiland zum endlichen Triumph gelangen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2>Sonntag Septuagesima</h2>
|
||||
<p>13. Februar 2022 - <i>2. Klasse - Farbe violett</i></p>
|
||||
<h3> Schott </h3>
|
||||
<p class=schott>
|
||||
Durch den Kampf zum Sieg, duch Sterben zum Leben, zur Aufersteheung, zur Verklärung: das sind die Gedanken der Vorfastenzeit.
|
||||
Eine lichtvolle Darstellung dieser Gedanken ist der hl. Laurentius, der Patron der Katechumen in Rom und Patron heutigen Stationskirche.
|
||||
Larentius - in Todesnöten, auf dem glühenden Roste (Intr.). über ihm die Siegeskrone der Verklärung (vgl. Introituspsalm) - ist ein Wegweiser für die Katechumen und für uns.
|
||||
Mit ihm treten wir entschlossen und kampfbereit in die Rennbahn und eignen uns Pauli Geist und Grundsätze an (Epistel).
|
||||
Wir folgen dem Ruf des Hausvaters (Christi) in seinen Weinberg und sind bereit, seinen Willen zu tun (Evang.).
|
||||
Wir entsagen uns selbst und bringen uns in der Opfergabe dar.
|
||||
Gestützt auf die Kraft der Gnade Christi, die über uns im hl. Opfer uns besonders ind der hl. Kommunion verklärend aufleuchtet (Comm.), gehen wir neu gestärkt in den Kampf und die Mühsal unseres Christenberufes.
|
||||
</p>
|
||||
|
||||
<h3> Epistel </h3>
|
||||
<div class="epistel bibel">
|
||||
<ol><i>1 Cor. 9</i>
|
||||
<li value=24>Wisset ihr nicht, daß die, welche in der Rennbahn laufen, zwar alle laufen, aber nur einer erlangt den Preis? Laufet fo, daß ihr ihn erlanget!</li>
|
||||
<li>Jeder aber, der im Kampfspiele ringt, enthält sich von allem, und zwar jene, um eine vergängliche Krone zu empfangen, wir aber eine unvergängliche.</li>
|
||||
<li>Ich laufe demnach, nicht wie in's Ungewisse; ich kämpfe, nicht indem ich Luftstreiche thue,</li>
|
||||
<li>sondern ich züchtige meinen Leib, und bringe ihn in die Botmäßigkeit, damit ich nicht etwa, nachdem ich anderen gepredigt habe, selbst verworfen werde.</li>
|
||||
<i>1 Cor. 10</i>
|
||||
<li value=1>Denn ich will euch nicht in Unwissenheit lassen, Brüder! Daß unsere Väter alle unter der Wolke waren, und alle durch das Meer hindurch gingen,</li>
|
||||
<li>und alle auf Moses getauft wurden, in der Wolke und in dem Meere,</li>
|
||||
<li>und alle dieselbe geistige Speise aßen,</li>
|
||||
<li>und alle dieselbe geistigen Trank tranken (sie tranken nämlich aus einem geistigen, sie begleitenden Felsen, der Felsen aber war Christus);</li>
|
||||
<li>aber an der Mehrzahl von ihnen hatte Gott kein Wohlgefallen; denn sie wurden niedergestreckt in der Wüste.</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<h3> Evangelium </h3>
|
||||
<div class="evangelium bibel">
|
||||
<ol><i>Matth. 20</i>
|
||||
<li>Das Himmelreich ist gleich einem Hausvater, der am frühen Morgen ausging, um Arbeiter in seinen Weinberg zu dingen.</li>
|
||||
<li>Nachdem er nun mit den Arbeitern um einen Denar für den Tag übereingekommen war, sandte er sie in seinen Weinberg.</li>
|
||||
<li>Und als er um die dritte Stunde ausging, sah er andere au dem Markte müßig stehen,</li>
|
||||
<li>und sprach zu ihnen: Gehet auch ihr in meinen Weinberg, und was recht ist, werde ich euch geben.</li>
|
||||
<li>Sie aber gingen hin. Abermals ging er um die sechste und neunte Stunde aus, und that ebenso.</li>
|
||||
<li>Um die elfte Stunde aber ging er aus, und fand andere andere stehen, und sprach zu ihnen: Was stehet ihr hier den ganzen Tag müßig?</li>
|
||||
<li>Sie antworteten ihm: Weil uns niemand gedungen hat. Da sprach er zu ihnen: Gehet auch ihr in meinen Weinberg,</li>
|
||||
<li>Als es nun Abend geworden, sagte der Herr des Weinberges zu seinem Verwalter: Rufe die Arbeiter, und gib ihnen den Lohn, von den letzten anfangend, bis zu den ersten.</li>
|
||||
<li>Da nun die kamen, welche um die elfte Stunde eingetreten waren, empfingen sie jeder einen Denar.</li>
|
||||
<li>Wie aber auch die ersten kamen, meinten sie, daß sie mehr empfangen würden, aber auch sie erhielten jeder einen Denar.</li>
|
||||
<li>Und da sie ihn empfingen, murrten sie wider den Hausvater.</li>
|
||||
<li>und sprachen: Siese letzten haben eine einzige Stunde gearbeitet, und du hast sie uns gleich gehalten, die wir die Last und Hitze des Tages getragen haben.</li>
|
||||
<li>Er aber antowrtete einem aus ihnen, und sprach: Freund! ich thue dir nicht Unrecht; bist du nicht auf einen Denar mit mir eins geworden?</li>
|
||||
<li>Nimm, was dein ist, und gehe hin; ich will aber auch diesem letzten geben, wie dir.</li>
|
||||
<li>Oder ist es mir nicht erlaubt zu thun, was ich will? Ist etwa dein Auge darum böse, weil ich gut bin?</li>
|
||||
<li>So werden die Letzten die Ersten, und die Ersten die Letzten sein; denn viele sind berufen, aber wenige auserwählt!</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<h3> Predigt </h3>
|
||||
<p class="predigt einleitung" >
|
||||
Es handelt sich dabei um die Predigt von H. H. Pater Cadiet zu Zaitzkofen welche man <a href="https://www.youtube.com/watch?v=mzqmcbYq9Xk">hier</a> (<a href="https://bocken.org/static/predigten/20220213-Predigt_am_Sonntag_Septuagesima.mp4">Mirror</a>) auch noch nachschauen kann. Die Messe wurde via Livestream mitverfolgt.
|
||||
</p>
|
||||
<div class="predigt inhalt">
|
||||
|
||||
<p>Es ist nicht einfach in der heutigen Zeit als Gläubiger. Vieles was unnatürlich ist wird als natürlich verlogen und Gottes Werk verneint. Somit ist der Aufruf zum Kampf passender den je.
|
||||
Wir sollen gleich den Sportlern verzichten für das Heil der Seelen, dem Heil der eigenen Seele.
|
||||
</p>
|
||||
<p>
|
||||
Diese Bildnis der Spiele im Stadion waren vermutlich ein gutes Bildnis für die Korinter. So hatten sie alle zwei Jahre Sportspiele von April bis Anfang Mai, welche wie auch Fußball heute noch, vieles der Gesellschaft lahmlegte.
|
||||
Auch die Gläubigen gehörten damals wie heute zu den Begeisterten solcher Spiele.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Jedoch ist die heutige Lehre, dass es nicht nur eine Medaillie gibt, wie vielleicht die Epistel einen erwarten lässt.
|
||||
Daher das gewählte Evangelium für den heutigen Tag, um zu zeigen, dass das Relevante ist zu kämpfen als gäbe es nur einen Sieger.
|
||||
Auch diese, welche erst <q>zur elften Stunde</q> anfangen zu kämpfen werden einen Sieg erreichen können.
|
||||
<p>
|
||||
|
||||
<p>
|
||||
Diese aber, die nicht kämpfen, werden es bitter bereuen. Dazu gibt es 3 Beispiele aus der heiligen Schrift:
|
||||
</p>
|
||||
|
||||
<h4>1. König David:</h4>
|
||||
|
||||
<p>
|
||||
Sein Leben ist ein Kampf, aber als er seine Macht erreicht hatte und keine Gegner mehr sah, da ruhte er sich aus und sandte seine Kämpfer aus während er auf seiner Terasse zurückblieb.
|
||||
</p>
|
||||
<p>
|
||||
Er hat sich auf seinen Loorbeeren ausgeruht. <q>Was hat er noch zu tun?</q> <q>Hat er nicht alles bereits erreicht?</q>
|
||||
Somit wird mit einem Blick besiegt.
|
||||
Nicht von Anderen Menschen, aber vom Teufel durch sich selbst.
|
||||
So beging er zwei Todsünden durch einen Blick auf eine Frau: Ehebruch und Mord am Manne dieser Frau.
|
||||
</p>
|
||||
<p>
|
||||
Als Kind hat er Löwen und Bären mit bloßen Händen besiegt, aber nun wird dieser einst mutige und starke Mann bezwungen wegen seinem <em>Müßiggang</em>.
|
||||
Wie man zu sagen pflegt: Wer man kein Beschäftigugn welche Platz einnimmt so wird der Teufel selbst den ganzen Platz einnehmen, den wir freigelassen haben.
|
||||
Es wäre besser gewesen für David, noch in der Ängstlichkeit der Flucht vor Saulus zu sein, als in seinem Palast in Jerusalem.
|
||||
</p>
|
||||
|
||||
<div class=quote>
|
||||
<p class=title>
|
||||
<a href=https://en.wikipedia.org/wiki/Ignatius_of_Loyola>Der Heilige Ignatius von Loyola</a>; Regel zur Unterscheidung der Geister:
|
||||
</p>
|
||||
<q>Wenn wir in Schwierigkeiten sind, dann beten wir den lieben Gott treu zu bleiben.
|
||||
Wenn alles gut geht denken wir schon daran, welche Schwierigkeiten kommen können und beten wir um Vorrat von Mut und von Eifer für die Zeit der Trostlosigkeit, der Prüfung.</q>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Müßiggang hat ihn besiegt und der Gedanke, dass er nichts mehr zu erobern hatte.
|
||||
Es gibt immer etwas weiteres zu erobern. Es gibt immer eine Ecke in unserer Seele die uns, und damit Gott, nicht gehört.
|
||||
Es gibt immer etwas, was man besser tun kann, es gibt immer schlechte Gewohnheiten die man ablegen muss.
|
||||
In dieser Vorfastenzeit geht es darum sich diesem deutlicher bewusst zu werden und seinen Kampf gegen diese Müßigkeit für die kommende Fastenzeit zu planen.
|
||||
</p>
|
||||
<p>
|
||||
Als Beispiel hilft hier das Königreich Spanien. Jahrhunderte von Kampf um die Anwesenheit der Muslime zu bekämpfen. Als sie endlich die letzte Stadt, welche unter der Vollmacht der Muslime war, erobert hatten, hat die Vorsehung ihnen noch etwas zu erobern gegeben:
|
||||
Im Jahre 1492, dem Fall von Granada, wurde Spanien (und Portugal) ein ganzer Kontinent zum Erobern im Namen Christi geschenkt.
|
||||
Als Spanien endlich von der Versuchung der Apostasie, dem Glaubensabfall, befreit wurde hat sich diese Chance eröffnet.
|
||||
Es gibt immer noch etwas zu erobern.
|
||||
Falls wir nichts finden, dann wird die Vorsehung uns etwas zeigen. (Siehe Zitat oben).
|
||||
</p>
|
||||
|
||||
<h4>2. Die Hebräer in der Wüste: <i>4 Mose 13</i></h4>
|
||||
|
||||
<p>
|
||||
Von den Ägyptern durch die Wunder Gottes befreit, sind sie nun auf dem weiten Weg zum versprochenen Land.
|
||||
Sie schicken Kundschafter in dieses Land. Diese Kundschafter gehen und verbringen 40 Tage dort.
|
||||
Diese Kundschafter finden ein wunderbares Land. Es fließt Honig und Milch. Aber es ist nicht unbevölkert. Es gibt viele, starke Stämme.
|
||||
Die Kundschafter haben Angst, sie verbreiteten Lügen über dieses Land da sie Angst haben zu fallen im Versuch es einzunehmen.
|
||||
Das Volk will murren, beklagen. Sie wollen nicht kämpfen.
|
||||
</p>
|
||||
<p>
|
||||
Warum haben sie gelogen? Sie wollten die eigene Angst rechtfertigen. Der liebe Gott lässt sie daher als Strafe 40 Jahre lang leben in der Wüste, sodass nur die nächst e Generation in Besitzt kommen wird des versprochenem Lande.
|
||||
Wie konnten sie Gott so verletzen? Als ob Er keine Wunder für sie getätigt hätte? </p>
|
||||
<div class=quote><p class=title>Hl. Thomas von Aquin in einem Kommentar zur Bibel</p>
|
||||
<div class=bibel>
|
||||
<ol><i>Kol. 3</i>
|
||||
<li value=21>Ihr Väter! reizet eure Kinder nicht zum Zorne, damit sie nicht mutlos werden.</li>
|
||||
</ol>
|
||||
</div>
|
||||
<q>
|
||||
Der Sinn dieses Ratschlags ist, dass der Mensch den Eindruck seiner Kindheit behält.
|
||||
Es ist natürlich demjenigen, der in Knechtschaft erzogen wurde, immer kleinmütig, mutlos zu bleiben.
|
||||
Und daraus haben die Israeliten in der Wüste Angst gehabt vor dem Kampf weil sie immer in Knechtschaft erzogen wurden.
|
||||
Sie hatten keinen Mut mehr.
|
||||
</q>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Diese Knechtschaft soll abgelegt werden um so zu einem kämpferischen Geist zu kommen. Das wird auch bestätigt im Johannesevangelium wo steht:
|
||||
<div class=bibel>
|
||||
<ol><i> Johannes 15</i>
|
||||
<li value=15>Ich nenne euch nun nicht mehr Knechte, denn der Knecht weiß nicht, was sein Herr tut; euch aber habe ich Freunde genannt; denn alles, was ich von meinem Vater gehört, habe ich euch kundgetan.
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<p>
|
||||
Was der Herr von seinen Jüngern erwartet ist Liebe, inneres Verständnis und Kühnheit.
|
||||
Kampf gegen unsere Menschenfurcht.
|
||||
Dazu erzieht Er seine Jünger zur Freiheit.
|
||||
So sagt auch Paulus in seinen Briefen an die Galater:
|
||||
<div class=bibel>
|
||||
<ol><i>Galater 4</i>
|
||||
<li value=31>Demnach, Brüder! sind wir nicht Kinder der Magd, sondern der Freien, vermöge der Freiheit, mit der Christus uns befreit hat.</li>
|
||||
</ol>
|
||||
</div>
|
||||
<p>
|
||||
Eine Lektion für uns:
|
||||
Jeder Ausbilder erzieht zur Freiheit, zur Autonomie. Jeder Ausbilder arbeitet um nutzlos zu werden, das seine Jünglinge alleine tun können, was er mit ihm erlernt.
|
||||
Der Jüngling alles selbst tun lassen, damit er lernt.
|
||||
</p>
|
||||
|
||||
<h4>3. Petrus:</h4>
|
||||
<p>
|
||||
Der hl. Petrus, der immer mutig ist, Jesus zu verteidigen und seine Treue zu bekennen. Er wird von einer Magd besiegt werden.
|
||||
</p>
|
||||
<div class=bibel>
|
||||
<ol><i>Johannes 18</i>
|
||||
<li value=10>Simon Petrus also, der sein Schwert hatte, zog es und schlug den Knecht des Hohenpriesters, und hieb ihm sein rechtes Ohr ab. Der Name des Knechtes aber war Malchus.</li>
|
||||
<i>Matthäus 26</i>
|
||||
<li value=35>Da sprach Petrus zu ihm: Wenn ich auch mit dir sterben müsste, werde ich dich doch nicht verleugnen. In gleicher Weise sprachen auch alle Jünger.</li>
|
||||
<li value=69>Petrus aber saß draußen in dem Hofe; und eine Magd trat zu ihm hin, und sprach: Du warest auch bei Jesus, dem Galiläer!</li>
|
||||
<li>Doch er leugnete vor allen, und sprach: Ich weiss nicht, was du sagst.</li>
|
||||
</ol>
|
||||
</div>
|
||||
<p>
|
||||
War Petrus nicht kühn genug? War er nicht kämpferisch genug?
|
||||
Er wird dieses mal gegen sich Selbst kämpfen müssen.
|
||||
Daraus sollte er beten. Er hat nicht gebetet, er hat geschlafen.
|
||||
</p>
|
||||
<p>
|
||||
Lektion für uns:
|
||||
Nur im Gebet werden wir die Kraft und Hellsichtigkeit schöpfen, um richtig zu kämpfen.
|
||||
Nur durch das Gebet hätte Petrus verstanden, was Jesus gesagt hatte nach dem Abendmahl.
|
||||
</p>
|
||||
<div class=bibel>
|
||||
<ol><i>Matthäus 26</i>
|
||||
|
||||
<li value=37>Und er nahm den Petrus und die zwei Söhne des Zebedäus mit sich, und fing an, sich zu betrüben und zu bangen.</li>
|
||||
<li>Da sprach er zu ihnen: Meine Seele ist betrübt bis in den Tod, bleibet hier und wachet mit mir!</li>
|
||||
<li>Und nachdem er ein wenig vorwärts gegangen war, fiel er auf sein Angesicht, betete, und sprach: Mein Vater! wenn es möglich ist, so gehe dieser Kelch an mir vorüber; jedoch nicht wie ich will, sondern wie du.</li>
|
||||
<li>Und er kam zu seinen Jüngern, und fand sie schlafend, und sprach zu Petrus: So vermochtet ihr nicht eine Stunde mit mir zu wachen?</li>
|
||||
<li>Wachet und betet, damit ihr nicht in Versuchung geratet! Der Geist zwar ist willig, das Fleisch aber ist schwach.</li>
|
||||
</ol>
|
||||
</div>
|
||||
<p>
|
||||
Die neue Lebensweise der Apostel: Es wird Schwert geben, es wird Kampf geben, nimmt euch Beutel Stab usw mit euch.
|
||||
Drei Beispiele von Leuten die nicht kämpften. Aber: Die Hebräer haben trotzdem 40 Jahre später das Land erobert. König David bekehrte sich, hat das wunderbare Vorbild der Reue gegeben. So auch der hl. Petrus und wurde zum Fürst der Apostel und unser Glaube hängt an seinem.</p>
|
||||
<p>
|
||||
Alle bekamen die Kraft Gottes, Petrus sogar so sehr, dass er lehrte bis an seinen eigenen Kreuzestod.
|
||||
Es ist die Zeit gekommen um sich selbst zu erobern, sich selbst zu besiegen um Gott völlig gefällig zu sein.
|
||||
</p>
|
||||
</div>
|
641
src/routes/glaube/rosenkranz/+page.svelte
Normal file
641
src/routes/glaube/rosenkranz/+page.svelte
Normal file
File diff suppressed because one or more lines are too long
7
src/routes/rezepte/+layout.server.ts
Normal file
7
src/routes/rezepte/+layout.server.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import type { PageServerLoad } from "./$types"
|
||||
|
||||
export const load : PageServerLoad = async ({locals}) => {
|
||||
return {
|
||||
session: await locals.auth()
|
||||
}
|
||||
};
|
@ -2,9 +2,9 @@
|
||||
import Header from '$lib/components/Header.svelte'
|
||||
import UserHeader from '$lib/components/UserHeader.svelte';
|
||||
export let data
|
||||
let username = ""
|
||||
if(data.user){
|
||||
username = data.user.username
|
||||
let user;
|
||||
if(data.session){
|
||||
user = data.session.user
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -15,7 +15,8 @@ if(data.user){
|
||||
<li><a href="/rezepte/category">Kategorie</a></li>
|
||||
<li><a href="/rezepte/icon">Icon</a></li>
|
||||
<li><a href="/rezepte/tag">Stichwörter</a></li>
|
||||
<li><a href="/rezepte/tips-and-tricks">Tipps</a></li>
|
||||
</ul>
|
||||
<UserHeader slot=right_side {username}></UserHeader>
|
||||
<UserHeader slot=right_side {user}></UserHeader>
|
||||
<slot></slot>
|
||||
</Header>
|
@ -6,7 +6,7 @@
|
||||
import Search from '$lib/components/Search.svelte';
|
||||
export let data: PageData;
|
||||
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>
|
||||
<style>
|
||||
h1{
|
||||
@ -14,30 +14,36 @@ h1{
|
||||
margin-bottom: 0;
|
||||
font-size: 4rem;
|
||||
}
|
||||
.subheading{
|
||||
text-align: center;
|
||||
margin-top: 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
</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/al_ragu.webp" />
|
||||
<meta property="og:image:secure_url" content="https://bocken.org/static/rezepte/thumb/al_ragu.webp" />
|
||||
<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>Rezepte</h1>
|
||||
<p class=subheading>{data.all_brief.length} Rezepte und stetig wachsend...</p>
|
||||
|
||||
<Search></Search>
|
||||
|
||||
<MediaScroller title="In Saison">
|
||||
{#each data.season as recipe}
|
||||
<Card {recipe} {current_month}></Card>
|
||||
<Card {recipe} {current_month} loading_strat={"eager"} do_margin_right={true}></Card>
|
||||
{/each}
|
||||
</MediaScroller>
|
||||
|
||||
{#each categories as category}
|
||||
<MediaScroller title={category}>
|
||||
{#each data.all_brief.filter(recipe => recipe.category == category) as recipe}
|
||||
<Card {recipe} {current_month}></Card>
|
||||
<Card {recipe} {current_month} do_margin_right={true}></Card>
|
||||
{/each}
|
||||
</MediaScroller>
|
||||
{/each}
|
@ -15,8 +15,8 @@
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let hero_img_src = "https://bocken.org/static/rezepte/full/" + data.short_name + ".webp"
|
||||
let placeholder_src = "https://bocken.org/static/rezepte/placeholder/" + 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?v=" + data.dateModified
|
||||
export let months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]
|
||||
function season_intervals() {
|
||||
let interval_arr = []
|
||||
@ -185,6 +185,7 @@ h1{
|
||||
}
|
||||
|
||||
.icon{
|
||||
font-family: "Noto Color Emoji", emoji;
|
||||
position: absolute;
|
||||
top: -1em;
|
||||
right: -0.75em;
|
||||
@ -292,7 +293,9 @@ h4{
|
||||
<h4>Saison:</h4>
|
||||
{#each season_iv as season}
|
||||
<a class=tag href="/rezepte/season/{season[0]}">
|
||||
{months[season[0] - 1]}
|
||||
{#if season[0]}
|
||||
{months[season[0] - 1]}
|
||||
{/if}
|
||||
{#if season[1]}
|
||||
- {months[season[1] - 1]}
|
||||
{/if}
|
@ -85,7 +85,7 @@
|
||||
console.log(img_local)
|
||||
const data = {
|
||||
image: img_local,
|
||||
name: short_name,
|
||||
name: short_name.trim(),
|
||||
}
|
||||
await fetch(`/api/rezepte/img/add`, {
|
||||
method: 'POST',
|
||||
@ -108,9 +108,9 @@
|
||||
recipe: {
|
||||
...card_data,
|
||||
...add_info,
|
||||
images: {mediapath: short_name + '.webp', alt: "", caption: ""}, // TODO
|
||||
images: {mediapath: short_name.trim() + '.webp', alt: "", caption: ""}, // TODO
|
||||
season: season_local,
|
||||
short_name,
|
||||
short_name : short_name.trim(),
|
||||
portions: portions_local,
|
||||
datecreated,
|
||||
datemodified,
|
@ -10,7 +10,7 @@
|
||||
export let data: PageData;
|
||||
let preamble = data.recipe.preamble
|
||||
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
|
||||
|
||||
import { season } from '$lib/js/season_store';
|
||||
@ -160,7 +160,7 @@
|
||||
async function upload_img(){
|
||||
const data = {
|
||||
image: img_local,
|
||||
name: short_name,
|
||||
name: short_name.trim(),
|
||||
}
|
||||
const res = await fetch(`/api/rezepte/img/add`, {
|
||||
method: 'POST',
|
||||
@ -191,7 +191,7 @@
|
||||
},
|
||||
body: JSON.stringify({
|
||||
old_name: old_short_name,
|
||||
new_name: short_name,
|
||||
new_name: short_name.trim(),
|
||||
})
|
||||
})
|
||||
if(!res_img.ok){
|
||||
@ -208,7 +208,7 @@
|
||||
...add_info,
|
||||
images, // TODO
|
||||
season: season_local,
|
||||
short_name,
|
||||
short_name: short_name.trim(),
|
||||
datecreated,
|
||||
portions: portions_local,
|
||||
datemodified,
|
||||
@ -228,7 +228,7 @@
|
||||
if(res.ok){
|
||||
const url = location.href.split('/');
|
||||
url.splice(url.length -2, 2);
|
||||
url.push(short_name);
|
||||
url.push(short_name.trim());
|
||||
location.assign(url.join('/'))
|
||||
}
|
||||
else{
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import '$lib/components/nordtheme.css';
|
||||
import '$lib/css/nordtheme.css';
|
||||
import Recipes from '$lib/components/Recipes.svelte';
|
||||
import MediaScroller from '$lib/components/MediaScroller.svelte';
|
||||
import SeasonLayout from '$lib/components/SeasonLayout.svelte'
|
||||
@ -10,6 +10,7 @@
|
||||
</script>
|
||||
<style>
|
||||
a{
|
||||
font-family: "Noto Color Emoji", emoji, sans-serif;
|
||||
--padding: 0.5em;
|
||||
font-size: 3rem;
|
||||
text-decoration: none;
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import '$lib/components/nordtheme.css';
|
||||
import '$lib/css/nordtheme.css';
|
||||
import Recipes from '$lib/components/Recipes.svelte';
|
||||
import MediaScroller from '$lib/components/MediaScroller.svelte';
|
||||
import SeasonLayout from '$lib/components/SeasonLayout.svelte'
|
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>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user