Skip to content

Commit fc79657

Browse files
authored
Supabase gallery image source (#21)
Switch image gallery storage to supabase
1 parent 25d7916 commit fc79657

15 files changed

+658
-51
lines changed

.github/workflows/test.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99
jobs:
1010
test:
1111
# The type of runner that the job will run on
12-
runs-on: ubuntu-latest
12+
runs-on: ubuntu-20.04
1313

1414
# Steps represent a sequence of tasks that will be executed as part of the job
1515
steps:
@@ -27,13 +27,13 @@ jobs:
2727
- run: yarn test
2828
name: 🔬 Testing the repo
2929
- name: Archive code coverage results
30-
uses: actions/upload-artifact@v2
30+
uses: actions/upload-artifact@v4
3131
with:
3232
name: code-coverage-report
3333
path: testres.json
3434
- name: Uploading failing screenshots
3535
if: ${{ !success() }}
36-
uses: actions/upload-artifact@v2
36+
uses: actions/upload-artifact@v4
3737
with:
3838
name: failing-screenshots
3939
path: __screenshot-tests__/__image_snapshots__/__diff_output__
639 Bytes
Loading
55.3 KB
Loading

next.config.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
const nextConfig = {
33
reactStrictMode: true,
44
webpack(config) {
5+
config.module.rules.push({
6+
test: /supabase\/.*/,
7+
use: 'ignore-loader',
8+
});
59
config.module.rules.push({
610
test: /\.svg$/i,
711
issuer: /\.[jt]sx?$/,
@@ -12,6 +16,12 @@ const nextConfig = {
1216
},
1317
images: {
1418
remotePatterns: [
19+
{
20+
protocol: 'https',
21+
hostname: 'fpbswrebvsmjdwekyznx.supabase.co',
22+
port: '',
23+
pathname: '**',
24+
},
1525
{
1626
protocol: 'https',
1727
hostname: 'content-eu.drive.amazonaws.com',

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
},
1212
"dependencies": {
1313
"@emotion/react": "^11.8.2",
14+
"@supabase/supabase-js": "^2.45.4",
1415
"@types/express-useragent": "^1.0.2",
1516
"@types/jwk-to-pem": "^2.0.1",
1617
"@types/node-jose": "^1.1.10",
1718
"@types/uuid": "^8.3.4",
1819
"axios": "^0.27.2",
20+
"buffer-image-size": "^0.6.4",
1921
"express-useragent": "^1.0.15",
2022
"jest-environment-jsdom": "^28.1.1",
2123
"jwk-to-pem": "^2.0.5",
@@ -55,6 +57,7 @@
5557
"eslint-plugin-prettier": "^4.0.0",
5658
"eslint-plugin-react": "^7.29.4",
5759
"eslint-plugin-react-hooks": "^4.3.0",
60+
"ignore-loader": "^0.1.2",
5861
"jest": "^28.1.1",
5962
"jest-image-snapshot": "^5.1.0",
6063
"jest-puppeteer-docker": "https://github.com/bertuz/jest-puppeteer-docker.git#specify-docker-build",

pages/api/galleryPhotos/index.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import sizeOf from 'buffer-image-size';
2+
3+
import type { NextApiRequest, NextApiResponse } from 'next';
4+
5+
type SupabaseStorageQueryResponse = ReadonlyArray<{
6+
name: string;
7+
}>;
8+
9+
const SUPABASE_PUBLIC_KEY = process.env.SUPABASE_PUBLIC_KEY || ' ';
10+
const SUPABASE_HEADERS = new Headers({
11+
'Content-Type': 'application/json',
12+
Authorization: `Bearer ${SUPABASE_PUBLIC_KEY}`,
13+
apikey: SUPABASE_PUBLIC_KEY,
14+
});
15+
16+
type ResponseData = ReadonlyArray<{
17+
contentProperties: { image: { height: number; width: number } };
18+
url: string;
19+
name: string;
20+
}>;
21+
22+
export async function getImageData(): Promise<ResponseData> {
23+
const requestOptions = {
24+
method: 'POST',
25+
headers: SUPABASE_HEADERS,
26+
body: JSON.stringify({
27+
prefix: '',
28+
limit: 20,
29+
offset: 0,
30+
sortBy: {
31+
column: 'created_at',
32+
order: 'desc',
33+
},
34+
search: '',
35+
}),
36+
};
37+
38+
const SUPABASE_DOMAIN = process.env.SUPABASE_DOMAIN || '';
39+
const IMAGES_OBJECTS_URL = `${SUPABASE_DOMAIN}/storage/v1/object/list/photos`;
40+
const IMAGES_URL = `${SUPABASE_DOMAIN}/storage/v1/object/public/photos`;
41+
const images = (await fetch(IMAGES_OBJECTS_URL, requestOptions).then(
42+
(response) => response.json()
43+
)) as SupabaseStorageQueryResponse;
44+
45+
const imagesToReturn = [];
46+
for (const image of images) {
47+
const { name: nameWithExtension } = image;
48+
const name = nameWithExtension.replace(/\.[a-zA-Z0-9]{1,3}$/, '');
49+
const url = `${IMAGES_URL}/${nameWithExtension}`;
50+
const contentImage = await fetch(url, {
51+
method: 'GET',
52+
});
53+
const imageBuffer = Buffer.from(await contentImage.arrayBuffer());
54+
const imageSize = sizeOf(imageBuffer);
55+
56+
imagesToReturn.push({
57+
url,
58+
contentProperties: {
59+
image: { height: imageSize.height, width: imageSize.width },
60+
},
61+
name,
62+
});
63+
}
64+
65+
return imagesToReturn;
66+
}
67+
68+
export default async function handler(
69+
_: NextApiRequest,
70+
res: NextApiResponse<ResponseData>
71+
) {
72+
const imagesToReturn = await getImageData();
73+
res.status(200).json(imagesToReturn);
74+
}

pages/index.tsx

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import useScreenSize from '../utils/useScreenSize';
3333
import toBase64 from '../utils/toBase64';
3434
import { isRunningAcceptanceTest } from '../utils/testUtils';
3535

36+
import { createClient } from '@supabase/supabase-js';
37+
3638
import { Transition } from 'react-transition-group';
3739

3840
import { useEffect, useMemo, useRef, useState } from 'react';
@@ -742,6 +744,17 @@ const Home: NextPage<HomeProperties> = ({ galleryPics }) => {
742744
With that in mind, this is what I currently have in my toolbox 🧑🏽‍💻:
743745
</p>
744746
<ul>
747+
<li>
748+
Mobile
749+
<ul>
750+
<li>
751+
<TextLink href="https://github.com/gmadridsports/app-natacion">
752+
Flutter
753+
</TextLink>
754+
</li>
755+
<li>Supabase</li>
756+
</ul>
757+
</li>
745758
<li>
746759
Frontend
747760
<ul>
@@ -764,8 +777,11 @@ const Home: NextPage<HomeProperties> = ({ galleryPics }) => {
764777
<ul>
765778
<li>Agile: kanban and scrum</li>
766779
<li>Kubernetes</li>
767-
<li>Hexagonal architecture</li>
768-
<li>DDD</li>
780+
<li>Clean architectures: hexagonal, DDD, even on front</li>
781+
<li>
782+
Foster <strong>async</strong> - yet effective - communication
783+
within a team
784+
</li>
769785
</ul>
770786
</li>
771787
</ul>
@@ -994,51 +1010,55 @@ export async function getServerSideProps() {
9941010
};
9951011
}
9961012

997-
const response = await fetch(
998-
'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'
999-
);
1013+
const supabaseUrl = process.env.SUPABASE_NEXT_PUBLIC_SUPABASE_URL || '';
1014+
const supabaseAnonKey =
1015+
process.env.SUPABASE_NEXT_PUBLIC_SUPABASE_ANON_KEY || '';
10001016

1001-
if (!response?.body) {
1002-
return [];
1017+
const supabase = createClient(supabaseUrl, supabaseAnonKey, {
1018+
global: {
1019+
headers: {
1020+
Authorization: `Bearer ${process.env.SUPABASE_SUPABASE_SERVICE_ROLE_KEY}`,
1021+
},
1022+
},
1023+
});
1024+
const { data, error } = await supabase
1025+
.from('gallery-images')
1026+
.select('url, name, height, width')
1027+
.limit(30)
1028+
.order('created_at', { ascending: false });
1029+
1030+
if (data === null) {
1031+
throw new Error(error?.message);
10031032
}
10041033

1005-
const data = await response.json();
1006-
1007-
const images = data.data.map(
1008-
(photo: {
1009-
contentProperties: { image: { height: number; width: number } };
1010-
tempLink: string;
1011-
name: string;
1012-
}) => {
1013-
const { height: originalHeight, width: originalWidth } =
1014-
photo.contentProperties.image;
1015-
const dimensionsRatio = originalHeight / originalWidth;
1016-
const thumbnailFitWidth =
1017-
originalHeight > 200
1018-
? [200, 200 / dimensionsRatio]
1019-
: [originalHeight, originalWidth];
1020-
const thumbnailFit =
1021-
originalWidth > 150
1022-
? [thumbnailFitWidth[0] * dimensionsRatio, 150]
1023-
: thumbnailFitWidth;
1024-
1025-
return {
1026-
src: photo.tempLink,
1027-
name: photo.name,
1028-
dimensions: {
1029-
ratio: dimensionsRatio,
1030-
thumbnail: {
1031-
height: thumbnailFit[0],
1032-
width: thumbnailFit[1],
1033-
},
1034-
original: {
1035-
height: originalHeight,
1036-
width: originalWidth,
1037-
},
1034+
const images = data.map((photo) => {
1035+
const { height: originalHeight, width: originalWidth } = photo;
1036+
const dimensionsRatio = originalHeight / originalWidth;
1037+
const thumbnailFitWidth =
1038+
originalHeight > 200
1039+
? [200, 200 / dimensionsRatio]
1040+
: [originalHeight, originalWidth];
1041+
const thumbnailFit =
1042+
originalWidth > 150
1043+
? [thumbnailFitWidth[0] * dimensionsRatio, 150]
1044+
: thumbnailFitWidth;
1045+
1046+
return {
1047+
src: photo.url,
1048+
name: photo.name,
1049+
dimensions: {
1050+
ratio: dimensionsRatio,
1051+
thumbnail: {
1052+
height: thumbnailFit[0],
1053+
width: thumbnailFit[1],
10381054
},
1039-
};
1040-
}
1041-
);
1055+
original: {
1056+
height: originalHeight,
1057+
width: originalWidth,
1058+
},
1059+
},
1060+
};
1061+
});
10421062

10431063
return {
10441064
props: {

supabase/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Supabase
2+
.branches
3+
.temp
4+
.env

0 commit comments

Comments
 (0)