Files
archtika/web-app/src/routes/(authenticated)/website/[websiteId]/publish/+page.server.ts

261 lines
8.3 KiB
TypeScript
Raw Normal View History

import { readFile, mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { md } from "$lib/utils";
import type { Actions, PageServerLoad } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils";
import { render } from "svelte/server";
import BlogIndex from "$lib/templates/blog/BlogIndex.svelte";
import BlogArticle from "$lib/templates/blog/BlogArticle.svelte";
import DocsIndex from "$lib/templates/docs/DocsIndex.svelte";
2024-08-27 16:39:29 +02:00
import DocsArticle from "$lib/templates/docs/DocsArticle.svelte";
import { dev } from "$app/environment";
2024-09-07 13:04:09 +02:00
interface WebsiteData {
id: string;
content_type: "Blog" | "Docs";
favicon_image: string | null;
title: string;
logo_type: "text" | "image";
logo_text: string | null;
logo_image: string | null;
main_content: string;
additional_text: string;
accent_color_light_theme: string;
accent_color_dark_theme: string;
articles: {
cover_image: string | null;
title: string;
publication_date: string;
meta_description: string;
main_content: string;
}[];
categorized_articles: {
[key: string]: { title: string; publication_date: string; meta_description: string }[];
};
}
2024-08-20 19:17:05 +02:00
export const load: PageServerLoad = async ({ params, fetch, cookies, parent }) => {
const websiteOverviewData = await fetch(
`${API_BASE_PREFIX}/website_overview?id=eq.${params.websiteId}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`,
Accept: "application/vnd.pgrst.object+json"
}
}
);
const websiteOverview = await websiteOverviewData.json();
2024-08-20 19:17:05 +02:00
const { website } = await parent();
2024-08-07 19:13:39 +02:00
generateStaticFiles(websiteOverview);
const websitePreviewUrl = `${
dev
? "http://localhost:18000"
: process.env.ORIGIN
? process.env.ORIGIN
: "http://localhost:18000"
}/previews/${websiteOverview.id}/index.html`;
const websiteProdUrl = dev
? `http://localhost:18000/${websiteOverview.id}/index.html`
: process.env.ORIGIN
? process.env.ORIGIN.replace("//", `//${websiteOverview.id}.`)
: `http://localhost:18000/${websiteOverview.id}/index.html`;
return {
websiteOverview,
2024-08-20 19:17:05 +02:00
websitePreviewUrl,
websiteProdUrl,
2024-08-20 19:17:05 +02:00
website
};
};
export const actions: Actions = {
2024-08-27 16:39:29 +02:00
publishWebsite: async ({ fetch, params, cookies }) => {
const websiteOverviewData = await fetch(
`${API_BASE_PREFIX}/website_overview?id=eq.${params.websiteId}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`,
Accept: "application/vnd.pgrst.object+json"
}
}
);
2024-08-27 16:39:29 +02:00
const websiteOverview = await websiteOverviewData.json();
2024-08-07 19:13:39 +02:00
generateStaticFiles(websiteOverview, false);
const res = await fetch(`${API_BASE_PREFIX}/website?id=eq.${params.websiteId}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
},
body: JSON.stringify({
is_published: true
})
});
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully published website" };
}
};
2024-09-07 13:04:09 +02:00
const generateStaticFiles = async (websiteData: WebsiteData, isPreview: boolean = true) => {
let head = "";
let body = "";
switch (websiteData.content_type) {
case "Blog":
{
({ head, body } = render(BlogIndex, {
props: {
2024-08-25 16:31:12 +02:00
favicon: websiteData.favicon_image
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.favicon_image}`
: "",
title: websiteData.title,
logoType: websiteData.logo_type,
2024-08-25 16:31:12 +02:00
logo:
websiteData.logo_type === "text"
2024-09-07 13:04:09 +02:00
? (websiteData.logo_text ?? "")
2024-08-25 16:31:12 +02:00
: `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`,
mainContent: md(websiteData.main_content ?? "", false),
articles: websiteData.articles ?? [],
footerAdditionalText: md(websiteData.additional_text ?? "")
}
}));
}
break;
case "Docs":
{
({ head, body } = render(DocsIndex, {
props: {
2024-08-27 16:39:29 +02:00
favicon: websiteData.favicon_image
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.favicon_image}`
: "",
title: websiteData.title,
logoType: websiteData.logo_type,
2024-08-27 16:39:29 +02:00
logo:
websiteData.logo_type === "text"
2024-09-07 13:04:09 +02:00
? (websiteData.logo_text ?? "")
2024-08-27 16:39:29 +02:00
: `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`,
mainContent: md(websiteData.main_content ?? "", false),
2024-08-28 17:30:32 +02:00
categorizedArticles: websiteData.categorized_articles ?? [],
footerAdditionalText: md(websiteData.additional_text ?? "")
}
}));
}
break;
}
const indexFileContents = head.concat(body);
let uploadDir = "";
if (isPreview) {
uploadDir = join("/", "var", "www", "archtika-websites", "previews", websiteData.id);
} else {
uploadDir = join("/", "var", "www", "archtika-websites", websiteData.id);
}
await mkdir(uploadDir, { recursive: true });
await writeFile(join(uploadDir, "index.html"), indexFileContents);
2024-08-27 16:39:29 +02:00
await mkdir(join(uploadDir, "articles"), {
recursive: true
});
for (const article of websiteData.articles ?? []) {
const articleFileName = article.title.toLowerCase().split(" ").join("-");
let head = "";
let body = "";
switch (websiteData.content_type) {
case "Blog":
{
({ head, body } = render(BlogArticle, {
props: {
2024-08-25 16:31:12 +02:00
favicon: websiteData.favicon_image
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.favicon_image}`
: "",
title: article.title,
logoType: websiteData.logo_type,
2024-08-25 16:31:12 +02:00
logo:
websiteData.logo_type === "text"
2024-09-07 13:04:09 +02:00
? (websiteData.logo_text ?? "")
2024-08-25 16:31:12 +02:00
: `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`,
coverImage: article.cover_image
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${article.cover_image}`
: "",
publicationDate: article.publication_date,
mainContent: md(article.main_content ?? ""),
footerAdditionalText: md(websiteData.additional_text ?? "")
}
}));
}
break;
case "Docs":
{
2024-08-27 16:39:29 +02:00
({ head, body } = render(DocsArticle, {
props: {
2024-08-27 16:39:29 +02:00
favicon: websiteData.favicon_image
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.favicon_image}`
: "",
title: article.title,
logoType: websiteData.logo_type,
2024-08-27 16:39:29 +02:00
logo:
websiteData.logo_type === "text"
2024-09-07 13:04:09 +02:00
? (websiteData.logo_text ?? "")
2024-08-27 16:39:29 +02:00
: `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`,
mainContent: md(article.main_content ?? ""),
2024-08-28 17:30:32 +02:00
categorizedArticles: websiteData.categorized_articles ?? [],
footerAdditionalText: md(websiteData.additional_text ?? "")
}
}));
}
break;
}
const articleFileContents = head.concat(body);
2024-08-27 16:39:29 +02:00
await writeFile(join(uploadDir, "articles", `${articleFileName}.html`), articleFileContents);
}
2024-08-20 19:17:05 +02:00
const commonStyles = await readFile(`${process.cwd()}/template-styles/common-styles.css`, {
encoding: "utf-8"
});
2024-08-27 16:39:29 +02:00
const specificStyles = await readFile(
`${process.cwd()}/template-styles/${websiteData.content_type.toLowerCase()}-styles.css`,
{
encoding: "utf-8"
}
);
2024-08-25 16:31:12 +02:00
await writeFile(
join(uploadDir, "styles.css"),
commonStyles
.concat(specificStyles)
.replace(
/--color-accent:\s*(.*?);/,
`--color-accent: ${websiteData.accent_color_dark_theme};`
)
.replace(
/@media\s*\(prefers-color-scheme:\s*dark\)\s*{[^}]*--color-accent:\s*(.*?);/,
(match) =>
match.replace(
/--color-accent:\s*(.*?);/,
`--color-accent: ${websiteData.accent_color_light_theme};`
)
)
);
};