mirror of
https://github.com/thiloho/archtika.git
synced 2025-11-22 02:41:35 +01:00
Set favicon and logo on output
This commit is contained in:
@@ -0,0 +1,74 @@
|
|||||||
|
-- migrate:up
|
||||||
|
CREATE OR REPLACE FUNCTION api.upload_file (BYTEA, OUT file_id UUID)
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
_headers JSON := CURRENT_SETTING('request.headers', TRUE)::JSON;
|
||||||
|
_website_id UUID := (_headers ->> 'x-website-id')::UUID;
|
||||||
|
_mimetype TEXT := _headers ->> 'x-mimetype';
|
||||||
|
_original_filename TEXT := _headers ->> 'x-original-filename';
|
||||||
|
_allowed_mimetypes TEXT[] := ARRAY['image/png', 'image/jpeg', 'image/webp'];
|
||||||
|
_max_file_size INT := 5 * 1024 * 1024;
|
||||||
|
BEGIN
|
||||||
|
IF OCTET_LENGTH($1) = 0 THEN
|
||||||
|
RAISE invalid_parameter_value
|
||||||
|
USING message = 'No file data was provided';
|
||||||
|
END IF;
|
||||||
|
IF _mimetype IS NULL OR _mimetype NOT IN (
|
||||||
|
SELECT
|
||||||
|
UNNEST(_allowed_mimetypes)) THEN
|
||||||
|
RAISE invalid_parameter_value
|
||||||
|
USING message = 'Invalid MIME type. Allowed types are: png, jpg, webp';
|
||||||
|
END IF;
|
||||||
|
IF OCTET_LENGTH($1) > _max_file_size THEN
|
||||||
|
RAISE program_limit_exceeded
|
||||||
|
USING message = FORMAT('File size exceeds the maximum limit of %s MB', _max_file_size / (1024 * 1024));
|
||||||
|
END IF;
|
||||||
|
INSERT INTO internal.media (website_id, blob, mimetype, original_name)
|
||||||
|
VALUES (_website_id, $1, _mimetype, _original_filename)
|
||||||
|
RETURNING
|
||||||
|
id INTO file_id;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
SECURITY DEFINER;
|
||||||
|
|
||||||
|
GRANT EXECUTE ON FUNCTION api.upload_file (BYTEA) TO authenticated_user;
|
||||||
|
|
||||||
|
-- migrate:down
|
||||||
|
|
||||||
|
DROP FUNCTION api.upload_file(BYTEA);
|
||||||
|
|
||||||
|
CREATE FUNCTION api.upload_file (BYTEA, OUT file_id UUID)
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
_headers JSON := CURRENT_SETTING('request.headers', TRUE)::JSON;
|
||||||
|
_website_id UUID := (_headers ->> 'x-website-id')::UUID;
|
||||||
|
_mimetype TEXT := _headers ->> 'x-mimetype';
|
||||||
|
_original_filename TEXT := _headers ->> 'x-original-filename';
|
||||||
|
_allowed_mimetypes TEXT[] := ARRAY['image/png', 'image/jpeg', 'image/webp'];
|
||||||
|
_max_file_size INT := 5 * 1024 * 1024;
|
||||||
|
BEGIN
|
||||||
|
IF OCTET_LENGTH($1) = 0 THEN
|
||||||
|
RAISE invalid_parameter_value
|
||||||
|
USING message = 'No file data was provided';
|
||||||
|
END IF;
|
||||||
|
IF _mimetype IS NULL OR _mimetype NOT IN (
|
||||||
|
SELECT
|
||||||
|
UNNEST(_allowed_mimetypes)) THEN
|
||||||
|
RAISE invalid_parameter_value
|
||||||
|
USING message = 'Invalid MIME type. Allowed types are: png, svg, jpg, webp';
|
||||||
|
END IF;
|
||||||
|
IF OCTET_LENGTH($1) > _max_file_size THEN
|
||||||
|
RAISE program_limit_exceeded
|
||||||
|
USING message = FORMAT('File size exceeds the maximum limit of %s MB', _max_file_size / (1024 * 1024));
|
||||||
|
END IF;
|
||||||
|
INSERT INTO internal.media (website_id, blob, mimetype, original_name)
|
||||||
|
VALUES (_website_id, $1, _mimetype, _original_filename)
|
||||||
|
RETURNING
|
||||||
|
id INTO file_id;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
SECURITY DEFINER;
|
||||||
|
|
||||||
|
GRANT EXECUTE ON FUNCTION api.upload_file (BYTEA) TO authenticated_user;
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
import BlogFooter from "./common/BlogFooter.svelte";
|
import BlogFooter from "./common/BlogFooter.svelte";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
favicon,
|
||||||
title,
|
title,
|
||||||
logoType,
|
logoType,
|
||||||
logo,
|
logo,
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
publicationDate,
|
publicationDate,
|
||||||
footerAdditionalText
|
footerAdditionalText
|
||||||
}: {
|
}: {
|
||||||
|
favicon: string;
|
||||||
title: string;
|
title: string;
|
||||||
logoType: "text" | "image";
|
logoType: "text" | "image";
|
||||||
logo: string;
|
logo: string;
|
||||||
@@ -22,7 +24,7 @@
|
|||||||
} = $props();
|
} = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BlogHead {title} nestingLevel={1} />
|
<BlogHead {title} {favicon} nestingLevel={1} />
|
||||||
|
|
||||||
<BlogNav {logoType} {logo} />
|
<BlogNav {logoType} {logo} />
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import BlogFooter from "./common/BlogFooter.svelte";
|
import BlogFooter from "./common/BlogFooter.svelte";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
favicon,
|
||||||
title,
|
title,
|
||||||
logoType,
|
logoType,
|
||||||
logo,
|
logo,
|
||||||
@@ -11,6 +12,7 @@
|
|||||||
articles,
|
articles,
|
||||||
footerAdditionalText
|
footerAdditionalText
|
||||||
}: {
|
}: {
|
||||||
|
favicon: string;
|
||||||
title: string;
|
title: string;
|
||||||
logoType: "text" | "image";
|
logoType: "text" | "image";
|
||||||
logo: string;
|
logo: string;
|
||||||
@@ -20,7 +22,7 @@
|
|||||||
} = $props();
|
} = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BlogHead {title} />
|
<BlogHead {title} {favicon} />
|
||||||
|
|
||||||
<BlogNav {logoType} {logo} />
|
<BlogNav {logoType} {logo} />
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
const { title, nestingLevel = 0 }: { title: string; nestingLevel?: number } = $props();
|
const {
|
||||||
|
title,
|
||||||
|
favicon,
|
||||||
|
nestingLevel = 0
|
||||||
|
}: { title: string; favicon: string; nestingLevel?: number } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -8,5 +12,8 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<link rel="stylesheet" href={`${"../".repeat(nestingLevel)}styles.css`} />
|
<link rel="stylesheet" href={`${"../".repeat(nestingLevel)}styles.css`} />
|
||||||
|
{#if favicon}
|
||||||
|
<link rel="icon" href={favicon} />
|
||||||
|
{/if}
|
||||||
</head>
|
</head>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<strong>{logo}</strong>
|
<strong>{logo}</strong>
|
||||||
</p>
|
</p>
|
||||||
{:else}
|
{:else}
|
||||||
<img src={logo} alt="" />
|
<img src={logo} width="24" height="24" alt="" />
|
||||||
{/if}
|
{/if}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -57,9 +57,15 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
|
|||||||
{
|
{
|
||||||
({ head, body } = render(BlogIndex, {
|
({ head, body } = render(BlogIndex, {
|
||||||
props: {
|
props: {
|
||||||
|
favicon: websiteData.favicon_image
|
||||||
|
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.favicon_image}`
|
||||||
|
: "",
|
||||||
title: websiteData.title,
|
title: websiteData.title,
|
||||||
logoType: websiteData.logo_type,
|
logoType: websiteData.logo_type,
|
||||||
logo: websiteData.logo_text,
|
logo:
|
||||||
|
websiteData.logo_type === "text"
|
||||||
|
? websiteData.logo_text
|
||||||
|
: `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`,
|
||||||
mainContent: md(websiteData.main_content ?? "", false),
|
mainContent: md(websiteData.main_content ?? "", false),
|
||||||
articles: websiteData.articles ?? [],
|
articles: websiteData.articles ?? [],
|
||||||
footerAdditionalText: md(websiteData.additional_text ?? "")
|
footerAdditionalText: md(websiteData.additional_text ?? "")
|
||||||
@@ -110,9 +116,15 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
|
|||||||
{
|
{
|
||||||
({ head, body } = render(BlogArticle, {
|
({ head, body } = render(BlogArticle, {
|
||||||
props: {
|
props: {
|
||||||
|
favicon: websiteData.favicon_image
|
||||||
|
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.favicon_image}`
|
||||||
|
: "",
|
||||||
title: article.title,
|
title: article.title,
|
||||||
logoType: websiteData.logo_type,
|
logoType: websiteData.logo_type,
|
||||||
logo: websiteData.logo_text,
|
logo:
|
||||||
|
websiteData.logo_type === "text"
|
||||||
|
? websiteData.logo_text
|
||||||
|
: `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`,
|
||||||
coverImage: article.cover_image
|
coverImage: article.cover_image
|
||||||
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${article.cover_image}`
|
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${article.cover_image}`
|
||||||
: "",
|
: "",
|
||||||
@@ -160,5 +172,21 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
|
|||||||
const specificStyles = await readFile(`${process.cwd()}/template-styles/blog-styles.css`, {
|
const specificStyles = await readFile(`${process.cwd()}/template-styles/blog-styles.css`, {
|
||||||
encoding: "utf-8"
|
encoding: "utf-8"
|
||||||
});
|
});
|
||||||
await writeFile(join(uploadDir, "styles.css"), commonStyles.concat(specificStyles));
|
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};`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ summary {
|
|||||||
background-color: var(--bg-tertiary);
|
background-color: var(--bg-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
:is(button, input, textarea, select, a, summary):focus,
|
:is(button, input, textarea, select, a, summary, pre):focus,
|
||||||
#toggle-mobile-preview:checked + label {
|
#toggle-mobile-preview:checked + label {
|
||||||
outline: 0.125rem solid var(--color-accent);
|
outline: 0.125rem solid var(--color-accent);
|
||||||
outline-offset: 0.25rem;
|
outline-offset: 0.25rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user