Add collaborator page

This commit is contained in:
Thilo Hohlt
2024-08-05 19:33:35 +02:00
parent 62db2776a7
commit 9f948ba0d4
11 changed files with 470 additions and 173 deletions

View File

@@ -1,4 +1,11 @@
-- migrate:up
CREATE VIEW api.account
WITH (security_invoker = on)
AS
SELECT id, username
FROM internal.user
WHERE id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID;
CREATE VIEW api.user
WITH (security_invoker = on)
AS
@@ -151,6 +158,7 @@ GRANT EXECUTE ON FUNCTION api.create_website(VARCHAR(10), VARCHAR(50)) TO authen
-- 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.account TO authenticated_user;
GRANT SELECT ON api.user TO authenticated_user;
GRANT SELECT, UPDATE, DELETE ON internal.website TO authenticated_user;
GRANT SELECT, UPDATE, DELETE ON api.website TO authenticated_user;
@@ -194,3 +202,4 @@ DROP VIEW api.settings;
DROP VIEW api.media;
DROP VIEW api.website;
DROP VIEW api.user;
DROP VIEW api.account;

View File

@@ -8,9 +8,9 @@ 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
CREATE POLICY view_user ON internal.user
FOR SELECT
USING (id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID);
USING (true);
CREATE POLICY view_own_websites ON internal.website
FOR SELECT
@@ -179,7 +179,7 @@ USING (
-- migrate:down
DROP POLICY view_own_user ON internal.user;
DROP POLICY view_user ON internal.user;
DROP POLICY view_own_websites ON internal.website;
DROP POLICY delete_own_website ON internal.website;
DROP POLICY update_own_website ON internal.website;

View File

@@ -0,0 +1,54 @@
-- migrate:up
ALTER TABLE internal.collab ENABLE ROW LEVEL SECURITY;
CREATE POLICY view_collaborations ON internal.collab
FOR SELECT
USING (
EXISTS (
SELECT 1
FROM internal.website
WHERE internal.website.id = internal.collab.website_id
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
)
);
CREATE POLICY insert_collaborations ON internal.collab
FOR INSERT
WITH CHECK (
EXISTS (
SELECT 1
FROM internal.website
WHERE internal.website.id = internal.collab.website_id
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
)
);
CREATE POLICY update_collaborations ON internal.collab
FOR UPDATE
USING (
EXISTS (
SELECT 1
FROM internal.website
WHERE internal.website.id = internal.collab.website_id
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
)
);
CREATE POLICY delete_collaborations ON internal.collab
FOR DELETE
USING (
EXISTS (
SELECT 1
FROM internal.website
WHERE internal.website.id = internal.collab.website_id
AND internal.website.owner_id = (current_setting('request.jwt.claims', true)::json->>'user_id')::UUID
)
);
-- migrate:down
DROP POLICY view_collaborations ON internal.collab;
DROP POLICY insert_collaborations ON internal.collab;
DROP POLICY update_collaborations ON internal.collab;
DROP POLICY delete_collaborations ON internal.collab;
ALTER TABLE internal.collab DISABLE ROW LEVEL SECURITY;

View File

@@ -546,63 +546,6 @@
}
}
},
"node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/@rollup/plugin-commonjs/node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"dev": true,
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^3.1.2",
"minimatch": "^9.0.4",
"minipass": "^7.1.2",
"package-json-from-dist": "^1.0.0",
"path-scurry": "^1.11.1"
},
"bin": {
"glob": "dist/esm/bin.mjs"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@rollup/plugin-commonjs/node_modules/is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/@rollup/plugin-commonjs/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@rollup/plugin-json": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
@@ -674,9 +617,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz",
"integrity": "sha512-XzqSg714++M+FXhHfXpS1tDnNZNpgxxuGZWlRG/jSj+VEPmZ0yg6jV4E0AL3uyBKxO8mO3xtOsP5mQ+XLfrlww==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz",
"integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==",
"cpu": [
"arm"
],
@@ -688,9 +631,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.1.tgz",
"integrity": "sha512-thFUbkHteM20BGShD6P08aungq4irbIZKUNbG70LN8RkO7YztcGPiKTTGZS7Kw+x5h8hOXs0i4OaHwFxlpQN6A==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz",
"integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==",
"cpu": [
"arm64"
],
@@ -702,9 +645,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.1.tgz",
"integrity": "sha512-8o6eqeFZzVLia2hKPUZk4jdE3zW7LCcZr+MD18tXkgBBid3lssGVAYuox8x6YHoEPDdDa9ixTaStcmx88lio5Q==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz",
"integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==",
"cpu": [
"arm64"
],
@@ -716,9 +659,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.1.tgz",
"integrity": "sha512-4T42heKsnbjkn7ovYiAdDVRRWZLU9Kmhdt6HafZxFcUdpjlBlxj4wDrt1yFWLk7G4+E+8p2C9tcmSu0KA6auGA==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz",
"integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==",
"cpu": [
"x64"
],
@@ -730,9 +673,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.1.tgz",
"integrity": "sha512-MXg1xp+e5GhZ3Vit1gGEyoC+dyQUBy2JgVQ+3hUrD9wZMkUw/ywgkpK7oZgnB6kPpGrxJ41clkPPnsknuD6M2Q==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz",
"integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==",
"cpu": [
"arm"
],
@@ -744,9 +687,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.1.tgz",
"integrity": "sha512-DZNLwIY4ftPSRVkJEaxYkq7u2zel7aah57HESuNkUnz+3bZHxwkCUkrfS2IWC1sxK6F2QNIR0Qr/YXw7nkF3Pw==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz",
"integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==",
"cpu": [
"arm"
],
@@ -758,9 +701,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.1.tgz",
"integrity": "sha512-C7evongnjyxdngSDRRSQv5GvyfISizgtk9RM+z2biV5kY6S/NF/wta7K+DanmktC5DkuaJQgoKGf7KUDmA7RUw==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz",
"integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==",
"cpu": [
"arm64"
],
@@ -772,9 +715,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.1.tgz",
"integrity": "sha512-89tFWqxfxLLHkAthAcrTs9etAoBFRduNfWdl2xUs/yLV+7XDrJ5yuXMHptNqf1Zw0UCA3cAutkAiAokYCkaPtw==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz",
"integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==",
"cpu": [
"arm64"
],
@@ -786,9 +729,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.1.tgz",
"integrity": "sha512-PromGeV50sq+YfaisG8W3fd+Cl6mnOOiNv2qKKqKCpiiEke2KiKVyDqG/Mb9GWKbYMHj5a01fq/qlUR28PFhCQ==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz",
"integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==",
"cpu": [
"ppc64"
],
@@ -800,9 +743,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.1.tgz",
"integrity": "sha512-/1BmHYh+iz0cNCP0oHCuF8CSiNj0JOGf0jRlSo3L/FAyZyG2rGBuKpkZVH9YF+x58r1jgWxvm1aRg3DHrLDt6A==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz",
"integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==",
"cpu": [
"riscv64"
],
@@ -814,9 +757,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.1.tgz",
"integrity": "sha512-0cYP5rGkQWRZKy9/HtsWVStLXzCF3cCBTRI+qRL8Z+wkYlqN7zrSYm6FuY5Kd5ysS5aH0q5lVgb/WbG4jqXN1Q==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz",
"integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==",
"cpu": [
"s390x"
],
@@ -828,9 +771,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.1.tgz",
"integrity": "sha512-XUXeI9eM8rMP8aGvii/aOOiMvTs7xlCosq9xCjcqI9+5hBxtjDpD+7Abm1ZhVIFE1J2h2VIg0t2DX/gjespC2Q==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz",
"integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==",
"cpu": [
"x64"
],
@@ -842,9 +785,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.1.tgz",
"integrity": "sha512-V7cBw/cKXMfEVhpSvVZhC+iGifD6U1zJ4tbibjjN+Xi3blSXaj/rJynAkCFFQfoG6VZrAiP7uGVzL440Q6Me2Q==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz",
"integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==",
"cpu": [
"x64"
],
@@ -856,9 +799,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.1.tgz",
"integrity": "sha512-88brja2vldW/76jWATlBqHEoGjJLRnP0WOEKAUbMcXaAZnemNhlAHSyj4jIwMoP2T750LE9lblvD4e2jXleZsA==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz",
"integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==",
"cpu": [
"arm64"
],
@@ -870,9 +813,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.1.tgz",
"integrity": "sha512-LdxxcqRVSXi6k6JUrTah1rHuaupoeuiv38du8Mt4r4IPer3kwlTo+RuvfE8KzZ/tL6BhaPlzJ3835i6CxrFIRQ==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz",
"integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==",
"cpu": [
"ia32"
],
@@ -884,9 +827,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.1.tgz",
"integrity": "sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz",
"integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==",
"cpu": [
"x64"
],
@@ -927,9 +870,9 @@
}
},
"node_modules/@sveltejs/kit": {
"version": "2.5.18",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.18.tgz",
"integrity": "sha512-+g06hvpVAnH7b4CDjhnTDgFWBKBiQJpuSmQeGYOuzbO3SC3tdYjRNlDCrafvDtKbGiT2uxY5Dn9qdEUGVZdWOQ==",
"version": "2.5.20",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.20.tgz",
"integrity": "sha512-47rJ5BoYwURE/Rp7FNMLp3NzdbWC9DQ/PmKd0mebxT2D/PrPxZxcLImcD3zsWdX2iS6oJk8ITJbO/N2lWnnUqA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -1053,13 +996,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.0.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.0.0.tgz",
"integrity": "sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw==",
"version": "22.1.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz",
"integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.11.1"
"undici-types": "~6.13.0"
}
},
"node_modules/@types/pug": {
@@ -1186,14 +1129,13 @@
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
"balanced-match": "^1.0.0"
}
},
"node_modules/braces": {
@@ -1531,22 +1473,21 @@
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"dev": true,
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
"foreground-child": "^3.1.0",
"jackspeak": "^3.1.2",
"minimatch": "^9.0.4",
"minipass": "^7.1.2",
"package-json-from-dist": "^1.0.0",
"path-scurry": "^1.11.1"
},
"engines": {
"node": "*"
"bin": {
"glob": "dist/esm/bin.mjs"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -1734,9 +1675,9 @@
}
},
"node_modules/is-reference": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1843,16 +1784,19 @@
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
"brace-expansion": "^2.0.1"
},
"engines": {
"node": "*"
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/minimist": {
@@ -2135,10 +2079,56 @@
"rimraf": "bin.js"
}
},
"node_modules/rimraf/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/rimraf/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/rimraf/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/rollup": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.19.1.tgz",
"integrity": "sha512-K5vziVlg7hTpYfFBI+91zHBEMo6jafYXpkMlqZjg7/zhIG9iHqazBf4xz9AVdjS9BruRn280ROqLI7G3OFRIlw==",
"version": "4.20.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz",
"integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2152,22 +2142,22 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.19.1",
"@rollup/rollup-android-arm64": "4.19.1",
"@rollup/rollup-darwin-arm64": "4.19.1",
"@rollup/rollup-darwin-x64": "4.19.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.19.1",
"@rollup/rollup-linux-arm-musleabihf": "4.19.1",
"@rollup/rollup-linux-arm64-gnu": "4.19.1",
"@rollup/rollup-linux-arm64-musl": "4.19.1",
"@rollup/rollup-linux-powerpc64le-gnu": "4.19.1",
"@rollup/rollup-linux-riscv64-gnu": "4.19.1",
"@rollup/rollup-linux-s390x-gnu": "4.19.1",
"@rollup/rollup-linux-x64-gnu": "4.19.1",
"@rollup/rollup-linux-x64-musl": "4.19.1",
"@rollup/rollup-win32-arm64-msvc": "4.19.1",
"@rollup/rollup-win32-ia32-msvc": "4.19.1",
"@rollup/rollup-win32-x64-msvc": "4.19.1",
"@rollup/rollup-android-arm-eabi": "4.20.0",
"@rollup/rollup-android-arm64": "4.20.0",
"@rollup/rollup-darwin-arm64": "4.20.0",
"@rollup/rollup-darwin-x64": "4.20.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.20.0",
"@rollup/rollup-linux-arm-musleabihf": "4.20.0",
"@rollup/rollup-linux-arm64-gnu": "4.20.0",
"@rollup/rollup-linux-arm64-musl": "4.20.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.20.0",
"@rollup/rollup-linux-riscv64-gnu": "4.20.0",
"@rollup/rollup-linux-s390x-gnu": "4.20.0",
"@rollup/rollup-linux-x64-gnu": "4.20.0",
"@rollup/rollup-linux-x64-musl": "4.20.0",
"@rollup/rollup-win32-arm64-msvc": "4.20.0",
"@rollup/rollup-win32-ia32-msvc": "4.20.0",
"@rollup/rollup-win32-x64-msvc": "4.20.0",
"fsevents": "~2.3.2"
}
},
@@ -2198,9 +2188,9 @@
}
},
"node_modules/set-cookie-parser": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
"integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==",
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.0.tgz",
"integrity": "sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ==",
"dev": true,
"license": "MIT"
},
@@ -2412,9 +2402,9 @@
}
},
"node_modules/svelte": {
"version": "5.0.0-next.203",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.0.0-next.203.tgz",
"integrity": "sha512-kVJVExxJEU7WTnvLfCXO2+tg20m/lWcszNMtG7sKP+fhlTAWzpZMCjA4PonJWYmJHCrqblZPwfG8o9i5kxnjwg==",
"version": "5.0.0-next.208",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.0.0-next.208.tgz",
"integrity": "sha512-gYABb68367fGnY/GIpb/j3DD5DUqhVWi6EodEBVpiRA9wLQTpY8QmuPg04xut/0EHqlFU3a40wbpjJKmpL3Y/g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2520,6 +2510,16 @@
}
}
},
"node_modules/svelte/node_modules/is-reference": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/tiny-glob": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
@@ -2575,9 +2575,9 @@
"license": "MIT"
},
"node_modules/undici-types": {
"version": "6.11.1",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.11.1.tgz",
"integrity": "sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==",
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz",
"integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==",
"dev": true,
"license": "MIT"
},

View File

@@ -1,7 +1,7 @@
import { redirect } from "@sveltejs/kit";
export const handle = async ({ event, resolve }) => {
const userData = await event.fetch("http://localhost:3000/user", {
const userData = await event.fetch("http://localhost:3000/account", {
method: "GET",
headers: {
"Content-Type": "application/json",

View File

@@ -23,6 +23,7 @@
<nav class="operations__nav">
<a href="/website/{id}">Settings</a>
<a href="/website/{id}/articles">Articles</a>
<a href="/website/{id}/collaborators">Collaborators</a>
<a href="/website/{id}/publish">Publish</a>
</nav>

View File

@@ -5,7 +5,7 @@ import { ALLOWED_MIME_TYPES } from "../utils";
export const handleFileUpload = async (
file: File,
contentId: string,
websiteId: string,
userId: string,
session_token: string | undefined,
customFetch: typeof fetch
@@ -49,7 +49,7 @@ export const handleFileUpload = async (
Accept: "application/vnd.pgrst.object+json"
},
body: JSON.stringify({
website_id: contentId,
website_id: websiteId,
user_id: userId,
original_name: file.name,
file_system_path: relativePath

View File

@@ -0,0 +1,95 @@
import type { Actions, PageServerLoad } from "./$types";
export const load: PageServerLoad = async ({ parent, params, fetch, cookies }) => {
const { website, home } = await parent();
const collabData = await fetch(
`http://localhost:3000/collab?website_id=eq.${params.websiteId}&select=*,user!user_id(*)`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
}
}
);
const collaborators = await collabData.json();
return {
website,
home,
collaborators
};
};
export const actions: Actions = {
addCollaborator: async ({ request, fetch, cookies, params }) => {
const data = await request.formData();
const res = await fetch("http://localhost:3000/collab", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
},
body: JSON.stringify({
website_id: params.websiteId,
user_id: data.get("user-id"),
permission_level: data.get("permission-level")
})
});
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully added collaborator" };
},
updateCollaborator: async ({ request, fetch, cookies, params }) => {
const data = await request.formData();
const res = await fetch(
`http://localhost:3000/collab?website_id=eq.${params.websiteId}&user_id=eq.${data.get("user-id")}`,
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
},
body: JSON.stringify({
permission_level: data.get("permission-level")
})
}
);
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully updated collaborator" };
},
removeCollaborator: async ({ request, fetch, cookies, params }) => {
const data = await request.formData();
const res = await fetch(
`http://localhost:3000/collab?website_id=eq.${params.websiteId}&user_id=eq.${data.get("user-id")}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${cookies.get("session_token")}`
}
}
);
if (!res.ok) {
const response = await res.json();
return { success: false, message: response.message };
}
return { success: true, message: "Successfully removed collaborator" };
}
};

View File

@@ -0,0 +1,135 @@
<script lang="ts">
import { enhance } from "$app/forms";
import WebsiteEditor from "$lib/components/WebsiteEditor.svelte";
import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import Modal from "$lib/components/Modal.svelte";
import type { ActionData, PageServerData } from "./$types";
const { data, form } = $props<{ data: PageServerData; form: ActionData }>();
</script>
<SuccessOrError success={form?.success} message={form?.message} />
<WebsiteEditor
id={data.website.id}
title={data.website.title}
previewContent={data.home.main_content}
>
<section>
<h2>Add collaborator</h2>
<Modal id="add-collaborator" text="Add collaborator">
<h3>Add collaborator</h3>
<form
method="POST"
action="?/addCollaborator"
use:enhance={() => {
return async ({ update }) => {
await update();
window.location.hash = "!";
};
}}
>
<label>
User id:
<input type="text" name="user-id" minlength="36" maxlength="36" required />
</label>
<label>
Permission level:
<select name="permission-level">
<option value="10">10 - View</option>
<option value="20">20 - Edit</option>
<option value="30">30 - Manage</option>
</select>
</label>
<button type="submit">Submit</button>
</form>
</Modal>
</section>
<section>
<h2>All collaborators</h2>
{#if data.collaborators.length > 0}
{#each data.collaborators as { website_id, user_id, permission_level, user: { username } } (`${website_id}-${user_id}`)}
<article class="collaborator-card">
<h3>{username} ({permission_level})</h3>
<div class="collaborator-card__actions">
<Modal id="update-collaborator-{user_id}" text="Update">
<h4>Update collaborator</h4>
<form
method="POST"
action="?/updateCollaborator"
use:enhance={() => {
return async ({ update }) => {
await update({ reset: false });
window.location.hash = "!";
};
}}
>
<input type="hidden" name="user-id" value={user_id} />
<label>
Permission level:
<select name="permission-level">
<option value="10" selected={10 === permission_level}>10 - View</option>
<option value="20" selected={20 === permission_level}>20 - Edit</option>
<option value="30" selected={30 === permission_level}>30 - Manage</option>
</select>
</label>
<button type="submit">Update collaborator</button>
</form>
</Modal>
<Modal id="remove-collaborator-{user_id}" text="Remove">
<h4>Remove collaborator</h4>
<p>Do you really want to remove the collaborator?</p>
<form
method="POST"
action="?/removeCollaborator"
use:enhance={() => {
return async ({ update }) => {
await update();
window.location.hash = "!";
};
}}
>
<input type="hidden" name="user-id" value={user_id} />
<button type="submit">Remove collaborator</button>
</form>
</Modal>
</div>
</article>
{/each}
{/if}
</section>
</WebsiteEditor>
<style>
.collaborator-card {
display: flex;
align-items: center;
column-gap: 2rem;
row-gap: 0.5rem;
flex-wrap: wrap;
justify-content: space-between;
}
.collaborator-card:nth-of-type(1) {
margin-block-start: 1rem;
}
.collaborator-card__actions {
display: flex;
gap: 0.5rem;
align-items: center;
}
</style>

View File

@@ -26,7 +26,7 @@ export const load: PageServerLoad = async ({ params, fetch, cookies, locals }) =
};
export const actions: Actions = {
publishWebsite: async ({ request, fetch, cookies, params, locals }) => {
publishWebsite: async ({ request, params, locals }) => {
const data = await request.formData();
const websiteOverview = JSON.parse(data.get("website-overview") as string);

View File

@@ -1,11 +1,14 @@
<script lang="ts">
import { enhance } from "$app/forms";
import WebsiteEditor from "$lib/components/WebsiteEditor.svelte";
import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import type { ActionData, PageServerData } from "./$types";
const { data, form } = $props<{ data: PageServerData; form: ActionData }>();
</script>
<SuccessOrError success={form?.success} message={form?.message} />
<WebsiteEditor
id={data.website.id}
title={data.website.title}