Compare commits

..

No commits in common. "a781be8d008e85877449fd5c5174e8f274152c00" and "4c92c1c43da48f2c63b1bd510601779542c109b5" have entirely different histories.

38 changed files with 1810 additions and 240 deletions

141
package-lock.json generated
View File

@ -8,7 +8,6 @@
"name": "sk-recipes-test", "name": "sk-recipes-test",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@auth/sveltekit": "^0.12.3",
"@sveltejs/adapter-node": "^1.3.1", "@sveltejs/adapter-node": "^1.3.1",
"argon2": "^0.30.3", "argon2": "^0.30.3",
"cheerio": "1.0.0-rc.12", "cheerio": "1.0.0-rc.12",
@ -29,62 +28,6 @@
"vite": "^4.4.4" "vite": "^4.4.4"
} }
}, },
"node_modules/@auth/core": {
"version": "0.26.3",
"resolved": "https://registry.npmjs.org/@auth/core/-/core-0.26.3.tgz",
"integrity": "sha512-Ka6rMjWMdiQCvLW/CnYZxj4Rq2bhQ/ZtU32NLxmtyAaixGb0mRXQ9MxJUBZA7GHovbghdzu55p2Cb54qNlVFzw==",
"dependencies": {
"@panva/hkdf": "^1.1.1",
"@types/cookie": "0.6.0",
"cookie": "0.6.0",
"jose": "^5.1.3",
"oauth4webapi": "^2.4.0",
"preact": "10.11.3",
"preact-render-to-string": "5.2.3"
},
"peerDependencies": {
"@simplewebauthn/browser": "^9.0.1",
"@simplewebauthn/server": "^9.0.1",
"nodemailer": "^6.8.0"
},
"peerDependenciesMeta": {
"@simplewebauthn/browser": {
"optional": true
},
"@simplewebauthn/server": {
"optional": true
},
"nodemailer": {
"optional": true
}
}
},
"node_modules/@auth/core/node_modules/@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="
},
"node_modules/@auth/core/node_modules/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/@auth/sveltekit": {
"version": "0.12.3",
"resolved": "https://registry.npmjs.org/@auth/sveltekit/-/sveltekit-0.12.3.tgz",
"integrity": "sha512-ZZhLPTkLBccJbBPom6n1ARqMNGpSO55ZJUiWhAqY8jPW+K3DtVOd4e/Je+e7g2NJ7vaDoQI4uI0V+jLDBp4B9g==",
"dependencies": {
"@auth/core": "0.26.3",
"set-cookie-parser": "^2.6.0"
},
"peerDependencies": {
"@sveltejs/kit": "^1.0.0 || ^2.0.0",
"svelte": "^3.54.0 || ^4.0.0 || ^5"
}
},
"node_modules/@esbuild/android-arm": { "node_modules/@esbuild/android-arm": {
"version": "0.18.20", "version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
@ -510,14 +453,6 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/@panva/hkdf": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz",
"integrity": "sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/@phc/format": { "node_modules/@phc/format": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
@ -1618,17 +1553,10 @@
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
}, },
"node_modules/ip-address": { "node_modules/ip": {
"version": "9.0.5", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
"integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
"dependencies": {
"jsbn": "1.1.0",
"sprintf-js": "^1.1.3"
},
"engines": {
"node": ">= 12"
}
}, },
"node_modules/is-arrayish": { "node_modules/is-arrayish": {
"version": "0.3.2", "version": "0.3.2",
@ -1723,19 +1651,6 @@
"@types/estree": "*" "@types/estree": "*"
} }
}, },
"node_modules/jose": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/jose/-/jose-5.2.2.tgz",
"integrity": "sha512-/WByRr4jDcsKlvMd1dRJnPfS1GVO3WuKyaurJ/vvXcOaUQO8rnNObCQMlv/5uCceVQIq5Q4WLF44ohsdiTohdg==",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/jsbn": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
},
"node_modules/jsonwebtoken": { "node_modules/jsonwebtoken": {
"version": "9.0.2", "version": "9.0.2",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
@ -2222,14 +2137,6 @@
"url": "https://github.com/fb55/nth-check?sponsor=1" "url": "https://github.com/fb55/nth-check?sponsor=1"
} }
}, },
"node_modules/oauth4webapi": {
"version": "2.10.3",
"resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-2.10.3.tgz",
"integrity": "sha512-9FkXEXfzVKzH63GUOZz1zMr3wBaICSzk6DLXx+CGdrQ10ItNk2ePWzYYc1fdmKq1ayGFb2aX97sRCoZ2s0mkDw==",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/object-assign": { "node_modules/object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@ -2337,26 +2244,6 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/preact": {
"version": "10.11.3",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz",
"integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/preact-render-to-string": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.3.tgz",
"integrity": "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==",
"dependencies": {
"pretty-format": "^3.8.0"
},
"peerDependencies": {
"preact": ">=10"
}
},
"node_modules/prebuild-install": { "node_modules/prebuild-install": {
"version": "7.1.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
@ -2413,11 +2300,6 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/pretty-format": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
},
"node_modules/pump": { "node_modules/pump": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
@ -2864,15 +2746,15 @@
} }
}, },
"node_modules/socks": { "node_modules/socks": {
"version": "2.7.3", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.7.3.tgz", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
"integrity": "sha512-vfuYK48HXCTFD03G/1/zkIls3Ebr2YNa4qU9gHDZdblHLiqhJrJGkY3+0Nx0JpN9qBhJbVObc1CNciT1bIZJxw==", "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
"dependencies": { "dependencies": {
"ip-address": "^9.0.5", "ip": "^2.0.0",
"smart-buffer": "^4.2.0" "smart-buffer": "^4.2.0"
}, },
"engines": { "engines": {
"node": ">= 10.0.0", "node": ">= 10.13.0",
"npm": ">= 3.0.0" "npm": ">= 3.0.0"
} }
}, },
@ -2908,11 +2790,6 @@
"memory-pager": "^1.0.2" "memory-pager": "^1.0.2"
} }
}, },
"node_modules/sprintf-js": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="
},
"node_modules/streamx": { "node_modules/streamx": {
"version": "2.15.6", "version": "2.15.6",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.6.tgz", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.6.tgz",

View File

@ -21,7 +21,6 @@
"vite": "^4.4.4" "vite": "^4.4.4"
}, },
"dependencies": { "dependencies": {
"@auth/sveltekit": "^0.12.3",
"@sveltejs/adapter-node": "^1.3.1", "@sveltejs/adapter-node": "^1.3.1",
"argon2": "^0.30.3", "argon2": "^0.30.3",
"cheerio": "1.0.0-rc.12", "cheerio": "1.0.0-rc.12",

View File

@ -1,12 +0,0 @@
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,
})],
})

View File

@ -2,28 +2,27 @@ import { authenticateUser } from "$lib/js/authenticate"
import type { Handle } from "@sveltejs/kit" import type { Handle } from "@sveltejs/kit"
import { redirect } from "@sveltejs/kit" import { redirect } from "@sveltejs/kit"
import { error } from "@sveltejs/kit" import { error } from "@sveltejs/kit"
export { handle } from "./auth"
//export const handle : Handle = async({event, resolve}) => { export const handle : Handle = async({event, resolve}) => {
// if(event.url.pathname.startsWith('/rezepte/edit') || event.url.pathname.startsWith('/rezepte/add')){ if(event.url.pathname.startsWith('/rezepte/edit') || event.url.pathname.startsWith('/rezepte/add')){
// event.locals.user = await authenticateUser(event.cookies) event.locals.user = await authenticateUser(event.cookies)
// if(!event.locals.user){ if(!event.locals.user){
// throw redirect(303, "/login") throw redirect(303, "/login")
// } }
// else if(!event.locals.user.access.includes("rezepte")){ else if(!event.locals.user.access.includes("rezepte")){
// throw error(401, "Your user does not have access to this page") throw error(401, "Your user does not have access to this page")
// } }
// } }
// else if(event.url.pathname.startsWith('/abrechnung')){ else if(event.url.pathname.startsWith('/abrechnung')){
// event.locals.user = await authenticateUser(event.cookies) event.locals.user = await authenticateUser(event.cookies)
// if(!event.locals.user){ if(!event.locals.user){
// throw redirect(303, "/login") throw redirect(303, "/login")
// } }
// else if(!event.locals.user.access.includes("abrechnung")){ else if(!event.locals.user.access.includes("abrechnung")){
// throw error(401, "Your User does not have access to this page") throw error(401, "Your User does not have access to this page")
// } }
// } }
//
// const response = await resolve(event) const response = await resolve(event)
// return response return response
//} }

View File

@ -130,11 +130,11 @@ h2{
<div id=options class="speech top" hidden> <div id=options class="speech top" hidden>
<h2>{username}</h2> <h2>{username}</h2>
<ul> <ul>
<li><a href="https://sso.bocken.org/if/user/#/settings" >Einstellungen</a></li> <li><a href="/settings" >Einstellungen</a></li>
<li><a href="/auth/signout" >Log Out</a></li> <li><a href="/logout" >Log Out</a></li>
</ul> </ul>
</div> </div>
</button> </button>
{:else} {:else}
<a class=entry href=/auth/signin>Log In</a> <a class=entry href=/login>Log In</a>
{/if} {/if}

15
src/models/Payment.ts Normal file
View File

@ -0,0 +1,15 @@
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);

View File

@ -2,8 +2,6 @@ import { get_username } from '$lib/js/get_username';;
import type { Actions, PageServerLoad } from "./$types" import type { Actions, PageServerLoad } from "./$types"
import { error } from "@sveltejs/kit" import { error } from "@sveltejs/kit"
export const load = (async ({cookies, locals}) => { export const load = (async ({cookies}) => {
return { return { user: await get_username(cookies) }
session: await locals.auth(),
}
}); });

View File

@ -3,10 +3,9 @@ import Header from '$lib/components/Header.svelte'
import UserHeader from '$lib/components/UserHeader.svelte'; import UserHeader from '$lib/components/UserHeader.svelte';
export let data export let data
let username = "" let username = ""
if(data.session){ if(data.user){
username = data.session.user.name username = data.user.username
} }
console.log(data)
</script> </script>
<Header> <Header>

View File

@ -2,10 +2,6 @@
import "$lib/css/nordtheme.css"; import "$lib/css/nordtheme.css";
import LinksGrid from "$lib/components/LinksGrid.svelte"; import LinksGrid from "$lib/components/LinksGrid.svelte";
export let data; export let data;
import { SignIn, SignOut } from "@auth/sveltekit/components"
import { page } from "$app/stores"
/*console.log($page)*/
/*console.log($page.daja.session.user)*/
</script> </script>
<style> <style>
.hero{ .hero{
@ -53,8 +49,7 @@ section h2{
} }
</style> </style>
{#if ! data.session} {#if ! data.user}
<section class=hero> <section class=hero>
<img src="https://bocken.org/static/user/full/Alexander.webp" alt="Smiling Alexander Bocken"> <img src="https://bocken.org/static/user/full/Alexander.webp" alt="Smiling Alexander Bocken">
<div> <div>

View File

@ -0,0 +1,35 @@
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")
},
}

View File

@ -0,0 +1,23 @@
<script>
import "$lib/css/form.css"
import "$lib/css/nordtheme.css"
</script>
<form action="?/login" method=POST>
<h1>Log In</h1>
<p>
Note: This is a separate account to all the services running on this server.
Please only use this login if you want to edit recipes.
In the future, this login will be merged with the other services.
</p>
<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>

View File

@ -0,0 +1 @@
{"terminal": "nvimterm"}

View File

@ -0,0 +1,8 @@
import redirect from "@sveltejs/kit"
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({cookies}) => {
cookies.delete("UserSession")
}
} satisfies Actions;

View File

@ -0,0 +1,7 @@
<script>
import "$lib/css/form.css"
</script>
<form method='POST'>
<h1>Log out</h1>
<button type='submit'>Log Out</button>
</form>

View File

@ -0,0 +1,61 @@
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,
});
}
};

View File

@ -0,0 +1,24 @@
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,
});
}
}

View File

@ -0,0 +1,27 @@
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,
});
}
};

View File

@ -0,0 +1,27 @@
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,
});
}
};

View File

@ -0,0 +1,28 @@
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,
});
}
};

View File

@ -0,0 +1,26 @@
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,
});
}
};

View File

@ -2,28 +2,29 @@ import type { RequestHandler } from '@sveltejs/kit';
import { Recipe } from '../../../../models/Recipe'; import { Recipe } from '../../../../models/Recipe';
import { dbConnect, dbDisconnect } from '../../../../utils/db'; import { dbConnect, dbDisconnect } from '../../../../utils/db';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import { authenticateUser } from '$lib/js/authenticate';;
// header: use for bearer token for now // header: use for bearer token for now
// recipe json in body // recipe json in body
export const POST: RequestHandler = async ({request, cookies, locals}) => { export const POST: RequestHandler = async ({request, cookies}) => {
let message = await request.json() let message = await request.json()
const recipe_json = message.recipe const recipe_json = message.recipe
let auth = await locals.auth(); const user = await authenticateUser(cookies)
/*const user = session.user;*/ if(!user){
console.log(auth)
if(!auth){
throw error(401, "Not logged in") 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") throw error(401, "This user does not have permissions to add recipes")
}*/ }
await dbConnect(); else{
try{ await dbConnect();
await Recipe.create(recipe_json); try{
} catch(e){ await Recipe.create(recipe_json);
throw error(400, e) } catch(e){
} throw error(400, e)
await dbDisconnect(); }
return new Response(JSON.stringify({msg: "Added recipe successfully"}),{ await dbDisconnect();
status: 200, return new Response(JSON.stringify({msg: "Added recipe successfully"}),{
}); status: 200,
});
}
}; };

View File

@ -3,13 +3,15 @@ import { Recipe } from '../../../../models/Recipe';
import { dbConnect, dbDisconnect } from '../../../../utils/db'; import { dbConnect, dbDisconnect } from '../../../../utils/db';
import type {RecipeModelType} from '../../../../types/types'; import type {RecipeModelType} from '../../../../types/types';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import { authenticateUser } from '$lib/js/authenticate';
// header: use for bearer token for now // header: use for bearer token for now
// recipe json in body // recipe json in body
export const POST: RequestHandler = async ({request, locals}) => { export const POST: RequestHandler = async ({request, cookies}) => {
let message = await request.json() let message = await request.json()
const auth = await locals.auth(); const user = await authenticateUser(cookies)
if(!auth) throw error(401, "Need to be logged in") if(!user) throw error(401, "Need to be logged in")
if(!user.access.includes("rezepte")) throw error(401, "Insufficient permissions")
const short_name = message.old_short_name const short_name = message.old_short_name
await dbConnect(); await dbConnect();

View File

@ -3,15 +3,20 @@ import { Recipe } from '../../../../models/Recipe';
import { dbConnect, dbDisconnect } from '../../../../utils/db'; import { dbConnect, dbDisconnect } from '../../../../utils/db';
import type {RecipeModelType} from '../../../../types/types'; import type {RecipeModelType} from '../../../../types/types';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import { authenticateUser } from '$lib/js/authenticate';
// header: use for bearer token for now // header: use for bearer token for now
// recipe json in body // recipe json in body
export const POST: RequestHandler = async ({request, locals}) => { export const POST: RequestHandler = async ({request, cookies}) => {
let message = await request.json() let message = await request.json()
const recipe_json = message.recipe const recipe_json = message.recipe
const auth = await locals.auth(); const user = await authenticateUser(cookies)
if(!auth){ console.log(user)
if(!user){
throw error(403, "Not logged in") 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{ else{
await dbConnect(); await dbConnect();
await Recipe.findOneAndUpdate({short_name: message.old_short_name }, recipe_json); await Recipe.findOneAndUpdate({short_name: message.old_short_name }, recipe_json);

View File

@ -3,11 +3,13 @@ import type { RequestHandler } from '@sveltejs/kit';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import { IMAGE_DIR } from '$env/static/private' import { IMAGE_DIR } from '$env/static/private'
import sharp from 'sharp'; import sharp from 'sharp';
import { authenticateUser } from '$lib/js/authenticate';
export const POST = (async ({ request, locals}) => { export const POST = (async ({ request, cookies }) => {
const data = await request.json(); const data = await request.json();
const auth = await locals.auth(); const user = await authenticateUser(cookies)
if (!auth) throw error(401, "Need to be logged in") 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")
let full_res = new Buffer.from(data.image, 'base64') let full_res = new Buffer.from(data.image, 'base64')
// reduce image size if over 500KB // reduce image size if over 500KB
const MAX_SIZE_KB = 500 const MAX_SIZE_KB = 500

View File

@ -3,12 +3,13 @@ import type { RequestHandler } from '@sveltejs/kit';
import { IMAGE_DIR } from '$env/static/private' import { IMAGE_DIR } from '$env/static/private'
import { unlink } from 'node:fs'; import { unlink } from 'node:fs';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import { authenticateUser } from '$lib/js/authenticate';;
export const POST = (async ({ request, locals}) => { export const POST = (async ({ request, cookies }) => {
const data = await request.json(); const data = await request.json();
const auth = await locals.auth() const user = await authenticateUser(cookies)
if(!auth) throw error(401, "You need to be logged in") 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")
[ "full", "thumb", "placeholder"].forEach((folder) => { [ "full", "thumb", "placeholder"].forEach((folder) => {
unlink(path.join(IMAGE_DIR, "rezepte", folder, data.name + ".webp"), (e) => { unlink(path.join(IMAGE_DIR, "rezepte", folder, data.name + ".webp"), (e) => {
if(e) error(404, "could not delete: " + folder + "/" + data.name + ".webp" + e) if(e) error(404, "could not delete: " + folder + "/" + data.name + ".webp" + e)

View File

@ -3,11 +3,13 @@ import type { RequestHandler } from '@sveltejs/kit';
import { IMAGE_DIR } from '$env/static/private' import { IMAGE_DIR } from '$env/static/private'
import { rename } from 'node:fs'; import { rename } from 'node:fs';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import { authenticateUser } from '$lib/js/authenticate';
export const POST = (async ({ request, locals}) => { export const POST = (async ({ request, cookies }) => {
const data = await request.json(); const data = await request.json();
const auth = await locals.auth(); const user = await authenticateUser(cookies)
if(!auth ) throw error(401, "need to be logged in") 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")
[ "full", "thumb", "placeholder"].forEach((folder) => { [ "full", "thumb", "placeholder"].forEach((folder) => {
const old_path = path.join(IMAGE_DIR, "rezepte", folder, data.old_name + ".webp") const old_path = path.join(IMAGE_DIR, "rezepte", folder, data.old_name + ".webp")

View File

@ -56,20 +56,47 @@
<h3>Rosenkranz</h3> <h3>Rosenkranz</h3>
</a> </a>
<a href="/glaube/predigten"> <a href="/glaube/predigten">
<svg <svg viewBox="0 0 56.664422 57.373692">
enable-background="new 0 0 512 512" <g
viewBox="0 0 512 512" transform="translate(-346.55989,-393.80378)">
xmlns="http://www.w3.org/2000/svg"> <g>
<g> <path
<path style="color:#000000;stroke-linecap:round;-inkscape-stroke:none"
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"/> d="m 456.23047,509.13672 a 1.5,1.5 0 0 0 -1.5,1.5 1.5,1.5 0 0 0 1.5,1.5 h 53.66406 a 1.5,1.5 0 0 0 1.5,-1.5 1.5,1.5 0 0 0 -1.5,-1.5 z"
<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"/> transform="translate(-108.17112,-60.95933)" />
<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"/> <g
<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"/> transform="translate(-108.17112,-60.959331)">
<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
<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"/> style="color:#000000;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
</g> d="m 459.49805,499.79492 a 1.50015,1.50015 0 0 0 -1.5,1.5 v 9.24219 a 1.5,1.5 0 0 0 1.5,1.5 1.5,1.5 0 0 0 1.5,-1.5 v -7.74219 H 504.875 v 7.16602 a 1.5,1.5 0 0 0 1.5,1.5 1.5,1.5 0 0 0 1.5,-1.5 v -8.66602 a 1.50015,1.50015 0 0 0 -1.5,-1.5 z"/>
</svg> <path
style="color:#000000;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="m 464.54883,468.27344 a 1.5,1.5 0 0 0 -1.5,1.5 v 31.46289 a 1.5,1.5 0 0 0 1.5,1.5 1.5,1.5 0 0 0 1.5,-1.5 v -31.46289 a 1.5,1.5 0 0 0 -1.5,-1.5 z"/>
<path
style="color:#000000;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="m 501.99023,467.93359 a 1.5,1.5 0 0 0 -1.49804,1.5 l 0.01,31.32813 a 1.5,1.5 0 0 0 1.50196,1.5 1.5,1.5 0 0 0 1.49804,-1.50195 l -0.01,-31.32618 a 1.5,1.5 0 0 0 -1.50196,-1.5 z"/>
<path
style="color:#000000;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="m 460.71289,461.20898 a 1.50015,1.50015 0 0 0 -1.5,1.5 v 6.88672 a 1.50015,1.50015 0 0 0 1.5,1.5 h 10.29688 a 1.5,1.5 0 0 0 1.5,-1.5 1.5,1.5 0 0 0 -1.5,-1.5 h -8.79688 v -3.88672 h 41.50391 v 3.69141 h -8.31641 a 1.5,1.5 0 0 0 -1.5,1.5 1.5,1.5 0 0 0 1.5,1.5 h 9.81641 a 1.50015,1.50015 0 0 0 1.5,-1.5 v -6.69141 a 1.50015,1.50015 0 0 0 -1.5,-1.5 z"/>
<path
style="color:#000000;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="m 471.07227,461.29883 a 1.5,1.5 0 0 0 -1.5,1.5 v 28.28711 a 1.50015,1.50015 0 0 0 1.88867,1.44922 l 11.50781,-3.08399 11.95508,3.20313 a 1.50015,1.50015 0 0 0 1.88867,-1.44727 v -28.27344 a 1.5,1.5 0 0 0 -1.5,-1.5 1.5,1.5 0 0 0 -1.5,1.5 v 26.31836 l -10.45508,-2.80273 a 1.50015,1.50015 0 0 0 -0.77734,0 l -10.00781,2.68359 v -26.33398 a 1.5,1.5 0 0 0 -1.5,-1.5 z"/>
<path
style="color:#000000;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="m 480.62109,454.93555 -9.63672,0.043 a 1.50015,1.50015 0 0 0 -1.49414,1.5 v 6.28125 a 1.5,1.5 0 0 0 1.5,1.5 1.5,1.5 0 0 0 1.5,-1.5 v -4.78711 l 8.13672,-0.0371 c 0.77001,0 1.02148,0.56256 1.10157,0.77148 l 0.0449,3.13867 a 1.5,1.5 0 0 0 1.52148,1.47852 1.5,1.5 0 0 0 1.47852,-1.52149 l -0.0508,-3.46875 a 1.50015,1.50015 0 0 0 -0.084,-0.47851 c 0,0 -1.09277,-2.91992 -4.01172,-2.91992 a 1.50015,1.50015 0 0 0 -0.006,0 z"/>
<path
style="color:#000000;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="m 495.15625,454.76367 -8.86523,0.008 c -2.5527,0.001 -4.04883,2.05469 -4.04883,2.05469 a 1.5,1.5 0 0 0 0.14648,2.11719 1.5,1.5 0 0 0 2.11719,-0.14649 c 0,0 1.18681,-1.02539 1.78711,-1.02539 a 1.50015,1.50015 0 0 0 0.002,0 l 7.35156,-0.006 -0.0215,3.83594 a 1.5,1.5 0 0 0 1.49023,1.50977 1.5,1.5 0 0 0 1.50977,-1.49219 l 0.0312,-5.34766 a 1.50015,1.50015 0 0 0 -1.5,-1.50781 z"/>
<path
style="color:#000000;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="m 483.12891,468.0293 a 1.5,1.5 0 0 0 -1.5,1.5 v 12.26953 a 1.5,1.5 0 0 0 1.5,1.5 1.5,1.5 0 0 0 1.5,-1.5 V 469.5293 a 1.5,1.5 0 0 0 -1.5,-1.5 z"/>
<path
style="color:#000000;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="m 479.31641,471.66797 a 1.5,1.5 0 0 0 -1.5,1.5 1.5,1.5 0 0 0 1.5,1.5 h 7.97265 a 1.5,1.5 0 0 0 1.5,-1.5 1.5,1.5 0 0 0 -1.5,-1.5 z"/>
</g>
</g>
</g>
</svg>
<h3>Predigten<h3> <h3>Predigten<h3>
</a> </a>
</LinksGrid> </LinksGrid>

View File

@ -1,8 +1,5 @@
import type { Actions, PageServerLoad } from "./$types" import { get_username } from '$lib/js/get_username';;
import { error } from "@sveltejs/kit"
export const load = (async ({cookies, locals}) => { export const load = (async ({cookies}) => {
return { return { user: await get_username(cookies) }
session: await locals.getSession()
}
}); });

View File

@ -3,8 +3,8 @@ import Header from '$lib/components/Header.svelte'
import UserHeader from '$lib/components/UserHeader.svelte'; import UserHeader from '$lib/components/UserHeader.svelte';
export let data export let data
let username = "" let username = ""
if(data.session){ if(data.user){
username = data.session.user.name username = data.user.username
} }
</script> </script>

1
src/utils/seed-data.ts Normal file
View File

@ -0,0 +1 @@
export {};

BIN
static/flims.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

1
static/icons/Git.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="92pt" height="92pt" viewBox="0 0 92 92"><defs><clipPath id="a"><path d="M0 .113h91.887V92H0Zm0 0"/></clipPath></defs><g clip-path="url(#a)"><path style="stroke:none;fill-rule:nonzero;fill:#f03c2e;fill-opacity:1" d="M90.156 41.965 50.036 1.848a5.918 5.918 0 0 0-8.372 0l-8.328 8.332 10.566 10.566a7.03 7.03 0 0 1 7.23 1.684 7.034 7.034 0 0 1 1.669 7.277l10.187 10.184a7.028 7.028 0 0 1 7.278 1.672 7.04 7.04 0 0 1 0 9.957 7.05 7.05 0 0 1-9.965 0 7.044 7.044 0 0 1-1.528-7.66l-9.5-9.497V59.36a7.04 7.04 0 0 1 1.86 11.29 7.04 7.04 0 0 1-9.957 0 7.04 7.04 0 0 1 0-9.958 7.06 7.06 0 0 1 2.304-1.539V33.926a7.049 7.049 0 0 1-3.82-9.234L29.242 14.272 1.73 41.777a5.925 5.925 0 0 0 0 8.371L41.852 90.27a5.925 5.925 0 0 0 8.37 0l39.934-39.934a5.925 5.925 0 0 0 0-8.371"/></g></svg>

After

Width:  |  Height:  |  Size: 819 B

97
static/icons/Jellyfin.svg Normal file
View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- ***** BEGIN LICENSE BLOCK *****
- Part of the Jellyfin project (https://jellyfin.media)
-
- All copyright belongs to the Jellyfin contributors; a full list can
- be found in the file CONTRIBUTORS.md
-
- This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/.
- ***** END LICENSE BLOCK ***** -->
<svg
id="svg30"
viewBox="0 0 465.40958 465.39288"
version="1.1"
sodipodi:docname="Jelly-banner-light.svg"
width="465.40958"
height="465.39288"
inkscape:export-filename="src/homepage/jellyfin_logo.svg"
inkscape:export-xdpi="300"
inkscape:export-ydpi="300"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview32"
pagecolor="#ffffff"
bordercolor="#111111"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
showgrid="false" />
<defs
id="defs7">
<linearGradient
id="linear-gradient"
x1="110.25"
y1="213.3"
x2="496.14001"
y2="436.09"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
stop-color="#aa5cc3"
id="stop2" />
<stop
offset="1"
stop-color="#00a4dc"
id="stop4" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linear-gradient"
id="linearGradient679"
gradientUnits="userSpaceOnUse"
x1="110.25"
y1="213.3"
x2="496.14001"
y2="436.09" />
<linearGradient
inkscape:collect="always"
xlink:href="#linear-gradient"
id="linearGradient681"
gradientUnits="userSpaceOnUse"
x1="110.25"
y1="213.3"
x2="496.14001"
y2="436.09" />
</defs>
<title
id="title9">banner-light</title>
<g
id="banner-light"
transform="translate(-28.68525,-23.3)">
<g
id="banner-light-icon">
<path
id="inner-shape"
d="m 261.42,201.62 c -20.44,0 -86.24,119.29 -76.2,139.43 10.04,20.14 142.48,19.92 152.4,0 9.92,-19.92 -55.76,-139.42 -76.2,-139.43 z"
fill="url(#linear-gradient)"
style="fill:url(#linearGradient679)" />
<path
id="outer-shape"
d="M 261.42,23.3 C 199.83,23.3 1.57,382.73 31.8,443.43 c 30.23,60.7 429.34,60 459.24,0 C 520.94,383.43 323,23.3 261.42,23.3 Z M 411.9,390.76 c -19.59,39.33 -281.08,39.77 -300.9,0 -19.82,-39.77 110.1,-275.28 150.45,-275.28 40.35,0 170.04,235.94 150.45,275.28 z"
fill="url(#linear-gradient)"
style="fill:url(#linearGradient681)" />
</g>
<g
id="jellyfin-dark-outlines"
style="isolation:isolate"
transform="translate(43.8)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

650
static/icons/Jitsi.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 35 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1 @@
<svg data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 266 266"><defs><linearGradient id="a" x1="45.04" y1="231.72" x2="231.72" y2="45.04" gradientUnits="userSpaceOnUse" gradientTransform="translate(-5.38 -5.38)"><stop offset="0" stop-color="#fff"/><stop offset="0" stop-color="#b8edff"/><stop offset="1" stop-color="#d4b8ff"/></linearGradient></defs><circle cx="133" cy="133" r="132" style="fill:url(#a)"/><path data-name="Logo Pfad" d="m224.19 176.51-4 24.19M41.91 177.5l14.81 14m95.76-137.65L56.62 191.31a.09.09 0 0 0 .07.15l163.41 9.37a.09.09 0 0 0 .09-.13L152.62 53.87a.1.1 0 0 0-.14-.02zm-19.74-13.29L41.8 177.31a.13.13 0 0 0 .11.19l182.18-.8a.12.12 0 0 0 .1-.19L132.95 40.56a.12.12 0 0 0-.21 0zm.11-.16 19.77 13.32" style="fill:none;stroke:#1d1d1b;stroke-miterlimit:10;stroke-width:6px"/></svg>

After

Width:  |  Height:  |  Size: 819 B

198
static/icons/Searx.svg Normal file
View File

@ -0,0 +1,198 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="330.93661"
height="303.22852"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="searx_logo.svg"
inkscape:export-filename="/home/a/magnif.png"
inkscape:export-xdpi="23.1774"
inkscape:export-ydpi="203.1774">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient3857">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop3859" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop3861" />
</linearGradient>
<linearGradient
id="linearGradient3790">
<stop
style="stop-color:#a9a9a9;stop-opacity:1;"
offset="0"
id="stop3792" />
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="1"
id="stop3794" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3790"
id="radialGradient3798"
cx="294.45947"
cy="208.37973"
fx="294.45947"
fy="208.37973"
r="107.58125"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3857"
id="linearGradient3865"
x1="120.68947"
y1="239.61774"
x2="120.68947"
y2="602.17517"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3790"
id="linearGradient3912"
x1="186.74416"
y1="354.42426"
x2="255.84358"
y2="254.35953"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2227304,0,0,0.89945099,-289.31433,113.40259)" />
<filter
inkscape:collect="always"
id="filter4024"
x="-0.12996517"
width="1.2599303"
y="-0.14709377"
height="1.2941875"
color-interpolation-filters="sRGB">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="6.4759344"
id="feGaussianBlur4026" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="-11.542922"
inkscape:cy="142.31651"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1855"
inkscape:window-height="1056"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:window-maximized="1"
showguides="true"
inkscape:guide-bbox="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="-24"
fit-margin-bottom="-6" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-61.719803,-34.870671)">
<path
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 70.523181,34.870671 c -7.11959,15.242893 -10.17798,31.779192 -8.22563,48.814566 5.01677,43.774133 41.675309,79.324503 91.536109,95.162893 -6.62576,-22.40752 -5.34093,-44.9362 2.6395,-65.84431 C 108.73618,98.821131 74.828141,70.195435 70.523181,34.870671 z"
id="path3814-0-7"
inkscape:connector-curvature="0" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 303.77876,36.21406 c 7.11959,15.242893 10.17798,31.779192 8.22563,48.814566 -5.01677,43.774134 -41.67531,79.324504 -91.53611,95.162894 6.62576,-22.40752 5.34093,-44.9362 -2.6395,-65.84431 47.73698,-14.18269 81.64502,-42.808386 85.94998,-78.13315 z"
id="path3814-0"
inkscape:connector-curvature="0" />
<path
transform="matrix(0.6556593,-0.75505688,0.75505688,0.6556593,0,0)"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m -5.0905523,259.06055 18.4167573,0 c 6.220455,0 11.228257,16.68196 11.228257,37.40349 l 0,172.83701 c 0,20.72153 -5.007802,37.40349 -11.228257,37.40349 l -18.4167573,0 c -6.2204547,0 -11.2282577,-16.68196 -11.2282577,-37.40349 l 0,-172.83701 c 0,-20.72153 5.007803,-37.40349 11.2282577,-37.40349 z"
id="rect3804"
inkscape:connector-curvature="0" />
<path
sodipodi:type="arc"
style="fill:url(#radialGradient3798);fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2987"
sodipodi:cx="294.45947"
sodipodi:cy="208.37973"
sodipodi:rx="107.58125"
sodipodi:ry="107.58125"
d="m 402.04073,208.37973 a 107.58125,107.58125 0 1 1 -215.16251,0 107.58125,107.58125 0 1 1 215.16251,0 z"
transform="translate(-107.07617,-60.609153)" />
<path
sodipodi:type="arc"
style="fill:url(#linearGradient3865);fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3757"
sodipodi:cx="131.82491"
sodipodi:cy="299.29346"
sodipodi:rx="101.52033"
sodipodi:ry="101.52033"
d="m 233.34524,299.29346 a 101.52033,101.52033 0 1 1 -203.040667,0 101.52033,101.52033 0 1 1 203.040667,0 z"
transform="matrix(0.76865672,0,0,0.76865672,85.80266,-82.535889)" />
<path
sodipodi:type="arc"
style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3800"
sodipodi:cx="183.34268"
sodipodi:cy="156.35687"
sodipodi:rx="27.274118"
sodipodi:ry="27.274118"
d="m 210.6168,156.35687 a 27.274118,27.274118 0 1 1 -54.54824,0 27.274118,27.274118 0 1 1 54.54824,0 z"
transform="translate(5,-7.1428572)" />
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3802"
sodipodi:cx="197.9899"
sodipodi:cy="203.32896"
sodipodi:rx="5.5558391"
sodipodi:ry="5.5558391"
d="m 203.54574,203.32896 a 5.5558391,5.5558391 0 1 1 -11.11168,0 5.5558391,5.5558391 0 1 1 11.11168,0 z"
transform="translate(1.4847712,-63.564549)" />
<rect
style="fill:#ffffff;fill-opacity:0.82211531;fill-rule:nonzero;stroke:none;filter:url(#filter4024)"
id="rect3916"
width="2.2392972"
height="159.43797"
x="19.525793"
y="337.8396"
rx="2.8666623"
ry="9.0007057"
transform="matrix(0.74466525,-0.84318084,0.84318084,0.74466525,-35.543204,-26.349917)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -0,0 +1,445 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48px"
height="48px"
id="svg5186"
sodipodi:version="0.32"
inkscape:version="0.45+devel"
sodipodi:docname="transmission.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
inkscape:export-filename="/home/andreas/project/application icons/48x48/transmission.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs5188">
<linearGradient
inkscape:collect="always"
id="linearGradient9795">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop9797" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop9799" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient9783">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop9785" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop9787" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient9775">
<stop
style="stop-color:#f9f9f9;stop-opacity:1"
offset="0"
id="stop9777" />
<stop
style="stop-color:#eeeeec;stop-opacity:0.62037037"
offset="1"
id="stop9779" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient5948">
<stop
style="stop-color:#787b76;stop-opacity:1;"
offset="0"
id="stop5950" />
<stop
id="stop5956"
offset="0.87125719"
style="stop-color:#babcb9;stop-opacity:1" />
<stop
style="stop-color:#787b76;stop-opacity:1"
offset="1"
id="stop5952" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient5908">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop5910" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop5912" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient5898">
<stop
style="stop-color:#cc0000;stop-opacity:1;"
offset="0"
id="stop5900" />
<stop
id="stop5906"
offset="0.36509839"
style="stop-color:#ef0000;stop-opacity:1" />
<stop
style="stop-color:#aa0000;stop-opacity:1"
offset="1"
id="stop5902" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient5871">
<stop
style="stop-color:#f0f2ef;stop-opacity:1"
offset="0"
id="stop5873" />
<stop
style="stop-color:#cdd1c8;stop-opacity:1"
offset="1"
id="stop5875" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient5843">
<stop
style="stop-color:#888a85;stop-opacity:1"
offset="0"
id="stop5845" />
<stop
style="stop-color:#2e3436;stop-opacity:1"
offset="1"
id="stop5847" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient5835">
<stop
style="stop-color:#555753;stop-opacity:1;"
offset="0"
id="stop5837" />
<stop
style="stop-color:#2e3436;stop-opacity:1"
offset="1"
id="stop5839" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient5823">
<stop
style="stop-color:#2e3436;stop-opacity:1;"
offset="0"
id="stop5825" />
<stop
style="stop-color:#2e3436;stop-opacity:0;"
offset="1"
id="stop5827" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient5234">
<stop
style="stop-color:#babdb6;stop-opacity:1;"
offset="0"
id="stop5236" />
<stop
id="stop5242"
offset="0.13299191"
style="stop-color:#eeeeec;stop-opacity:1" />
<stop
style="stop-color:#babdb6;stop-opacity:1"
offset="1"
id="stop5238" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5234"
id="linearGradient5240"
x1="23.738585"
y1="4.156569"
x2="23.738585"
y2="19.46567"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5823"
id="linearGradient5829"
x1="23.732271"
y1="30.057167"
x2="23.688078"
y2="22.632544"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5835"
id="linearGradient5841"
x1="23.9375"
y1="30.616879"
x2="23.9375"
y2="36.357994"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5843"
id="linearGradient5849"
x1="20.771132"
y1="32.248005"
x2="20.563131"
y2="23.939499"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5898"
id="linearGradient5904"
x1="14.8125"
y1="5.6244211"
x2="14.8125"
y2="9"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5908"
id="linearGradient5914"
x1="24.040522"
y1="5.0690055"
x2="24.040522"
y2="10.0086"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5871"
id="linearGradient5928"
x1="13.625"
y1="33.125"
x2="14.125"
y2="24"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5948"
id="linearGradient5954"
x1="10.1875"
y1="20.25"
x2="10.1875"
y2="42.5"
gradientUnits="userSpaceOnUse" />
<filter
inkscape:collect="always"
id="filter9771"
x="-0.02976581"
width="1.0595316"
y="-0.13995509"
height="1.2799102">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.5196773"
id="feGaussianBlur9773" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient9775"
id="linearGradient9781"
x1="24.71875"
y1="35.958694"
x2="23.936657"
y2="17.070877"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient9783"
id="linearGradient9789"
x1="18.3125"
y1="20.743757"
x2="18.3125"
y2="21.814325"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient9795"
id="linearGradient9801"
x1="30.4375"
y1="31.82852"
x2="29.742416"
y2="27.45352"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6568542"
inkscape:cx="30.372474"
inkscape:cy="21.423534"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1091"
inkscape:window-height="777"
inkscape:window-x="557"
inkscape:window-y="164">
<inkscape:grid
type="xygrid"
id="grid5195" />
</sodipodi:namedview>
<metadata
id="metadata5191">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="opacity:0.28240741;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter9771)"
id="rect9761"
width="41.901279"
height="8.9116125"
x="3"
y="39"
rx="2.2980971"
ry="2.2980971" />
<path
style="fill:url(#linearGradient5954);fill-rule:evenodd;stroke:#555753;stroke-width:1.00000011999999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="M 10,16.59375 C 8.8196081,16.548814 7.6402135,17.571722 7.53125,18.8125 C 6.643292,26.100083 5.3269606,33.403527 4.65625,40.6875 L 4.65625,43.75 C 4.6900093,45.329492 5.7271791,46.392039 6.875,46.59375 L 41.5,46.59375 C 42.479024,46.569246 43.565009,45.89005 43.53125,44.59375 L 43.53125,40.65625 L 40.40625,19.4375 C 40.152431,18.135677 39.039534,16.752716 37.5,16.59375 L 10,16.59375 z"
id="path5232"
sodipodi:nodetypes="ccccccccccc" />
<path
style="fill:url(#linearGradient5928);fill-opacity:1;fill-rule:evenodd;stroke:#555753;stroke-width:0.99999994000000003px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 10.601853,39.624614 C 9.47224,39.502143 8.6733861,38.760954 8.7014295,37.401046 L 10.601853,21.407733 C 10.893931,20.339398 11.586949,19.485349 12.680909,19.488442 L 34.605501,19.488442 C 35.691818,19.455762 36.778134,20.208796 37.062569,21.104687 L 39.478435,37.237611 C 39.535481,38.706714 38.931012,39.557098 37.913093,39.523599 L 10.601853,39.624614 z"
id="path5230"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:url(#linearGradient5841);fill-rule:evenodd;stroke:url(#linearGradient5849);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="M 20.46875,20.4375 L 18.40625,32.46875 L 15.4375,32.46875 L 23.46875,37.625 L 32.4375,32.46875 L 29.46875,32.46875 L 27.59375,20.4375 L 20.46875,20.4375 z"
id="path5197"
sodipodi:nodetypes="cccccccc" />
<rect
style="opacity:1;fill:url(#linearGradient5904);fill-opacity:1;stroke:#930000;stroke-width:1.00000011999999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect5224"
width="31.113209"
height="6.0609155"
x="8.4847708"
y="4.5135489"
rx="5.0159144"
ry="1.9854566" />
<rect
style="opacity:0.58333333;fill:none;fill-opacity:1;stroke:url(#linearGradient5914);stroke-width:1.00000011999999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect5896"
width="29.080278"
height="3.9395947"
x="9.5003824"
y="5.5690055"
rx="1.8339339"
ry="1.2783499" />
<path
style="opacity:0.24537036000000001;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient9781);stroke-width:1.00000011999999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 10.592965,17.57221 C 9.474152,17.53019 8.3562869,18.486727 8.2530054,19.647002 L 5.4687498,39.722803 C 5.4796612,39.847886 5.4997885,39.979699 5.5279893,40.102694 L 5.5279893,42.966491 C 5.559989,44.443503 6.5430497,45.407885 7.6309909,45.596509 L 40.479283,45.596509 C 41.407232,45.573597 42.406944,44.967688 42.374947,43.755497 L 42.374947,40.073472 C 42.382229,40.044972 42.398547,40.013922 42.404566,39.985805 L 42.374947,39.781247 L 42.374947,39.576691 L 42.345327,39.576691 L 39.442592,20.202228 C 39.202015,18.98487 38.147175,17.72086 36.687956,17.57221 L 10.592965,17.57221 z"
id="path5881" />
<path
style="fill:url(#linearGradient9789);fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1;opacity:0.20833333000000001"
d="M 10.210155,29.955767 L 12.048004,22 L 36.07815,22.05802 L 37.857941,31.044156 L 36.681164,21.969631 C 36.460193,20.967897 35.929863,20 34.957591,20.025088 L 13.037281,19.980893 C 11.606886,19.936699 11.32554,20.864777 11,21.969631 L 10.210155,29.955767 z"
id="path5926"
sodipodi:nodetypes="ccccccccc" />
<rect
style="opacity:1;fill:url(#linearGradient5240);fill-opacity:1;stroke:#888a85;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect5226"
width="7.0964494"
height="25.970053"
x="20.48369"
y="3.6044116"
rx="1.0763195"
ry="1.0763192" />
<rect
style="opacity:1;fill:url(#linearGradient5829);fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect5244"
width="8.1317272"
height="8.0433397"
x="19.975765"
y="22.013826"
rx="1.0763195"
ry="1.0763192" />
<path
style="opacity:0.43518521;fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
d="M 11.423372,41.486321 L 39.533811,41.486321"
id="path5879"
sodipodi:nodetypes="cc" />
<rect
style="opacity:0.22685185;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect5892"
width="5.151906"
height="23.93712"
x="21.428234"
y="4.6321397"
rx="1.0763195"
ry="1.0763192" />
<g
id="g5972"
style="opacity:0.62037037">
<path
sodipodi:nodetypes="cc"
id="path5831"
d="M 20.4375,30.5 L 27.5,30.5"
style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;opacity:1" />
<path
sodipodi:nodetypes="cc"
id="path5833"
d="M 19.960998,32.5 L 27.976504,32.5"
style="fill:none;fill-rule:evenodd;stroke:#888a85;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;opacity:0.68055556" />
<path
sodipodi:nodetypes="cc"
id="path5958"
d="M 20.273498,31.5 L 27.726504,31.5"
style="fill:none;fill-rule:evenodd;stroke:#5d5d5c;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;opacity:1" />
<path
sodipodi:nodetypes="cc"
id="path5960"
d="M 19.869986,33.488738 L 28.141277,33.488738"
style="fill:none;fill-rule:evenodd;stroke:#5d5d5c;stroke-width:0.99999994000000003px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;opacity:0.68055556" />
</g>
<path
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
d="M 14.381412,31.513733 L 17.519198,31.513733"
id="path9791"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
d="M 30.443912,31.451233 L 33.581698,31.451233"
id="path9803"
sodipodi:nodetypes="cc" />
<path
sodipodi:type="arc"
style="opacity:0.33500001;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path5119"
sodipodi:cx="9.8553009"
sodipodi:cy="42.188465"
sodipodi:rx="1.1932427"
sodipodi:ry="1.0827572"
d="M 11.048544,42.188465 A 1.1932427,1.0827572 0 1 1 8.6620582,42.188465 A 1.1932427,1.0827572 0 1 1 11.048544,42.188465 z"
transform="matrix(0.4216252,0,0,0.4766032,5.3634688,21.39228)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB