mirror of
https://github.com/thiloho/archtika.git
synced 2025-11-22 02:41:35 +01:00
Rename Postgres tables for better recognition and add additional routes in web app
This commit is contained in:
@@ -20,57 +20,57 @@ CREATE TABLE internal.user (
|
||||
role NAME NOT NULL DEFAULT 'authenticated_user'
|
||||
);
|
||||
|
||||
CREATE TABLE internal.cms_content (
|
||||
CREATE TABLE internal.website (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
owner_id UUID REFERENCES internal.user(id) ON DELETE CASCADE NOT NULL DEFAULT (current_setting('request.jwt.claims', true)::JSON->>'user_id')::UUID,
|
||||
content_type VARCHAR(10) CHECK (content_type IN ('Blog', 'Docs')) NOT NULL,
|
||||
project_name VARCHAR(50) NOT NULL,
|
||||
title VARCHAR(50) NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
|
||||
last_modified_at TIMESTAMPTZ,
|
||||
last_modified_by UUID REFERENCES internal.user(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE internal.cms_media (
|
||||
CREATE TABLE internal.media (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
content_id UUID REFERENCES internal.cms_content(id) ON DELETE CASCADE NOT NULL,
|
||||
website_id UUID REFERENCES internal.website(id) ON DELETE CASCADE NOT NULL,
|
||||
user_id UUID REFERENCES internal.user(id) ON DELETE CASCADE NOT NULL DEFAULT (current_setting('request.jwt.claims', true)::JSON->>'user_id')::UUID,
|
||||
original_name TEXT NOT NULL,
|
||||
file_system_path TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP()
|
||||
);
|
||||
|
||||
CREATE TABLE internal.cms_settings (
|
||||
content_id UUID PRIMARY KEY REFERENCES internal.cms_content(id) ON DELETE CASCADE,
|
||||
CREATE TABLE internal.settings (
|
||||
website_id UUID PRIMARY KEY REFERENCES internal.website(id) ON DELETE CASCADE,
|
||||
accent_color_light_theme CHAR(7) CHECK (accent_color_light_theme ~ '^#[a-fA-F0-9]{6}$') NOT NULL DEFAULT '#a5d8ff',
|
||||
accent_color_dark_theme CHAR(7) CHECK (accent_color_dark_theme ~ '^#[a-fA-F0-9]{6}$') NOT NULL DEFAULT '#114678',
|
||||
favicon_image UUID REFERENCES internal.cms_media(id) ON DELETE SET NULL,
|
||||
favicon_image UUID REFERENCES internal.media(id) ON DELETE SET NULL,
|
||||
last_modified_at TIMESTAMPTZ,
|
||||
last_modified_by UUID REFERENCES internal.user(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE internal.cms_header (
|
||||
content_id UUID PRIMARY KEY REFERENCES internal.cms_content(id) ON DELETE CASCADE,
|
||||
CREATE TABLE internal.header (
|
||||
website_id UUID PRIMARY KEY REFERENCES internal.website(id) ON DELETE CASCADE,
|
||||
logo_type TEXT CHECK (logo_type IN ('text', 'image')) NOT NULL DEFAULT 'text',
|
||||
logo_text VARCHAR(255),
|
||||
logo_image UUID REFERENCES internal.cms_media(id) ON DELETE SET NULL,
|
||||
logo_image UUID REFERENCES internal.media(id) ON DELETE SET NULL,
|
||||
last_modified_at TIMESTAMPTZ,
|
||||
last_modified_by UUID REFERENCES internal.user(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE internal.cms_home (
|
||||
content_id UUID PRIMARY KEY REFERENCES internal.cms_content(id) ON DELETE CASCADE,
|
||||
CREATE TABLE internal.home (
|
||||
website_id UUID PRIMARY KEY REFERENCES internal.website(id) ON DELETE CASCADE,
|
||||
main_content TEXT,
|
||||
last_modified_at TIMESTAMPTZ,
|
||||
last_modified_by UUID REFERENCES internal.user(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE internal.cms_article (
|
||||
CREATE TABLE internal.article (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
content_id UUID REFERENCES internal.cms_content(id) ON DELETE CASCADE NOT NULL,
|
||||
website_id UUID REFERENCES internal.website(id) ON DELETE CASCADE NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
meta_description VARCHAR(500),
|
||||
meta_author VARCHAR(255),
|
||||
cover_image UUID REFERENCES internal.cms_media(id) ON DELETE SET NULL,
|
||||
cover_image UUID REFERENCES internal.media(id) ON DELETE SET NULL,
|
||||
publication_date DATE NOT NULL DEFAULT CURRENT_DATE,
|
||||
main_content TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
|
||||
@@ -78,43 +78,43 @@ CREATE TABLE internal.cms_article (
|
||||
last_modified_by UUID REFERENCES internal.user(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE internal.cms_footer (
|
||||
content_id UUID PRIMARY KEY REFERENCES internal.cms_content(id) ON DELETE CASCADE,
|
||||
CREATE TABLE internal.footer (
|
||||
website_id UUID PRIMARY KEY REFERENCES internal.website(id) ON DELETE CASCADE,
|
||||
additional_text VARCHAR(255),
|
||||
last_modified_at TIMESTAMPTZ,
|
||||
last_modified_by UUID REFERENCES internal.user(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE internal.cms_collab (
|
||||
content_id UUID REFERENCES internal.cms_content(id) ON DELETE CASCADE,
|
||||
CREATE TABLE internal.collab (
|
||||
website_id UUID REFERENCES internal.website(id) ON DELETE CASCADE,
|
||||
user_id UUID REFERENCES internal.user(id) ON DELETE CASCADE,
|
||||
permission_level INTEGER CHECK (permission_level IN (10, 20, 30)) NOT NULL DEFAULT 10,
|
||||
added_at TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
|
||||
last_modified_at TIMESTAMPTZ,
|
||||
last_modified_by UUID REFERENCES internal.user(id) ON DELETE SET NULL,
|
||||
PRIMARY KEY (content_id, user_id)
|
||||
PRIMARY KEY (website_id, user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE internal.cms_change_log (
|
||||
content_id UUID REFERENCES internal.cms_content(id) ON DELETE CASCADE,
|
||||
CREATE TABLE internal.change_log (
|
||||
website_id UUID REFERENCES internal.website(id) ON DELETE CASCADE,
|
||||
user_id UUID REFERENCES internal.user(id) ON DELETE CASCADE DEFAULT (current_setting('request.jwt.claims', true)::JSON->>'user_id')::UUID,
|
||||
change_summary VARCHAR(255) NOT NULL,
|
||||
previous_value JSONB,
|
||||
new_value JSONB,
|
||||
timestamp TIMESTAMPTZ NOT NULL DEFAULT CLOCK_TIMESTAMP(),
|
||||
PRIMARY KEY (content_id, user_id, timestamp)
|
||||
PRIMARY KEY (website_id, user_id, timestamp)
|
||||
);
|
||||
|
||||
-- migrate:down
|
||||
DROP TABLE internal.cms_change_log;
|
||||
DROP TABLE internal.cms_collab;
|
||||
DROP TABLE internal.cms_footer;
|
||||
DROP TABLE internal.cms_article;
|
||||
DROP TABLE internal.cms_home;
|
||||
DROP TABLE internal.cms_header;
|
||||
DROP TABLE internal.cms_settings;
|
||||
DROP TABLE internal.cms_media;
|
||||
DROP TABLE internal.cms_content;
|
||||
DROP TABLE internal.change_log;
|
||||
DROP TABLE internal.collab;
|
||||
DROP TABLE internal.footer;
|
||||
DROP TABLE internal.article;
|
||||
DROP TABLE internal.home;
|
||||
DROP TABLE internal.header;
|
||||
DROP TABLE internal.settings;
|
||||
DROP TABLE internal.media;
|
||||
DROP TABLE internal.website;
|
||||
DROP SCHEMA api;
|
||||
|
||||
DROP TABLE internal.user;
|
||||
|
||||
@@ -5,136 +5,136 @@ AS
|
||||
SELECT id, username
|
||||
FROM internal.user;
|
||||
|
||||
CREATE VIEW api.cms_content
|
||||
CREATE VIEW api.website
|
||||
WITH (security_invoker = on)
|
||||
AS
|
||||
SELECT *
|
||||
FROM internal.cms_content;
|
||||
FROM internal.website;
|
||||
|
||||
CREATE VIEW api.cms_media
|
||||
CREATE VIEW api.media
|
||||
WITH (security_invoker = on)
|
||||
AS
|
||||
SELECT *
|
||||
FROM internal.cms_media;
|
||||
FROM internal.media;
|
||||
|
||||
CREATE VIEW api.cms_settings
|
||||
CREATE VIEW api.settings
|
||||
WITH (security_invoker = on)
|
||||
AS
|
||||
SELECT *
|
||||
FROM internal.cms_settings;
|
||||
FROM internal.settings;
|
||||
|
||||
CREATE VIEW api.cms_header
|
||||
CREATE VIEW api.header
|
||||
WITH (security_invoker = on)
|
||||
AS
|
||||
SELECT *
|
||||
FROM internal.cms_header;
|
||||
FROM internal.header;
|
||||
|
||||
CREATE view api.cms_home
|
||||
CREATE view api.home
|
||||
WITH (security_invoker = on)
|
||||
AS
|
||||
SELECT *
|
||||
FROM internal.cms_home;
|
||||
FROM internal.home;
|
||||
|
||||
CREATE VIEW api.cms_article
|
||||
CREATE VIEW api.article
|
||||
WITH (security_invoker = on)
|
||||
AS
|
||||
SELECT *
|
||||
FROM internal.cms_article;
|
||||
FROM internal.article;
|
||||
|
||||
CREATE VIEW api.cms_footer
|
||||
CREATE VIEW api.footer
|
||||
WITH (security_invoker = on)
|
||||
AS
|
||||
SELECT *
|
||||
FROM internal.cms_footer;
|
||||
FROM internal.footer;
|
||||
|
||||
CREATE VIEW api.cms_collab
|
||||
CREATE VIEW api.collab
|
||||
WITH (security_invoker = on)
|
||||
AS
|
||||
SELECT *
|
||||
FROM internal.cms_collab;
|
||||
FROM internal.collab;
|
||||
|
||||
CREATE VIEW api.cms_change_log
|
||||
CREATE VIEW api.change_log
|
||||
WITH (security_invoker = on)
|
||||
AS
|
||||
SELECT *
|
||||
FROM internal.cms_change_log;
|
||||
FROM internal.change_log;
|
||||
|
||||
CREATE FUNCTION
|
||||
api.create_project(content_type VARCHAR(10), project_name VARCHAR(50), OUT content_id UUID) AS $$
|
||||
api.create_website(content_type VARCHAR(10), title VARCHAR(50), OUT website_id UUID) AS $$
|
||||
DECLARE
|
||||
_content_id UUID;
|
||||
_website_id UUID;
|
||||
BEGIN
|
||||
INSERT INTO internal.cms_content (content_type, project_name)
|
||||
VALUES (create_project.content_type, create_project.project_name)
|
||||
RETURNING id INTO _content_id;
|
||||
INSERT INTO internal.website (content_type, title)
|
||||
VALUES (create_website.content_type, create_website.title)
|
||||
RETURNING id INTO _website_id;
|
||||
|
||||
INSERT INTO internal.cms_settings (content_id)
|
||||
VALUES (_content_id);
|
||||
INSERT INTO internal.settings (website_id)
|
||||
VALUES (_website_id);
|
||||
|
||||
INSERT INTO internal.cms_header (content_id, logo_text)
|
||||
VALUES (_content_id, 'archtika ' || create_project.content_type);
|
||||
INSERT INTO internal.header (website_id, logo_text)
|
||||
VALUES (_website_id, 'archtika ' || create_website.content_type);
|
||||
|
||||
INSERT INTO internal.cms_home (content_id, main_content)
|
||||
INSERT INTO internal.home (website_id, main_content)
|
||||
VALUES
|
||||
(_content_id, '## Main content comes in here');
|
||||
(_website_id, '## Main content comes in here');
|
||||
|
||||
INSERT INTO internal.cms_article (content_id, title, meta_description, meta_author, main_content)
|
||||
INSERT INTO internal.article (website_id, title, meta_description, meta_author, main_content)
|
||||
VALUES
|
||||
(_content_id, 'First article', 'This is the first sample article', 'Author Name', '## First article'),
|
||||
(_content_id, 'Second article', 'This is the second sample article', 'Author Name', '## Second article');
|
||||
(_website_id, 'First article', 'This is the first sample article', 'Author Name', '## First article'),
|
||||
(_website_id, 'Second article', 'This is the second sample article', 'Author Name', '## Second article');
|
||||
|
||||
INSERT INTO internal.cms_footer (content_id, additional_text)
|
||||
VALUES (_content_id, 'This website was created with archtika');
|
||||
INSERT INTO internal.footer (website_id, additional_text)
|
||||
VALUES (_website_id, 'This website was created with archtika');
|
||||
|
||||
content_id := _content_id;
|
||||
website_id := _website_id;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
GRANT EXECUTE ON FUNCTION api.create_project(VARCHAR(10), VARCHAR(50)) TO authenticated_user;
|
||||
GRANT EXECUTE ON FUNCTION api.create_website(VARCHAR(10), VARCHAR(50)) TO authenticated_user;
|
||||
|
||||
|
||||
-- Security invoker only works on views if the user has access to the underlying table
|
||||
GRANT SELECT ON internal.user TO authenticated_user;
|
||||
GRANT SELECT ON api.user TO authenticated_user;
|
||||
GRANT SELECT, UPDATE, DELETE ON internal.cms_content TO authenticated_user;
|
||||
GRANT SELECT, UPDATE, DELETE ON api.cms_content TO authenticated_user;
|
||||
GRANT SELECT, INSERT ON internal.cms_media TO authenticated_user;
|
||||
GRANT SELECT, INSERT ON api.cms_media TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON internal.cms_settings TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON api.cms_settings TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON internal.cms_header TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON api.cms_header TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON internal.cms_home TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON api.cms_home TO authenticated_user;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON internal.cms_article TO authenticated_user;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON api.cms_article TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON internal.cms_footer TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON api.cms_footer TO authenticated_user;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON internal.cms_collab TO authenticated_user;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON api.cms_collab TO authenticated_user;
|
||||
GRANT SELECT ON internal.cms_change_log TO authenticated_user;
|
||||
GRANT SELECT ON api.cms_change_log TO authenticated_user;
|
||||
GRANT SELECT, UPDATE, DELETE ON internal.website TO authenticated_user;
|
||||
GRANT SELECT, UPDATE, DELETE ON api.website TO authenticated_user;
|
||||
GRANT SELECT, INSERT ON internal.media TO authenticated_user;
|
||||
GRANT SELECT, INSERT ON api.media TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON internal.settings TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON api.settings TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON internal.header TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON api.header TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON internal.home TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON api.home TO authenticated_user;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON internal.article TO authenticated_user;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON api.article TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON internal.footer TO authenticated_user;
|
||||
GRANT SELECT, UPDATE ON api.footer TO authenticated_user;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON internal.collab TO authenticated_user;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON api.collab TO authenticated_user;
|
||||
GRANT SELECT ON internal.change_log TO authenticated_user;
|
||||
GRANT SELECT ON api.change_log TO authenticated_user;
|
||||
|
||||
-- migrate:down
|
||||
REVOKE SELECT ON internal.user FROM authenticated_user;
|
||||
REVOKE SELECT, UPDATE, DELETE ON internal.cms_content FROM authenticated_user;
|
||||
REVOKE SELECT, INSERT ON internal.cms_media FROM authenticated_user;
|
||||
REVOKE SELECT, UPDATE ON internal.cms_settings FROM authenticated_user;
|
||||
REVOKE SELECT, UPDATE ON internal.cms_header FROM authenticated_user;
|
||||
REVOKE SELECT, INSERT, UPDATE, DELETE ON internal.cms_article FROM authenticated_user;
|
||||
REVOKE SELECT, UPDATE ON internal.cms_footer FROM authenticated_user;
|
||||
REVOKE SELECT, INSERT, UPDATE, DELETE ON internal.cms_collab FROM authenticated_user;
|
||||
REVOKE SELECT ON internal.cms_change_log FROM authenticated_user;
|
||||
REVOKE SELECT, UPDATE, DELETE ON internal.website FROM authenticated_user;
|
||||
REVOKE SELECT, INSERT ON internal.media FROM authenticated_user;
|
||||
REVOKE SELECT, UPDATE ON internal.settings FROM authenticated_user;
|
||||
REVOKE SELECT, UPDATE ON internal.header FROM authenticated_user;
|
||||
REVOKE SELECT, INSERT, UPDATE, DELETE ON internal.article FROM authenticated_user;
|
||||
REVOKE SELECT, UPDATE ON internal.footer FROM authenticated_user;
|
||||
REVOKE SELECT, INSERT, UPDATE, DELETE ON internal.collab FROM authenticated_user;
|
||||
REVOKE SELECT ON internal.change_log FROM authenticated_user;
|
||||
|
||||
DROP FUNCTION api.create_project(VARCHAR(10), VARCHAR(50));
|
||||
DROP FUNCTION api.create_website(VARCHAR(10), VARCHAR(50));
|
||||
|
||||
DROP VIEW api.cms_change_log;
|
||||
DROP VIEW api.cms_collab;
|
||||
DROP VIEW api.cms_footer;
|
||||
DROP VIEW api.cms_home;
|
||||
DROP VIEW api.cms_article;
|
||||
DROP VIEW api.cms_header;
|
||||
DROP VIEW api.cms_settings;
|
||||
DROP VIEW api.cms_media;
|
||||
DROP VIEW api.cms_content;
|
||||
DROP VIEW api.change_log;
|
||||
DROP VIEW api.collab;
|
||||
DROP VIEW api.footer;
|
||||
DROP VIEW api.home;
|
||||
DROP VIEW api.article;
|
||||
DROP VIEW api.header;
|
||||
DROP VIEW api.settings;
|
||||
DROP VIEW api.media;
|
||||
DROP VIEW api.website;
|
||||
DROP VIEW api.user;
|
||||
@@ -1,208 +1,208 @@
|
||||
-- migrate:up
|
||||
ALTER TABLE internal.user ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_content ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_media ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_settings ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_header ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_home ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_article ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_footer ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.website ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.media ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.settings ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.header ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.home ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.article ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.footer ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY view_own_user ON internal.user
|
||||
FOR SELECT
|
||||
USING (id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID);
|
||||
|
||||
CREATE POLICY view_own_projects ON internal.cms_content
|
||||
CREATE POLICY view_own_websites ON internal.website
|
||||
FOR SELECT
|
||||
USING (owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID);
|
||||
|
||||
CREATE POLICY update_own_project ON internal.cms_content
|
||||
CREATE POLICY update_own_website ON internal.website
|
||||
FOR UPDATE
|
||||
USING (owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID);
|
||||
|
||||
CREATE POLICY delete_own_project ON internal.cms_content
|
||||
CREATE POLICY delete_own_website ON internal.website
|
||||
FOR DELETE
|
||||
USING (owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID);
|
||||
|
||||
|
||||
CREATE POLICY view_own_media ON internal.cms_media
|
||||
CREATE POLICY view_own_media ON internal.media
|
||||
FOR SELECT
|
||||
USING (user_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID);
|
||||
|
||||
CREATE POLICY insert_own_media ON internal.cms_media
|
||||
CREATE POLICY insert_own_media ON internal.media
|
||||
FOR INSERT
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.cms_content
|
||||
WHERE internal.cms_content.id = internal.cms_media.content_id
|
||||
AND internal.cms_content.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
FROM internal.website
|
||||
WHERE internal.website.id = internal.media.website_id
|
||||
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
CREATE POLICY view_own_settings ON internal.cms_settings
|
||||
CREATE POLICY view_own_settings ON internal.settings
|
||||
FOR SELECT
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.cms_content
|
||||
WHERE internal.cms_content.id = internal.cms_settings.content_id
|
||||
AND internal.cms_content.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
FROM internal.website
|
||||
WHERE internal.website.id = internal.settings.website_id
|
||||
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY update_own_settings ON internal.cms_settings
|
||||
CREATE POLICY update_own_settings ON internal.settings
|
||||
FOR UPDATE
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.cms_content
|
||||
WHERE internal.cms_content.id = internal.cms_settings.content_id
|
||||
AND internal.cms_content.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
FROM internal.website
|
||||
WHERE internal.website.id = internal.settings.website_id
|
||||
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
CREATE POLICY view_own_header ON internal.cms_header
|
||||
CREATE POLICY view_own_header ON internal.header
|
||||
FOR SELECT
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.cms_content
|
||||
WHERE internal.cms_content.id = internal.cms_header.content_id
|
||||
AND internal.cms_content.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
FROM internal.website
|
||||
WHERE internal.website.id = internal.header.website_id
|
||||
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY update_own_header ON internal.cms_header
|
||||
CREATE POLICY update_own_header ON internal.header
|
||||
FOR UPDATE
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.cms_content
|
||||
WHERE internal.cms_content.id = internal.cms_header.content_id
|
||||
AND internal.cms_content.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
FROM internal.website
|
||||
WHERE internal.website.id = internal.header.website_id
|
||||
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
CREATE POLICY view_own_home ON internal.cms_home
|
||||
CREATE POLICY view_own_home ON internal.home
|
||||
FOR SELECT
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.cms_content
|
||||
WHERE internal.cms_content.id = internal.cms_home.content_id
|
||||
AND internal.cms_content.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
FROM internal.website
|
||||
WHERE internal.website.id = internal.home.website_id
|
||||
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY update_own_home ON internal.cms_home
|
||||
CREATE POLICY update_own_home ON internal.home
|
||||
FOR UPDATE
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.cms_content
|
||||
WHERE internal.cms_content.id = internal.cms_home.content_id
|
||||
AND internal.cms_content.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
FROM internal.website
|
||||
WHERE internal.website.id = internal.home.website_id
|
||||
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
CREATE POLICY view_own_articles ON internal.cms_article
|
||||
CREATE POLICY view_own_articles ON internal.article
|
||||
FOR SELECT
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.cms_content
|
||||
WHERE internal.cms_content.id = internal.cms_article.content_id
|
||||
AND internal.cms_content.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
FROM internal.website
|
||||
WHERE internal.website.id = internal.article.website_id
|
||||
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY update_own_article ON internal.cms_article
|
||||
CREATE POLICY update_own_article ON internal.article
|
||||
FOR UPDATE
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.cms_content
|
||||
WHERE internal.cms_content.id = internal.cms_article.content_id
|
||||
AND internal.cms_content.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
FROM internal.website
|
||||
WHERE internal.website.id = internal.article.website_id
|
||||
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY delete_own_article ON internal.cms_article
|
||||
CREATE POLICY delete_own_article ON internal.article
|
||||
FOR DELETE
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.cms_content
|
||||
WHERE internal.cms_content.id = internal.cms_article.content_id
|
||||
AND internal.cms_content.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
FROM internal.website
|
||||
WHERE internal.website.id = internal.article.website_id
|
||||
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY insert_own_article ON internal.cms_article
|
||||
CREATE POLICY insert_own_article ON internal.article
|
||||
FOR INSERT
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.cms_content
|
||||
WHERE internal.cms_content.id = internal.cms_article.content_id
|
||||
AND internal.cms_content.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
FROM internal.website
|
||||
WHERE internal.website.id = internal.article.website_id
|
||||
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
CREATE POLICY view_own_footer ON internal.cms_footer
|
||||
CREATE POLICY view_own_footer ON internal.footer
|
||||
FOR SELECT
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.cms_content
|
||||
WHERE internal.cms_content.id = internal.cms_footer.content_id
|
||||
AND internal.cms_content.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
FROM internal.website
|
||||
WHERE internal.website.id = internal.footer.website_id
|
||||
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY update_own_footer ON internal.cms_footer
|
||||
CREATE POLICY update_own_footer ON internal.footer
|
||||
FOR UPDATE
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM internal.cms_content
|
||||
WHERE internal.cms_content.id = internal.cms_footer.content_id
|
||||
AND internal.cms_content.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
FROM internal.website
|
||||
WHERE internal.website.id = internal.footer.website_id
|
||||
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
-- migrate:down
|
||||
DROP POLICY view_own_user ON internal.user;
|
||||
DROP POLICY view_own_projects ON internal.cms_content;
|
||||
DROP POLICY delete_own_project ON internal.cms_content;
|
||||
DROP POLICY update_own_project ON internal.cms_content;
|
||||
DROP POLICY view_own_media ON internal.cms_media;
|
||||
DROP POLICY insert_own_media ON internal.cms_media;
|
||||
DROP POLICY view_own_settings ON internal.cms_settings;
|
||||
DROP POLICY update_own_settings ON internal.cms_settings;
|
||||
DROP POLICY view_own_header ON internal.cms_header;
|
||||
DROP POLICY update_own_header ON internal.cms_header;
|
||||
DROP POLICY view_own_home ON internal.cms_home;
|
||||
DROP POLICY update_own_home ON internal.cms_home;
|
||||
DROP POLICY view_own_articles ON internal.cms_article;
|
||||
DROP POLICY update_own_article ON internal.cms_article;
|
||||
DROP POLICY delete_own_article ON internal.cms_article;
|
||||
DROP POLICY insert_own_article ON internal.cms_article;
|
||||
DROP POLICY view_own_footer ON internal.cms_footer;
|
||||
DROP POLICY update_own_footer ON internal.cms_footer;
|
||||
DROP POLICY view_own_websites ON internal.website;
|
||||
DROP POLICY delete_own_website ON internal.website;
|
||||
DROP POLICY update_own_website ON internal.website;
|
||||
DROP POLICY view_own_media ON internal.media;
|
||||
DROP POLICY insert_own_media ON internal.media;
|
||||
DROP POLICY view_own_settings ON internal.settings;
|
||||
DROP POLICY update_own_settings ON internal.settings;
|
||||
DROP POLICY view_own_header ON internal.header;
|
||||
DROP POLICY update_own_header ON internal.header;
|
||||
DROP POLICY view_own_home ON internal.home;
|
||||
DROP POLICY update_own_home ON internal.home;
|
||||
DROP POLICY view_own_articles ON internal.article;
|
||||
DROP POLICY update_own_article ON internal.article;
|
||||
DROP POLICY delete_own_article ON internal.article;
|
||||
DROP POLICY insert_own_article ON internal.article;
|
||||
DROP POLICY view_own_footer ON internal.footer;
|
||||
DROP POLICY update_own_footer ON internal.footer;
|
||||
|
||||
ALTER TABLE internal.user DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_content DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_media DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_settings DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_header DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_home DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_article DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.cms_footer DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.website DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.media DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.settings DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.header DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.home DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.article DISABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE internal.footer DISABLE ROW LEVEL SECURITY;
|
||||
@@ -1 +1,26 @@
|
||||
<form action=""></form>
|
||||
<script lang="ts">
|
||||
import { enhance } from "$app/forms";
|
||||
|
||||
const { form } = $props();
|
||||
</script>
|
||||
|
||||
<form method="POST" use:enhance>
|
||||
{#if form?.success}
|
||||
<p>Successfully logged in</p>
|
||||
{/if}
|
||||
|
||||
{#if form?.success === false}
|
||||
<p>{form.message}</p>
|
||||
{/if}
|
||||
|
||||
<label>
|
||||
Username
|
||||
<input type="text" name="username" required />
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input type="password" name="password" required />
|
||||
</label>
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
@@ -1 +1,26 @@
|
||||
<form action=""></form>
|
||||
<script lang="ts">
|
||||
import { enhance } from "$app/forms";
|
||||
|
||||
const { form } = $props();
|
||||
</script>
|
||||
|
||||
<form method="POST" use:enhance>
|
||||
{#if form?.success}
|
||||
<p>Successfully registered</p>
|
||||
{/if}
|
||||
|
||||
{#if form?.success === false}
|
||||
<p>{form.message}</p>
|
||||
{/if}
|
||||
|
||||
<label>
|
||||
Username
|
||||
<input type="text" name="username" minlength="3" maxlength="16" required />
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input type="password" name="password" minlength="12" maxlength="128" required />
|
||||
</label>
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
107
web-app/src/routes/(authenticated)/+page.server.ts
Normal file
107
web-app/src/routes/(authenticated)/+page.server.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
export const load = async ({ fetch, cookies, url }) => {
|
||||
const searchQuery = url.searchParams.get("website_search_query");
|
||||
const sortBy = url.searchParams.get("website_sort");
|
||||
|
||||
const params = new URLSearchParams();
|
||||
|
||||
const baseFetchUrl = "http://localhost:3000/website";
|
||||
|
||||
if (searchQuery) {
|
||||
params.append("website_title", `ilike.*${searchQuery}*`);
|
||||
}
|
||||
|
||||
switch (sortBy) {
|
||||
case "creation-time":
|
||||
params.append("order", "created_at.desc");
|
||||
break;
|
||||
case "last-modified":
|
||||
params.append("order", "last_modified_at.desc");
|
||||
break;
|
||||
case "title-a-to-z":
|
||||
params.append("order", "title.asc");
|
||||
break;
|
||||
case "title-z-to-a":
|
||||
params.append("order", "title.desc");
|
||||
break;
|
||||
}
|
||||
|
||||
const constructedFetchUrl = `${baseFetchUrl}?${params.toString()}`;
|
||||
|
||||
const websiteData = await fetch(constructedFetchUrl, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`
|
||||
}
|
||||
});
|
||||
|
||||
const websites = await websiteData.json();
|
||||
|
||||
return {
|
||||
websites
|
||||
};
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
createWebsite: async ({ request, fetch, cookies }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch("http://localhost:3000/rpc/create_website", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content_type: data.get("content-type"),
|
||||
title: data.get("title")
|
||||
})
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
return { createWebsite: { success: false, message: response.message } };
|
||||
}
|
||||
|
||||
return { createWebsite: { success: true, operation: "created" } };
|
||||
},
|
||||
updateWebsite: async ({ request, cookies, fetch }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch(`http://localhost:3000/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")
|
||||
})
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
return { updateWebsite: { success: false, message: response.message } };
|
||||
}
|
||||
|
||||
return { updateWebsite: { success: true, operation: "updated" } };
|
||||
},
|
||||
deleteWebsite: async ({ request, cookies, fetch }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch(`http://localhost:3000/website?id=eq.${data.get("id")}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
return { deleteWebsite: { success: false, message: response.message } };
|
||||
}
|
||||
|
||||
return { deleteWebsite: { success: true, operation: "deleted" } };
|
||||
}
|
||||
};
|
||||
103
web-app/src/routes/(authenticated)/+page.svelte
Normal file
103
web-app/src/routes/(authenticated)/+page.svelte
Normal file
@@ -0,0 +1,103 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from "$app/forms";
|
||||
import DateTime from "$lib/components/DateTime.svelte";
|
||||
|
||||
const { form, data } = $props();
|
||||
</script>
|
||||
|
||||
<section>
|
||||
<h2>Create website</h2>
|
||||
|
||||
<form method="POST" action="?/createWebsite" use:enhance>
|
||||
{#if form?.createWebsite?.success}
|
||||
<p>Successfully created website</p>
|
||||
{/if}
|
||||
|
||||
{#if form?.createWebsite?.success === false}
|
||||
<p>{form.createWebsite.message}</p>
|
||||
{/if}
|
||||
|
||||
<label>
|
||||
Type
|
||||
<select name="content-type">
|
||||
<option value="Blog">Blog</option>
|
||||
<option value="Docs">Docs</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Title
|
||||
<input type="text" name="title" />
|
||||
</label>
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Your websites</h2>
|
||||
|
||||
{#if form?.deleteWebsite?.success}
|
||||
<p>Successfully deleted website</p>
|
||||
{/if}
|
||||
|
||||
{#if form?.deleteWebsite?.success === false}
|
||||
<p>{form.deleteWebsite.message}</p>
|
||||
{/if}
|
||||
|
||||
{#each data.websites as { id, content_type, title, created_at }}
|
||||
<article>
|
||||
<h3>
|
||||
<a href="/website/{id}">{title}</a>
|
||||
</h3>
|
||||
<p>
|
||||
<strong>Type:</strong>
|
||||
{content_type}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Created at:</strong>
|
||||
<DateTime date={created_at} />
|
||||
</p>
|
||||
<details>
|
||||
<summary>Update</summary>
|
||||
<form
|
||||
method="POST"
|
||||
action="?/updateWebsite"
|
||||
use:enhance={() => {
|
||||
return async ({ update }) => {
|
||||
await update({ reset: false });
|
||||
};
|
||||
}}
|
||||
>
|
||||
{#if form?.updateWebsite?.success}
|
||||
<p>Successfully updated website</p>
|
||||
{/if}
|
||||
|
||||
{#if form?.updateWebsite?.success === false}
|
||||
<p>{form.updateWebsite.message}</p>
|
||||
{/if}
|
||||
|
||||
<input type="hidden" name="id" value={id} />
|
||||
<label>
|
||||
Title
|
||||
<input type="text" name="title" value={title} />
|
||||
</label>
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Delete</summary>
|
||||
<!-- TODO: Needs to be password protected -->
|
||||
<form method="POST" action="?/deleteWebsite" use:enhance>
|
||||
<input type="hidden" name="id" value={id} />
|
||||
|
||||
<button type="submit">Delete</button>
|
||||
</form>
|
||||
</details>
|
||||
</article>
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Shared with you</h2>
|
||||
</section>
|
||||
36
web-app/src/routes/(authenticated)/account/+page.server.ts
Normal file
36
web-app/src/routes/(authenticated)/account/+page.server.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
export const load = async ({ locals }) => {
|
||||
return {
|
||||
user: locals.user
|
||||
};
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
logout: async ({ cookies }) => {
|
||||
cookies.delete("session_token", { path: "/" });
|
||||
|
||||
return { logout: { success: true } };
|
||||
},
|
||||
deleteAccount: async ({ request, fetch, cookies }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch("http://localhost:3000/rpc/delete_account", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
password: data.get("password")
|
||||
})
|
||||
});
|
||||
|
||||
const response = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
return { deleteAccount: { success: false, message: response.message } };
|
||||
}
|
||||
|
||||
cookies.delete("session_token", { path: "/" });
|
||||
return { deleteAccount: { success: true } };
|
||||
}
|
||||
};
|
||||
51
web-app/src/routes/(authenticated)/account/+page.svelte
Normal file
51
web-app/src/routes/(authenticated)/account/+page.svelte
Normal file
@@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from "$app/forms";
|
||||
|
||||
const { data, form } = $props();
|
||||
</script>
|
||||
|
||||
<section>
|
||||
<h2>Overview</h2>
|
||||
|
||||
<p>
|
||||
<strong>Username:</strong>
|
||||
{data.user.username}
|
||||
</p>
|
||||
<p>
|
||||
<strong>ID:</strong>
|
||||
{data.user.id}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Logout</h2>
|
||||
|
||||
{#if form?.logout?.success}
|
||||
<p>Successfully logged out</p>
|
||||
{/if}
|
||||
|
||||
<form method="POST" action="?/logout" use:enhance>
|
||||
<button type="submit">Logout</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Delete account</h2>
|
||||
|
||||
{#if form?.deleteAccount?.success}
|
||||
<p>Account was deleted</p>
|
||||
{/if}
|
||||
|
||||
{#if form?.deleteAccount?.success === false}
|
||||
<p>{form.deleteAccount.message}</p>
|
||||
{/if}
|
||||
|
||||
<form method="POST" action="?/deleteAccount" use:enhance>
|
||||
<label>
|
||||
Password
|
||||
<input type="password" name="password" required />
|
||||
</label>
|
||||
|
||||
<button type="submit">Delete account</button>
|
||||
</form>
|
||||
</section>
|
||||
@@ -1,5 +1,5 @@
|
||||
export const load = async ({ params, fetch, cookies }) => {
|
||||
const websiteData = await fetch(`http://localhost:3000/cms_content?id=eq.${params.websiteId}`, {
|
||||
const websiteData = await fetch(`http://localhost:3000/content?id=eq.${params.websiteId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ALLOWED_MIME_TYPES } from "$lib/utils.js";
|
||||
|
||||
export const load = async ({ params, fetch, cookies, url }) => {
|
||||
const globalSettingsData = await fetch(
|
||||
`http://localhost:3000/cms_settings?content_id=eq.${params.websiteId}&select=*,cms_media(*)`,
|
||||
`http://localhost:3000/settings?website_id=eq.${params.websiteId}&select=*,media(*)`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
@@ -17,7 +17,7 @@ export const load = async ({ params, fetch, cookies, url }) => {
|
||||
);
|
||||
|
||||
const headerData = await fetch(
|
||||
`http://localhost:3000/cms_header?content_id=eq.${params.websiteId}&select=*,cms_media(*)`,
|
||||
`http://localhost:3000/header?website_id=eq.${params.websiteId}&select=*,media(*)`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
@@ -28,7 +28,7 @@ export const load = async ({ params, fetch, cookies, url }) => {
|
||||
}
|
||||
);
|
||||
|
||||
const homeData = await fetch(`http://localhost:3000/cms_home?content_id=eq.${params.websiteId}`, {
|
||||
const homeData = await fetch(`http://localhost:3000/home?website_id=eq.${params.websiteId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -37,24 +37,21 @@ export const load = async ({ params, fetch, cookies, url }) => {
|
||||
}
|
||||
});
|
||||
|
||||
const footerData = await fetch(
|
||||
`http://localhost:3000/cms_footer?content_id=eq.${params.websiteId}`,
|
||||
{
|
||||
const footerData = await fetch(`http://localhost:3000/footer?website_id=eq.${params.websiteId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`,
|
||||
Accept: "application/vnd.pgrst.object+json"
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const searchQuery = url.searchParams.get("article_search_query");
|
||||
const sortBy = url.searchParams.get("article_sort");
|
||||
|
||||
const parameters = new URLSearchParams();
|
||||
|
||||
const baseFetchUrl = `http://localhost:3000/cms_article?content_id=eq.${params.websiteId}&select=*,cms_media(*)`;
|
||||
const baseFetchUrl = `http://localhost:3000/article?website_id=eq.${params.websiteId}&select=*,media(*)`;
|
||||
|
||||
if (searchQuery) {
|
||||
parameters.append("title", `ilike.*${searchQuery}*`);
|
||||
@@ -117,9 +114,7 @@ export const actions = {
|
||||
return favicon;
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`http://localhost:3000/cms_settings?content_id=eq.${params.websiteId}`,
|
||||
{
|
||||
const res = await fetch(`http://localhost:3000/settings?website_id=eq.${params.websiteId}`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -130,8 +125,7 @@ export const actions = {
|
||||
accent_color_dark_theme: data.get("accent-color-dark"),
|
||||
favicon_image: favicon?.content
|
||||
})
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
@@ -160,7 +154,7 @@ export const actions = {
|
||||
return logo;
|
||||
}
|
||||
|
||||
const res = await fetch(`http://localhost:3000/cms_header?content_id=eq.${params.websiteId}`, {
|
||||
const res = await fetch(`http://localhost:3000/header?website_id=eq.${params.websiteId}`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -187,7 +181,7 @@ export const actions = {
|
||||
updateHome: async ({ request, fetch, cookies, params }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch(`http://localhost:3000/cms_home?content_id=eq.${params.websiteId}`, {
|
||||
const res = await fetch(`http://localhost:3000/home?website_id=eq.${params.websiteId}`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -208,7 +202,7 @@ export const actions = {
|
||||
updateFooter: async ({ request, fetch, cookies, params }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch(`http://localhost:3000/cms_footer?content_id=eq.${params.websiteId}`, {
|
||||
const res = await fetch(`http://localhost:3000/footer?website_id=eq.${params.websiteId}`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -233,14 +227,14 @@ export const actions = {
|
||||
createArticle: async ({ request, fetch, cookies, params }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch("http://localhost:3000/cms_article", {
|
||||
const res = await fetch("http://localhost:3000/article", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content_id: params.websiteId,
|
||||
website_id: params.websiteId,
|
||||
title: data.get("title")
|
||||
})
|
||||
});
|
||||
@@ -268,7 +262,7 @@ export const actions = {
|
||||
return cover;
|
||||
}
|
||||
|
||||
const res = await fetch(`http://localhost:3000/cms_article?id=eq.${data.get("article-id")}`, {
|
||||
const res = await fetch(`http://localhost:3000/article?id=eq.${data.get("article-id")}`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -294,7 +288,7 @@ export const actions = {
|
||||
deleteArticle: async ({ request, fetch, cookies }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch(`http://localhost:3000/cms_article?id=eq.${data.get("article-id")}`, {
|
||||
const res = await fetch(`http://localhost:3000/article?id=eq.${data.get("article-id")}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -348,7 +342,7 @@ const handleFileUpload = async (
|
||||
|
||||
const relativePath = relative(join(process.cwd(), "static"), filepath);
|
||||
|
||||
const res = await customFetch("http://localhost:3000/cms_media", {
|
||||
const res = await customFetch("http://localhost:3000/media", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -357,7 +351,7 @@ const handleFileUpload = async (
|
||||
Accept: "application/vnd.pgrst.object+json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content_id: contentId,
|
||||
website_id: contentId,
|
||||
user_id: userId,
|
||||
original_name: file.name,
|
||||
file_system_path: relativePath
|
||||
|
||||
28
web-app/src/routes/+layout.svelte
Normal file
28
web-app/src/routes/+layout.svelte
Normal file
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { page } from "$app/stores";
|
||||
const { data, children } = $props();
|
||||
</script>
|
||||
|
||||
<nav>
|
||||
<a href="/">archtika</a>
|
||||
{#if data.user}
|
||||
<a href="/account">Account</a>
|
||||
{:else}
|
||||
<a href="/register">Register</a>
|
||||
<a href="/login">Login</a>
|
||||
{/if}
|
||||
</nav>
|
||||
|
||||
<header>
|
||||
<h1>{$page.url.pathname}</h1>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
{@render children()}
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>
|
||||
<small>archtika</small>
|
||||
</p>
|
||||
</footer>
|
||||
@@ -1,11 +0,0 @@
|
||||
<section>
|
||||
<h2>Create website</h2>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Your websites</h2>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Shared with you</h2>
|
||||
</section>
|
||||
Reference in New Issue
Block a user