Create fetch utility function

This commit is contained in:
thiloho
2024-09-25 21:45:01 +02:00
parent a9e2bd4cb7
commit bc5e494bbb
20 changed files with 525 additions and 700 deletions

View File

@@ -30,3 +30,13 @@ export const handle = async ({ event, resolve }) => {
return await resolve(event); return await resolve(event);
}; };
export const handleFetch = async ({ event, request, fetch }) => {
const sessionToken = event.cookies.get("session_token");
if (sessionToken) {
request.headers.set("Authorization", `Bearer ${sessionToken}`);
}
return fetch(request);
};

View File

@@ -9,3 +9,48 @@ export const REGISTRATION_IS_DISABLED = dev
: process.env.REGISTRATION_IS_DISABLED : process.env.REGISTRATION_IS_DISABLED
? JSON.parse(process.env.REGISTRATION_IS_DISABLED) ? JSON.parse(process.env.REGISTRATION_IS_DISABLED)
: false; : false;
export const apiRequest = async (
customFetch: typeof fetch,
url: string,
method: "HEAD" | "GET" | "POST" | "PATCH" | "DELETE",
options: {
headers?: Record<string, string>;
body?: any;
successMessage?: string;
returnData?: boolean;
} = {
headers: {},
body: undefined,
successMessage: "Operation was successful",
returnData: false
}
) => {
const headers = {
"Content-Type": "application/json",
...options.headers
};
const response = await customFetch(url, {
method,
headers,
...(!["HEAD", "GET", "DELETE"].includes(method) && {
body: options.body instanceof ArrayBuffer ? options.body : JSON.stringify(options.body)
})
});
if (!response.ok) {
const errorData = await response.json();
return { success: false, message: errorData.message };
}
if (options.returnData) {
return {
success: true,
message: options.successMessage,
data: method === "HEAD" ? response : await response.json()
};
}
return { success: true, message: options.successMessage };
};

View File

@@ -177,7 +177,7 @@ export const handleImagePaste = async (event: ClipboardEvent, API_BASE_PREFIX: s
const response = await request.json(); const response = await request.json();
if (JSON.parse(response.data)[1]) { if (JSON.parse(response.data)[1]) {
const fileId = JSON.parse(response.data)[3]; const fileId = JSON.parse(response.data)[4];
const fileUrl = `${API_BASE_PREFIX}/rpc/retrieve_file?id=${fileId}`; const fileUrl = `${API_BASE_PREFIX}/rpc/retrieve_file?id=${fileId}`;
const target = event.target as HTMLTextAreaElement; const target = event.target as HTMLTextAreaElement;

View File

@@ -1,26 +1,24 @@
import type { Actions } from "./$types"; import type { Actions } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils"; import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
export const actions: Actions = { export const actions: Actions = {
default: async ({ request, cookies, fetch }) => { default: async ({ request, cookies, fetch }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch(`${API_BASE_PREFIX}/rpc/login`, { const response = await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/login`, "POST", {
method: "POST", body: {
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username: data.get("username"), username: data.get("username"),
pass: data.get("password") pass: data.get("password")
}) },
returnData: true,
successMessage: "Successfully logged in, you can refresh the page"
}); });
const response = await res.json(); if (!response.success) {
return response;
if (!res.ok) {
return { success: false, message: response.message };
} }
cookies.set("session_token", response.token, { path: "/" }); cookies.set("session_token", response.data.token, { path: "/" });
return { success: true, message: "Successfully logged in" }; return response;
} }
}; };

View File

@@ -1,5 +1,5 @@
import type { Actions, PageServerLoad } from "./$types"; import type { Actions, PageServerLoad } from "./$types";
import { API_BASE_PREFIX, REGISTRATION_IS_DISABLED } from "$lib/server/utils"; import { API_BASE_PREFIX, REGISTRATION_IS_DISABLED, apiRequest } from "$lib/server/utils";
export const load: PageServerLoad = async () => { export const load: PageServerLoad = async () => {
return { return {
@@ -11,21 +11,12 @@ export const actions: Actions = {
default: async ({ request, fetch }) => { default: async ({ request, fetch }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch(`${API_BASE_PREFIX}/rpc/register`, { return await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/register`, "POST", {
method: "POST", body: {
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username: data.get("username"), username: data.get("username"),
pass: data.get("password") pass: data.get("password")
}) },
successMessage: "Successfully registered, you can now login"
}); });
const response = await res.json();
if (!res.ok) {
return { success: false, message: response.message };
}
return { success: true, message: "Successfully registered, you can now login" };
} }
}; };

View File

@@ -1,10 +1,11 @@
import type { Actions, PageServerLoad } from "./$types"; import type { Actions, PageServerLoad } from "./$types";
import { apiRequest } from "$lib/server/utils";
import { API_BASE_PREFIX } from "$lib/server/utils"; import { API_BASE_PREFIX } from "$lib/server/utils";
import { rm } from "node:fs/promises"; import { rm } from "node:fs/promises";
import { join } from "node:path"; import { join } from "node:path";
import type { Website, WebsiteInput } from "$lib/db-schema"; import type { Website } from "$lib/db-schema";
export const load: PageServerLoad = async ({ fetch, cookies, url, locals }) => { export const load: PageServerLoad = async ({ fetch, url, locals }) => {
const searchQuery = url.searchParams.get("website_search_query"); const searchQuery = url.searchParams.get("website_search_query");
const filterBy = url.searchParams.get("website_filter"); const filterBy = url.searchParams.get("website_filter");
@@ -27,28 +28,22 @@ export const load: PageServerLoad = async ({ fetch, cookies, url, locals }) => {
const constructedFetchUrl = `${baseFetchUrl}&${params.toString()}`; const constructedFetchUrl = `${baseFetchUrl}&${params.toString()}`;
const totalWebsitesData = await fetch(baseFetchUrl, { const totalWebsites = await apiRequest(fetch, baseFetchUrl, "HEAD", {
method: "HEAD",
headers: { headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`,
Prefer: "count=exact" Prefer: "count=exact"
} },
returnData: true
}); });
const totalWebsiteCount = Number( const totalWebsiteCount = Number(
totalWebsitesData.headers.get("content-range")?.split("/").at(-1) totalWebsites.data.headers.get("content-range")?.split("/").at(-1)
); );
const websiteData = await fetch(constructedFetchUrl, { const websites: Website[] = (
method: "GET", await apiRequest(fetch, constructedFetchUrl, "GET", {
headers: { returnData: true
"Content-Type": "application/json", })
Authorization: `Bearer ${cookies.get("session_token")}` ).data;
}
});
const websites: Website[] = await websiteData.json();
return { return {
totalWebsiteCount, totalWebsiteCount,
@@ -57,91 +52,63 @@ export const load: PageServerLoad = async ({ fetch, cookies, url, locals }) => {
}; };
export const actions: Actions = { export const actions: Actions = {
createWebsite: async ({ request, fetch, cookies }) => { createWebsite: async ({ request, fetch }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch(`${API_BASE_PREFIX}/rpc/create_website`, { return await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/create_website`, "POST", {
method: "POST", body: {
headers: { content_type: data.get("content-type"),
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
},
body: JSON.stringify({
content_type: data.get("content-type") as string,
title: data.get("title") as string
} satisfies WebsiteInput)
});
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully created website" };
},
updateWebsite: async ({ request, cookies, fetch }) => {
const data = await request.formData();
const res = await fetch(`${API_BASE_PREFIX}/website?id=eq.${data.get("id")}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
},
body: JSON.stringify({
title: data.get("title") title: data.get("title")
}) },
successMessage: "Successfully created website"
}); });
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully updated website" };
}, },
deleteWebsite: async ({ request, cookies, fetch }) => { updateWebsite: async ({ request, fetch }) => {
const data = await request.formData(); const data = await request.formData();
const oldDomainPrefixData = await fetch( return await apiRequest(fetch, `${API_BASE_PREFIX}/website?id=eq.${data.get("id")}`, "PATCH", {
`${API_BASE_PREFIX}/domain_prefix?website_id=eq.${data.get("id")}`, body: {
{ title: data.get("title")
method: "GET", },
successMessage: "Successfully updated website"
});
},
deleteWebsite: async ({ request, fetch }) => {
const data = await request.formData();
const id = data.get("id");
const oldDomainPrefix = (
await apiRequest(fetch, `${API_BASE_PREFIX}/domain_prefix?website_id=eq.${id}`, "GET", {
headers: { headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`,
Accept: "application/vnd.pgrst.object+json" Accept: "application/vnd.pgrst.object+json"
} },
returnData: true
})
).data;
const deleteWebsite = await apiRequest(
fetch,
`${API_BASE_PREFIX}/website?id=eq.${id}`,
"DELETE",
{
successMessage: "Successfully deleted website"
} }
); );
const oldDomainPrefix = await oldDomainPrefixData.json();
const res = await fetch(`${API_BASE_PREFIX}/website?id=eq.${data.get("id")}`, { if (!deleteWebsite.success) {
method: "DELETE", return deleteWebsite;
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
}
});
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
} }
await rm(join("/", "var", "www", "archtika-websites", "previews", data.get("id") as string), { await rm(join("/", "var", "www", "archtika-websites", "previews", id as string), {
recursive: true, recursive: true,
force: true force: true
}); });
await rm( await rm(join("/", "var", "www", "archtika-websites", oldDomainPrefix?.prefix ?? id), {
join("/", "var", "www", "archtika-websites", oldDomainPrefix.prefix ?? data.get("id")), recursive: true,
{ force: true
recursive: true, });
force: true
}
);
return { success: true, message: "Successfully deleted website" }; return deleteWebsite;
} }
}; };

View File

@@ -1,5 +1,5 @@
import type { Actions, PageServerLoad } from "./$types"; import type { Actions, PageServerLoad } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils"; import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
export const load: PageServerLoad = async ({ locals }) => { export const load: PageServerLoad = async ({ locals }) => {
return { return {
@@ -16,24 +16,18 @@ export const actions: Actions = {
deleteAccount: async ({ request, fetch, cookies }) => { deleteAccount: async ({ request, fetch, cookies }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch(`${API_BASE_PREFIX}/rpc/delete_account`, { const response = await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/delete_account`, "POST", {
method: "POST", body: {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
},
body: JSON.stringify({
pass: data.get("password") pass: data.get("password")
}) },
successMessage: "Successfully deleted account"
}); });
const response = await res.json(); if (!response.success) {
return response;
if (!res.ok) {
return { success: false, message: response.message };
} }
cookies.delete("session_token", { path: "/" }); cookies.delete("session_token", { path: "/" });
return { success: true, message: "Successfully deleted account" }; return response;
} }
}; };

View File

@@ -1,36 +1,35 @@
import type { LayoutServerLoad } from "./$types"; import type { LayoutServerLoad } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils"; import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
import { error } from "@sveltejs/kit"; import { error } from "@sveltejs/kit";
import type { Website, Home, User } from "$lib/db-schema"; import type { Website, Home, User } from "$lib/db-schema";
export const load: LayoutServerLoad = async ({ params, fetch, cookies }) => { export const load: LayoutServerLoad = async ({ params, fetch }) => {
const websiteData = await fetch( const websiteData = await apiRequest(
fetch,
`${API_BASE_PREFIX}/website?id=eq.${params.websiteId}&select=*,user!user_id(username)`, `${API_BASE_PREFIX}/website?id=eq.${params.websiteId}&select=*,user!user_id(username)`,
"GET",
{ {
method: "GET",
headers: { headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`,
Accept: "application/vnd.pgrst.object+json" Accept: "application/vnd.pgrst.object+json"
} },
returnData: true
} }
); );
if (!websiteData.ok) { const website: Website & { user: { username: User["username"] } } = websiteData.data;
if (!websiteData.success) {
throw error(404, "Website not found"); throw error(404, "Website not found");
} }
const homeData = await fetch(`${API_BASE_PREFIX}/home?website_id=eq.${params.websiteId}`, { const home: Home = (
method: "GET", await apiRequest(fetch, `${API_BASE_PREFIX}/home?website_id=eq.${params.websiteId}`, "GET", {
headers: { headers: {
"Content-Type": "application/json", Accept: "application/vnd.pgrst.object+json"
Authorization: `Bearer ${cookies.get("session_token")}`, },
Accept: "application/vnd.pgrst.object+json" returnData: true
} })
}); ).data;
const website: Website & { user: { username: User["username"] } } = await websiteData.json();
const home: Home = await homeData.json();
return { return {
website, website,

View File

@@ -1,41 +1,40 @@
import type { Actions, PageServerLoad } from "./$types"; import type { Actions, PageServerLoad } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils"; import { API_BASE_PREFIX } from "$lib/server/utils";
import { apiRequest } from "$lib/server/utils";
import type { Settings, Header, Footer } from "$lib/db-schema"; import type { Settings, Header, Footer } from "$lib/db-schema";
export const load: PageServerLoad = async ({ params, fetch, cookies }) => { export const load: PageServerLoad = async ({ params, fetch }) => {
const globalSettingsData = await fetch( const globalSettings: Settings = (
`${API_BASE_PREFIX}/settings?website_id=eq.${params.websiteId}`, await apiRequest(
{ fetch,
method: "GET", `${API_BASE_PREFIX}/settings?website_id=eq.${params.websiteId}`,
headers: { "GET",
"Content-Type": "application/json", {
Authorization: `Bearer ${cookies.get("session_token")}`, headers: {
Accept: "application/vnd.pgrst.object+json" Accept: "application/vnd.pgrst.object+json"
},
returnData: true
} }
} )
); ).data;
const headerData = await fetch(`${API_BASE_PREFIX}/header?website_id=eq.${params.websiteId}`, { const header: Header = (
method: "GET", await apiRequest(fetch, `${API_BASE_PREFIX}/header?website_id=eq.${params.websiteId}`, "GET", {
headers: { headers: {
"Content-Type": "application/json", Accept: "application/vnd.pgrst.object+json"
Authorization: `Bearer ${cookies.get("session_token")}`, },
Accept: "application/vnd.pgrst.object+json" returnData: true
} })
}); ).data;
const footerData = await fetch(`${API_BASE_PREFIX}/footer?website_id=eq.${params.websiteId}`, { const footer: Footer = (
method: "GET", await apiRequest(fetch, `${API_BASE_PREFIX}/footer?website_id=eq.${params.websiteId}`, "GET", {
headers: { headers: {
"Content-Type": "application/json", Accept: "application/vnd.pgrst.object+json"
Authorization: `Bearer ${cookies.get("session_token")}`, },
Accept: "application/vnd.pgrst.object+json" returnData: true
} })
}); ).data;
const globalSettings: Settings = await globalSettingsData.json();
const header: Header = await headerData.json();
const footer: Footer = await footerData.json();
return { return {
globalSettings, globalSettings,
@@ -46,13 +45,12 @@ export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
}; };
export const actions: Actions = { export const actions: Actions = {
updateGlobal: async ({ request, fetch, cookies, params }) => { updateGlobal: async ({ request, fetch, params }) => {
const data = await request.formData(); const data = await request.formData();
const faviconFile = data.get("favicon") as File; const faviconFile = data.get("favicon") as File;
const headers: Record<string, string> = { const headers: Record<string, string> = {
"Content-Type": "application/octet-stream", "Content-Type": "application/octet-stream",
Authorization: `Bearer ${cookies.get("session_token")}`,
Accept: "application/vnd.pgrst.object+json", Accept: "application/vnd.pgrst.object+json",
"X-Website-Id": params.websiteId "X-Website-Id": params.websiteId
}; };
@@ -62,48 +60,36 @@ export const actions: Actions = {
headers["X-Original-Filename"] = faviconFile.name; headers["X-Original-Filename"] = faviconFile.name;
} }
const uploadedImageData = await fetch(`${API_BASE_PREFIX}/rpc/upload_file`, { const uploadedImage = await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/upload_file`, "POST", {
method: "POST",
headers, headers,
body: faviconFile ? await faviconFile.arrayBuffer() : null body: faviconFile ? await faviconFile.arrayBuffer() : null,
returnData: true
}); });
const uploadedImage = await uploadedImageData.json(); if (!uploadedImage.success && (faviconFile?.size ?? 0 > 0)) {
return uploadedImage;
if (!uploadedImageData.ok && (faviconFile?.size ?? 0 > 0)) {
return { success: false, message: uploadedImage.message };
} }
const res = await fetch(`${API_BASE_PREFIX}/settings?website_id=eq.${params.websiteId}`, { return await apiRequest(
method: "PATCH", fetch,
headers: { `${API_BASE_PREFIX}/settings?website_id=eq.${params.websiteId}`,
"Content-Type": "application/json", "PATCH",
Authorization: `Bearer ${cookies.get("session_token")}` {
}, body: {
body: JSON.stringify({ accent_color_light_theme: data.get("accent-color-light"),
accent_color_light_theme: data.get("accent-color-light"), accent_color_dark_theme: data.get("accent-color-dark"),
accent_color_dark_theme: data.get("accent-color-dark"), favicon_image: uploadedImage.data?.file_id
favicon_image: uploadedImage.file_id },
}) successMessage: "Successfully updated global settings"
}); }
);
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return {
success: true,
message: "Successfully updated global settings"
};
}, },
updateHeader: async ({ request, fetch, cookies, params }) => { updateHeader: async ({ request, fetch, params }) => {
const data = await request.formData(); const data = await request.formData();
const logoImage = data.get("logo-image") as File; const logoImage = data.get("logo-image") as File;
const headers: Record<string, string> = { const headers: Record<string, string> = {
"Content-Type": "application/octet-stream", "Content-Type": "application/octet-stream",
Authorization: `Bearer ${cookies.get("session_token")}`,
Accept: "application/vnd.pgrst.object+json", Accept: "application/vnd.pgrst.object+json",
"X-Website-Id": params.websiteId "X-Website-Id": params.websiteId
}; };
@@ -113,109 +99,75 @@ export const actions: Actions = {
headers["X-Original-Filename"] = logoImage.name; headers["X-Original-Filename"] = logoImage.name;
} }
const uploadedImageData = await fetch(`${API_BASE_PREFIX}/rpc/upload_file`, { const uploadedImage = await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/upload_file`, "POST", {
method: "POST",
headers, headers,
body: logoImage ? await logoImage.arrayBuffer() : null body: logoImage ? await logoImage.arrayBuffer() : null,
returnData: true
}); });
const uploadedImage = await uploadedImageData.json(); if (!uploadedImage.success && (logoImage?.size ?? 0 > 0)) {
if (!uploadedImageData.ok && (logoImage?.size ?? 0 > 0)) {
return { success: false, message: uploadedImage.message }; return { success: false, message: uploadedImage.message };
} }
const res = await fetch(`${API_BASE_PREFIX}/header?website_id=eq.${params.websiteId}`, { return await apiRequest(
method: "PATCH", fetch,
headers: { `${API_BASE_PREFIX}/header?website_id=eq.${params.websiteId}`,
"Content-Type": "application/json", "PATCH",
Authorization: `Bearer ${cookies.get("session_token")}` {
}, body: {
body: JSON.stringify({ logo_type: data.get("logo-type"),
logo_type: data.get("logo-type"), logo_text: data.get("logo-text"),
logo_text: data.get("logo-text"), logo_image: uploadedImage.data?.file_id
logo_image: uploadedImage.file_id },
}) successMessage: "Successfully updated header"
}); }
);
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return {
success: true,
message: "Successfully updated header"
};
}, },
updateHome: async ({ request, fetch, cookies, params }) => { updateHome: async ({ request, fetch, params }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch(`${API_BASE_PREFIX}/home?website_id=eq.${params.websiteId}`, { return await apiRequest(
method: "PATCH", fetch,
headers: { `${API_BASE_PREFIX}/home?website_id=eq.${params.websiteId}`,
"Content-Type": "application/json", "PATCH",
Authorization: `Bearer ${cookies.get("session_token")}` {
}, body: {
body: JSON.stringify({ main_content: data.get("main-content")
main_content: data.get("main-content") },
}) successMessage: "Successfully updated home"
}); }
);
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully updated home" };
}, },
updateFooter: async ({ request, fetch, cookies, params }) => { updateFooter: async ({ request, fetch, params }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch(`${API_BASE_PREFIX}/footer?website_id=eq.${params.websiteId}`, { return await apiRequest(
method: "PATCH", fetch,
headers: { `${API_BASE_PREFIX}/footer?website_id=eq.${params.websiteId}`,
"Content-Type": "application/json", "PATCH",
Authorization: `Bearer ${cookies.get("session_token")}` {
}, body: {
body: JSON.stringify({ additional_text: data.get("additional-text")
additional_text: data.get("additional-text") },
}) successMessage: "Successfully updated footer"
}); }
);
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return {
success: true,
message: "Successfully updated footer"
};
}, },
pasteImage: async ({ request, fetch, cookies, params }) => { pasteImage: async ({ request, fetch, params }) => {
const data = await request.formData(); const data = await request.formData();
const file = data.get("file") as File; const file = data.get("file") as File;
const fileData = await fetch(`${API_BASE_PREFIX}/rpc/upload_file`, { return await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/upload_file`, "POST", {
method: "POST",
headers: { headers: {
"Content-Type": "application/octet-stream", "Content-Type": "application/octet-stream",
Authorization: `Bearer ${cookies.get("session_token")}`,
Accept: "application/vnd.pgrst.object+json", Accept: "application/vnd.pgrst.object+json",
"X-Website-Id": params.websiteId, "X-Website-Id": params.websiteId,
"X-Mimetype": file.type, "X-Mimetype": file.type,
"X-Original-Filename": file.name "X-Original-Filename": file.name
}, },
body: await file.arrayBuffer() body: await file.arrayBuffer(),
successMessage: "Successfully uploaded image",
returnData: true
}); });
const fileJSON = await fileData.json();
if (!fileData.ok) {
return { success: false, message: fileJSON.message };
}
return { success: true, message: "Successfully uploaded image", fileId: fileJSON.file_id };
} }
}; };

View File

@@ -1,8 +1,9 @@
import type { Actions, PageServerLoad } from "./$types"; import type { Actions, PageServerLoad } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils"; import { API_BASE_PREFIX } from "$lib/server/utils";
import type { Article, ArticleInput, DocsCategory } from "$lib/db-schema"; import { apiRequest } from "$lib/server/utils";
import type { Article, DocsCategory } from "$lib/db-schema";
export const load: PageServerLoad = async ({ params, fetch, cookies, url, parent, locals }) => { export const load: PageServerLoad = async ({ params, fetch, url, parent, locals }) => {
const searchQuery = url.searchParams.get("article_search_query"); const searchQuery = url.searchParams.get("article_search_query");
const filterBy = url.searchParams.get("article_filter"); const filterBy = url.searchParams.get("article_filter");
@@ -34,28 +35,22 @@ export const load: PageServerLoad = async ({ params, fetch, cookies, url, parent
const constructedFetchUrl = `${baseFetchUrl}&${parameters.toString()}`; const constructedFetchUrl = `${baseFetchUrl}&${parameters.toString()}`;
const totalArticlesData = await fetch(baseFetchUrl, { const totalArticles = await apiRequest(fetch, baseFetchUrl, "HEAD", {
method: "HEAD",
headers: { headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`,
Prefer: "count=exact" Prefer: "count=exact"
} },
returnData: true
}); });
const totalArticleCount = Number( const totalArticleCount = Number(
totalArticlesData.headers.get("content-range")?.split("/").at(-1) totalArticles.data.headers.get("content-range")?.split("/").at(-1)
); );
const articlesData = await fetch(constructedFetchUrl, { const articles: (Article & { docs_category: DocsCategory | null })[] = (
method: "GET", await apiRequest(fetch, constructedFetchUrl, "GET", {
headers: { returnData: true
"Content-Type": "application/json", })
Authorization: `Bearer ${cookies.get("session_token")}` ).data;
}
});
const articles: (Article & { docs_category: DocsCategory | null })[] = await articlesData.json();
return { return {
totalArticleCount, totalArticleCount,
@@ -66,44 +61,22 @@ export const load: PageServerLoad = async ({ params, fetch, cookies, url, parent
}; };
export const actions: Actions = { export const actions: Actions = {
createArticle: async ({ request, fetch, cookies, params }) => { createArticle: async ({ request, fetch, params }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch(`${API_BASE_PREFIX}/article`, { return await apiRequest(fetch, `${API_BASE_PREFIX}/article`, "POST", {
method: "POST", body: {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
},
body: JSON.stringify({
website_id: params.websiteId, website_id: params.websiteId,
title: data.get("title") as string title: data.get("title")
} satisfies ArticleInput) },
successMessage: "Successfully created article"
}); });
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully created article" };
}, },
deleteArticle: async ({ request, fetch, cookies }) => { deleteArticle: async ({ request, fetch }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch(`${API_BASE_PREFIX}/article?id=eq.${data.get("id")}`, { return await apiRequest(fetch, `${API_BASE_PREFIX}/article?id=eq.${data.get("id")}`, "DELETE", {
method: "DELETE", successMessage: "Successfully deleted article"
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
}
}); });
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully deleted article" };
} }
}; };

View File

@@ -1,30 +1,28 @@
import type { Actions, PageServerLoad } from "./$types"; import type { Actions, PageServerLoad } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils"; import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
import type { Article, DocsCategory } from "$lib/db-schema"; import type { Article, DocsCategory } from "$lib/db-schema";
export const load: PageServerLoad = async ({ parent, params, cookies, fetch }) => { export const load: PageServerLoad = async ({ parent, params, fetch }) => {
const articleData = await fetch(`${API_BASE_PREFIX}/article?id=eq.${params.articleId}`, { const article: Article = (
method: "GET", await apiRequest(fetch, `${API_BASE_PREFIX}/article?id=eq.${params.articleId}`, "GET", {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`,
Accept: "application/vnd.pgrst.object+json"
}
});
const categoryData = await fetch(
`${API_BASE_PREFIX}/docs_category?website_id=eq.${params.websiteId}&order=category_weight.desc`,
{
method: "GET",
headers: { headers: {
"Content-Type": "application/json", Accept: "application/vnd.pgrst.object+json"
Authorization: `Bearer ${cookies.get("session_token")}` },
} returnData: true
} })
); ).data;
const categories: DocsCategory[] = (
await apiRequest(
fetch,
`${API_BASE_PREFIX}/docs_category?website_id=eq.${params.websiteId}&order=category_weight.desc`,
"GET",
{
returnData: true
}
)
).data;
const article: Article = await articleData.json();
const categories: DocsCategory[] = await categoryData.json();
const { website } = await parent(); const { website } = await parent();
return { website, article, categories, API_BASE_PREFIX }; return { website, article, categories, API_BASE_PREFIX };
@@ -47,66 +45,50 @@ export const actions: Actions = {
headers["X-Original-Filename"] = coverFile.name; headers["X-Original-Filename"] = coverFile.name;
} }
const uploadedImageData = await fetch(`${API_BASE_PREFIX}/rpc/upload_file`, { const uploadedImage = await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/upload_file`, "POST", {
method: "POST",
headers, headers,
body: coverFile ? await coverFile.arrayBuffer() : null body: coverFile ? await coverFile.arrayBuffer() : null,
returnData: true
}); });
const uploadedImage = await uploadedImageData.json(); if (!uploadedImage.success && (coverFile?.size ?? 0 > 0)) {
if (!uploadedImageData.ok && (coverFile?.size ?? 0 > 0)) {
return { success: false, message: uploadedImage.message }; return { success: false, message: uploadedImage.message };
} }
const res = await fetch(`${API_BASE_PREFIX}/article?id=eq.${params.articleId}`, { return await apiRequest(
method: "PATCH", fetch,
headers: { `${API_BASE_PREFIX}/article?id=eq.${params.articleId}`,
"Content-Type": "application/json", "PATCH",
Authorization: `Bearer ${cookies.get("session_token")}` {
}, body: {
body: JSON.stringify({ title: data.get("title"),
title: data.get("title"), meta_description: data.get("description"),
meta_description: data.get("description"), meta_author: data.get("author"),
meta_author: data.get("author"), cover_image: uploadedImage.data?.file_id,
cover_image: uploadedImage.file_id, publication_date: data.get("publication-date"),
publication_date: data.get("publication-date"), main_content: data.get("main-content"),
main_content: data.get("main-content"), category: data.get("category"),
category: data.get("category"), article_weight: data.get("article-weight") ? data.get("article-weight") : null
article_weight: data.get("article-weight") ? data.get("article-weight") : null },
}) successMessage: "Successfully updated article"
}); }
);
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully updated article" };
}, },
pasteImage: async ({ request, fetch, cookies, params }) => { pasteImage: async ({ request, fetch, params }) => {
const data = await request.formData(); const data = await request.formData();
const file = data.get("file") as File; const file = data.get("file") as File;
const fileData = await fetch(`${API_BASE_PREFIX}/rpc/upload_file`, { return await apiRequest(fetch, `${API_BASE_PREFIX}/rpc/upload_file`, "POST", {
method: "POST",
headers: { headers: {
"Content-Type": "application/octet-stream", "Content-Type": "application/octet-stream",
Authorization: `Bearer ${cookies.get("session_token")}`,
Accept: "application/vnd.pgrst.object+json", Accept: "application/vnd.pgrst.object+json",
"X-Website-Id": params.websiteId, "X-Website-Id": params.websiteId,
"X-Mimetype": file.type, "X-Mimetype": file.type,
"X-Original-Filename": file.name "X-Original-Filename": file.name
}, },
body: await file.arrayBuffer() body: await file.arrayBuffer(),
successMessage: "Successfully uploaded image",
returnData: true
}); });
const fileJSON = await fileData.json();
if (!fileData.ok) {
return { success: false, message: fileJSON.message };
}
return { success: true, message: "Successfully uploaded image", fileId: fileJSON.file_id };
} }
}; };

View File

@@ -1,20 +1,19 @@
import type { Actions, PageServerLoad } from "./$types"; import type { Actions, PageServerLoad } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils"; import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
import type { DocsCategory, DocsCategoryInput } from "$lib/db-schema"; import type { DocsCategory } from "$lib/db-schema";
export const load: PageServerLoad = async ({ parent, params, cookies, fetch }) => { export const load: PageServerLoad = async ({ parent, params, fetch }) => {
const categoryData = await fetch( const categories: DocsCategory[] = (
`${API_BASE_PREFIX}/docs_category?website_id=eq.${params.websiteId}&order=category_weight.desc`, await apiRequest(
{ fetch,
method: "GET", `${API_BASE_PREFIX}/docs_category?website_id=eq.${params.websiteId}&order=category_weight.desc`,
headers: { "GET",
"Content-Type": "application/json", {
Authorization: `Bearer ${cookies.get("session_token")}` returnData: true
} }
} )
); ).data;
const categories: DocsCategory[] = await categoryData.json();
const { website, home } = await parent(); const { website, home } = await parent();
return { return {
@@ -25,73 +24,44 @@ export const load: PageServerLoad = async ({ parent, params, cookies, fetch }) =
}; };
export const actions: Actions = { export const actions: Actions = {
createCategory: async ({ request, fetch, cookies, params }) => { createCategory: async ({ request, fetch, params }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch(`${API_BASE_PREFIX}/docs_category`, { return await apiRequest(fetch, `${API_BASE_PREFIX}/docs_category`, "POST", {
method: "POST", body: {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
},
body: JSON.stringify({
website_id: params.websiteId, website_id: params.websiteId,
category_name: data.get("category-name") as string, category_name: data.get("category-name"),
category_weight: data.get("category-weight") as unknown as number category_weight: data.get("category-weight")
} satisfies DocsCategoryInput) },
successMessage: "Successfully created category"
}); });
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully created category" };
}, },
updateCategory: async ({ request, fetch, cookies, params }) => { updateCategory: async ({ request, fetch }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch( return await apiRequest(
`${API_BASE_PREFIX}/docs_category?website_id=eq.${params.websiteId}&id=eq.${data.get("category-id")}`, fetch,
`${API_BASE_PREFIX}/docs_category?id=eq.${data.get("category-id")}`,
"PATCH",
{ {
method: "PATCH", body: {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
},
body: JSON.stringify({
category_name: data.get("category-name"), category_name: data.get("category-name"),
category_weight: data.get("category-weight") category_weight: data.get("category-weight")
}) },
successMessage: "Successfully updated category"
} }
); );
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully updated category" };
}, },
deleteCategory: async ({ request, fetch, cookies, params }) => { deleteCategory: async ({ request, fetch }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch( return await apiRequest(
`${API_BASE_PREFIX}/docs_category?website_id=eq.${params.websiteId}&id=eq.${data.get("category-id")}`, fetch,
`${API_BASE_PREFIX}/docs_category?id=eq.${data.get("category-id")}`,
"DELETE",
{ {
method: "DELETE", successMessage: "Successfully deleted category"
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
}
} }
); );
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully deleted category" };
} }
}; };

View File

@@ -1,22 +1,20 @@
import type { Actions, PageServerLoad } from "./$types"; import type { Actions, PageServerLoad } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils"; import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
import type { Collab, CollabInput, User } from "$lib/db-schema"; import type { Collab, User } from "$lib/db-schema";
export const load: PageServerLoad = async ({ parent, params, fetch, cookies }) => { export const load: PageServerLoad = async ({ parent, params, fetch }) => {
const { website, home } = await parent(); const collaborators: (Collab & { user: User })[] = (
await apiRequest(
const collabData = await fetch( fetch,
`${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&select=*,user!user_id(*)&order=last_modified_at.desc,added_at.desc`, `${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&select=*,user!user_id(*)&order=last_modified_at.desc,added_at.desc`,
{ "GET",
method: "GET", {
headers: { returnData: true
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
} }
} )
); ).data;
const collaborators: (Collab & { user: User })[] = await collabData.json(); const { website, home } = await parent();
return { return {
website, website,
@@ -26,83 +24,57 @@ export const load: PageServerLoad = async ({ parent, params, fetch, cookies }) =
}; };
export const actions: Actions = { export const actions: Actions = {
addCollaborator: async ({ request, fetch, cookies, params }) => { addCollaborator: async ({ request, fetch, params }) => {
const data = await request.formData(); const data = await request.formData();
const userData = await fetch(`${API_BASE_PREFIX}/user?username=eq.${data.get("username")}`, { const user: User = (
method: "GET", await apiRequest(
headers: { fetch,
"Content-Type": "application/json", `${API_BASE_PREFIX}/user?username=eq.${data.get("username")}`,
Authorization: `Bearer ${cookies.get("session_token")}`, "GET",
Accept: "application/vnd.pgrst.object+json" {
} headers: {
}); Accept: "application/vnd.pgrst.object+json"
},
returnData: true
}
)
).data;
const user: User = await userData.json(); return await apiRequest(fetch, `${API_BASE_PREFIX}/collab`, "POST", {
body: {
const res = await fetch(`${API_BASE_PREFIX}/collab`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
},
body: JSON.stringify({
website_id: params.websiteId, website_id: params.websiteId,
user_id: user.id, user_id: user.id,
permission_level: data.get("permission-level") as unknown as number permission_level: data.get("permission-level")
} satisfies CollabInput) },
successMessage: "Successfully added collaborator"
}); });
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully added collaborator" };
}, },
updateCollaborator: async ({ request, fetch, cookies, params }) => { updateCollaborator: async ({ request, fetch, params }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch( return await apiRequest(
fetch,
`${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&user_id=eq.${data.get("user-id")}`, `${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&user_id=eq.${data.get("user-id")}`,
"PATCH",
{ {
method: "PATCH", body: {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
},
body: JSON.stringify({
permission_level: data.get("permission-level") permission_level: data.get("permission-level")
}) },
successMessage: "Successfully updated collaborator"
} }
); );
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully updated collaborator" };
}, },
removeCollaborator: async ({ request, fetch, cookies, params }) => { removeCollaborator: async ({ request, fetch, params }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch( return await apiRequest(
fetch,
`${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&user_id=eq.${data.get("user-id")}`, `${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&user_id=eq.${data.get("user-id")}`,
"DELETE",
{ {
method: "DELETE", successMessage: "Successfully removed collaborator"
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
}
} }
); );
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully removed collaborator" };
} }
}; };

View File

@@ -1,23 +1,24 @@
import type { Actions, PageServerLoad } from "./$types"; import type { Actions, PageServerLoad } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils"; import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
import { rm } from "node:fs/promises"; import { rm } from "node:fs/promises";
import { join } from "node:path"; import { join } from "node:path";
import type { LegalInformation, LegalInformationInput } from "$lib/db-schema"; import type { LegalInformation } from "$lib/db-schema";
export const load: PageServerLoad = async ({ parent, fetch, params, cookies }) => { export const load: PageServerLoad = async ({ parent, fetch, params }) => {
const legalInformationData = await fetch( const legalInformation: LegalInformation = (
`${API_BASE_PREFIX}/legal_information?website_id=eq.${params.websiteId}`, await apiRequest(
{ fetch,
method: "GET", `${API_BASE_PREFIX}/legal_information?website_id=eq.${params.websiteId}`,
headers: { "GET",
"Content-Type": "application/json", {
Authorization: `Bearer ${cookies.get("session_token")}`, headers: {
Accept: "application/vnd.pgrst.object+json" Accept: "application/vnd.pgrst.object+json"
},
returnData: true
} }
} )
); ).data;
const legalInformation: LegalInformation = await legalInformationData.json();
const { website } = await parent(); const { website } = await parent();
return { return {
@@ -27,48 +28,33 @@ export const load: PageServerLoad = async ({ parent, fetch, params, cookies }) =
}; };
export const actions: Actions = { export const actions: Actions = {
createUpdateLegalInformation: async ({ request, fetch, cookies, params }) => { createUpdateLegalInformation: async ({ request, fetch, params }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch(`${API_BASE_PREFIX}/legal_information`, { return await apiRequest(fetch, `${API_BASE_PREFIX}/legal_information`, "POST", {
method: "POST",
headers: { headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`,
Prefer: "resolution=merge-duplicates", Prefer: "resolution=merge-duplicates",
Accept: "application/vnd.pgrst.object+json" Accept: "application/vnd.pgrst.object+json"
}, },
body: JSON.stringify({ body: {
website_id: params.websiteId, website_id: params.websiteId,
main_content: data.get("main-content") as string main_content: data.get("main-content")
} satisfies LegalInformationInput) },
successMessage: "Successfully created/updated legal information"
}); });
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return {
success: true,
message: `Successfully ${res.status === 201 ? "created" : "updated"} legal information`
};
}, },
deleteLegalInformation: async ({ fetch, cookies, params }) => { deleteLegalInformation: async ({ fetch, params }) => {
const res = await fetch( const deleteLegalInformation = await apiRequest(
fetch,
`${API_BASE_PREFIX}/legal_information?website_id=eq.${params.websiteId}`, `${API_BASE_PREFIX}/legal_information?website_id=eq.${params.websiteId}`,
"DELETE",
{ {
method: "DELETE", successMessage: "Successfully deleted legal information"
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
}
} }
); );
if (!res.ok) { if (!deleteLegalInformation.success) {
const response = await res.json(); return deleteLegalInformation;
return { success: false, message: response.message };
} }
await rm( await rm(
@@ -76,6 +62,6 @@ export const actions: Actions = {
{ force: true } { force: true }
); );
return { success: true, message: `Successfully deleted legal information` }; return deleteLegalInformation;
} }
}; };

View File

@@ -8,7 +8,7 @@
const { data, form }: { data: PageServerData; form: ActionData } = $props(); const { data, form }: { data: PageServerData; form: ActionData } = $props();
let previewContent = $state(data.legalInformation.main_content); let previewContent = $state(data.legalInformation?.main_content);
let mainContentTextarea: HTMLTextAreaElement; let mainContentTextarea: HTMLTextAreaElement;
let textareaScrollTop = $state(0); let textareaScrollTop = $state(0);
@@ -82,14 +82,14 @@
bind:value={previewContent} bind:value={previewContent}
bind:this={mainContentTextarea} bind:this={mainContentTextarea}
onscroll={updateScrollPercentage} onscroll={updateScrollPercentage}
required>{data.legalInformation.main_content ?? ""}</textarea required>{data.legalInformation?.main_content ?? ""}</textarea
> >
</label> </label>
<button type="submit">Submit</button> <button type="submit">Submit</button>
</form> </form>
{#if data.legalInformation.main_content} {#if data.legalInformation?.main_content}
<Modal id="delete-legal-information" text="Delete"> <Modal id="delete-legal-information" text="Delete">
<form <form
action="?/deleteLegalInformation" action="?/deleteLegalInformation"

View File

@@ -1,8 +1,8 @@
import type { PageServerLoad } from "./$types"; import type { PageServerLoad } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils"; import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
import type { ChangeLog, User, Collab } from "$lib/db-schema"; import type { ChangeLog, User, Collab } from "$lib/db-schema";
export const load: PageServerLoad = async ({ parent, fetch, params, cookies, url }) => { export const load: PageServerLoad = async ({ parent, fetch, params, url }) => {
const userFilter = url.searchParams.get("logs_filter_user"); const userFilter = url.searchParams.get("logs_filter_user");
const resourceFilter = url.searchParams.get("logs_filter_resource"); const resourceFilter = url.searchParams.get("logs_filter_resource");
const operationFilter = url.searchParams.get("logs_filter_operation"); const operationFilter = url.searchParams.get("logs_filter_operation");
@@ -27,41 +27,30 @@ export const load: PageServerLoad = async ({ parent, fetch, params, cookies, url
const constructedFetchUrl = `${baseFetchUrl}&${searchParams.toString()}&limit=50&offset=${resultOffset}`; const constructedFetchUrl = `${baseFetchUrl}&${searchParams.toString()}&limit=50&offset=${resultOffset}`;
const changeLogData = await fetch(constructedFetchUrl, { const changeLog: (ChangeLog & { user: { username: User["username"] } })[] = (
method: "GET", await apiRequest(fetch, constructedFetchUrl, "GET", { returnData: true })
headers: { ).data;
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
}
});
const resultChangeLogData = await fetch(constructedFetchUrl, { const resultChangeLogData = await apiRequest(fetch, constructedFetchUrl, "HEAD", {
method: "HEAD",
headers: { headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`,
Prefer: "count=exact" Prefer: "count=exact"
} },
returnData: true
}); });
const resultChangeLogCount = Number( const resultChangeLogCount = Number(
resultChangeLogData.headers.get("content-range")?.split("/").at(-1) resultChangeLogData.data.headers.get("content-range")?.split("/").at(-1)
); );
const collabData = await fetch( const collaborators: (Collab & { user: User })[] = (
`${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&select=*,user!user_id(*)&order=last_modified_at.desc,added_at.desc`, await apiRequest(
{ fetch,
method: "GET", `${API_BASE_PREFIX}/collab?website_id=eq.${params.websiteId}&select=*,user!user_id(*)&order=last_modified_at.desc,added_at.desc`,
headers: { "GET",
"Content-Type": "application/json", { returnData: true }
Authorization: `Bearer ${cookies.get("session_token")}` )
} ).data;
}
);
const changeLog: (ChangeLog & { user: { username: User["username"] } })[] =
await changeLogData.json();
const collaborators: (Collab & { user: User })[] = await collabData.json();
const { website, home } = await parent(); const { website, home } = await parent();
return { return {

View File

@@ -2,29 +2,28 @@ import { readFile, mkdir, writeFile, rename } from "node:fs/promises";
import { join } from "node:path"; import { join } from "node:path";
import { type WebsiteOverview, slugify } from "$lib/utils"; import { type WebsiteOverview, slugify } from "$lib/utils";
import type { Actions, PageServerLoad } from "./$types"; import type { Actions, PageServerLoad } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils"; import { API_BASE_PREFIX, apiRequest } from "$lib/server/utils";
import { render } from "svelte/server"; import { render } from "svelte/server";
import BlogIndex from "$lib/templates/blog/BlogIndex.svelte"; import BlogIndex from "$lib/templates/blog/BlogIndex.svelte";
import BlogArticle from "$lib/templates/blog/BlogArticle.svelte"; import BlogArticle from "$lib/templates/blog/BlogArticle.svelte";
import DocsIndex from "$lib/templates/docs/DocsIndex.svelte"; import DocsIndex from "$lib/templates/docs/DocsIndex.svelte";
import DocsArticle from "$lib/templates/docs/DocsArticle.svelte"; import DocsArticle from "$lib/templates/docs/DocsArticle.svelte";
import { dev } from "$app/environment"; import { dev } from "$app/environment";
import type { DomainPrefixInput } from "$lib/db-schema";
export const load: PageServerLoad = async ({ params, fetch, cookies }) => { export const load: PageServerLoad = async ({ params, fetch }) => {
const websiteOverviewData = await fetch( const websiteOverview: WebsiteOverview = (
`${API_BASE_PREFIX}/website?id=eq.${params.websiteId}&select=*,settings(*),header(*),home(*),footer(*),article(*,docs_category(*)),legal_information(*),domain_prefix(*)`, await apiRequest(
{ fetch,
method: "GET", `${API_BASE_PREFIX}/website?id=eq.${params.websiteId}&select=*,settings(*),header(*),home(*),footer(*),article(*,docs_category(*)),legal_information(*),domain_prefix(*)`,
headers: { "GET",
"Content-Type": "application/json", {
Authorization: `Bearer ${cookies.get("session_token")}`, headers: {
Accept: "application/vnd.pgrst.object+json" Accept: "application/vnd.pgrst.object+json"
},
returnData: true
} }
} )
); ).data;
const websiteOverview: WebsiteOverview = await websiteOverviewData.json();
generateStaticFiles(websiteOverview); generateStaticFiles(websiteOverview);
@@ -53,73 +52,66 @@ export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
}; };
export const actions: Actions = { export const actions: Actions = {
publishWebsite: async ({ fetch, params, cookies }) => { publishWebsite: async ({ fetch, params }) => {
const websiteOverviewData = await fetch( const websiteOverview: WebsiteOverview = (
`${API_BASE_PREFIX}/website?id=eq.${params.websiteId}&select=*,settings(*),header(*),home(*),footer(*),article(*,docs_category(*)),legal_information(*),domain_prefix(*)`, await apiRequest(
{ fetch,
method: "GET", `${API_BASE_PREFIX}/website?id=eq.${params.websiteId}&select=*,settings(*),header(*),home(*),footer(*),article(*,docs_category(*)),legal_information(*),domain_prefix(*)`,
headers: { "GET",
"Content-Type": "application/json", {
Authorization: `Bearer ${cookies.get("session_token")}`, headers: {
Accept: "application/vnd.pgrst.object+json" Accept: "application/vnd.pgrst.object+json"
},
returnData: true
} }
} )
); ).data;
const websiteOverview = await websiteOverviewData.json();
generateStaticFiles(websiteOverview, false); generateStaticFiles(websiteOverview, false);
const res = await fetch(`${API_BASE_PREFIX}/website?id=eq.${params.websiteId}`, { return await apiRequest(
method: "PATCH", fetch,
headers: { `${API_BASE_PREFIX}/website?id=eq.${params.websiteId}`,
"Content-Type": "application/json", "PATCH",
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" };
},
createUpdateCustomDomainPrefix: async ({ request, fetch, params, cookies }) => {
const data = await request.formData();
const oldDomainPrefixData = await fetch(
`${API_BASE_PREFIX}/domain_prefix?website_id=eq.${params.websiteId}`,
{ {
method: "GET", body: {
headers: { is_published: true
"Content-Type": "application/json", },
Authorization: `Bearer ${cookies.get("session_token")}`, successMessage: "Successfully published website"
Accept: "application/vnd.pgrst.object+json"
}
} }
); );
const oldDomainPrefix = await oldDomainPrefixData.json(); },
createUpdateCustomDomainPrefix: async ({ request, fetch, params }) => {
const data = await request.formData();
const res = await fetch(`${API_BASE_PREFIX}/domain_prefix`, { const oldDomainPrefix = (
method: "POST", await apiRequest(
fetch,
`${API_BASE_PREFIX}/domain_prefix?website_id=eq.${params.websiteId}`,
"GET",
{
headers: {
Accept: "application/vnd.pgrst.object+json"
},
returnData: true
}
)
).data;
const newDomainPrefix = await apiRequest(fetch, `${API_BASE_PREFIX}/domain_prefix`, "POST", {
headers: { headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`,
Prefer: "resolution=merge-duplicates", Prefer: "resolution=merge-duplicates",
Accept: "application/vnd.pgrst.object+json" Accept: "application/vnd.pgrst.object+json"
}, },
body: JSON.stringify({ body: {
website_id: params.websiteId, website_id: params.websiteId,
prefix: data.get("domain-prefix") as string prefix: data.get("domain-prefix") as string
} satisfies DomainPrefixInput) },
successMessage: "Successfully created/updated domain prefix"
}); });
if (!res.ok) { if (!newDomainPrefix.success) {
const response = await res.json(); return newDomainPrefix;
return { success: false, message: response.message };
} }
await rename( await rename(
@@ -128,39 +120,38 @@ export const actions: Actions = {
"var", "var",
"www", "www",
"archtika-websites", "archtika-websites",
res.status === 201 ? params.websiteId : oldDomainPrefix.prefix oldDomainPrefix?.prefix ? oldDomainPrefix.prefix : params.websiteId
), ),
join("/", "var", "www", "archtika-websites", data.get("domain-prefix") as string) join("/", "var", "www", "archtika-websites", data.get("domain-prefix") as string)
); );
return { return newDomainPrefix;
success: true,
message: `Successfully ${res.status === 201 ? "created" : "updated"} domain prefix`
};
}, },
deleteCustomDomainPrefix: async ({ fetch, params, cookies }) => { deleteCustomDomainPrefix: async ({ fetch, params }) => {
const res = await fetch(`${API_BASE_PREFIX}/domain_prefix?website_id=eq.${params.websiteId}`, { const customPrefix = await apiRequest(
method: "DELETE", fetch,
headers: { `${API_BASE_PREFIX}/domain_prefix?website_id=eq.${params.websiteId}`,
"Content-Type": "application/json", "DELETE",
Authorization: `Bearer ${cookies.get("session_token")}`, {
Prefer: "return=representation", headers: {
Accept: "application/vnd.pgrst.object+json" Prefer: "return=representation",
Accept: "application/vnd.pgrst.object+json"
},
successMessage: "Successfully deleted domain prefix",
returnData: true
} }
}); );
const response = await res.json(); if (!customPrefix.success) {
return customPrefix;
if (!res.ok) {
return { success: false, message: response.message };
} }
await rename( await rename(
join("/", "var", "www", "archtika-websites", response.prefix), join("/", "var", "www", "archtika-websites", customPrefix.data.prefix),
join("/", "var", "www", "archtika-websites", params.websiteId) join("/", "var", "www", "archtika-websites", params.websiteId)
); );
return { success: true, message: `Successfully deleted domain prefix` }; return customPrefix;
} }
}; };
@@ -178,6 +169,8 @@ const generateStaticFiles = async (websiteData: WebsiteOverview, isPreview = tru
</html>`; </html>`;
}; };
console.log(websiteData);
const { head, body } = render(websiteData.content_type === "Blog" ? BlogIndex : DocsIndex, { const { head, body } = render(websiteData.content_type === "Blog" ? BlogIndex : DocsIndex, {
props: { props: {
websiteOverview: websiteData, websiteOverview: websiteData,

View File

@@ -34,6 +34,10 @@
<svelte:head> <svelte:head>
<title>archtika | {routeName.replaceAll("/", " - ")}</title> <title>archtika | {routeName.replaceAll("/", " - ")}</title>
<meta
name="description"
content="FLOSS, modern, performant and lightweight CMS (Content Mangement System) with predefined templates"
/>
</svelte:head> </svelte:head>
<nav> <nav>

View File

@@ -335,7 +335,7 @@ test.describe.serial("Collaborator tests", () => {
await page.getByRole("button", { name: "Submit" }).click(); await page.getByRole("button", { name: "Submit" }).click();
if (permissionLevel === 30) { if (permissionLevel === 30) {
await expect(page.getByText("Successfully created legal")).toBeVisible(); await expect(page.getByText("Successfully created/updated legal")).toBeVisible();
} else { } else {
await expect(page.getByText("Insufficient permissions")).toBeVisible(); await expect(page.getByText("Insufficient permissions")).toBeVisible();
} }
@@ -345,7 +345,7 @@ test.describe.serial("Collaborator tests", () => {
await page.getByRole("button", { name: "Submit" }).click(); await page.getByRole("button", { name: "Submit" }).click();
if (permissionLevel === 30) { if (permissionLevel === 30) {
await expect(page.getByText("Successfully updated legal")).toBeVisible(); await expect(page.getByText("Successfully created/updated legal")).toBeVisible();
} else { } else {
await expect(page.getByText("Insufficient permissions")).toBeVisible(); await expect(page.getByText("Insufficient permissions")).toBeVisible();
} }

View File

@@ -238,12 +238,12 @@ test.describe.serial("Website tests", () => {
await page.getByPlaceholder("## Impressum\n\n## Privacy policy").click(); await page.getByPlaceholder("## Impressum\n\n## Privacy policy").click();
await page.getByPlaceholder("## Impressum\n\n## Privacy policy").fill("## Content"); await page.getByPlaceholder("## Impressum\n\n## Privacy policy").fill("## Content");
await page.getByRole("button", { name: "Submit" }).click(); await page.getByRole("button", { name: "Submit" }).click();
await expect(page.getByText("Successfully created legal")).toBeVisible(); await expect(page.getByText("Successfully created/updated legal")).toBeVisible();
await page.getByPlaceholder("## Impressum\n\n## Privacy policy").click(); await page.getByPlaceholder("## Impressum\n\n## Privacy policy").click();
await page.getByPlaceholder("## Impressum\n\n## Privacy policy").fill("## Content updated"); await page.getByPlaceholder("## Impressum\n\n## Privacy policy").fill("## Content updated");
await page.getByRole("button", { name: "Submit" }).click(); await page.getByRole("button", { name: "Submit" }).click();
await expect(page.getByText("Successfully updated legal")).toBeVisible(); await expect(page.getByText("Successfully created/updated legal")).toBeVisible();
}); });
test("Delete legal information", async ({ authenticatedPage: page }) => { test("Delete legal information", async ({ authenticatedPage: page }) => {
await page.getByRole("link", { name: "Blog" }).click(); await page.getByRole("link", { name: "Blog" }).click();