diff --git a/src/lib/components/ThumbnailButton.svelte b/src/lib/components/ThumbnailButton.svelte
index 2b7bd2b..37cf190 100644
--- a/src/lib/components/ThumbnailButton.svelte
+++ b/src/lib/components/ThumbnailButton.svelte
@@ -1,13 +1,7 @@
+
+
+
+
+
diff --git a/src/lib/interfaces/site.ts b/src/lib/interfaces/site.ts
new file mode 100644
index 0000000..387fa75
--- /dev/null
+++ b/src/lib/interfaces/site.ts
@@ -0,0 +1,9 @@
+export interface Site {
+ name: string;
+ link: string;
+ image: string;
+ color: string;
+ background: string;
+ created?: number;
+ updated?: number;
+}
diff --git a/src/lib/server/database/sites.ts b/src/lib/server/database/sites.ts
new file mode 100644
index 0000000..606e200
--- /dev/null
+++ b/src/lib/server/database/sites.ts
@@ -0,0 +1,21 @@
+import { getDb } from '../database';
+import type { Site } from '$lib/interfaces/site';
+
+export async function allSites(): Promise> {
+ 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 };
+}
diff --git a/src/routes/sites/+page.server.ts b/src/routes/sites/+page.server.ts
new file mode 100644
index 0000000..14b79ef
--- /dev/null
+++ b/src/routes/sites/+page.server.ts
@@ -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 }> => {
+ 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;
diff --git a/src/routes/sites/+page.svelte b/src/routes/sites/+page.svelte
index 2671b80..b81f97b 100644
--- a/src/routes/sites/+page.svelte
+++ b/src/routes/sites/+page.svelte
@@ -1,103 +1,26 @@
-Sites
+Sites
+
+
+
- {#each sites as site}
+ {#each sites as site (site)}
-
-
-
-
-
-
-
-
-
+{#if open}
+
+{/if}
diff --git a/static/images/bitwarden.png b/static/images/bitwarden.png
new file mode 100644
index 0000000..340eae7
Binary files /dev/null and b/static/images/bitwarden.png differ
diff --git a/static/images/brew.svg b/static/images/brew.svg
new file mode 100644
index 0000000..271b07c
--- /dev/null
+++ b/static/images/brew.svg
@@ -0,0 +1,37 @@
+
+
+
diff --git a/static/images/grafana.png b/static/images/grafana.png
index 3f3fa78..eda286e 100644
Binary files a/static/images/grafana.png and b/static/images/grafana.png differ
diff --git a/static/images/influxdb.svg b/static/images/influxdb.svg
new file mode 100644
index 0000000..c38fd25
--- /dev/null
+++ b/static/images/influxdb.svg
@@ -0,0 +1,23 @@
+
+
+
diff --git a/static/images/ollama.png b/static/images/ollama.png
new file mode 100644
index 0000000..85dd87b
Binary files /dev/null and b/static/images/ollama.png differ
diff --git a/static/images/part-db.png b/static/images/part-db.png
new file mode 100644
index 0000000..5c2a5d7
Binary files /dev/null and b/static/images/part-db.png differ
diff --git a/static/images/request.png b/static/images/request.png
new file mode 100644
index 0000000..a2f3e4a
Binary files /dev/null and b/static/images/request.png differ
diff --git a/static/images/sonarr.png b/static/images/sonarr.png
new file mode 100644
index 0000000..8164915
Binary files /dev/null and b/static/images/sonarr.png differ
diff --git a/static/images/spoolman.png b/static/images/spoolman.png
new file mode 100644
index 0000000..fcda69c
Binary files /dev/null and b/static/images/spoolman.png differ
diff --git a/static/images/tautulli.png b/static/images/tautulli.png
new file mode 100644
index 0000000..f93e7c0
Binary files /dev/null and b/static/images/tautulli.png differ