feat: nextcloud sync
This commit is contained in:
@@ -1,47 +1,46 @@
|
||||
<script lang="ts">
|
||||
import { Marked, Renderer } from "@ts-stack/markdown";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
Marked.setOptions({
|
||||
renderer: new Renderer(),
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: false,
|
||||
smartLists: true,
|
||||
smartypants: false,
|
||||
});
|
||||
|
||||
interface Props {
|
||||
src: string;
|
||||
content?: string;
|
||||
basePath?: string;
|
||||
}
|
||||
|
||||
let { src }: Props = $props();
|
||||
let { content = "", basePath = "" }: Props = $props();
|
||||
|
||||
let content: Element[] = $state([]);
|
||||
const renderer = new Renderer();
|
||||
|
||||
// Custom image renderer to proxy through /assets/
|
||||
renderer.image = (href, title, text) => {
|
||||
let src = href;
|
||||
if (href && !href.startsWith('http')) {
|
||||
// Ensure basePath starts with / if provided
|
||||
const normalizedBasePath = basePath.startsWith('/') ? basePath : `/${basePath}`;
|
||||
// If href starts with /, it's relative to root. Otherwise, relative to basePath.
|
||||
if (!href.startsWith('/')) {
|
||||
src = `${normalizedBasePath}/${href}`.replace(/\/+/g, '/');
|
||||
}
|
||||
src = `/assets${src}`;
|
||||
}
|
||||
return `<img src="${src}" alt="${text}" title="${title || ''}" />`;
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
const DOM_PARSER = new DOMParser();
|
||||
const markdown = await (await fetch(src)).text();
|
||||
const parsedMarkdown = DOM_PARSER.parseFromString(
|
||||
Marked.parse(markdown ? markdown : "__nothing to see here__"),
|
||||
"text/html",
|
||||
);
|
||||
|
||||
content = [...parsedMarkdown.body.children];
|
||||
// content.forEach((element) => console.log(element.nodeName));
|
||||
});
|
||||
|
||||
//have to impelment correct relative img path's
|
||||
let html = $derived(
|
||||
Marked.parse(content || "__nothing to see here__", {
|
||||
renderer,
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: false,
|
||||
smartLists: true,
|
||||
smartypants: false,
|
||||
})
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="markdown">
|
||||
{#if content != null}
|
||||
{#each content as element, i}
|
||||
{@html element.outerHTML}
|
||||
{/each}
|
||||
{/if}
|
||||
{@html html}
|
||||
</div>
|
||||
|
||||
<style global>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { createClient } from "webdav";
|
||||
import { env } from "$env/dynamic/private";
|
||||
|
||||
if (!env.NEXTCLOUD_URL || !env.NEXTCLOUD_USER || !env.NEXTCLOUD_PASSWORD) {
|
||||
console.warn("Nextcloud environment variables are not fully set. WebDAV client might not work correctly.");
|
||||
}
|
||||
|
||||
// Construct the standard Nextcloud WebDAV URL: https://example.com/remote.php/dav/files/USERNAME/
|
||||
const getWebdavUrl = () => {
|
||||
if (!env.NEXTCLOUD_URL) return "";
|
||||
const base = env.NEXTCLOUD_URL.endsWith("/") ? env.NEXTCLOUD_URL : `${env.NEXTCLOUD_URL}/`;
|
||||
return `${base}remote.php/dav/files/${env.NEXTCLOUD_USER}/`;
|
||||
};
|
||||
|
||||
export const client = createClient(
|
||||
getWebdavUrl(),
|
||||
{
|
||||
username: env.NEXTCLOUD_USER || "",
|
||||
password: env.NEXTCLOUD_PASSWORD || ""
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,29 @@
|
||||
import { client } from "$lib/server/nextcloud";
|
||||
import { error } from "@sveltejs/kit";
|
||||
import type { RequestHandler } from "./$types";
|
||||
import mime from "mime-types";
|
||||
|
||||
export const GET: RequestHandler = async ({ params }) => {
|
||||
const { path } = params;
|
||||
|
||||
if (!path) {
|
||||
throw error(400, "Path is required");
|
||||
}
|
||||
|
||||
try {
|
||||
// Fetch from Nextcloud. We assume the path is relative to the root or a specific folder.
|
||||
// If your files are in a specific folder, e.g., "Schreifuchs", prepend it here.
|
||||
const buffer = await client.getFileContents("/" + path);
|
||||
const contentType = mime.lookup(path) || "application/octet-stream";
|
||||
|
||||
return new Response(buffer as any, {
|
||||
headers: {
|
||||
"Content-Type": contentType,
|
||||
"Cache-Control": "public, max-age=3600" // Cache for 1 hour
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(`Error fetching asset from Nextcloud: ${path}`, e);
|
||||
throw error(404, "Asset not found");
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
import { client } from "$lib/server/nextcloud";
|
||||
import type { PageServerLoad } from "./$types";
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
try {
|
||||
const content = await client.getFileContents("/fotos/Fotos.md", { format: "text" });
|
||||
return {
|
||||
content: content as string
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Error fetching Fotos.md from Nextcloud", e);
|
||||
return {
|
||||
content: "Failed to load content from Nextcloud."
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -2,6 +2,9 @@
|
||||
import ImageTile from "$lib/components/ImageTile.svelte";
|
||||
import Markdown from "$lib/components/Markdown.svelte";
|
||||
import HeadImage from "$lib/images/fotos/sunne_untergang.webp";
|
||||
import type { PageData } from "./$types";
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
</script>
|
||||
|
||||
<ImageTile src={HeadImage}>
|
||||
@@ -13,6 +16,6 @@
|
||||
</ImageTile>
|
||||
<main class="flex items-center justify-center pt-8 bg-black min-h-screen px-12">
|
||||
<section class="max-w-screen-md">
|
||||
<Markdown src="/fotos/Fotos.md" />
|
||||
<Markdown content={data.content} basePath="/fotos" />
|
||||
</section>
|
||||
</main>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { client } from "$lib/server/nextcloud";
|
||||
import type { PageServerLoad } from "./$types";
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
try {
|
||||
const content = await client.getFileContents("/informatik/Informatik.md", { format: "text" });
|
||||
return {
|
||||
content: content as string
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Error fetching Informatik.md from Nextcloud", e);
|
||||
return {
|
||||
content: "Failed to load content from Nextcloud."
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -2,6 +2,9 @@
|
||||
import ImageTile from "$lib/components/ImageTile.svelte";
|
||||
import Markdown from "$lib/components/Markdown.svelte";
|
||||
import HeadImage from "$lib/images/informatik/monitor.webp";
|
||||
import type { PageData } from "./$types";
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
</script>
|
||||
|
||||
<ImageTile src={HeadImage}>
|
||||
@@ -13,6 +16,6 @@
|
||||
</ImageTile>
|
||||
<main class="flex items-center justify-center pt-8 bg-black min-h-screen px-12">
|
||||
<section class="max-w-screen-md">
|
||||
<Markdown src="/informatik/Informatik.md" />
|
||||
<Markdown content={data.content} basePath="/informatik" />
|
||||
</section>
|
||||
</main>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { client } from "$lib/server/nextcloud";
|
||||
import type { PageServerLoad } from "./$types";
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
try {
|
||||
const content = await client.getFileContents("/musig/Musig.md", { format: "text" });
|
||||
return {
|
||||
content: content as string
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Error fetching Musig.md from Nextcloud", e);
|
||||
return {
|
||||
content: "Failed to load content from Nextcloud."
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -2,6 +2,9 @@
|
||||
import ImageTile from "$lib/components/ImageTile.svelte";
|
||||
import Markdown from "$lib/components/Markdown.svelte";
|
||||
import HeadImage from "$lib/images/musig/plattespiler.webp";
|
||||
import type { PageData } from "./$types";
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
</script>
|
||||
|
||||
<ImageTile src={HeadImage}>
|
||||
@@ -13,6 +16,6 @@
|
||||
</ImageTile>
|
||||
<main class="flex items-center justify-center pt-8 bg-black min-h-screen px-12">
|
||||
<section class="max-w-screen-md">
|
||||
<Markdown src="/musig/Musig.md" />
|
||||
<Markdown content={data.content} basePath="/musig" />
|
||||
</section>
|
||||
</main>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { client } from "$lib/server/nextcloud";
|
||||
import type { PageServerLoad } from "./$types";
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
try {
|
||||
const content = await client.getFileContents("/video/Video.md", { format: "text" });
|
||||
return {
|
||||
content: content as string
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Error fetching Video.md from Nextcloud", e);
|
||||
return {
|
||||
content: "Failed to load content from Nextcloud."
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -2,6 +2,9 @@
|
||||
import ImageTile from "$lib/components/ImageTile.svelte";
|
||||
import Markdown from "$lib/components/Markdown.svelte";
|
||||
import HeadImage from "$lib/images/video/jochen.webp";
|
||||
import type { PageData } from "./$types";
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
</script>
|
||||
|
||||
<ImageTile src={HeadImage}>
|
||||
@@ -13,6 +16,6 @@
|
||||
</ImageTile>
|
||||
<main class="flex items-center justify-center pt-8 bg-black min-h-screen px-12">
|
||||
<section class="max-w-screen-md">
|
||||
<Markdown src="/video/Video.md" />
|
||||
<Markdown content={data.content} basePath="/video" />
|
||||
</section>
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user