+ {title} +
++ Type: + {content_type} +
+
+ Created at:
+
diff --git a/rest-api/db/migrations/20240719071602_main_tables.sql b/rest-api/db/migrations/20240719071602_main_tables.sql index d70a788..987297c 100644 --- a/rest-api/db/migrations/20240719071602_main_tables.sql +++ b/rest-api/db/migrations/20240719071602_main_tables.sql @@ -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; diff --git a/rest-api/db/migrations/20240720132802_exposed_views_functions.sql b/rest-api/db/migrations/20240720132802_exposed_views_functions.sql index a40d5b9..0ea536d 100644 --- a/rest-api/db/migrations/20240720132802_exposed_views_functions.sql +++ b/rest-api/db/migrations/20240720132802_exposed_views_functions.sql @@ -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; \ No newline at end of file diff --git a/rest-api/db/migrations/20240724191017_row_level_security.sql b/rest-api/db/migrations/20240724191017_row_level_security.sql index c47af36..bc588ce 100644 --- a/rest-api/db/migrations/20240724191017_row_level_security.sql +++ b/rest-api/db/migrations/20240724191017_row_level_security.sql @@ -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; \ No newline at end of file +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; \ No newline at end of file diff --git a/web-app/src/routes/(anonymous)/login/+page.svelte b/web-app/src/routes/(anonymous)/login/+page.svelte index 489c2e9..f086a8b 100644 --- a/web-app/src/routes/(anonymous)/login/+page.svelte +++ b/web-app/src/routes/(anonymous)/login/+page.svelte @@ -1 +1,26 @@ -
+ + + diff --git a/web-app/src/routes/(anonymous)/register/+page.svelte b/web-app/src/routes/(anonymous)/register/+page.svelte index 489c2e9..b365c94 100644 --- a/web-app/src/routes/(anonymous)/register/+page.svelte +++ b/web-app/src/routes/(anonymous)/register/+page.svelte @@ -1 +1,26 @@ - + + + diff --git a/web-app/src/routes/(authenticated)/+page.server.ts b/web-app/src/routes/(authenticated)/+page.server.ts new file mode 100644 index 0000000..0e9043d --- /dev/null +++ b/web-app/src/routes/(authenticated)/+page.server.ts @@ -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" } }; + } +}; diff --git a/web-app/src/routes/(authenticated)/+page.svelte b/web-app/src/routes/(authenticated)/+page.svelte new file mode 100644 index 0000000..2d00648 --- /dev/null +++ b/web-app/src/routes/(authenticated)/+page.svelte @@ -0,0 +1,103 @@ + + +Successfully deleted website
+ {/if} + + {#if form?.deleteWebsite?.success === false} +{form.deleteWebsite.message}
+ {/if} + + {#each data.websites as { id, content_type, title, created_at }} ++ Type: + {content_type} +
+
+ Created at:
+
+ Username: + {data.user.username} +
++ ID: + {data.user.id} +
+Successfully logged out
+ {/if} + + +Account was deleted
+ {/if} + + {#if form?.deleteAccount?.success === false} +{form.deleteAccount.message}
+ {/if} + + +