From 8121be1d961d3b5de72f0968f5170773ff211a8f Mon Sep 17 00:00:00 2001 From: thiloho <123883702+thiloho@users.noreply.github.com> Date: Sun, 8 Sep 2024 16:42:32 +0200 Subject: [PATCH 01/15] Add legal information operation site --- nix/dev-vm.nix | 2 +- .../20240908104458_extra_page_table.sql | 55 +++++++ ...706_adjust_website_overview_extra_page.sql | 142 ++++++++++++++++++ .../src/lib/components/WebsiteEditor.svelte | 3 + .../src/lib/templates/blog/BlogArticle.svelte | 2 +- .../src/lib/templates/common/Footer.svelte | 7 +- .../src/lib/templates/docs/DocsArticle.svelte | 2 +- web-app/src/lib/utils.ts | 2 +- .../website/[websiteId]/+page.svelte | 3 +- .../legal-information/+page.server.ts | 80 ++++++++++ .../legal-information/+page.svelte | 121 +++++++++++++++ .../[websiteId]/publish/+page.server.ts | 62 ++++++++ .../website/[websiteId]/publish/+page.svelte | 2 +- web-app/tests/collaborator.spec.ts | 44 ++++++ web-app/tests/website.spec.ts | 23 +++ 15 files changed, 542 insertions(+), 8 deletions(-) create mode 100644 rest-api/db/migrations/20240908104458_extra_page_table.sql create mode 100644 rest-api/db/migrations/20240908115706_adjust_website_overview_extra_page.sql create mode 100644 web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.server.ts create mode 100644 web-app/src/routes/(authenticated)/website/[websiteId]/legal-information/+page.svelte diff --git a/nix/dev-vm.nix b/nix/dev-vm.nix index 773664a..48bacd4 100644 --- a/nix/dev-vm.nix +++ b/nix/dev-vm.nix @@ -68,7 +68,7 @@ ]; locations = { "/" = { - root = "/var/www/archtika-websites"; + root = "/var/www/archtika-websites/"; index = "index.html"; tryFiles = "$uri $uri/ $uri.html $uri/index.html index.html =404"; extraConfig = '' diff --git a/rest-api/db/migrations/20240908104458_extra_page_table.sql b/rest-api/db/migrations/20240908104458_extra_page_table.sql new file mode 100644 index 0000000..287562f --- /dev/null +++ b/rest-api/db/migrations/20240908104458_extra_page_table.sql @@ -0,0 +1,55 @@ +-- migrate:up +CREATE TABLE internal.legal_information ( + website_id UUID PRIMARY KEY REFERENCES internal.website (id) ON DELETE CASCADE, + main_content TEXT NOT NULL CHECK (TRIM(main_content) != ''), + last_modified_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(), + last_modified_by UUID REFERENCES internal.user (id) ON DELETE SET NULL +); + +CREATE VIEW api.legal_information WITH ( security_invoker = ON +) AS +SELECT + website_id, + main_content, + last_modified_at, + last_modified_by +FROM + internal.legal_information; + +GRANT SELECT, INSERT, UPDATE, DELETE ON internal.legal_information TO authenticated_user; + +GRANT SELECT, INSERT, UPDATE, DELETE ON api.legal_information TO authenticated_user; + +ALTER TABLE internal.legal_information ENABLE ROW LEVEL SECURITY; + +CREATE POLICY view_legal_information ON internal.legal_information + FOR SELECT + USING (internal.user_has_website_access (website_id, 10)); + +CREATE POLICY update_legal_information ON internal.legal_information + FOR UPDATE + USING (internal.user_has_website_access (website_id, 30)); + +CREATE POLICY delete_legal_information ON internal.legal_information + FOR DELETE + USING (internal.user_has_website_access (website_id, 30)); + +CREATE POLICY insert_legal_information ON internal.legal_information + FOR INSERT + WITH CHECK (internal.user_has_website_access (website_id, 30)); + +-- migrate:down +DROP POLICY insert_legal_information ON internal.legal_information; + +DROP POLICY delete_legal_information ON internal.legal_information; + +DROP POLICY update_legal_information ON internal.legal_information; + +DROP POLICY view_legal_information ON internal.legal_information; + +ALTER TABLE internal.legal_information DISABLE ROW LEVEL SECURITY; + +DROP VIEW api.legal_information; + +DROP TABLE internal.legal_information; + diff --git a/rest-api/db/migrations/20240908115706_adjust_website_overview_extra_page.sql b/rest-api/db/migrations/20240908115706_adjust_website_overview_extra_page.sql new file mode 100644 index 0000000..127813c --- /dev/null +++ b/rest-api/db/migrations/20240908115706_adjust_website_overview_extra_page.sql @@ -0,0 +1,142 @@ +-- migrate:up +CREATE OR REPLACE VIEW api.website_overview WITH ( security_invoker = ON +) AS +SELECT + w.id, + w.user_id, + w.content_type, + w.title, + s.accent_color_light_theme, + s.accent_color_dark_theme, + s.favicon_image, + h.logo_type, + h.logo_text, + h.logo_image, + ho.main_content, + f.additional_text, + ( + SELECT + JSON_AGG( + JSON_BUILD_OBJECT( + 'id', a.id, 'title', a.title, 'meta_description', a.meta_description, 'meta_author', a.meta_author, 'cover_image', a.cover_image, 'publication_date', a.publication_date, 'main_content', a.main_content, 'created_at', a.created_at, 'last_modified_at', a.last_modified_at +) +) + FROM + internal.article a + WHERE + a.website_id = w.id +) AS articles, + CASE WHEN w.content_type = 'Docs' THEN + ( + SELECT + JSON_OBJECT_AGG( + COALESCE( + category_name, 'Uncategorized' +), articles +) + FROM ( + SELECT + dc.category_name, + dc.category_weight AS category_weight, + JSON_AGG( + JSON_BUILD_OBJECT( + 'id', a.id, 'title', a.title, 'meta_description', a.meta_description, 'meta_author', a.meta_author, 'cover_image', a.cover_image, 'publication_date', a.publication_date, 'main_content', a.main_content, 'created_at', a.created_at, 'last_modified_at', a.last_modified_at +) ORDER BY a.article_weight DESC NULLS LAST +) AS articles + FROM + internal.article a + LEFT JOIN internal.docs_category dc ON a.category = dc.id + WHERE + a.website_id = w.id + GROUP BY + dc.id, + dc.category_name, + dc.category_weight + ORDER BY + category_weight DESC NULLS LAST +) AS categorized_articles) +ELSE + NULL + END AS categorized_articles, + li.main_content legal_information_main_content +FROM + internal.website w + JOIN internal.settings s ON w.id = s.website_id + JOIN internal.header h ON w.id = h.website_id + JOIN internal.home ho ON w.id = ho.website_id + JOIN internal.footer f ON w.id = f.website_id + LEFT JOIN internal.legal_information li ON w.id = li.website_id; + +GRANT SELECT ON api.website_overview TO authenticated_user; + +-- migrate:down +DROP VIEW api.website_overview; + +CREATE VIEW api.website_overview WITH ( security_invoker = ON +) AS +SELECT + w.id, + w.user_id, + w.content_type, + w.title, + s.accent_color_light_theme, + s.accent_color_dark_theme, + s.favicon_image, + h.logo_type, + h.logo_text, + h.logo_image, + ho.main_content, + f.additional_text, + ( + SELECT + JSON_AGG( + JSON_BUILD_OBJECT( + 'id', a.id, 'title', a.title, 'meta_description', a.meta_description, 'meta_author', a.meta_author, 'cover_image', a.cover_image, 'publication_date', a.publication_date, 'main_content', a.main_content, 'created_at', a.created_at, 'last_modified_at', a.last_modified_at +) +) + FROM + internal.article a + WHERE + a.website_id = w.id +) AS articles, + CASE WHEN w.content_type = 'Docs' THEN + ( + SELECT + JSON_OBJECT_AGG( + COALESCE( + category_name, 'Uncategorized' +), articles +) + FROM ( + SELECT + dc.category_name, + dc.category_weight AS category_weight, + JSON_AGG( + JSON_BUILD_OBJECT( + 'id', a.id, 'title', a.title, 'meta_description', a.meta_description, 'meta_author', a.meta_author, 'cover_image', a.cover_image, 'publication_date', a.publication_date, 'main_content', a.main_content, 'created_at', a.created_at, 'last_modified_at', a.last_modified_at +) ORDER BY a.article_weight DESC NULLS LAST +) AS articles + FROM + internal.article a + LEFT JOIN internal.docs_category dc ON a.category = dc.id + WHERE + a.website_id = w.id + GROUP BY + dc.id, + dc.category_name, + dc.category_weight + ORDER BY + category_weight DESC NULLS LAST +) AS categorized_articles) +ELSE + NULL + END AS categorized_articles +FROM + internal.website w + JOIN internal.settings s ON w.id = s.website_id + JOIN internal.header h ON w.id = h.website_id + JOIN internal.home ho ON w.id = ho.website_id + JOIN internal.footer f ON w.id = f.website_id; + +GRANT SELECT ON api.website_overview TO authenticated_user; + diff --git a/web-app/src/lib/components/WebsiteEditor.svelte b/web-app/src/lib/components/WebsiteEditor.svelte index 9715784..6280bd6 100644 --- a/web-app/src/lib/components/WebsiteEditor.svelte +++ b/web-app/src/lib/components/WebsiteEditor.svelte @@ -49,6 +49,9 @@
+ Static websites that do not collect user data and do not use cookies generally have minimal + legal obligations regarding privacy policies, imprints, etc. However, it may still be a good + idea to include, for example: +
+ +Always consult local laws and regulations for specific requirements in your jurisdiction.
+ +
+ To include a link to your legal information in the footer, you can write !!legal.
+
{publicationDate}
-{article.publication_date}
+{article.publication_date}
@@ -62,4 +61,4 @@+ {data.resultChangeLogCount} + results +
+ +| User | +Resource | +Operation | +Date and time | +Changes | +
|---|---|---|---|---|
| {user.username} | +{table_name} | +{operation} | +
+ |
+
+ Log changes+{table_name} — {operation} + + +{@html htmlDiff(oldValue, newValue)}
+ |
+
{article.publication_date}
+ {#if article.publication_date} +{article.publication_date}
+ {/if}{article.publication_date}
+ {#if article.publication_date} +{article.publication_date}
+ {/if}
{article.title}
From 79d1c9f5c7e216bea386dbe16a68b9bba9299a51 Mon Sep 17 00:00:00 2001
From: thiloho <123883702+thiloho@users.noreply.github.com>
Date: Fri, 13 Sep 2024 19:30:56 +0200
Subject: [PATCH 11/15] Use a more robust slugify function
---
web-app/package-lock.json | 7 ---
web-app/package.json | 1 -
.../src/lib/templates/blog/BlogIndex.svelte | 5 +-
web-app/src/lib/templates/common/Nav.svelte | 5 +-
web-app/src/lib/utils.ts | 54 +++++++------------
.../[websiteId]/publish/+page.server.ts | 6 +--
6 files changed, 25 insertions(+), 53 deletions(-)
diff --git a/web-app/package-lock.json b/web-app/package-lock.json
index 810a9fe..97b6ede 100644
--- a/web-app/package-lock.json
+++ b/web-app/package-lock.json
@@ -9,7 +9,6 @@
"version": "0.0.1",
"dependencies": {
"fast-diff": "1.3.0",
- "github-slugger": "2.0.0",
"highlight.js": "11.10.0",
"isomorphic-dompurify": "2.14.0",
"marked": "14.0.0",
@@ -2734,12 +2733,6 @@
"node": "6.* || 8.* || >= 10.*"
}
},
- "node_modules/github-slugger": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
- "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==",
- "license": "ISC"
- },
"node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
diff --git a/web-app/package.json b/web-app/package.json
index 2a62160..44c0c95 100644
--- a/web-app/package.json
+++ b/web-app/package.json
@@ -39,7 +39,6 @@
"type": "module",
"dependencies": {
"fast-diff": "1.3.0",
- "github-slugger": "2.0.0",
"highlight.js": "11.10.0",
"isomorphic-dompurify": "2.14.0",
"marked": "14.0.0",
diff --git a/web-app/src/lib/templates/blog/BlogIndex.svelte b/web-app/src/lib/templates/blog/BlogIndex.svelte
index a0033b2..604cb8e 100644
--- a/web-app/src/lib/templates/blog/BlogIndex.svelte
+++ b/web-app/src/lib/templates/blog/BlogIndex.svelte
@@ -2,7 +2,7 @@
import Head from "../common/Head.svelte";
import Nav from "../common/Nav.svelte";
import Footer from "../common/Footer.svelte";
- import { md, type WebsiteOverview } from "../../utils";
+ import { md, slugify, type WebsiteOverview } from "$lib/utils";
const {
websiteOverview,
@@ -42,14 +42,13 @@
{article.publication_date}
{#each websiteOverview.article as article}
- {@const articleFileName = article.title.toLowerCase().split(" ").join("-")}