moved sites to postgres database, use button to add more
@@ -1,13 +1,7 @@
|
||||
<script lang="ts">
|
||||
import External from '$lib/icons/external.svelte';
|
||||
import type { Site } from '$lib/interfaces/site.ts';
|
||||
|
||||
interface Site {
|
||||
title: string;
|
||||
image: string;
|
||||
link: string;
|
||||
background?: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
let { title, image, background, color, link }: Site = $props();
|
||||
|
||||
@@ -60,7 +54,7 @@
|
||||
h2,
|
||||
.link,
|
||||
.title {
|
||||
transition: all 0.2s ease-in-out;
|
||||
transition: all 0.18s ease-in-out;
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -90,7 +84,8 @@
|
||||
.image {
|
||||
height: 8rem;
|
||||
width: 100%;
|
||||
margin: 1.2rem 0;
|
||||
width: 8rem;
|
||||
margin: 1.2rem auto;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
@@ -105,6 +100,7 @@
|
||||
left: calc(100% - 2rem);
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
fill: var(--color);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 750px) {
|
||||
@@ -130,7 +126,6 @@
|
||||
opacity: 1;
|
||||
left: calc(100% - 1rem);
|
||||
top: 2px;
|
||||
fill: var(--color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
71
src/lib/components/forms/FormSite.svelte
Normal file
@@ -0,0 +1,71 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import Input from '$lib/components/Input.svelte';
|
||||
import ColorInput from '$lib/components/ColorInput.svelte';
|
||||
import Id from '$lib/icons/id.svelte';
|
||||
import TextColor from '$lib/icons/text-color.svelte';
|
||||
import TextSize from '$lib/icons/text-size.svelte';
|
||||
import Window from '$lib/icons/window.svelte';
|
||||
import Quill from '$lib/icons/quill.svelte';
|
||||
import Tag from '$lib/icons/tag.svelte';
|
||||
import Link from '$lib/icons/link.svelte';
|
||||
import PaintRoller from '$lib/icons/paint-roller.svelte';
|
||||
import Bucket from '$lib/icons/bucket.svelte';
|
||||
import Picture from '$lib/icons/picture.svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const close = () => dispatch('close');
|
||||
</script>
|
||||
|
||||
<form method="POST">
|
||||
<div class="wrapper">
|
||||
<Input label="Name" icon={TextSize} placeholder="Website name" required />
|
||||
<Input label="Link" icon={Link} placeholder="https://site.tld" required />
|
||||
<Input label="Image" icon={Picture} placeholder="/images/site.png" required />
|
||||
<ColorInput label="Color" icon={PaintRoller} placeholder="#21ADF6" required />
|
||||
<ColorInput label="Background" icon={Bucket} />
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<button on:click={close} aria-disabled="false" type="button" tabindex="0"
|
||||
><span tabindex="-1">Cancel</span></button
|
||||
>
|
||||
<button class="affirmative" type="submit" tabindex="-1">
|
||||
<span tabindex="-1">Add connection</span>
|
||||
</button>
|
||||
</footer>
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
form {
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 0.75rem 1.5rem;
|
||||
max-width: 100%;
|
||||
height: 2.5rem;
|
||||
width: auto;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
flex: 0 0 auto;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
|
||||
button {
|
||||
flex: unset;
|
||||
|
||||
span {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(form .wrapper div) {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
9
src/lib/interfaces/site.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface Site {
|
||||
name: string;
|
||||
link: string;
|
||||
image: string;
|
||||
color: string;
|
||||
background: string;
|
||||
created?: number;
|
||||
updated?: number;
|
||||
}
|
||||
21
src/lib/server/database/sites.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { getDb } from '../database';
|
||||
import type { Site } from '$lib/interfaces/site';
|
||||
|
||||
export async function allSites(): Promise<Array<Site>> {
|
||||
const pool = await getDb();
|
||||
const query = 'SELECT * FROM site';
|
||||
const result = await pool.query(query);
|
||||
return result.rows || [];
|
||||
}
|
||||
|
||||
export async function addSite(site: Site) {
|
||||
const timestamp = Math.floor(new Date().getTime() / 1000);
|
||||
const query = `INSERT INTO site (name, link, image, color, background, updated)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id`;
|
||||
const { name, link, image, color, background } = site;
|
||||
|
||||
const pool = await getDb();
|
||||
const result = await pool.query(query, [name, link, image, color, background, timestamp]);
|
||||
return { id: result.rows[0].id };
|
||||
}
|
||||
49
src/routes/sites/+page.server.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { allSites, addSite } from '$lib/server/database/sites';
|
||||
import type { Site } from '$lib/interfaces/site';
|
||||
import type { Actions, PageServerLoad } from './$types';
|
||||
|
||||
export const load: PageServerLoad = async (): Promise<{ sites: Array<Site> }> => {
|
||||
let sites: Site[] = [];
|
||||
|
||||
try {
|
||||
sites = await allSites();
|
||||
console.log('got sites:', sites);
|
||||
} catch (error) {
|
||||
console.error('error while fetching sites server props, likely db issue');
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
return { sites };
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
default: async ({ request }) => {
|
||||
try {
|
||||
const formData = await request.formData();
|
||||
|
||||
// Extract values by input `name` attributes
|
||||
const name = formData.get('Name')?.toString().trim();
|
||||
const link = formData.get('Link')?.toString().trim();
|
||||
const image = formData.get('Image')?.toString().trim();
|
||||
const color = formData.get('Color')?.toString().trim();
|
||||
const background = formData.get('Background')?.toString().trim();
|
||||
|
||||
if (!name || !link || !image || !color || !background) {
|
||||
return { error: 'All fields are required!', success: false, statusCode: 400 };
|
||||
}
|
||||
|
||||
if (!name || !link) {
|
||||
return { error: 'name & link are required', success: false, statusCode: 400 };
|
||||
}
|
||||
|
||||
const site: Site = { name, link, image, color, background };
|
||||
await addSite(site);
|
||||
|
||||
return { success: true };
|
||||
} catch (err: unknown) {
|
||||
console.log(err);
|
||||
console.error('Failed to add site:', err.message);
|
||||
return { error: 'internal server error', success: false, statusCode: 500 };
|
||||
}
|
||||
}
|
||||
} satisfies Actions;
|
||||
@@ -1,103 +1,26 @@
|
||||
<script lang="ts">
|
||||
import PageHeader from '$lib/components/PageHeader.svelte';
|
||||
import Dialog from '$lib/components/Dialog.svelte';
|
||||
import Section from '$lib/components/Section.svelte';
|
||||
import FormSite from '$lib/components/forms/FormSite.svelte';
|
||||
import ThumbnailButton from '$lib/components/ThumbnailButton.svelte';
|
||||
import type { Site } from '$lib/interfaces/site.ts';
|
||||
|
||||
interface Site {
|
||||
title: string;
|
||||
image: string;
|
||||
link: string;
|
||||
background?: string;
|
||||
color?: string;
|
||||
}
|
||||
let { data }: { data: { site: Site } } = $props();
|
||||
let open = $state(false);
|
||||
|
||||
const sites: Array<Site> = [
|
||||
{
|
||||
title: 'Grafana',
|
||||
image: '/images/grafana.png',
|
||||
link: 'https://grafana.schleppe.cloud',
|
||||
background: '#F5E3DC',
|
||||
color: '#F05A24'
|
||||
},
|
||||
{
|
||||
title: 'Prometheus',
|
||||
image: '/images/prometheus.svg',
|
||||
link: 'http://prome.schleppe:9090',
|
||||
background: '#262221',
|
||||
color: '#F3BFA2'
|
||||
},
|
||||
{
|
||||
title: 'Traefik',
|
||||
image: '/images/traefik.png',
|
||||
link: 'https://grafana.schleppe.cloud',
|
||||
background: '#30A4C2',
|
||||
color: 'white'
|
||||
},
|
||||
{
|
||||
title: 'Kibana',
|
||||
image: '/images/kibana.svg',
|
||||
link: 'https://kibana.schleppe.cloud',
|
||||
background: '#f6cfdd',
|
||||
color: '#401C26'
|
||||
},
|
||||
{
|
||||
title: 'HASS',
|
||||
image: '/images/hass.png',
|
||||
link: 'http://homeassistant.schleppe:8123',
|
||||
background: '#1ABCF2',
|
||||
color: 'white'
|
||||
},
|
||||
{
|
||||
title: 'Vault',
|
||||
image: '/images/vault.svg',
|
||||
link: 'http://vault.schleppe:8200',
|
||||
background: 'white',
|
||||
color: 'black'
|
||||
},
|
||||
{
|
||||
title: 'Drone',
|
||||
image: '/images/drone.png',
|
||||
link: 'https://drone.schleppe.cloud',
|
||||
background: '#D8E2F0',
|
||||
color: '#1E375A'
|
||||
},
|
||||
{
|
||||
title: 'Immich',
|
||||
image: '/images/immich.png',
|
||||
link: 'http://immich.schleppe:2283',
|
||||
background: 'white',
|
||||
color: 'black'
|
||||
},
|
||||
{
|
||||
title: 'Wiki',
|
||||
image: '/images/xwiki.png',
|
||||
link: 'https://wiki.schleppe.cloud',
|
||||
background: 'white',
|
||||
color: 'black'
|
||||
},
|
||||
{
|
||||
title: 'Gitea',
|
||||
image: '/images/gitea.png',
|
||||
link: 'https://git.schleppe.cloud',
|
||||
background: '#E6E7D7',
|
||||
color: '#609925'
|
||||
},
|
||||
{
|
||||
title: 'PBS',
|
||||
image: '/images/proxmox.png',
|
||||
link: 'https://clio.schleppe:8007',
|
||||
background: '#EDE1D2',
|
||||
color: '#E66B00'
|
||||
}
|
||||
];
|
||||
const { sites } = data;
|
||||
</script>
|
||||
|
||||
<PageHeader>Sites</PageHeader>
|
||||
<PageHeader>Sites
|
||||
|
||||
<button class="add-site-btn affirmative" on:click={() => (open = true)}><span>Add new site</span></button>
|
||||
</PageHeader>
|
||||
|
||||
<div class="section-wrapper">
|
||||
{#each sites as site}
|
||||
{#each sites as site (site)}
|
||||
<ThumbnailButton
|
||||
title={site.title}
|
||||
title={site.name}
|
||||
image={site.image}
|
||||
background={site.background}
|
||||
color={site.color}
|
||||
@@ -106,27 +29,15 @@
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="section-wrapper full-width">
|
||||
<Section
|
||||
title="Expose HTTP traffic"
|
||||
description="You can reach your Application on a specific Port you configure, redirecting all your domains to it. You can make it Private by disabling HTTP traffic."
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="IP restrictions"
|
||||
description="Restrict or block access to your application based on specific IP addresses or CIDR blocks."
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="Expose HTTP traffic"
|
||||
description="You can reach your Application on a specific Port you configure, redirecting all your domains to it. You can make it Private by disabling HTTP traffic."
|
||||
/>
|
||||
|
||||
<Section
|
||||
title="Connected services"
|
||||
description="Connected services can communicate with your application over the private network."
|
||||
/>
|
||||
</div>
|
||||
{#if open}
|
||||
<Dialog
|
||||
on:close={() => (open = false)}
|
||||
title="Add new site"
|
||||
description="You can select anything deployed in <b>Belgium (europe-west1) datacenter</b> and create an internal connection with your service."
|
||||
>
|
||||
<FormSite on:close={() => (open = false)} />
|
||||
</Dialog>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.section-wrapper {
|
||||
@@ -149,4 +60,10 @@
|
||||
margin-top: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
:global(button.add-site-btn) {
|
||||
font-size: 1.2rem;
|
||||
float: right;
|
||||
height: 2.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
BIN
static/images/bitwarden.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
37
static/images/brew.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="200.000000pt" height="200.000000pt" viewBox="0 0 200.000000 200.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,200.000000) scale(0.100000,-0.100000)"
|
||||
fill="#F4400E" stroke="none">
|
||||
<path d="M956 1798 c-9 -12 -16 -39 -16 -60 0 -23 -5 -38 -12 -39 -166 -9
|
||||
-356 -117 -473 -269 -50 -65 -121 -206 -130 -257 -3 -18 -9 -42 -13 -55 -5
|
||||
-13 -10 -45 -11 -73 -2 -27 -5 -53 -7 -56 -5 -9 31 -40 53 -44 10 -2 30 -4 44
|
||||
-4 14 -1 26 -7 26 -13 1 -92 68 -295 107 -327 15 -12 69 -12 115 0 8 3 20 -14
|
||||
31 -47 40 -115 127 -235 230 -317 83 -66 109 -70 166 -28 123 90 227 227 264
|
||||
349 l13 43 59 -7 c42 -5 62 -3 74 7 28 23 86 173 98 255 12 78 17 93 31 88 7
|
||||
-3 31 0 53 5 35 10 40 15 44 46 2 19 -2 63 -10 97 -7 35 -15 74 -18 88 -3 14
|
||||
-27 70 -54 125 -100 205 -282 347 -492 386 l-68 12 0 42 c0 28 -6 47 -18 58
|
||||
-26 24 -68 21 -86 -5z m159 -223 c59 -10 173 -66 233 -114 109 -87 194 -227
|
||||
227 -375 l6 -29 -50 6 c-106 13 -244 77 -322 151 -48 45 -102 117 -135 179
|
||||
-32 62 -64 87 -96 74 -9 -3 -35 -40 -58 -83 -41 -75 -173 -224 -198 -224 -6 0
|
||||
-12 -4 -14 -8 -3 -10 -109 -61 -142 -69 -61 -15 -136 -26 -142 -20 -12 11 44
|
||||
166 82 228 88 145 239 258 380 283 15 3 28 7 31 9 6 6 152 1 198 -8z m-56
|
||||
-371 c29 -36 79 -86 112 -111 50 -39 59 -51 59 -77 0 -43 -28 -120 -66 -182
|
||||
-30 -47 -149 -174 -165 -174 -4 0 -38 31 -76 69 -80 80 -140 187 -149 266 l-6
|
||||
50 60 47 c33 26 84 76 113 112 29 36 55 66 59 66 4 0 30 -30 59 -66z m405
|
||||
-292 c-15 -101 -52 -205 -70 -198 -7 3 -32 10 -56 16 -81 22 -81 23 -49 88 16
|
||||
31 37 82 45 113 l17 56 59 -19 60 -20 -6 -36z m-801 41 c10 -55 16 -72 46
|
||||
-132 27 -55 28 -62 13 -70 -14 -7 -119 -41 -128 -41 -4 0 -24 64 -22 70 1 3
|
||||
-4 14 -10 26 -6 11 -14 34 -17 50 -19 101 -20 99 29 108 16 3 31 8 34 11 3 3
|
||||
15 5 28 5 16 0 23 -7 27 -27z m198 -330 c50 -48 117 -93 138 -93 23 0 68 30
|
||||
128 84 43 39 56 46 78 40 23 -6 26 -10 20 -33 -8 -32 -59 -144 -77 -168 -28
|
||||
-36 -140 -143 -150 -143 -6 0 -43 33 -82 73 -100 102 -171 251 -127 269 27 11
|
||||
31 9 72 -29z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 41 KiB |
23
static/images/influxdb.svg
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="InfluxData_Symbol_Only" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px" width="900px" height="900px" viewBox="-173 -143 900 900" style="enable-background:new -173 -143 900 900;"
|
||||
xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;}
|
||||
.st1{fill:#22ADF6;}
|
||||
</style>
|
||||
<rect id="Background" x="-173" y="-143" class="st0" width="900" height="900"/>
|
||||
<path id="Cuboctahedron" class="st1" d="M694.1,394.9l-81-352.7C608.5,22.9,591,3.6,571.7-2L201.5-116.2c-4.6-1.8-10.1-1.8-15.7-1.8
|
||||
c-15.7,0-32.2,6.4-43.3,15.7l-265.2,246.8c-14.7,12.9-22.1,38.7-17.5,57.1l86.6,377.6c4.6,19.3,22.1,38.7,41.4,44.2l346.2,106.8
|
||||
c4.6,1.8,10.1,1.8,15.7,1.8c15.7,0,32.2-6.4,43.3-15.7L676.6,453C691.4,439.2,698.7,414.3,694.1,394.9z M240.2-32.4l254.1,78.3
|
||||
c10.1,2.8,10.1,7.4,0,10.1L360.8,86.4c-10.1,2.8-23.9-1.8-31.3-9.2l-93-100.4C228.2-31.4,230-35.1,240.2-32.4z M398.5,423.5
|
||||
c2.8,10.1-3.7,15.7-13.8,12.9l-274.4-84.7c-10.1-2.8-12-11.1-4.6-18.4L315.7,138c7.4-7.4,15.7-4.6,18.4,5.5L398.5,423.5z
|
||||
M-53.6,174.8L169.3-32.4c7.4-7.4,19.3-6.4,26.7,0.9L307.4,89.2c7.4,7.4,6.4,19.3-0.9,26.7L83.6,323.1c-7.4,7.4-19.3,6.4-26.7-0.9
|
||||
L-54.5,201.6C-61.9,193.3-60.9,181.3-53.6,174.8z M0.8,503.6l-58.9-258.8c-2.8-10.1,1.8-12,8.3-4.6l93,100.4
|
||||
c7.4,7.4,10.1,22.1,7.4,32.2L10,503.6C7.2,513.7,2.6,513.7,0.8,503.6z M326.7,654.6l-291-89.3c-10.1-2.8-15.7-13.8-12.9-23.9
|
||||
l48.8-156.6c2.8-10.1,13.8-15.7,23.9-12.9l291,89.3c10.1,2.8,15.7,13.8,12.9,23.9l-48.8,156.6C347,651.9,336.9,657.4,326.7,654.6z
|
||||
M584.5,442.8L390.3,623.3c-7.4,7.4-11,4.6-8.3-5.5L422.5,487c2.8-10.1,13.8-20.3,23.9-22.1l133.5-30.4
|
||||
C590.1,431.8,591.9,436.4,584.5,442.8z M605.7,404.2L445.5,441c-10.1,2.8-20.3-3.7-23-13.8l-68.1-296.5c-2.8-10.1,3.7-20.3,13.8-23
|
||||
l160.2-36.8c10.1-2.8,20.3,3.7,23,13.8l68.1,296.5C622.3,392.2,615.9,402.3,605.7,404.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
BIN
static/images/ollama.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
static/images/part-db.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
static/images/request.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
static/images/sonarr.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
static/images/spoolman.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
static/images/tautulli.png
Normal file
|
After Width: | Height: | Size: 20 KiB |