API routes now return proper Responses and basic errors are handled
slight improvements in layouting
This commit is contained in:
parent
ece3b3634c
commit
24ddd39f35
@ -5,6 +5,7 @@ function show_sidebar(){
|
||||
const nav_el = document.querySelector("nav")
|
||||
nav_el.hidden = !nav_el.hidden
|
||||
}
|
||||
|
||||
</script>
|
||||
<style>
|
||||
:global(*){
|
||||
@ -28,13 +29,13 @@ nav[hidden]{
|
||||
display:block;
|
||||
}
|
||||
|
||||
li{
|
||||
:global(.site_header li){
|
||||
list-style-type:none;
|
||||
transition: 100ms;
|
||||
color: white;
|
||||
user-select: none;
|
||||
}
|
||||
li>a{
|
||||
:global(.site_header li>a){
|
||||
text-decoration: none;
|
||||
font-family: sans-serif;
|
||||
font-size: 1.2rem;
|
||||
@ -43,14 +44,14 @@ li>a{
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
li:hover,
|
||||
li:focus-within
|
||||
:global(.site_header li:hover),
|
||||
:global(.site_header li:focus-within)
|
||||
{
|
||||
cursor: pointer;
|
||||
color: var(--red);
|
||||
transform: scale(1.1,1.1);
|
||||
}
|
||||
ul {
|
||||
:global(.site_header) {
|
||||
padding-block: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -104,18 +105,18 @@ ul {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
ul{
|
||||
:global(.site_header){
|
||||
flex-direction: column;
|
||||
padding-top: min(10rem, 10vh);
|
||||
}
|
||||
li{
|
||||
:global(.site_header li){
|
||||
font-size: 4rem;
|
||||
}
|
||||
li > a{
|
||||
:global(.site_header li > a){
|
||||
font-size: 2rem;
|
||||
}
|
||||
li:hover,
|
||||
li:focus-within{
|
||||
:global(.site_header li:hover),
|
||||
:global(.site_header li:focus-within){
|
||||
transform: unset;
|
||||
}
|
||||
}
|
||||
@ -136,14 +137,9 @@ margin-top: auto;
|
||||
<button class=nav_button on:click={show_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>
|
||||
</div>
|
||||
<nav hidden>
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/rezepte">Alle Rezepte</a></li>
|
||||
<li><a href="/rezepte/season">In Saison</a></li>
|
||||
<li><a href="/rezepte/category">Nach Kategorie</a></li>
|
||||
<li><a href="/rezepte/tag">Stichwörter</a></li>
|
||||
</ul>
|
||||
<slot name=links></slot>
|
||||
</nav>
|
||||
|
||||
<slot></slot>
|
||||
|
||||
</div>
|
@ -1,3 +1,11 @@
|
||||
<h1>Welcome to SvelteKit</h1>
|
||||
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
|
||||
<a href="/rezepte"> REZEPTE </a>
|
||||
<script>
|
||||
import Header from '$lib/components/Header.svelte'
|
||||
</script>
|
||||
<Header>
|
||||
<ul class=site_header slot=links>
|
||||
<li><a href="/rezepte">Rezepte</a></li>
|
||||
<li><a href="https://bilder.bocken.org">Familienbilder</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
</Header>
|
||||
|
@ -3,23 +3,26 @@ import { Recipe } from '../../../models/Recipe';
|
||||
import { dbConnect, dbDisconnect } from '../../../utils/db';
|
||||
import type {RecipeModelType} from '../../../types/types';
|
||||
import { BEARER_TOKEN } from '$env/static/private'
|
||||
import { error } from '@sveltejs/kit';
|
||||
// header: use for bearer token for now
|
||||
// recipe json in body
|
||||
export const POST: RequestHandler = async ({request}) => {
|
||||
let message = await request.json()
|
||||
const recipe_json = message.recipe
|
||||
const bearer_token = message.headers.bearer
|
||||
console.log("RECIPE:", recipe_json)
|
||||
console.log("BEARER:", bearer_token)
|
||||
if(bearer_token === BEARER_TOKEN){
|
||||
console.log("PASSWORD CORRECT")
|
||||
await dbConnect();
|
||||
try{
|
||||
await Recipe.create(recipe_json);
|
||||
} catch(e){
|
||||
throw error(400, e)
|
||||
}
|
||||
await dbDisconnect();
|
||||
return {status: 200} //TODO: cleanup error throwing
|
||||
return new Response(JSON.stringify({msg: "Added recipe successfully"}),{
|
||||
status: 200,
|
||||
});
|
||||
}
|
||||
else{
|
||||
console.log("PASSWORD INCORRECT")
|
||||
return {status: 403}
|
||||
throw error(403, "Password incorrect")
|
||||
}
|
||||
};
|
||||
|
@ -3,6 +3,7 @@ import { Recipe } from '../../../models/Recipe';
|
||||
import { dbConnect, dbDisconnect } from '../../../utils/db';
|
||||
import type {RecipeModelType} from '../../../types/types';
|
||||
import { BEARER_TOKEN } from '$env/static/private'
|
||||
import { error } from '@sveltejs/kit';
|
||||
// header: use for bearer token for now
|
||||
// recipe json in body
|
||||
export const POST: RequestHandler = async ({request}) => {
|
||||
@ -10,14 +11,14 @@ export const POST: RequestHandler = async ({request}) => {
|
||||
const short_name = message.old_short_name
|
||||
const bearer_token = message.headers.bearer
|
||||
if(bearer_token === BEARER_TOKEN){
|
||||
console.log("PASSWORD CORRECT")
|
||||
await dbConnect();
|
||||
await Recipe.findOneAndDelete({short_name: short_name});
|
||||
await dbDisconnect();
|
||||
return {status: 400} //TODO: cleanup error throwing
|
||||
return new Response(JSON.stringify({msg: "Deleted recipe successfully"}),{
|
||||
status: 200,
|
||||
});
|
||||
}
|
||||
else{
|
||||
console.log("PASSWORD INCORRECT")
|
||||
return {status: 403}
|
||||
throw error(403, "Password incorrect")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -15,11 +15,12 @@ export const POST: RequestHandler = async ({request}) => {
|
||||
await dbConnect();
|
||||
await Recipe.findOneAndUpdate({short_name: message.old_short_name }, recipe_json);
|
||||
await dbDisconnect();
|
||||
const res = new Response(JSON.stringify({ message: "Updated Recipe successfully"}), { status: 200 })
|
||||
return res
|
||||
return new Response(JSON.stringify({msg: "Edited recipe successfully"}),{
|
||||
status: 200,
|
||||
});
|
||||
|
||||
}
|
||||
else{
|
||||
console.log("INCORRECT PASSWORD")
|
||||
throw error(403, "Password incorrect")
|
||||
}
|
||||
};
|
||||
|
@ -6,7 +6,6 @@ import { error } from '@sveltejs/kit';
|
||||
|
||||
export const POST = (async ({ request }) => {
|
||||
const data = await request.json();
|
||||
console.log(data)
|
||||
const filePath = path.join(
|
||||
process.cwd(),
|
||||
"static",
|
||||
@ -14,13 +13,13 @@ export const POST = (async ({ request }) => {
|
||||
data.filename as string
|
||||
);
|
||||
const file = data.image;
|
||||
console.log(data.headers)
|
||||
if(data.bearer === BEARER_TOKEN){
|
||||
console.log("PASSWORD CORRECT")
|
||||
writeFileSync(filePath, file, 'base64');
|
||||
return new Response(JSON.stringify({msg: "Added image successfully"}),{
|
||||
status: 200,
|
||||
});
|
||||
}
|
||||
else{
|
||||
console.log("PASSWORD INCORRECT")
|
||||
throw error(403, "Password incorrect")
|
||||
}
|
||||
|
||||
|
@ -1,20 +0,0 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs/promises'
|
||||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
export const POST = (async ({ request, url}) => {
|
||||
try {
|
||||
const data = Object.fromEntries(await request.formData())
|
||||
const filePath = path.join(
|
||||
process.cwd(),
|
||||
"static",
|
||||
"images",
|
||||
data.filename as string
|
||||
);
|
||||
await fs.writeFile(filePath, Buffer.from(await (data.image as Blob).arrayBuffer()))
|
||||
return new Response(String({status: 200}))
|
||||
} catch (err) {
|
||||
throw fail(500, { err: err })
|
||||
}
|
||||
}) satisfies RequestHandler;
|
15
src/routes/rezepte/+layout.svelte
Normal file
15
src/routes/rezepte/+layout.svelte
Normal file
@ -0,0 +1,15 @@
|
||||
<script>
|
||||
import Header from '$lib/components/Header.svelte'
|
||||
|
||||
</script>
|
||||
|
||||
<Header>
|
||||
<ul class=site_header slot=links>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/rezepte">Alle Rezepte</a></li>
|
||||
<li><a href="/rezepte/season">In Saison</a></li>
|
||||
<li><a href="/rezepte/category">Nach Kategorie</a></li>
|
||||
<li><a href="/rezepte/tag">Stichwörter</a></li>
|
||||
</ul>
|
||||
<slot></slot>
|
||||
</Header>
|
@ -89,12 +89,13 @@ h1{
|
||||
|
||||
.wrapper_wrapper{
|
||||
background-color: #fbf9f3;
|
||||
width: 100svw;
|
||||
padding-top: 3rem;
|
||||
padding-top: 10rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 3rem;
|
||||
transform: translateY(-7rem);
|
||||
z-index: -2;
|
||||
}
|
||||
|
||||
.wrapper{
|
||||
@ -118,7 +119,7 @@ h1{
|
||||
background-color: var(--nord6);
|
||||
padding: 1rem 2rem;
|
||||
translate: 0 1px; /*bruh*/
|
||||
z-index: -1;
|
||||
z-index: 1;
|
||||
}
|
||||
.icon{
|
||||
position: absolute;
|
||||
@ -185,22 +186,13 @@ h4{
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
<!--<MultiImgWrapper wrap={data.images.length>1} class=double>
|
||||
{#each data.images as img}
|
||||
<img width=100% src="/images/{img.mediapath}" alt="{img.alt}">
|
||||
<figure>
|
||||
{#if img.caption}
|
||||
<caption>{img.caption}</caption>
|
||||
{/if}
|
||||
</figure>
|
||||
{/each}
|
||||
</MultiImgWrapper>-->
|
||||
|
||||
<TitleImgParallax src=/images/{data.images[0].mediapath}>
|
||||
<div class=title>
|
||||
<a class="icon" href='/rezepte/season/{data.season[0]}'>{data.icon}</a>
|
||||
<h1>{data.name}</h1>
|
||||
{#if data.description && ! data.preamble}
|
||||
<p>{data.description}</p>
|
||||
{/if}
|
||||
{#if data.preamble}
|
||||
<p>{data.preamble}</p>
|
||||
{/if}
|
||||
|
@ -126,7 +126,17 @@
|
||||
bearer: password,
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
if(res.status === 200){
|
||||
const url = location.href.split('/')
|
||||
url.splice(url.length -1, 1);
|
||||
url.push(short_name)
|
||||
location.assign(url.join('/'))
|
||||
}
|
||||
else{
|
||||
const item = await res.json();
|
||||
alert(item.message)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -98,6 +98,15 @@
|
||||
})
|
||||
|
||||
})
|
||||
if(res.status === 200){
|
||||
const url = location.href.split('/')
|
||||
url.splice(url.length -2, 2);
|
||||
location.assign(url.join('/'))
|
||||
}
|
||||
else{
|
||||
const item = await res.json();
|
||||
alert(item.message)
|
||||
}
|
||||
}
|
||||
async function doEdit() {
|
||||
const res = await fetch('/api/edit', {
|
||||
@ -124,7 +133,15 @@
|
||||
}
|
||||
})
|
||||
})
|
||||
const item = await res.json();
|
||||
if(res.status === 200){
|
||||
const url = location.href.split('/');
|
||||
url.splice(url.length -2, 1);
|
||||
location.assign(url.join('/'))
|
||||
}
|
||||
else{
|
||||
const item = await res.json()
|
||||
alert(item.message)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
249
src/routes/test/+page.svelte_
Normal file
249
src/routes/test/+page.svelte_
Normal file
@ -0,0 +1,249 @@
|
||||
<form action="/api/img/add" method="post" enctype="multipart/form-data">
|
||||
<input type="file" name="image" placeholder="avatar" />
|
||||
<input type="text" name="filename" placeholder=filename />
|
||||
<button type="submit">upload</button>
|
||||
</form>
|
||||
|
||||
<script lang="ts">
|
||||
import Check from '$lib/assets/icons/Check.svelte';
|
||||
import SeasonSelect from '$lib/components/SeasonSelect.svelte';
|
||||
import '$lib/css/action_button.css'
|
||||
|
||||
export let data: PageData;
|
||||
let preamble = ""
|
||||
let addendum = ""
|
||||
|
||||
import { season } from '$lib/js/season_store';
|
||||
season.update(() => [])
|
||||
let season_local
|
||||
season.subscribe((s) => {
|
||||
season_local = s
|
||||
});
|
||||
|
||||
export let card_data ={
|
||||
icon: "",
|
||||
category: "",
|
||||
name: "",
|
||||
description: "",
|
||||
tags: [],
|
||||
}
|
||||
export let add_info ={
|
||||
preparation: "",
|
||||
fermentation: {
|
||||
bulk: "",
|
||||
final: "",
|
||||
},
|
||||
baking: {
|
||||
length: "",
|
||||
temperature: "",
|
||||
mode: "",
|
||||
},
|
||||
total_time: "",
|
||||
cooking: "",
|
||||
}
|
||||
|
||||
let images = []
|
||||
export let portions = ""
|
||||
|
||||
let short_name = ""
|
||||
let password
|
||||
let datecreated = new Date()
|
||||
let datemodified = datecreated
|
||||
|
||||
import type { PageData } from './$types';
|
||||
import CardAdd from '$lib/components/CardAdd.svelte';
|
||||
|
||||
import CreateIngredientList from '$lib/components/CreateIngredientList.svelte';
|
||||
export let ingredients = []
|
||||
|
||||
import CreateStepList from '$lib/components/CreateStepList.svelte';
|
||||
export let instructions = []
|
||||
|
||||
|
||||
function get_season(){
|
||||
let season = []
|
||||
const el = document.getElementById("labels");
|
||||
for(var i = 0; i < el.children.length; i++){
|
||||
if(el.children[i].children[0].children[0].checked){
|
||||
season.push(i+1)
|
||||
}
|
||||
}
|
||||
return season
|
||||
}
|
||||
function write_season(season){
|
||||
const el = document.getElementById("labels");
|
||||
for(var i = 0; i < season.length; i++){
|
||||
el.children[i].children[0].children[0].checked = true
|
||||
}
|
||||
}
|
||||
|
||||
async function doPost () {
|
||||
console.log(add_info.total_time)
|
||||
const res = await fetch('/api/add', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
recipe: {
|
||||
...card_data,
|
||||
...add_info,
|
||||
images: {mediapath: short_name + '.webp', alt: "", caption: ""}, // TODO
|
||||
season: season_local,
|
||||
short_name,
|
||||
portions,
|
||||
datecreated,
|
||||
datemodified,
|
||||
instructions,
|
||||
ingredients,
|
||||
preamble,
|
||||
addendum,
|
||||
},
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
bearer: password,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
input{
|
||||
display: block;
|
||||
border: unset;
|
||||
margin: 1rem auto;
|
||||
padding: 0.5em 1em;
|
||||
border-radius: 1000px;
|
||||
background-color: var(--nord4);
|
||||
font-size: 1.1rem;
|
||||
transition: 100ms;
|
||||
|
||||
}
|
||||
input:hover,
|
||||
input:focus-visible
|
||||
{
|
||||
scale: 1.05 1.05;
|
||||
}
|
||||
.list_wrapper{
|
||||
margin-inline: auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-width: 1000px;
|
||||
gap: 2rem;
|
||||
justify-content: center;
|
||||
}
|
||||
@media screen and (max-width: 700px){
|
||||
.list_wrapper{
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
input[type=password]{
|
||||
box-sizing: border-box;
|
||||
font-size: 1.5rem;
|
||||
padding-block: 0.5em;
|
||||
display: inline;
|
||||
width: 100%;
|
||||
}
|
||||
.submit_wrapper{
|
||||
position: relative;
|
||||
margin-inline: auto;
|
||||
width: max(300px, 50vw)
|
||||
}
|
||||
.submit_wrapper button{
|
||||
position: absolute;
|
||||
right:-1em;
|
||||
bottom: -0.5em;
|
||||
}
|
||||
.submit_wrapper h2{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
h1{
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.title_container{
|
||||
max-width: 1000px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-inline: auto;
|
||||
}
|
||||
.title{
|
||||
position: relative;
|
||||
width: min(800px, 80vw);
|
||||
margin-block: 2rem;
|
||||
margin-inline: auto;
|
||||
background-color: var(--nord6);
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
.title p{
|
||||
border: 2px solid var(--nord1);
|
||||
border-radius: 10000px;
|
||||
padding: 0.5em 1em;
|
||||
font-size: 1.1rem;
|
||||
transition: 200ms;
|
||||
}
|
||||
.title p:hover,
|
||||
.title p:focus-within{
|
||||
scale: 1.02 1.02;
|
||||
}
|
||||
.addendum{
|
||||
font-size: 1.1rem;
|
||||
max-width: 90%;
|
||||
margin-inline: auto;
|
||||
border: 2px solid var(--nord1);
|
||||
border-radius: 45px;
|
||||
padding: 1em 1em;
|
||||
transition: 100ms;
|
||||
}
|
||||
.addendum:hover,
|
||||
.addendum:focus-within
|
||||
{
|
||||
scale: 1.02 1.02;
|
||||
}
|
||||
.addendum_wrapper{
|
||||
max-width: 1000px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
h3{
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<h1>Rezept erstellen</h1>
|
||||
<form action="/api/img/add" method="post" enctype="multipart/form-data">
|
||||
<CardAdd {card_data}></CardAdd>
|
||||
|
||||
<h3>Kurzname (für URL):</h3>
|
||||
<input bind:value={short_name} placeholder="Kurzname"/>
|
||||
|
||||
<div class=title_container>
|
||||
<div class=title>
|
||||
<h4>Eine etwas längere Beschreibung:</h4>
|
||||
<p bind:innerText={preamble} contenteditable></p>
|
||||
<div class=tags>
|
||||
<h4>Saison:</h4>
|
||||
<SeasonSelect></SeasonSelect>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=list_wrapper>
|
||||
<div>
|
||||
<CreateIngredientList {ingredients} {portions}></CreateIngredientList>
|
||||
</div>
|
||||
<div>
|
||||
<CreateStepList {instructions} {add_info}></CreateStepList>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=addendum_wrapper>
|
||||
<h3>Nachtrag:</h3>
|
||||
<div class=addendum bind:innerText={addendum} contenteditable></div>
|
||||
</div>
|
||||
|
||||
<div class=submit_wrapper>
|
||||
<h2>Neues Rezept hinzufügen:</h2>
|
||||
<input type="password" placeholder=Passwort bind:value={password}>
|
||||
<button type=submit class=action_button on:click={doPost}><Check fill=white width=2rem height=2rem></Check></button>
|
||||
</form>
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user