Skip to content

Commit e11f3de

Browse files
authored
Merge pull request #43 from khaykingleb/use-supabase-storage
Use supabase storage
2 parents 82f8204 + 45c4b60 commit e11f3de

File tree

20 files changed

+149
-38
lines changed

20 files changed

+149
-38
lines changed

.github/workflows/release.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,5 @@ jobs:
5656
- name: Link Supabase to Project
5757
run: supabase link --project-ref $PROJECT_ID
5858

59-
- name: Run Migrations and Seeds
60-
run: supabase db push --include-seed
59+
- name: Run Migrations
60+
run: supabase db push

app/components/atoms/Avatar.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66
export const Avatar = () => {
77
return (
8-
<div className="h-48 w-48 overflow-hidden rounded-full sm:h-64 sm:w-64">
8+
<div className="h-48 w-48 overflow-hidden rounded-full sm:h-80 sm:w-80">
99
<img
1010
src="/avatar.webp"
1111
alt="Avatar"

app/root.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ export const links: LinksFunction = () => {
6868
},
6969

7070
// Images
71-
// TODO: move to webp
7271
{
7372
rel: "preload",
7473
as: "image",
@@ -126,8 +125,8 @@ export function ErrorBoundary() {
126125
</Link>
127126
</main>
128127
<Footer />
129-
<Scripts />
130128
<ScrollRestoration />
129+
<Scripts />
131130
</body>
132131
</html>
133132
);

app/routes/_index.tsx

+13-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { SEOHandle } from "@nasa-gcn/remix-seo";
22
import { MetaFunction } from "@remix-run/node";
33
import { Link } from "@remix-run/react";
4+
import { GoArrowUpRight } from "react-icons/go";
45

56
import { AsciiDonut } from "~/components/molecules/AsciiDonut";
67
import { Footer } from "~/components/organisms/Footer";
@@ -71,15 +72,21 @@ export default function IndexRoute() {
7172
to="/Gleb_Khaykin.pdf"
7273
prefetch="intent"
7374
target="_blank"
74-
className="text-xl sm:text-2xl"
75+
className="inline-flex items-center gap-1 text-xl sm:text-2xl"
7576
>
76-
CV
77+
<GoArrowUpRight className="h-6 w-6" /> CV
7778
</Link>
78-
<Link to="/blog" className="text-xl sm:text-2xl">
79-
↗ Blog
79+
<Link
80+
to="/blog"
81+
className="inline-flex items-center gap-1 text-xl sm:text-2xl"
82+
>
83+
<GoArrowUpRight className="h-6 w-6" /> Blog
8084
</Link>
81-
<Link to="/contact" className="text-xl sm:text-2xl">
82-
↗ Contact
85+
<Link
86+
to="/contact"
87+
className="inline-flex items-center gap-1 text-xl sm:text-2xl"
88+
>
89+
<GoArrowUpRight className="h-6 w-6" /> Contact
8390
</Link>
8491
</div>
8592
</div>

app/routes/blog.$slug.tsx

+7-5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { NotionRenderer } from "vendor/react-notion-x/packages/react-notion-x";
2727

2828
import { Footer } from "~/components/organisms/Footer";
2929
import { Tables } from "~/integrations/supabase/database.types";
30+
import { getPostImageUrl } from "~/utils/supabase";
3031
import { useTheme } from "~/utils/theme";
3132

3233
const Equation = React.lazy(() =>
@@ -111,11 +112,11 @@ const NotionPage = ({ recordMap }: { recordMap: ExtendedRecordMap }) => {
111112
export const loader: LoaderFunction = async ({
112113
params,
113114
}: LoaderFunctionArgs) => {
114-
const supabase = createClient(
115+
const supabaseClient = createClient(
115116
process.env.SUPABASE_URL!,
116117
process.env.SUPABASE_SERVICE_ROLE_KEY!,
117118
);
118-
const { data, error } = await supabase
119+
const { data, error } = await supabaseClient
119120
.from("posts")
120121
.select("*")
121122
.eq("slug", params.slug)
@@ -124,6 +125,7 @@ export const loader: LoaderFunction = async ({
124125

125126
if (error) throw new Response("Failed to load post", { status: 500 });
126127
if (!data) throw new Response("Post not found", { status: 404 });
128+
data.image_url = getPostImageUrl(supabaseClient, data.image_url);
127129

128130
const notion = new NotionAPI();
129131
const recordMapPromise = notion.getPage(data.notion_page_id);
@@ -186,11 +188,11 @@ export const handle: SEOHandle = {
186188
* @returns The sitemap.xml entries for the route
187189
*/
188190
getSitemapEntries: async () => {
189-
const supabase = createClient(
191+
const supabaseClient = createClient(
190192
process.env.SUPABASE_URL!,
191193
process.env.SUPABASE_SERVICE_ROLE_KEY!,
192194
);
193-
const { data: posts } = await supabase
195+
const { data: posts } = await supabaseClient
194196
.from("posts")
195197
.select("slug")
196198
.returns<Tables<"posts">[]>();
@@ -268,7 +270,7 @@ export default function BlogPostRoute() {
268270
const { post, recordMap } = useLoaderData<typeof loader>();
269271

270272
return (
271-
<div className="mx-auto flex min-h-screen w-full max-w-[800px] flex-col px-4 sm:px-6 lg:px-8">
273+
<div className="mx-auto flex min-h-screen w-full max-w-[850px] flex-col px-4 sm:px-6 lg:px-8">
272274
<header className="mt-4">
273275
<div className="flex items-center gap-2 text-3xl font-semibold sm:text-4xl">
274276
<Link to="/blog">&lt;</Link>

app/routes/blog._index.tsx

+8-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { Footer } from "~/components/organisms/Footer";
1515
import { Header } from "~/components/organisms/Header";
1616
import { Pagination } from "~/components/organisms/Pagination";
1717
import { Tables } from "~/integrations/supabase/database.types";
18+
import { getPostImageUrl } from "~/utils/supabase";
1819

1920
export const handle: SEOHandle = {
2021
/**
@@ -60,18 +61,21 @@ export const meta: MetaFunction = () => {
6061
};
6162

6263
export const loader = async () => {
63-
const supabase = createClient(
64+
const supabaseClient = createClient(
6465
process.env.SUPABASE_URL!,
6566
process.env.SUPABASE_SERVICE_ROLE_KEY!,
6667
);
6768

68-
const postsPromise = supabase
69+
const postsPromise = supabaseClient
6970
.from("posts")
7071
.select("*")
7172
.returns<Tables<"posts">[]>()
7273
.then(async ({ data, error }) => {
7374
if (error) throw new Response("Failed to load posts", { status: 500 });
74-
return data.reverse();
75+
return data.reverse().map((post) => ({
76+
...post,
77+
image_url: getPostImageUrl(supabaseClient, post.image_url),
78+
}));
7579
});
7680

7781
return defer({ posts: postsPromise as Promise<Tables<"posts">[]> });
@@ -204,7 +208,7 @@ export default function BlogRoute() {
204208
const { posts } = useLoaderData<typeof loader>();
205209

206210
return (
207-
<div className="mx-auto flex min-h-screen w-full max-w-[800px] flex-grow flex-col px-4 sm:px-6 lg:px-8">
211+
<div className="mx-auto flex min-h-screen w-full max-w-[850px] flex-grow flex-col px-4 sm:px-6 lg:px-8">
208212
<div className="flex flex-grow flex-col">
209213
<Suspense fallback={<LoadingFallback />}>
210214
<Await resolve={posts}>

app/routes/contact.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export const meta: MetaFunction = () => {
5656
*/
5757
export default function ContactRoute() {
5858
return (
59-
<div className="mx-auto flex min-h-screen w-full max-w-[800px] flex-grow flex-col px-4 sm:px-6 lg:px-8">
59+
<div className="mx-auto flex min-h-screen w-full max-w-[850px] flex-grow flex-col px-4 sm:px-6 lg:px-8">
6060
<div className="flex flex-grow flex-col">
6161
<Header headerName="Contact" />
6262
<main className="flex flex-grow flex-col">

app/styles/notion.css

+2-2
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@
9696
.notion-code,
9797
.notion-code pre,
9898
.notion-code code {
99-
color: #24292e !important;
100-
background-color: #f7f6f3 !important;
99+
color: hsl(var(--bc)) !important;
100+
background: hsl(var(--b1)) !important;
101101
}
102102

103103
.katex {

app/utils/supabase.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { SupabaseClient } from "@supabase/supabase-js";
2+
3+
/**
4+
* Get a public URL for an image stored in Supabase Storage
5+
*
6+
* @param supabaseClient - Supabase client instance
7+
* @param path - Path to the image in storage
8+
* @returns Public URL for the image
9+
*/
10+
export function getPostImageUrl(
11+
supabaseClient: SupabaseClient,
12+
path: string,
13+
): string {
14+
const { data } = supabaseClient.storage.from("posts").getPublicUrl(path);
15+
return data.publicUrl;
16+
}

cspell.config.js

+2
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,7 @@ export default {
3232
"changefreq",
3333
"doesn",
3434
"supabase",
35+
"jsonencode",
36+
"signup",
3537
],
3638
};

infra/.terraform.lock.hcl

+23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

infra/outputs.tf

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
output "supabase_url" {
2+
value = "https://${supabase_project.this.id}.supabase.co"
3+
description = "The URL of the Supabase project"
4+
}
5+
6+
output "supabase_anon_key" {
7+
value = data.supabase_apikeys.this.anon_key
8+
description = "The anon key of the Supabase project"
9+
sensitive = true
10+
}
11+
12+
output "supabase_service_role_key" {
13+
value = data.supabase_apikeys.this.service_role_key
14+
description = "The service role key of the Supabase project"
15+
sensitive = true
16+
}

infra/provider.tf

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
provider "vercel" {
22
api_token = var.vercel_api_token
33
}
4+
5+
provider "supabase" {
6+
access_token = var.supabase_access_token
7+
}

infra/supabase.tf

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
resource "supabase_project" "this" {
2+
name = "supabase-db"
3+
organization_id = var.supabase_organization_id
4+
5+
region = "eu-west-2"
6+
database_password = var.supabase_database_password
7+
instance_size = "nano"
8+
9+
lifecycle {
10+
ignore_changes = [
11+
database_password,
12+
instance_size,
13+
]
14+
}
15+
}
16+
17+
resource "supabase_settings" "this" {
18+
project_ref = supabase_project.this.id
19+
20+
api = jsonencode({
21+
db_extra_search_path = "public, extensions"
22+
db_schema = "public,graphql_public"
23+
max_rows = 1000
24+
})
25+
26+
auth = jsonencode({
27+
disable_signup = false
28+
jwt_exp = 3600
29+
})
30+
}
31+
32+
data "supabase_apikeys" "this" {
33+
project_ref = supabase_project.this.id
34+
}

infra/terraform.tf

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ terraform {
44
source = "vercel/vercel"
55
version = "~> 2.8.0"
66
}
7+
supabase = {
8+
source = "supabase/supabase"
9+
version = "~> 1.5.1"
10+
}
711
}
812

913
required_version = "~> 1.5.4"

infra/variables.tf

+6-6
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ variable "vercel_api_token" {
44
sensitive = true
55
}
66

7-
variable "supabase_url" {
8-
description = "The Supabase URL"
7+
variable "supabase_organization_id" {
8+
description = "The ID of the Supabase organization"
99
type = string
1010
sensitive = true
1111
}
1212

13-
variable "supabase_service_role_key" {
14-
description = "The Supabase service role key"
13+
variable "supabase_access_token" {
14+
description = "The personal access token for the Supabase"
1515
type = string
1616
sensitive = true
1717
}
1818

19-
variable "supabase_anon_key" {
20-
description = "The Supabase anon key"
19+
variable "supabase_database_password" {
20+
description = "The password for the Supabase database"
2121
type = string
2222
sensitive = true
2323
}

infra/vercel.tf

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,23 @@ resource "vercel_project" "khaykingleb_com" {
1515
resource "vercel_project_environment_variable" "supabase_url" {
1616
project_id = vercel_project.khaykingleb_com.id
1717
key = "SUPABASE_URL"
18-
value = var.supabase_url
18+
value = "https://${supabase_project.this.id}.supabase.co"
1919
target = ["production", "preview", "development"]
2020
comment = "Supabase URL"
2121
}
2222

2323
resource "vercel_project_environment_variable" "supabase_anon_key" {
2424
project_id = vercel_project.khaykingleb_com.id
2525
key = "SUPABASE_ANON_KEY"
26-
value = var.supabase_anon_key
26+
value = data.supabase_apikeys.this.anon_key
2727
target = ["production", "preview", "development"]
2828
comment = "Supabase anon key for client-side code"
2929
}
3030

3131
resource "vercel_project_environment_variable" "supabase_service_role_key" {
3232
project_id = vercel_project.khaykingleb_com.id
3333
key = "SUPABASE_SERVICE_ROLE_KEY"
34-
value = var.supabase_service_role_key
34+
value = data.supabase_apikeys.this.service_role_key
3535
target = ["production", "preview"]
3636
sensitive = true
3737
comment = "Supabase service role key for server-side code"

supabase/config.toml

+5-5
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,11 @@ file_size_limit = "50MiB"
8787
# enabled = true
8888

8989
# Uncomment to configure local storage buckets
90-
# [storage.buckets.images]
91-
# public = false
92-
# file_size_limit = "50MiB"
93-
# allowed_mime_types = ["image/png", "image/jpeg"]
94-
# objects_path = "./images"
90+
[storage.buckets.posts]
91+
public = true
92+
file_size_limit = "50MiB"
93+
allowed_mime_types = ["image/png", "image/jpeg", "image/webp"]
94+
objects_path = "./seeds/storage/posts"
9595

9696
[auth]
9797
enabled = true

supabase/seeds/posts.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ values (
44
'introduction-to-digital-signal-processing',
55
'5987cc697c874323920215fbaad8cbbd', -- pragma: allowlist secret
66
array['notes', 'speech'],
7-
'/img/posts/introduction-to-digital-signal-processing.webp'
7+
'/5987cc697c874323920215fbaad8cbbd/img.webp'
88
);

0 commit comments

Comments
 (0)