2024-08-10 17:09:12 +02:00
|
|
|
-- migrate:up
|
2024-10-30 21:33:44 +01:00
|
|
|
CREATE DOMAIN "*/*" AS BYTEA;
|
2024-08-10 17:09:12 +02:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
_original_filename TEXT := _headers ->> 'x-original-filename';
|
2024-09-17 22:44:16 +02:00
|
|
|
_allowed_mimetypes TEXT[] := ARRAY['image/png', 'image/jpeg', 'image/webp', 'image/avif', 'image/gif', 'image/svg+xml'];
|
2024-10-08 21:20:44 +02:00
|
|
|
_max_file_size BIGINT := 5 * 1024 * 1024;
|
2024-09-10 17:29:57 +02:00
|
|
|
_has_access BOOLEAN;
|
2024-10-25 19:23:38 +02:00
|
|
|
_mimetype TEXT;
|
2024-08-10 17:09:12 +02:00
|
|
|
BEGIN
|
2024-09-10 17:29:57 +02:00
|
|
|
_has_access = internal.user_has_website_access (_website_id, 20);
|
2024-10-25 19:23:38 +02:00
|
|
|
_mimetype := CASE WHEN SUBSTRING($1 FROM 1 FOR 8) = '\x89504E470D0A1A0A'::BYTEA THEN
|
|
|
|
|
'image/png'
|
|
|
|
|
WHEN SUBSTRING($1 FROM 1 FOR 3) = '\xFFD8FF'::BYTEA THEN
|
|
|
|
|
'image/jpeg'
|
|
|
|
|
WHEN SUBSTRING($1 FROM 1 FOR 4) = '\x52494646'::BYTEA
|
|
|
|
|
AND SUBSTRING($1 FROM 9 FOR 4) = '\x57454250'::BYTEA THEN
|
|
|
|
|
'image/webp'
|
|
|
|
|
WHEN SUBSTRING($1 FROM 5 FOR 7) = '\x66747970617669'::BYTEA THEN
|
|
|
|
|
'image/avif'
|
|
|
|
|
WHEN SUBSTRING($1 FROM 1 FOR 6) = '\x474946383761'::BYTEA
|
|
|
|
|
OR SUBSTRING($1 FROM 1 FOR 6) = '\x474946383961'::BYTEA THEN
|
|
|
|
|
'image/gif'
|
|
|
|
|
WHEN SUBSTRING($1 FROM 1 FOR 5) = '\x3C3F786D6C'::BYTEA
|
|
|
|
|
OR SUBSTRING($1 FROM 1 FOR 4) = '\x3C737667'::BYTEA THEN
|
|
|
|
|
'image/svg+xml'
|
|
|
|
|
ELSE
|
|
|
|
|
NULL
|
|
|
|
|
END;
|
2024-08-14 21:37:19 +02:00
|
|
|
IF OCTET_LENGTH($1) = 0 THEN
|
2024-08-10 17:09:12 +02:00
|
|
|
RAISE invalid_parameter_value
|
2024-08-10 22:20:57 +02:00
|
|
|
USING message = 'No file data was provided';
|
2024-09-14 15:12:08 +02:00
|
|
|
ELSIF (_mimetype IS NULL
|
|
|
|
|
OR _mimetype NOT IN (
|
|
|
|
|
SELECT
|
|
|
|
|
UNNEST(_allowed_mimetypes))) THEN
|
|
|
|
|
RAISE invalid_parameter_value
|
2024-10-03 18:51:30 +02:00
|
|
|
USING message = 'Invalid MIME type. Allowed types are: png, jpg, webp, avif, gif, svg';
|
2024-09-14 15:12:08 +02:00
|
|
|
ELSIF OCTET_LENGTH($1) > _max_file_size THEN
|
|
|
|
|
RAISE program_limit_exceeded
|
2024-10-08 21:20:44 +02:00
|
|
|
USING message = FORMAT('File size exceeds the maximum limit of %s', PG_SIZE_PRETTY(_max_file_size));
|
2024-09-14 15:12:08 +02:00
|
|
|
ELSE
|
|
|
|
|
INSERT INTO internal.media (website_id, blob, mimetype, original_name)
|
|
|
|
|
VALUES (_website_id, $1, _mimetype, _original_filename)
|
|
|
|
|
RETURNING
|
|
|
|
|
id INTO file_id;
|
2024-08-10 17:09:12 +02:00
|
|
|
END IF;
|
|
|
|
|
END;
|
|
|
|
|
$$
|
|
|
|
|
LANGUAGE plpgsql
|
|
|
|
|
SECURITY DEFINER;
|
|
|
|
|
|
|
|
|
|
CREATE FUNCTION api.retrieve_file (id UUID)
|
|
|
|
|
RETURNS "*/*"
|
|
|
|
|
AS $$
|
|
|
|
|
DECLARE
|
|
|
|
|
_headers TEXT;
|
|
|
|
|
_blob BYTEA;
|
|
|
|
|
BEGIN
|
|
|
|
|
SELECT
|
|
|
|
|
FORMAT('[{ "Content-Type": "%s" },'
|
|
|
|
|
'{ "Content-Disposition": "inline; filename=\"%s\"" },'
|
|
|
|
|
'{ "Cache-Control": "max-age=259200" }]', m.mimetype, m.original_name)
|
|
|
|
|
FROM
|
2024-09-10 17:29:57 +02:00
|
|
|
internal.media AS m
|
2024-08-10 17:09:12 +02:00
|
|
|
WHERE
|
|
|
|
|
m.id = retrieve_file.id INTO _headers;
|
|
|
|
|
PERFORM
|
|
|
|
|
SET_CONFIG('response.headers', _headers, TRUE);
|
|
|
|
|
SELECT
|
|
|
|
|
m.blob
|
|
|
|
|
FROM
|
2024-10-03 18:51:30 +02:00
|
|
|
internal.media AS m
|
2024-08-10 17:09:12 +02:00
|
|
|
WHERE
|
|
|
|
|
m.id = retrieve_file.id INTO _blob;
|
|
|
|
|
IF FOUND THEN
|
|
|
|
|
RETURN _blob;
|
|
|
|
|
ELSE
|
|
|
|
|
RAISE invalid_parameter_value
|
|
|
|
|
USING message = 'Invalid file id';
|
|
|
|
|
END IF;
|
|
|
|
|
END;
|
|
|
|
|
$$
|
|
|
|
|
LANGUAGE plpgsql
|
|
|
|
|
SECURITY DEFINER;
|
|
|
|
|
|
2024-10-08 21:20:44 +02:00
|
|
|
GRANT EXECUTE ON FUNCTION api.upload_file TO authenticated_user;
|
2024-08-10 17:09:12 +02:00
|
|
|
|
2024-10-08 21:20:44 +02:00
|
|
|
GRANT EXECUTE ON FUNCTION api.retrieve_file TO anon;
|
2024-09-10 18:13:00 +02:00
|
|
|
|
2024-10-08 21:20:44 +02:00
|
|
|
GRANT EXECUTE ON FUNCTION api.retrieve_file TO authenticated_user;
|
2024-08-10 17:09:12 +02:00
|
|
|
|
|
|
|
|
-- migrate:down
|
2024-10-08 21:20:44 +02:00
|
|
|
DROP FUNCTION api.upload_file;
|
2024-08-10 17:09:12 +02:00
|
|
|
|
2024-10-08 21:20:44 +02:00
|
|
|
DROP FUNCTION api.retrieve_file;
|
2024-08-10 17:09:12 +02:00
|
|
|
|
|
|
|
|
DROP DOMAIN "*/*";
|
|
|
|
|
|