mirror of
https://github.com/thiloho/archtika.git
synced 2025-11-22 02:41:35 +01:00
Run specific test suites in serial matter and restrict file uploads
This commit is contained in:
@@ -0,0 +1,76 @@
|
|||||||
|
-- migrate:up
|
||||||
|
CREATE OR REPLACE FUNCTION api.upload_file (BYTEA, OUT file_id UUID)
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
_headers JSON := CURRENT_SETTING('request.headers', TRUE)::JSON;
|
||||||
|
_website_id UUID := (_headers ->> 'x-website-id')::UUID;
|
||||||
|
_mimetype TEXT := _headers ->> 'x-mimetype';
|
||||||
|
_original_filename TEXT := _headers ->> 'x-original-filename';
|
||||||
|
_allowed_mimetypes TEXT[] := ARRAY['image/png', 'image/jpeg', 'image/webp'];
|
||||||
|
_max_file_size INT := 5 * 1024 * 1024;
|
||||||
|
_has_access BOOLEAN;
|
||||||
|
BEGIN
|
||||||
|
_has_access = internal.user_has_website_access (_website_id, 20);
|
||||||
|
IF OCTET_LENGTH($1) = 0 THEN
|
||||||
|
RAISE invalid_parameter_value
|
||||||
|
USING message = 'No file data was provided';
|
||||||
|
END IF;
|
||||||
|
IF _mimetype IS NULL OR _mimetype NOT IN (
|
||||||
|
SELECT
|
||||||
|
UNNEST(_allowed_mimetypes)) THEN
|
||||||
|
RAISE invalid_parameter_value
|
||||||
|
USING message = 'Invalid MIME type. Allowed types are: png, jpg, webp';
|
||||||
|
END IF;
|
||||||
|
IF OCTET_LENGTH($1) > _max_file_size THEN
|
||||||
|
RAISE program_limit_exceeded
|
||||||
|
USING message = FORMAT('File size exceeds the maximum limit of %s MB', _max_file_size / (1024 * 1024));
|
||||||
|
END IF;
|
||||||
|
INSERT INTO internal.media (website_id, blob, mimetype, original_name)
|
||||||
|
VALUES (_website_id, $1, _mimetype, _original_filename)
|
||||||
|
RETURNING
|
||||||
|
id INTO file_id;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
SECURITY DEFINER;
|
||||||
|
|
||||||
|
GRANT EXECUTE ON FUNCTION api.upload_file (BYTEA) TO authenticated_user;
|
||||||
|
|
||||||
|
-- migrate:down
|
||||||
|
DROP FUNCTION api.upload_file (BYTEA);
|
||||||
|
|
||||||
|
CREATE FUNCTION api.upload_file (BYTEA, OUT file_id UUID)
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
_headers JSON := CURRENT_SETTING('request.headers', TRUE)::JSON;
|
||||||
|
_website_id UUID := (_headers ->> 'x-website-id')::UUID;
|
||||||
|
_mimetype TEXT := _headers ->> 'x-mimetype';
|
||||||
|
_original_filename TEXT := _headers ->> 'x-original-filename';
|
||||||
|
_allowed_mimetypes TEXT[] := ARRAY['image/png', 'image/jpeg', 'image/webp'];
|
||||||
|
_max_file_size INT := 5 * 1024 * 1024;
|
||||||
|
BEGIN
|
||||||
|
IF OCTET_LENGTH($1) = 0 THEN
|
||||||
|
RAISE invalid_parameter_value
|
||||||
|
USING message = 'No file data was provided';
|
||||||
|
END IF;
|
||||||
|
IF _mimetype IS NULL OR _mimetype NOT IN (
|
||||||
|
SELECT
|
||||||
|
UNNEST(_allowed_mimetypes)) THEN
|
||||||
|
RAISE invalid_parameter_value
|
||||||
|
USING message = 'Invalid MIME type. Allowed types are: png, jpg, webp';
|
||||||
|
END IF;
|
||||||
|
IF OCTET_LENGTH($1) > _max_file_size THEN
|
||||||
|
RAISE program_limit_exceeded
|
||||||
|
USING message = FORMAT('File size exceeds the maximum limit of %s MB', _max_file_size / (1024 * 1024));
|
||||||
|
END IF;
|
||||||
|
INSERT INTO internal.media (website_id, blob, mimetype, original_name)
|
||||||
|
VALUES (_website_id, $1, _mimetype, _original_filename)
|
||||||
|
RETURNING
|
||||||
|
id INTO file_id;
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
SECURITY DEFINER;
|
||||||
|
|
||||||
|
GRANT EXECUTE ON FUNCTION api.upload_file (BYTEA) TO authenticated_user;
|
||||||
|
|
||||||
3
web-app/.gitignore
vendored
3
web-app/.gitignore
vendored
@@ -19,3 +19,6 @@ Thumbs.db
|
|||||||
# Vite
|
# Vite
|
||||||
vite.config.js.timestamp-*
|
vite.config.js.timestamp-*
|
||||||
vite.config.ts.timestamp-*
|
vite.config.ts.timestamp-*
|
||||||
|
|
||||||
|
# Playwright
|
||||||
|
test-results
|
||||||
@@ -77,9 +77,19 @@
|
|||||||
<label>
|
<label>
|
||||||
Filter:
|
Filter:
|
||||||
<select name="website_filter">
|
<select name="website_filter">
|
||||||
<option value="all">Show all</option>
|
<option value="all" selected={"all" === $page.url.searchParams.get("website_filter")}
|
||||||
<option value="creations">Created by you</option>
|
>Show all</option
|
||||||
<option value="shared">Shared with you</option>
|
>
|
||||||
|
<option
|
||||||
|
value="creations"
|
||||||
|
selected={"creations" === $page.url.searchParams.get("website_filter")}
|
||||||
|
>Created by you</option
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
value="shared"
|
||||||
|
selected={"shared" === $page.url.searchParams.get("website_filter")}
|
||||||
|
>Shared with you</option
|
||||||
|
>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<button type="submit">Submit</button>
|
<button type="submit">Submit</button>
|
||||||
|
|||||||
@@ -66,9 +66,19 @@
|
|||||||
<label>
|
<label>
|
||||||
Filter:
|
Filter:
|
||||||
<select name="article_filter">
|
<select name="article_filter">
|
||||||
<option value="all">Show all</option>
|
<option value="all" selected={"all" === $page.url.searchParams.get("article_filter")}
|
||||||
<option value="creations">Created by you</option>
|
>Show all</option
|
||||||
<option value="shared">Created by others</option>
|
>
|
||||||
|
<option
|
||||||
|
value="creations"
|
||||||
|
selected={"creations" === $page.url.searchParams.get("article_filter")}
|
||||||
|
>Created by you</option
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
value="shared"
|
||||||
|
selected={"shared" === $page.url.searchParams.get("article_filter")}
|
||||||
|
>Created by others</option
|
||||||
|
>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<button type="submit">Submit</button>
|
<button type="submit">Submit</button>
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ test.describe("Blog", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("Articles", () => {
|
test.describe.serial("Articles", () => {
|
||||||
test("Create article", async ({ authenticatedPage: page }) => {
|
test("Create article", async ({ authenticatedPage: page }) => {
|
||||||
await page.getByRole("link", { name: "Blog" }).click();
|
await page.getByRole("link", { name: "Blog" }).click();
|
||||||
await page.getByRole("link", { name: "Articles" }).click();
|
await page.getByRole("link", { name: "Articles" }).click();
|
||||||
@@ -171,7 +171,7 @@ test.describe("Blog", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("Collaborators", () => {
|
test.describe.serial("Collaborators", () => {
|
||||||
test("Add collaborator", async ({ authenticatedPage: page }) => {
|
test("Add collaborator", async ({ authenticatedPage: page }) => {
|
||||||
await page.getByRole("link", { name: "Blog" }).click();
|
await page.getByRole("link", { name: "Blog" }).click();
|
||||||
await page.getByRole("link", { name: "Collaborators" }).click();
|
await page.getByRole("link", { name: "Collaborators" }).click();
|
||||||
@@ -202,7 +202,7 @@ test.describe("Blog", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test.describe("Docs", () => {
|
test.describe("Docs", () => {
|
||||||
test.describe("Categories", () => {
|
test.describe.serial("Categories", () => {
|
||||||
test("Create category", async ({ authenticatedPage: page }) => {
|
test("Create category", async ({ authenticatedPage: page }) => {
|
||||||
await page.getByRole("link", { name: "Documentation" }).click();
|
await page.getByRole("link", { name: "Documentation" }).click();
|
||||||
await page.getByRole("link", { name: "Categories" }).click();
|
await page.getByRole("link", { name: "Categories" }).click();
|
||||||
@@ -299,7 +299,7 @@ test("Delete websites", async ({ authenticatedPage: page }) => {
|
|||||||
await expect(page.getByRole("link", { name: "All websites" })).toBeHidden();
|
await expect(page.getByRole("link", { name: "All websites" })).toBeHidden();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Delete account", async ({ authenticatedPage: page }) => {
|
test("Delete accounts", async ({ authenticatedPage: page }) => {
|
||||||
await page.getByRole("link", { name: "Account" }).click();
|
await page.getByRole("link", { name: "Account" }).click();
|
||||||
await page.getByRole("button", { name: "Delete account" }).click();
|
await page.getByRole("button", { name: "Delete account" }).click();
|
||||||
await page.getByLabel("Password:").click();
|
await page.getByLabel("Password:").click();
|
||||||
|
|||||||
Reference in New Issue
Block a user