mirror of
https://github.com/thiloho/archtika.git
synced 2025-11-22 10:51:36 +01:00
Add OpenGraph head tags and more tests
This commit is contained in:
@@ -299,18 +299,26 @@ const header = {
|
||||
export interface Home {
|
||||
website_id: string;
|
||||
main_content: string;
|
||||
meta_description: string | null;
|
||||
last_modified_at: Date;
|
||||
last_modified_by: string | null;
|
||||
}
|
||||
export interface HomeInput {
|
||||
website_id: string;
|
||||
main_content: string;
|
||||
meta_description?: string | null;
|
||||
last_modified_at?: Date;
|
||||
last_modified_by?: string | null;
|
||||
}
|
||||
const home = {
|
||||
tableName: "home",
|
||||
columns: ["website_id", "main_content", "last_modified_at", "last_modified_by"],
|
||||
columns: [
|
||||
"website_id",
|
||||
"main_content",
|
||||
"meta_description",
|
||||
"last_modified_at",
|
||||
"last_modified_by"
|
||||
],
|
||||
requiredForInsert: ["website_id", "main_content"],
|
||||
primaryKey: "website_id",
|
||||
foreignKeys: {
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
const {
|
||||
websiteOverview,
|
||||
article,
|
||||
apiUrl
|
||||
}: { websiteOverview: WebsiteOverview; article: Article; apiUrl: string } = $props();
|
||||
apiUrl,
|
||||
websiteUrl
|
||||
}: { websiteOverview: WebsiteOverview; article: Article; apiUrl: string; websiteUrl: string } =
|
||||
$props();
|
||||
</script>
|
||||
|
||||
<Head
|
||||
@@ -18,6 +20,7 @@
|
||||
{apiUrl}
|
||||
title={article.title}
|
||||
metaDescription={article.meta_description}
|
||||
{websiteUrl}
|
||||
/>
|
||||
|
||||
<Nav {websiteOverview} isDocsTemplate={false} isIndexPage={false} {apiUrl} />
|
||||
|
||||
@@ -7,8 +7,14 @@
|
||||
const {
|
||||
websiteOverview,
|
||||
apiUrl,
|
||||
isLegalPage
|
||||
}: { websiteOverview: WebsiteOverview; apiUrl: string; isLegalPage: boolean } = $props();
|
||||
isLegalPage,
|
||||
websiteUrl
|
||||
}: {
|
||||
websiteOverview: WebsiteOverview;
|
||||
apiUrl: string;
|
||||
isLegalPage: boolean;
|
||||
websiteUrl: string;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<Head
|
||||
@@ -16,6 +22,8 @@
|
||||
nestingLevel={0}
|
||||
{apiUrl}
|
||||
title={isLegalPage ? "Legal information" : websiteOverview.title}
|
||||
metaDescription={websiteOverview.home.meta_description}
|
||||
{websiteUrl}
|
||||
/>
|
||||
|
||||
<Nav {websiteOverview} isDocsTemplate={false} isIndexPage={true} {apiUrl} />
|
||||
|
||||
@@ -1,31 +1,86 @@
|
||||
<script lang="ts">
|
||||
import type { WebsiteOverview } from "../../utils";
|
||||
import { slugify, type WebsiteOverview } from "../../utils";
|
||||
|
||||
const {
|
||||
websiteOverview,
|
||||
nestingLevel,
|
||||
apiUrl,
|
||||
title,
|
||||
metaDescription
|
||||
metaDescription,
|
||||
websiteUrl
|
||||
}: {
|
||||
websiteOverview: WebsiteOverview;
|
||||
nestingLevel: number;
|
||||
apiUrl: string;
|
||||
title: string;
|
||||
metaDescription?: string | null;
|
||||
websiteUrl: string;
|
||||
} = $props();
|
||||
|
||||
const constructedTitle =
|
||||
websiteOverview.title === title ? title : `${websiteOverview.title} | ${title}`;
|
||||
|
||||
let ogUrl = `${websiteUrl.replace(/\/$/, "")}${nestingLevel === 0 ? (websiteOverview.title === title ? "" : `/${slugify(title)}`) : `/articles/${slugify(title)}`}`;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{websiteOverview.title === title ? title : `${websiteOverview.title} | ${title}`}</title>
|
||||
<title>{constructedTitle}</title>
|
||||
<meta name="description" content={metaDescription ?? title} />
|
||||
<link rel="stylesheet" href={`${"../".repeat(nestingLevel)}styles.css`} />
|
||||
<link rel="stylesheet" href={`${"../".repeat(nestingLevel)}variables.css`} />
|
||||
<link rel="stylesheet" href={`${"../".repeat(nestingLevel)}common.css`} />
|
||||
<link rel="stylesheet" href={`${"../".repeat(nestingLevel)}scoped.css`} />
|
||||
{#if websiteOverview.settings.favicon_image}
|
||||
<link
|
||||
rel="icon"
|
||||
href="{apiUrl}/rpc/retrieve_file?id={websiteOverview.settings.favicon_image}"
|
||||
/>
|
||||
{/if}
|
||||
<meta property="og:site_name" content={websiteOverview.title} />
|
||||
<meta property="og:title" content={constructedTitle} />
|
||||
<meta property="og:description" content={metaDescription ?? title} />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:url" content={ogUrl} />
|
||||
{#if websiteOverview.header.logo_image}
|
||||
<meta
|
||||
property="og:image"
|
||||
content="{apiUrl}/rpc/retrieve_file?id={websiteOverview.header.logo_image}"
|
||||
/>
|
||||
{/if}
|
||||
<script>
|
||||
const determineTheme = (skipSetTheme = false) => {
|
||||
const lightMode = window
|
||||
.getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--display-light");
|
||||
const darkMode = window
|
||||
.getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--display-dark");
|
||||
|
||||
if (!skipSetTheme && lightMode === "none") {
|
||||
localStorage.setItem("theme", "light");
|
||||
}
|
||||
|
||||
if (!skipSetTheme && darkMode === "none") {
|
||||
localStorage.setItem("theme", "dark");
|
||||
}
|
||||
|
||||
const currentTheme = localStorage.getItem("theme");
|
||||
|
||||
window.addEventListener("DOMContentLoaded", (event) => {
|
||||
const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
|
||||
document.querySelector("#toggle-theme").checked =
|
||||
darkMode === "none" ? currentTheme === "light" : currentTheme === "dark";
|
||||
});
|
||||
};
|
||||
|
||||
determineTheme(true);
|
||||
|
||||
window.addEventListener("DOMContentLoaded", (event) => {
|
||||
document.querySelector('label[for="toggle-theme"]').addEventListener("click", () => {
|
||||
determineTheme();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</svelte:head>
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
</ul>
|
||||
</section>
|
||||
{/if}
|
||||
<a href={isIndexPage ? "." : ".."}>
|
||||
<svelte:element this={isIndexPage ? "span" : "a"} href="..">
|
||||
{#if websiteOverview.header.logo_type === "text"}
|
||||
<strong>{websiteOverview.header.logo_text}</strong>
|
||||
{:else}
|
||||
@@ -81,7 +81,7 @@
|
||||
alt=""
|
||||
/>
|
||||
{/if}
|
||||
</a>
|
||||
</svelte:element>
|
||||
<label style="margin-inline-start: auto;" for="toggle-theme">
|
||||
<input type="checkbox" id="toggle-theme" hidden />
|
||||
<svg
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
const {
|
||||
websiteOverview,
|
||||
article,
|
||||
apiUrl
|
||||
}: { websiteOverview: WebsiteOverview; article: Article; apiUrl: string } = $props();
|
||||
apiUrl,
|
||||
websiteUrl
|
||||
}: { websiteOverview: WebsiteOverview; article: Article; apiUrl: string; websiteUrl: string } =
|
||||
$props();
|
||||
</script>
|
||||
|
||||
<Head
|
||||
@@ -18,6 +20,7 @@
|
||||
{apiUrl}
|
||||
title={article.title}
|
||||
metaDescription={article.meta_description}
|
||||
{websiteUrl}
|
||||
/>
|
||||
|
||||
<Nav {websiteOverview} isDocsTemplate={true} isIndexPage={false} {apiUrl} />
|
||||
|
||||
@@ -7,8 +7,14 @@
|
||||
const {
|
||||
websiteOverview,
|
||||
apiUrl,
|
||||
isLegalPage
|
||||
}: { websiteOverview: WebsiteOverview; apiUrl: string; isLegalPage: boolean } = $props();
|
||||
isLegalPage,
|
||||
websiteUrl
|
||||
}: {
|
||||
websiteOverview: WebsiteOverview;
|
||||
apiUrl: string;
|
||||
isLegalPage: boolean;
|
||||
websiteUrl: string;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<Head
|
||||
@@ -16,6 +22,8 @@
|
||||
nestingLevel={0}
|
||||
{apiUrl}
|
||||
title={isLegalPage ? "Legal information" : websiteOverview.title}
|
||||
metaDescription={websiteOverview.home.meta_description}
|
||||
{websiteUrl}
|
||||
/>
|
||||
|
||||
<Nav {websiteOverview} isDocsTemplate={true} isIndexPage={true} {apiUrl} />
|
||||
|
||||
@@ -134,7 +134,8 @@ export const actions: Actions = {
|
||||
"PATCH",
|
||||
{
|
||||
body: {
|
||||
main_content: data.get("main-content")
|
||||
main_content: data.get("main-content"),
|
||||
meta_description: data.get("description")
|
||||
},
|
||||
successMessage: "Successfully updated home"
|
||||
}
|
||||
|
||||
@@ -149,6 +149,12 @@
|
||||
</h2>
|
||||
|
||||
<form action="?/updateHome" method="POST" use:enhance={enhanceForm({ reset: false })}>
|
||||
<label>
|
||||
Description:
|
||||
<textarea name="description" rows="5" maxlength="250" required
|
||||
>{data.home.meta_description}</textarea
|
||||
>
|
||||
</label>
|
||||
<MarkdownEditor
|
||||
apiPrefix={data.API_BASE_PREFIX}
|
||||
label="Main content"
|
||||
|
||||
@@ -25,24 +25,7 @@ export const load: PageServerLoad = async ({ params, fetch }) => {
|
||||
)
|
||||
).data;
|
||||
|
||||
generateStaticFiles(websiteOverview);
|
||||
|
||||
const websitePreviewUrl = `${
|
||||
dev
|
||||
? "http://localhost:18000"
|
||||
: process.env.ORIGIN
|
||||
? process.env.ORIGIN
|
||||
: "http://localhost:18000"
|
||||
}/previews/${websiteOverview.id}/`;
|
||||
|
||||
const websiteProdUrl = dev
|
||||
? `http://localhost:18000/${websiteOverview.domain_prefix?.prefix ?? websiteOverview.id}/`
|
||||
: process.env.ORIGIN
|
||||
? process.env.ORIGIN.replace(
|
||||
"//",
|
||||
`//${websiteOverview.domain_prefix?.prefix ?? websiteOverview.id}.`
|
||||
)
|
||||
: `http://localhost:18000/${websiteOverview.domain_prefix?.prefix ?? websiteOverview.id}/`;
|
||||
const { websitePreviewUrl, websiteProdUrl } = await generateStaticFiles(websiteOverview);
|
||||
|
||||
return {
|
||||
websiteOverview,
|
||||
@@ -67,7 +50,7 @@ export const actions: Actions = {
|
||||
)
|
||||
).data;
|
||||
|
||||
generateStaticFiles(websiteOverview, false);
|
||||
await generateStaticFiles(websiteOverview, false);
|
||||
|
||||
return await apiRequest(
|
||||
fetch,
|
||||
@@ -156,6 +139,23 @@ export const actions: Actions = {
|
||||
};
|
||||
|
||||
const generateStaticFiles = async (websiteData: WebsiteOverview, isPreview = true) => {
|
||||
const websitePreviewUrl = `${
|
||||
dev
|
||||
? "http://localhost:18000"
|
||||
: process.env.ORIGIN
|
||||
? process.env.ORIGIN
|
||||
: "http://localhost:18000"
|
||||
}/previews/${websiteData.id}/`;
|
||||
|
||||
const websiteProdUrl = dev
|
||||
? `http://localhost:18000/${websiteData.domain_prefix?.prefix ?? websiteData.id}/`
|
||||
: process.env.ORIGIN
|
||||
? process.env.ORIGIN.replace(
|
||||
"//",
|
||||
`//${websiteData.domain_prefix?.prefix ?? websiteData.id}.`
|
||||
)
|
||||
: `http://localhost:18000/${websiteData.domain_prefix?.prefix ?? websiteData.id}/`;
|
||||
|
||||
const fileContents = (head: string, body: string) => {
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
@@ -173,7 +173,8 @@ const generateStaticFiles = async (websiteData: WebsiteOverview, isPreview = tru
|
||||
props: {
|
||||
websiteOverview: websiteData,
|
||||
apiUrl: API_BASE_PREFIX,
|
||||
isLegalPage: false
|
||||
isLegalPage: false,
|
||||
websiteUrl: isPreview ? websitePreviewUrl : websiteProdUrl
|
||||
}
|
||||
});
|
||||
|
||||
@@ -202,7 +203,8 @@ const generateStaticFiles = async (websiteData: WebsiteOverview, isPreview = tru
|
||||
props: {
|
||||
websiteOverview: websiteData,
|
||||
article,
|
||||
apiUrl: API_BASE_PREFIX
|
||||
apiUrl: API_BASE_PREFIX,
|
||||
websiteUrl: isPreview ? websitePreviewUrl : websiteProdUrl
|
||||
}
|
||||
});
|
||||
|
||||
@@ -217,13 +219,17 @@ const generateStaticFiles = async (websiteData: WebsiteOverview, isPreview = tru
|
||||
props: {
|
||||
websiteOverview: websiteData,
|
||||
apiUrl: API_BASE_PREFIX,
|
||||
isLegalPage: true
|
||||
isLegalPage: true,
|
||||
websiteUrl: isPreview ? websitePreviewUrl : websiteProdUrl
|
||||
}
|
||||
});
|
||||
|
||||
await writeFile(join(uploadDir, "legal-information.html"), fileContents(head, body));
|
||||
}
|
||||
|
||||
const variableStyles = await readFile(`${process.cwd()}/template-styles/variables.css`, {
|
||||
encoding: "utf-8"
|
||||
});
|
||||
const commonStyles = await readFile(`${process.cwd()}/template-styles/common-styles.css`, {
|
||||
encoding: "utf-8"
|
||||
});
|
||||
@@ -246,22 +252,43 @@ const generateStaticFiles = async (websiteData: WebsiteOverview, isPreview = tru
|
||||
} = hexToHSL(websiteData.settings.background_color_light_theme);
|
||||
|
||||
await writeFile(
|
||||
join(uploadDir, "styles.css"),
|
||||
commonStyles
|
||||
.concat(specificStyles)
|
||||
.replace(/(?<=\/\* BACKGROUND_COLOR_DARK_THEME_H \*\/\s*).*(?=;)/, ` ${hDark}`)
|
||||
.replace(/(?<=\/\* BACKGROUND_COLOR_DARK_THEME_S \*\/\s*).*(?=;)/, ` ${sDark}%`)
|
||||
.replace(/(?<=\/\* BACKGROUND_COLOR_DARK_THEME_L \*\/\s*).*(?=;)/, ` ${lDark}%`)
|
||||
.replace(/(?<=\/\* BACKGROUND_COLOR_LIGHT_THEME_H \*\/\s*).*(?=;)/, ` ${hLight}`)
|
||||
.replace(/(?<=\/\* BACKGROUND_COLOR_LIGHT_THEME_S \*\/\s*).*(?=;)/, ` ${sLight}%`)
|
||||
.replace(/(?<=\/\* BACKGROUND_COLOR_LIGHT_THEME_L \*\/\s*).*(?=;)/, ` ${lLight}%`)
|
||||
.replace(
|
||||
/(?<=\/\* ACCENT_COLOR_DARK_THEME \*\/\s*).*(?=;)/,
|
||||
` ${websiteData.settings.accent_color_dark_theme}`
|
||||
join(uploadDir, "variables.css"),
|
||||
variableStyles
|
||||
.replaceAll(
|
||||
/\/\* BACKGROUND_COLOR_DARK_THEME_H \*\/\s*.*?;/g,
|
||||
`/* BACKGROUND_COLOR_DARK_THEME_H */ ${hDark};`
|
||||
)
|
||||
.replace(
|
||||
/(?<=\/\* ACCENT_COLOR_LIGHT_THEME \*\/\s*).*(?=;)/,
|
||||
` ${websiteData.settings.accent_color_light_theme}`
|
||||
.replaceAll(
|
||||
/\/\* BACKGROUND_COLOR_DARK_THEME_S \*\/\s*.*?;/g,
|
||||
`/* BACKGROUND_COLOR_DARK_THEME_S */ ${sDark}%;`
|
||||
)
|
||||
.replaceAll(
|
||||
/\/\* BACKGROUND_COLOR_DARK_THEME_L \*\/\s*.*?;/g,
|
||||
`/* BACKGROUND_COLOR_DARK_THEME_L */ ${lDark}%;`
|
||||
)
|
||||
.replaceAll(
|
||||
/\/\* BACKGROUND_COLOR_LIGHT_THEME_H \*\/\s*.*?;/g,
|
||||
`/* BACKGROUND_COLOR_LIGHT_THEME_H */ ${hLight};`
|
||||
)
|
||||
.replaceAll(
|
||||
/\/\* BACKGROUND_COLOR_LIGHT_THEME_S \*\/\s*.*?;/g,
|
||||
`/* BACKGROUND_COLOR_LIGHT_THEME_S */ ${sLight}%;`
|
||||
)
|
||||
.replaceAll(
|
||||
/\/\* BACKGROUND_COLOR_LIGHT_THEME_L \*\/\s*.*?;/g,
|
||||
`/* BACKGROUND_COLOR_LIGHT_THEME_L */ ${lLight}%;`
|
||||
)
|
||||
.replaceAll(
|
||||
/\/\* ACCENT_COLOR_DARK_THEME \*\/\s*.*?;/g,
|
||||
`/* ACCENT_COLOR_DARK_THEME */ ${websiteData.settings.accent_color_dark_theme};`
|
||||
)
|
||||
.replaceAll(
|
||||
/\/\* ACCENT_COLOR_LIGHT_THEME \*\/\s*.*?;/g,
|
||||
`/* ACCENT_COLOR_LIGHT_THEME */ ${websiteData.settings.accent_color_light_theme};`
|
||||
)
|
||||
);
|
||||
await writeFile(join(uploadDir, "common.css"), commonStyles);
|
||||
await writeFile(join(uploadDir, "scoped.css"), specificStyles);
|
||||
|
||||
return { websitePreviewUrl, websiteProdUrl };
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import "../../template-styles/variables.css";
|
||||
import "../../template-styles/common-styles.css";
|
||||
import { page } from "$app/stores";
|
||||
import type { LayoutServerData } from "./$types";
|
||||
|
||||
Reference in New Issue
Block a user