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";
|
||||
|
||||
const {
|
||||
favicon,
|
||||
title,
|
||||
logoType,
|
||||
logo,
|
||||
@@ -12,6 +13,7 @@
|
||||
publicationDate,
|
||||
footerAdditionalText
|
||||
}: {
|
||||
favicon: string;
|
||||
title: string;
|
||||
logoType: "text" | "image";
|
||||
logo: string;
|
||||
@@ -22,7 +24,7 @@
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<BlogHead {title} nestingLevel={1} />
|
||||
<BlogHead {title} {favicon} nestingLevel={1} />
|
||||
|
||||
<BlogNav {logoType} {logo} />
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import BlogFooter from "./common/BlogFooter.svelte";
|
||||
|
||||
const {
|
||||
favicon,
|
||||
title,
|
||||
logoType,
|
||||
logo,
|
||||
@@ -11,6 +12,7 @@
|
||||
articles,
|
||||
footerAdditionalText
|
||||
}: {
|
||||
favicon: string;
|
||||
title: string;
|
||||
logoType: "text" | "image";
|
||||
logo: string;
|
||||
@@ -20,7 +22,7 @@
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<BlogHead {title} />
|
||||
<BlogHead {title} {favicon} />
|
||||
|
||||
<BlogNav {logoType} {logo} />
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<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>
|
||||
|
||||
<svelte:head>
|
||||
@@ -8,5 +12,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{title}</title>
|
||||
<link rel="stylesheet" href={`${"../".repeat(nestingLevel)}styles.css`} />
|
||||
{#if favicon}
|
||||
<link rel="icon" href={favicon} />
|
||||
{/if}
|
||||
</head>
|
||||
</svelte:head>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<strong>{logo}</strong>
|
||||
</p>
|
||||
{:else}
|
||||
<img src={logo} alt="" />
|
||||
<img src={logo} width="24" height="24" alt="" />
|
||||
{/if}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -57,9 +57,15 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
|
||||
{
|
||||
({ head, body } = render(BlogIndex, {
|
||||
props: {
|
||||
favicon: websiteData.favicon_image
|
||||
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.favicon_image}`
|
||||
: "",
|
||||
title: websiteData.title,
|
||||
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),
|
||||
articles: websiteData.articles ?? [],
|
||||
footerAdditionalText: md(websiteData.additional_text ?? "")
|
||||
@@ -110,9 +116,15 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
|
||||
{
|
||||
({ head, body } = render(BlogArticle, {
|
||||
props: {
|
||||
favicon: websiteData.favicon_image
|
||||
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.favicon_image}`
|
||||
: "",
|
||||
title: article.title,
|
||||
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
|
||||
? `${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`, {
|
||||
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);
|
||||
}
|
||||
|
||||
:is(button, input, textarea, select, a, summary):focus,
|
||||
:is(button, input, textarea, select, a, summary, pre):focus,
|
||||
#toggle-mobile-preview:checked + label {
|
||||
outline: 0.125rem solid var(--color-accent);
|
||||
outline-offset: 0.25rem;
|
||||
|
||||
Reference in New Issue
Block a user