diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 3ec39d0..ba22a9b 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -9,7 +9,7 @@ on:
jobs:
test:
# The type of runner that the job will run on
- runs-on: ubuntu-latest
+ runs-on: ubuntu-20.04
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
@@ -27,13 +27,13 @@ jobs:
- run: yarn test
name: 🔬 Testing the repo
- name: Archive code coverage results
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: code-coverage-report
path: testres.json
- name: Uploading failing screenshots
if: ${{ !success() }}
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: failing-screenshots
path: __screenshot-tests__/__image_snapshots__/__diff_output__
\ No newline at end of file
diff --git a/__screenshot-tests__/__image_snapshots__/index-screentest-ts-the-home-page-clicking-the-pic-the-gallery-popup-is-opened-1-snap.png b/__screenshot-tests__/__image_snapshots__/index-screentest-ts-the-home-page-clicking-the-pic-the-gallery-popup-is-opened-1-snap.png
index 8dc9a05..e41e140 100644
Binary files a/__screenshot-tests__/__image_snapshots__/index-screentest-ts-the-home-page-clicking-the-pic-the-gallery-popup-is-opened-1-snap.png and b/__screenshot-tests__/__image_snapshots__/index-screentest-ts-the-home-page-clicking-the-pic-the-gallery-popup-is-opened-1-snap.png differ
diff --git a/__screenshot-tests__/__image_snapshots__/index-screentest-ts-the-home-page-should-be-shown-according-to-the-mobile-design-1-snap.png b/__screenshot-tests__/__image_snapshots__/index-screentest-ts-the-home-page-should-be-shown-according-to-the-mobile-design-1-snap.png
index 5f1b6ee..b666e82 100644
Binary files a/__screenshot-tests__/__image_snapshots__/index-screentest-ts-the-home-page-should-be-shown-according-to-the-mobile-design-1-snap.png and b/__screenshot-tests__/__image_snapshots__/index-screentest-ts-the-home-page-should-be-shown-according-to-the-mobile-design-1-snap.png differ
diff --git a/next.config.js b/next.config.js
index a47e545..670a4a0 100644
--- a/next.config.js
+++ b/next.config.js
@@ -2,6 +2,10 @@
const nextConfig = {
reactStrictMode: true,
webpack(config) {
+ config.module.rules.push({
+ test: /supabase\/.*/,
+ use: 'ignore-loader',
+ });
config.module.rules.push({
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
@@ -12,6 +16,12 @@ const nextConfig = {
},
images: {
remotePatterns: [
+ {
+ protocol: 'https',
+ hostname: 'fpbswrebvsmjdwekyznx.supabase.co',
+ port: '',
+ pathname: '**',
+ },
{
protocol: 'https',
hostname: 'content-eu.drive.amazonaws.com',
diff --git a/package.json b/package.json
index b425d6f..56f4eb7 100644
--- a/package.json
+++ b/package.json
@@ -11,11 +11,13 @@
},
"dependencies": {
"@emotion/react": "^11.8.2",
+ "@supabase/supabase-js": "^2.45.4",
"@types/express-useragent": "^1.0.2",
"@types/jwk-to-pem": "^2.0.1",
"@types/node-jose": "^1.1.10",
"@types/uuid": "^8.3.4",
"axios": "^0.27.2",
+ "buffer-image-size": "^0.6.4",
"express-useragent": "^1.0.15",
"jest-environment-jsdom": "^28.1.1",
"jwk-to-pem": "^2.0.5",
@@ -55,6 +57,7 @@
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.3.0",
+ "ignore-loader": "^0.1.2",
"jest": "^28.1.1",
"jest-image-snapshot": "^5.1.0",
"jest-puppeteer-docker": "https://github.com/bertuz/jest-puppeteer-docker.git#specify-docker-build",
diff --git a/pages/api/galleryPhotos/index.ts b/pages/api/galleryPhotos/index.ts
new file mode 100644
index 0000000..0e7e8d9
--- /dev/null
+++ b/pages/api/galleryPhotos/index.ts
@@ -0,0 +1,74 @@
+import sizeOf from 'buffer-image-size';
+
+import type { NextApiRequest, NextApiResponse } from 'next';
+
+type SupabaseStorageQueryResponse = ReadonlyArray<{
+ name: string;
+}>;
+
+const SUPABASE_PUBLIC_KEY = process.env.SUPABASE_PUBLIC_KEY || ' ';
+const SUPABASE_HEADERS = new Headers({
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${SUPABASE_PUBLIC_KEY}`,
+ apikey: SUPABASE_PUBLIC_KEY,
+});
+
+type ResponseData = ReadonlyArray<{
+ contentProperties: { image: { height: number; width: number } };
+ url: string;
+ name: string;
+}>;
+
+export async function getImageData(): Promise {
+ const requestOptions = {
+ method: 'POST',
+ headers: SUPABASE_HEADERS,
+ body: JSON.stringify({
+ prefix: '',
+ limit: 20,
+ offset: 0,
+ sortBy: {
+ column: 'created_at',
+ order: 'desc',
+ },
+ search: '',
+ }),
+ };
+
+ const SUPABASE_DOMAIN = process.env.SUPABASE_DOMAIN || '';
+ const IMAGES_OBJECTS_URL = `${SUPABASE_DOMAIN}/storage/v1/object/list/photos`;
+ const IMAGES_URL = `${SUPABASE_DOMAIN}/storage/v1/object/public/photos`;
+ const images = (await fetch(IMAGES_OBJECTS_URL, requestOptions).then(
+ (response) => response.json()
+ )) as SupabaseStorageQueryResponse;
+
+ const imagesToReturn = [];
+ for (const image of images) {
+ const { name: nameWithExtension } = image;
+ const name = nameWithExtension.replace(/\.[a-zA-Z0-9]{1,3}$/, '');
+ const url = `${IMAGES_URL}/${nameWithExtension}`;
+ const contentImage = await fetch(url, {
+ method: 'GET',
+ });
+ const imageBuffer = Buffer.from(await contentImage.arrayBuffer());
+ const imageSize = sizeOf(imageBuffer);
+
+ imagesToReturn.push({
+ url,
+ contentProperties: {
+ image: { height: imageSize.height, width: imageSize.width },
+ },
+ name,
+ });
+ }
+
+ return imagesToReturn;
+}
+
+export default async function handler(
+ _: NextApiRequest,
+ res: NextApiResponse
+) {
+ const imagesToReturn = await getImageData();
+ res.status(200).json(imagesToReturn);
+}
diff --git a/pages/index.tsx b/pages/index.tsx
index f554e6d..05cfe40 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -33,6 +33,8 @@ import useScreenSize from '../utils/useScreenSize';
import toBase64 from '../utils/toBase64';
import { isRunningAcceptanceTest } from '../utils/testUtils';
+import { createClient } from '@supabase/supabase-js';
+
import { Transition } from 'react-transition-group';
import { useEffect, useMemo, useRef, useState } from 'react';
@@ -742,6 +744,17 @@ const Home: NextPage = ({ galleryPics }) => {
With that in mind, this is what I currently have in my toolbox 🧑🏽💻:
+ -
+ Mobile
+
+ -
+
+ Flutter
+
+
+ - Supabase
+
+
-
Frontend
@@ -764,8 +777,11 @@ const Home: NextPage = ({ galleryPics }) => {
- Agile: kanban and scrum
- Kubernetes
- - Hexagonal architecture
- - DDD
+ - Clean architectures: hexagonal, DDD, even on front
+ -
+ Foster async - yet effective - communication
+ within a team
+
@@ -994,51 +1010,55 @@ export async function getServerSideProps() {
};
}
- const response = await fetch(
- 'https://www.amazon.it/drive/v1/nodes/mmVUOJzUS_KqKRykQrzFPA/children?asset=ALL&filters=kind%3A(FILE*+OR+FOLDER*)+AND+contentProperties.contentType%3A(image*)+AND+status%3A(AVAILABLE*)&limit=15&lowResThumbnail=true&searchOnFamily=true&sort=%5B%27contentProperties.contentDate+DESC%27%5D&tempLink=true&shareId=qFervNlenYwkjdQ1o26YOsWhld5fnsJ0t89xbcv2Vep&offset=0&resourceVersion=V2&ContentType=JSON&_=1660508015523'
- );
+ const supabaseUrl = process.env.SUPABASE_NEXT_PUBLIC_SUPABASE_URL || '';
+ const supabaseAnonKey =
+ process.env.SUPABASE_NEXT_PUBLIC_SUPABASE_ANON_KEY || '';
- if (!response?.body) {
- return [];
+ const supabase = createClient(supabaseUrl, supabaseAnonKey, {
+ global: {
+ headers: {
+ Authorization: `Bearer ${process.env.SUPABASE_SUPABASE_SERVICE_ROLE_KEY}`,
+ },
+ },
+ });
+ const { data, error } = await supabase
+ .from('gallery-images')
+ .select('url, name, height, width')
+ .limit(30)
+ .order('created_at', { ascending: false });
+
+ if (data === null) {
+ throw new Error(error?.message);
}
- const data = await response.json();
-
- const images = data.data.map(
- (photo: {
- contentProperties: { image: { height: number; width: number } };
- tempLink: string;
- name: string;
- }) => {
- const { height: originalHeight, width: originalWidth } =
- photo.contentProperties.image;
- const dimensionsRatio = originalHeight / originalWidth;
- const thumbnailFitWidth =
- originalHeight > 200
- ? [200, 200 / dimensionsRatio]
- : [originalHeight, originalWidth];
- const thumbnailFit =
- originalWidth > 150
- ? [thumbnailFitWidth[0] * dimensionsRatio, 150]
- : thumbnailFitWidth;
-
- return {
- src: photo.tempLink,
- name: photo.name,
- dimensions: {
- ratio: dimensionsRatio,
- thumbnail: {
- height: thumbnailFit[0],
- width: thumbnailFit[1],
- },
- original: {
- height: originalHeight,
- width: originalWidth,
- },
+ const images = data.map((photo) => {
+ const { height: originalHeight, width: originalWidth } = photo;
+ const dimensionsRatio = originalHeight / originalWidth;
+ const thumbnailFitWidth =
+ originalHeight > 200
+ ? [200, 200 / dimensionsRatio]
+ : [originalHeight, originalWidth];
+ const thumbnailFit =
+ originalWidth > 150
+ ? [thumbnailFitWidth[0] * dimensionsRatio, 150]
+ : thumbnailFitWidth;
+
+ return {
+ src: photo.url,
+ name: photo.name,
+ dimensions: {
+ ratio: dimensionsRatio,
+ thumbnail: {
+ height: thumbnailFit[0],
+ width: thumbnailFit[1],
},
- };
- }
- );
+ original: {
+ height: originalHeight,
+ width: originalWidth,
+ },
+ },
+ };
+ });
return {
props: {
diff --git a/supabase/.gitignore b/supabase/.gitignore
new file mode 100644
index 0000000..a3ad880
--- /dev/null
+++ b/supabase/.gitignore
@@ -0,0 +1,4 @@
+# Supabase
+.branches
+.temp
+.env
diff --git a/supabase/config.toml b/supabase/config.toml
new file mode 100644
index 0000000..1744970
--- /dev/null
+++ b/supabase/config.toml
@@ -0,0 +1,161 @@
+# A string used to distinguish different Supabase projects on the same host. Defaults to the
+# working directory name when running `supabase init`.
+project_id = "bertuz.github.io"
+
+[api]
+enabled = true
+# Port to use for the API URL.
+port = 54321
+# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API
+# endpoints. public and storage are always included.
+schemas = ["public", "storage", "graphql_public"]
+# Extra schemas to add to the search_path of every request. public is always included.
+extra_search_path = ["public", "extensions"]
+# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size
+# for accidental or malicious requests.
+max_rows = 1000
+
+[db]
+# Port to use for the local database URL.
+port = 54322
+# Port used by db diff command to initialize the shadow database.
+shadow_port = 54320
+# The database major version to use. This has to be the same as your remote database's. Run `SHOW
+# server_version;` on the remote database to check.
+major_version = 15
+
+[db.pooler]
+enabled = false
+# Port to use for the local connection pooler.
+port = 54329
+# Specifies when a server connection can be reused by other clients.
+# Configure one of the supported pooler modes: `transaction`, `session`.
+pool_mode = "transaction"
+# How many server connections to allow per user/database pair.
+default_pool_size = 20
+# Maximum number of client connections allowed.
+max_client_conn = 100
+
+[realtime]
+enabled = true
+# Bind realtime via either IPv4 or IPv6. (default: IPv6)
+# ip_version = "IPv6"
+# The maximum length in bytes of HTTP request headers. (default: 4096)
+# max_header_length = 4096
+
+[studio]
+enabled = true
+# Port to use for Supabase Studio.
+port = 54323
+# External URL of the API server that frontend connects to.
+api_url = "http://127.0.0.1"
+# OpenAI API Key to use for Supabase AI in the Supabase Studio.
+openai_api_key = "env(OPENAI_API_KEY)"
+
+# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they
+# are monitored, and you can view the emails that would have been sent from the web interface.
+[inbucket]
+enabled = true
+# Port to use for the email testing server web interface.
+port = 54324
+# Uncomment to expose additional ports for testing user applications that send emails.
+# smtp_port = 54325
+# pop3_port = 54326
+
+[storage]
+enabled = true
+# The maximum file size allowed (e.g. "5MB", "500KB").
+file_size_limit = "50MiB"
+
+[auth]
+enabled = true
+# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used
+# in emails.
+site_url = "http://127.0.0.1:3000"
+# A list of *exact* URLs that auth providers are permitted to redirect to post authentication.
+additional_redirect_urls = ["https://127.0.0.1:3000"]
+# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week).
+jwt_expiry = 3600
+# If disabled, the refresh token will never expire.
+enable_refresh_token_rotation = true
+# Allows refresh tokens to be reused after expiry, up to the specified interval in seconds.
+# Requires enable_refresh_token_rotation = true.
+refresh_token_reuse_interval = 10
+# Allow/disallow new user signups to your project.
+enable_signup = true
+# Allow/disallow testing manual linking of accounts
+enable_manual_linking = false
+
+[auth.email]
+# Allow/disallow new user signups via email to your project.
+enable_signup = true
+# If enabled, a user will be required to confirm any email change on both the old, and new email
+# addresses. If disabled, only the new email is required to confirm.
+double_confirm_changes = true
+# If enabled, users need to confirm their email address before signing in.
+enable_confirmations = false
+
+# Uncomment to customize email template
+# [auth.email.template.invite]
+# subject = "You have been invited"
+# content_path = "./supabase/templates/invite.html"
+
+[auth.sms]
+# Allow/disallow new user signups via SMS to your project.
+enable_signup = true
+# If enabled, users need to confirm their phone number before signing in.
+enable_confirmations = false
+# Template for sending OTP to users
+template = "Your code is {{ .Code }} ."
+
+# Use pre-defined map of phone number to OTP for testing.
+[auth.sms.test_otp]
+# 4152127777 = "123456"
+
+# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used.
+[auth.hook.custom_access_token]
+# enabled = true
+# uri = "pg-functions:////"
+
+
+# Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`.
+[auth.sms.twilio]
+enabled = false
+account_sid = ""
+message_service_sid = ""
+# DO NOT commit your Twilio auth token to git. Use environment variable substitution instead:
+auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)"
+
+# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`,
+# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`,
+# `twitter`, `slack`, `spotify`, `workos`, `zoom`.
+[auth.external.apple]
+enabled = false
+client_id = ""
+# DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead:
+secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)"
+# Overrides the default auth redirectUrl.
+redirect_uri = ""
+# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure,
+# or any other third-party OIDC providers.
+url = ""
+
+[analytics]
+enabled = false
+port = 54327
+vector_port = 54328
+# Configure one of the supported backends: `postgres`, `bigquery`.
+backend = "postgres"
+
+# Experimental features may be deprecated any time
+[experimental]
+# Configures Postgres storage engine to use OrioleDB (S3)
+orioledb_version = ""
+# Configures S3 bucket URL, eg. .s3-.amazonaws.com
+s3_host = "env(S3_HOST)"
+# Configures S3 bucket region, eg. us-east-1
+s3_region = "env(S3_REGION)"
+# Configures AWS_ACCESS_KEY_ID for S3 bucket
+s3_access_key = "env(S3_ACCESS_KEY)"
+# Configures AWS_SECRET_ACCESS_KEY for S3 bucket
+s3_secret_key = "env(S3_SECRET_KEY)"
diff --git a/supabase/functions/after_insert_image/index.ts b/supabase/functions/after_insert_image/index.ts
new file mode 100644
index 0000000..f24f8d6
--- /dev/null
+++ b/supabase/functions/after_insert_image/index.ts
@@ -0,0 +1,50 @@
+import { createClient } from 'jsr:@supabase/supabase-js@2';
+import sizeOf from 'npm:buffer-image-size';
+
+import { Buffer } from 'node:buffer';
+
+const SUPABASE_DOMAIN = Deno.env.get('SUPABASE_URL');
+const IMAGES_URL = `${SUPABASE_DOMAIN}/storage/v1/object/public/photos`;
+
+Deno.serve(async (req) => {
+ const { 'new-image': image } = await req.json();
+
+ const { name: nameWithExtension, id } = image;
+ console.log('id', id);
+ const name = nameWithExtension.replace(/\.[a-zA-Z0-9]{1,3}$/, '');
+ const url = `${IMAGES_URL}/${nameWithExtension}`;
+ const contentImage = await fetch(url, {
+ method: 'GET',
+ });
+ const imageBuffer = Buffer.from(await contentImage.arrayBuffer());
+ const imageSize = sizeOf(imageBuffer);
+
+ const dataToSend = {
+ id,
+ url,
+ height: imageSize.height,
+ width: imageSize.width,
+ name,
+ };
+ console.log(dataToSend);
+ console.log(req.headers.get('Authorization'));
+
+ const supabaseClient = createClient(
+ Deno.env.get('SUPABASE_URL') ?? '',
+ Deno.env.get('SUPABASE_ANON_KEY') ?? '',
+ {
+ global: { headers: { Authorization: req.headers.get('Authorization')! } },
+ }
+ );
+
+ const { data, error } = await supabaseClient
+ .from('gallery-images')
+ .insert(dataToSend);
+ console.log(data);
+ console.log(error);
+ console.log(` inserted image data ${data}, error (if any): ${error}`);
+
+ return new Response(JSON.stringify('Gotcha'), {
+ headers: { 'Content-Type': 'application/json' },
+ });
+});
diff --git a/supabase/migrations/20240914225051_remote_schema.sql b/supabase/migrations/20240914225051_remote_schema.sql
new file mode 100644
index 0000000..179ee71
--- /dev/null
+++ b/supabase/migrations/20240914225051_remote_schema.sql
@@ -0,0 +1,109 @@
+
+SET statement_timeout = 0;
+SET lock_timeout = 0;
+SET idle_in_transaction_session_timeout = 0;
+SET client_encoding = 'UTF8';
+SET standard_conforming_strings = on;
+SELECT pg_catalog.set_config('search_path', '', false);
+SET check_function_bodies = false;
+SET xmloption = content;
+SET client_min_messages = warning;
+SET row_security = off;
+
+CREATE EXTENSION IF NOT EXISTS "pgsodium" WITH SCHEMA "pgsodium";
+
+COMMENT ON SCHEMA "public" IS 'standard public schema';
+
+CREATE EXTENSION IF NOT EXISTS "pg_graphql" WITH SCHEMA "graphql";
+
+CREATE EXTENSION IF NOT EXISTS "pg_stat_statements" WITH SCHEMA "extensions";
+
+CREATE EXTENSION IF NOT EXISTS "pgcrypto" WITH SCHEMA "extensions";
+
+CREATE EXTENSION IF NOT EXISTS "pgjwt" WITH SCHEMA "extensions";
+
+CREATE EXTENSION IF NOT EXISTS "supabase_vault" WITH SCHEMA "vault";
+
+CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA "extensions";
+
+CREATE OR REPLACE FUNCTION "public"."after_insert_image"() RETURNS "text"
+ LANGUAGE "plpgsql"
+ AS $$
+declare
+ project_id text;
+ private_key text;
+BEGIN
+select decrypted_secret into project_id
+from vault.decrypted_secrets
+where name = 'PROJECT_ID';
+select decrypted_secret into private_key
+from vault.decrypted_secrets
+where name = 'PRIVATE_KEY';
+ perform "net"."http_post"(
+ 'https://'||project_id||'.supabase.co/functions/v1/insert-image-metadata'::text,
+ jsonb_build_object(
+ 'new-image', to_jsonb(new.*)
+ ),
+ headers:='{"Content-Type": "application/json", "Authorization": "Bearer "||private_key||""}'::jsonb
+ ) as request_id;
+
+ RETURN 'https://'||project_id;
+END;
+$$;
+
+ALTER FUNCTION "public"."after_insert_image"() OWNER TO "postgres";
+
+SET default_tablespace = '';
+
+SET default_table_access_method = "heap";
+
+CREATE TABLE IF NOT EXISTS "public"."gallery-images" (
+ "id" "uuid" NOT NULL,
+ "created_at" timestamp with time zone DEFAULT "now"() NOT NULL,
+ "url" "text" NOT NULL,
+ "name" "text" NOT NULL,
+ "height" bigint NOT NULL,
+ "width" bigint NOT NULL
+);
+
+ALTER TABLE "public"."gallery-images" OWNER TO "postgres";
+
+ALTER TABLE ONLY "public"."gallery-images"
+ ADD CONSTRAINT "gallery-images_pkey" PRIMARY KEY ("id");
+
+ALTER TABLE ONLY "public"."gallery-images"
+ ADD CONSTRAINT "gallery-images_id_fkey" FOREIGN KEY ("id") REFERENCES "storage"."objects"("id") ON UPDATE CASCADE ON DELETE CASCADE;
+
+ALTER TABLE "public"."gallery-images" ENABLE ROW LEVEL SECURITY;
+
+ALTER PUBLICATION "supabase_realtime" OWNER TO "postgres";
+
+GRANT USAGE ON SCHEMA "public" TO "postgres";
+GRANT USAGE ON SCHEMA "public" TO "anon";
+GRANT USAGE ON SCHEMA "public" TO "authenticated";
+GRANT USAGE ON SCHEMA "public" TO "service_role";
+
+GRANT ALL ON FUNCTION "public"."after_insert_image"() TO "anon";
+GRANT ALL ON FUNCTION "public"."after_insert_image"() TO "authenticated";
+GRANT ALL ON FUNCTION "public"."after_insert_image"() TO "service_role";
+
+GRANT ALL ON TABLE "public"."gallery-images" TO "anon";
+GRANT ALL ON TABLE "public"."gallery-images" TO "authenticated";
+GRANT ALL ON TABLE "public"."gallery-images" TO "service_role";
+
+ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "postgres";
+ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "anon";
+ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "authenticated";
+ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "service_role";
+
+ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "postgres";
+ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "anon";
+ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "authenticated";
+ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "service_role";
+
+ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "postgres";
+ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "anon";
+ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "authenticated";
+ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "service_role";
+
+RESET ALL;
diff --git a/supabase/migrations/20240915114926_remote_schema.sql b/supabase/migrations/20240915114926_remote_schema.sql
new file mode 100644
index 0000000..dec2aa6
--- /dev/null
+++ b/supabase/migrations/20240915114926_remote_schema.sql
@@ -0,0 +1,89 @@
+drop function if exists "public"."after_insert_image"();
+
+create table "public"."ress" (
+ "content" text
+);
+
+
+set check_function_bodies = off;
+
+CREATE OR REPLACE FUNCTION public.trigger_update_image_metadata()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ SECURITY DEFINER
+AS $function$
+declare
+ project_id text;
+ private_key text;
+ net_req_id bigint;
+BEGIN
+select decrypted_secret into project_id
+from vault.decrypted_secrets
+where name = 'PROJECT_ID';
+
+select decrypted_secret into private_key
+from vault.decrypted_secrets
+where name = 'PRIVATE_KEY';
+
+select
+ net.http_post(
+ url:='https://'||project_id||'.supabase.co/functions/v1/after_insert_image',
+ body:=jsonb_build_object(
+ 'new-image', to_jsonb(new.*)
+ ),
+ headers:=json_build_object('Authorization', 'Bearer '||private_key)::jsonb
+ ) as request_id into net_req_id;
+ RETURN new;
+exception
+ when others then
+ raise exception 'An error occurred: %', SQLERRM;
+end;
+$function$
+;
+
+grant delete on table "public"."ress" to "anon";
+
+grant insert on table "public"."ress" to "anon";
+
+grant references on table "public"."ress" to "anon";
+
+grant select on table "public"."ress" to "anon";
+
+grant trigger on table "public"."ress" to "anon";
+
+grant truncate on table "public"."ress" to "anon";
+
+grant update on table "public"."ress" to "anon";
+
+grant delete on table "public"."ress" to "authenticated";
+
+grant insert on table "public"."ress" to "authenticated";
+
+grant references on table "public"."ress" to "authenticated";
+
+grant select on table "public"."ress" to "authenticated";
+
+grant trigger on table "public"."ress" to "authenticated";
+
+grant truncate on table "public"."ress" to "authenticated";
+
+grant update on table "public"."ress" to "authenticated";
+
+grant delete on table "public"."ress" to "service_role";
+
+grant insert on table "public"."ress" to "service_role";
+
+grant references on table "public"."ress" to "service_role";
+
+grant select on table "public"."ress" to "service_role";
+
+grant trigger on table "public"."ress" to "service_role";
+
+grant truncate on table "public"."ress" to "service_role";
+
+grant update on table "public"."ress" to "service_role";
+
+
+CREATE TRIGGER update_image_metadata
+ AFTER INSERT ON storage.objects FOR EACH row
+ EXECUTE FUNCTION public.trigger_update_image_metadata();
diff --git a/supabase/seed.sql b/supabase/seed.sql
new file mode 100644
index 0000000..e69de29
diff --git a/tsconfig.json b/tsconfig.json
index 756c800..e97a164 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -36,6 +36,7 @@
"**/*.tsx"
],
"exclude": [
- "node_modules"
+ "node_modules",
+ "supabase"
]
}
diff --git a/yarn.lock b/yarn.lock
index 6f493c8..48dfbb9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1917,6 +1917,63 @@
dependencies:
"@babel/core" "^7.17.9"
+"@supabase/auth-js@2.65.0":
+ version "2.65.0"
+ resolved "https://registry.yarnpkg.com/@supabase/auth-js/-/auth-js-2.65.0.tgz#e345c492f8cbc31cd6289968eae0e349ff0f39e9"
+ integrity sha512-+wboHfZufAE2Y612OsKeVP4rVOeGZzzMLD/Ac3HrTQkkY4qXNjI6Af9gtmxwccE5nFvTiF114FEbIQ1hRq5uUw==
+ dependencies:
+ "@supabase/node-fetch" "^2.6.14"
+
+"@supabase/functions-js@2.4.1":
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/@supabase/functions-js/-/functions-js-2.4.1.tgz#373e75f8d3453bacd71fb64f88d7a341d7b53ad7"
+ integrity sha512-8sZ2ibwHlf+WkHDUZJUXqqmPvWQ3UHN0W30behOJngVh/qHHekhJLCFbh0AjkE9/FqqXtf9eoVvmYgfCLk5tNA==
+ dependencies:
+ "@supabase/node-fetch" "^2.6.14"
+
+"@supabase/node-fetch@2.6.15", "@supabase/node-fetch@^2.6.14":
+ version "2.6.15"
+ resolved "https://registry.yarnpkg.com/@supabase/node-fetch/-/node-fetch-2.6.15.tgz#731271430e276983191930816303c44159e7226c"
+ integrity sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==
+ dependencies:
+ whatwg-url "^5.0.0"
+
+"@supabase/postgrest-js@1.16.1":
+ version "1.16.1"
+ resolved "https://registry.yarnpkg.com/@supabase/postgrest-js/-/postgrest-js-1.16.1.tgz#68dfa0581d8ae4296378cb8815bbde3f4602aef5"
+ integrity sha512-EOSEZFm5pPuCPGCmLF1VOCS78DfkSz600PBuvBND/IZmMciJ1pmsS3ss6TkB6UkuvTybYiBh7gKOYyxoEO3USA==
+ dependencies:
+ "@supabase/node-fetch" "^2.6.14"
+
+"@supabase/realtime-js@2.10.2":
+ version "2.10.2"
+ resolved "https://registry.yarnpkg.com/@supabase/realtime-js/-/realtime-js-2.10.2.tgz#c2b42d17d723d2d2a9146cfad61dc3df1ce3127e"
+ integrity sha512-qyCQaNg90HmJstsvr2aJNxK2zgoKh9ZZA8oqb7UT2LCh3mj9zpa3Iwu167AuyNxsxrUE8eEJ2yH6wLCij4EApA==
+ dependencies:
+ "@supabase/node-fetch" "^2.6.14"
+ "@types/phoenix" "^1.5.4"
+ "@types/ws" "^8.5.10"
+ ws "^8.14.2"
+
+"@supabase/storage-js@2.7.0":
+ version "2.7.0"
+ resolved "https://registry.yarnpkg.com/@supabase/storage-js/-/storage-js-2.7.0.tgz#9ff322d2c3b141087aa34115cf14205e4980ce75"
+ integrity sha512-iZenEdO6Mx9iTR6T7wC7sk6KKsoDPLq8rdu5VRy7+JiT1i8fnqfcOr6mfF2Eaqky9VQzhP8zZKQYjzozB65Rig==
+ dependencies:
+ "@supabase/node-fetch" "^2.6.14"
+
+"@supabase/supabase-js@^2.45.4":
+ version "2.45.4"
+ resolved "https://registry.yarnpkg.com/@supabase/supabase-js/-/supabase-js-2.45.4.tgz#0bcf8722f1732dfe3e4c5190d23e3938dcc689c3"
+ integrity sha512-E5p8/zOLaQ3a462MZnmnz03CrduA5ySH9hZyL03Y+QZLIOO4/Gs8Rdy4ZCKDHsN7x0xdanVEWWFN3pJFQr9/hg==
+ dependencies:
+ "@supabase/auth-js" "2.65.0"
+ "@supabase/functions-js" "2.4.1"
+ "@supabase/node-fetch" "2.6.15"
+ "@supabase/postgrest-js" "1.16.1"
+ "@supabase/realtime-js" "2.10.2"
+ "@supabase/storage-js" "2.7.0"
+
"@svgr/babel-plugin-add-jsx-attribute@^6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.0.0.tgz#bd6d1ff32a31b82b601e73672a789cc41e84fe18"
@@ -2300,6 +2357,11 @@
resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb"
integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==
+"@types/phoenix@^1.5.4":
+ version "1.6.5"
+ resolved "https://registry.yarnpkg.com/@types/phoenix/-/phoenix-1.6.5.tgz#5654e14ec7ad25334a157a20015996b6d7d2075e"
+ integrity sha512-xegpDuR+z0UqG9fwHqNoy3rI7JDlvaPh2TY47Fl80oq6g+hXT+c/LEuE43X48clZ6lOfANl5WrPur9fYO1RJ/w==
+
"@types/pixelmatch@*":
version "5.2.4"
resolved "https://registry.yarnpkg.com/@types/pixelmatch/-/pixelmatch-5.2.4.tgz#ca145cc5ede1388c71c68edf2d1f5190e5ddd0f6"
@@ -2405,6 +2467,13 @@
"@types/node" "*"
"@types/webidl-conversions" "*"
+"@types/ws@^8.5.10":
+ version "8.5.12"
+ resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e"
+ integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==
+ dependencies:
+ "@types/node" "*"
+
"@types/yargs-parser@*":
version "21.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b"
@@ -3111,6 +3180,13 @@ buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+buffer-image-size@^0.6.4:
+ version "0.6.4"
+ resolved "https://registry.yarnpkg.com/buffer-image-size/-/buffer-image-size-0.6.4.tgz#357e8173e951ced3b5a2785c695993aa29dbcac4"
+ integrity sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==
+ dependencies:
+ "@types/node" "*"
+
buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
@@ -3685,9 +3761,9 @@ dir-glob@^3.0.1:
dependencies:
path-type "^4.0.0"
-"docker-chromium@github:bertuz/docker-chromium#specify-docker-build":
+"docker-chromium@github:bertuz/docker-chromium#explicit-platform":
version "1.4.2"
- resolved "https://codeload.github.com/bertuz/docker-chromium/tar.gz/6f237a5576458731310b162695a4068a6ca03a0c"
+ resolved "https://codeload.github.com/bertuz/docker-chromium/tar.gz/484c52d91e102d88e37d4ce7d753da42e9a21384"
dependencies:
colors "^1.4.0"
request "^2.88.2"
@@ -5030,6 +5106,11 @@ ieee754@^1.1.13, ieee754@^1.2.1:
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+ignore-loader@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/ignore-loader/-/ignore-loader-0.1.2.tgz#d81f240376d0ba4f0d778972c3ad25874117a463"
+ integrity sha512-yOJQEKrNwoYqrWLS4DcnzM7SEQhRKis5mB+LdKKh4cPmGYlLPR0ozRzHV5jmEk2IxptqJNQA5Cc0gw8Fj12bXA==
+
ignore@^5.2.0, ignore@^5.2.4:
version "5.2.4"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
@@ -5719,9 +5800,9 @@ jest-pnp-resolver@^1.2.2:
"jest-puppeteer-docker@https://github.com/bertuz/jest-puppeteer-docker.git#specify-docker-build":
version "1.4.2"
- resolved "https://github.com/bertuz/jest-puppeteer-docker.git#f814cb2761337281a10dfb294a7d58025551ddc6"
+ resolved "https://github.com/bertuz/jest-puppeteer-docker.git#30ca45d8b7dfd077ab09233aceac20cb4571b7fc"
dependencies:
- docker-chromium "github:bertuz/docker-chromium#specify-docker-build"
+ docker-chromium "github:bertuz/docker-chromium#explicit-platform"
find-node-modules "^2.0.0"
jest-puppeteer "^4.4.0"
@@ -8574,6 +8655,11 @@ ws@8.8.0, ws@^8.2.3:
resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769"
integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==
+ws@^8.14.2:
+ version "8.18.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
+ integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
+
xml-name-validator@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"