Compare commits
2 commits
9ab243fa4e
...
0f6a60bec9
Author | SHA1 | Date | |
---|---|---|---|
Kaan Barmore-Genç | 0f6a60bec9 | ||
Kaan Barmore-Genç | 30da668bd0 |
|
@ -12,14 +12,20 @@
|
|||
<meta property="article:author" content="https://bgenc.net" />
|
||||
<meta property="og:url" content={`https://bgenc.net/${data.slug}/`} />
|
||||
<meta property="article:published_time" content={formatISO(new Date(data.date))} />
|
||||
{#if data.updated}
|
||||
<meta property="article:modified_time" content={formatISO(new Date(data.updated))} />
|
||||
{/if}
|
||||
{#if data.image}
|
||||
<meta property="og:image" content={data.image} />
|
||||
<meta
|
||||
property="og:image"
|
||||
content={data.image.startsWith('https://') ? data.image : `https://bgenc.net${data.image}`}
|
||||
/>
|
||||
{:else}
|
||||
<meta property="og:image" content={`https://bgenc.net${Jpeg}`} />
|
||||
{/if}
|
||||
{#if data.excerpt}
|
||||
<meta name="description" content={data.excerpt} />
|
||||
<meta property="og:description" content={data.excerpt} />
|
||||
{#if data.description}
|
||||
<meta name="description" content={data.description} />
|
||||
<meta property="og:description" content={data.description} />
|
||||
{/if}
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:site_name" content="Kaan Barmore-Genç's Site" />
|
||||
|
@ -27,15 +33,26 @@
|
|||
|
||||
<article data-pagefind-body>
|
||||
<h1>{data.title}</h1>
|
||||
<time datetime={data.date}>
|
||||
{format(new Date(data.date), 'MMMM d, yyyy')}
|
||||
</time>
|
||||
{#if data.updated}
|
||||
<p>
|
||||
Written on <time datetime={data.date}
|
||||
>{format(new Date(data.date), 'MMMM d, yyyy')}, last updated on
|
||||
<time datetime={data.updated}
|
||||
>{format(new Date(data.updated), 'MMMM d, yyyy')}.
|
||||
</time></time
|
||||
>
|
||||
</p>
|
||||
{:else}
|
||||
<p>
|
||||
Written on <time datetime={data.date}>{format(new Date(data.date), 'MMMM d, yyyy')}.</time>
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<svelte:component this={data.content} />
|
||||
</article>
|
||||
|
||||
<style>
|
||||
time {
|
||||
display: block;
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
export async function load({ params }) {
|
||||
const post = await import(`../posts/${params.slug}.md`);
|
||||
const { title, date, excerpt, image } = post.metadata;
|
||||
const { title, date, description, image, updated } = post.metadata;
|
||||
const content = post.default;
|
||||
|
||||
return {
|
||||
content,
|
||||
title,
|
||||
excerpt,
|
||||
description,
|
||||
image,
|
||||
date,
|
||||
updated,
|
||||
slug: params.slug,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ tags:
|
|||
- react
|
||||
- web
|
||||
- dev
|
||||
description: Stop Redux from tanking your web app's performance. Follow along as I diagnose and solve a React performance issue.
|
||||
---
|
||||
|
||||
This might be obvious for some, but I was struggling with a performance issue in
|
||||
|
@ -88,7 +89,7 @@ the objects (the object is flat so I don't need recursion).
|
|||
```ts
|
||||
export function shallowEquals<
|
||||
Left extends Record<string, unknown>,
|
||||
Right extends Record<string, unknown>
|
||||
Right extends Record<string, unknown>,
|
||||
>(left: Left, right: Right) {
|
||||
if (Object.keys(left).length !== Object.keys(right).length) return false;
|
||||
for (const key of Object.keys(left)) {
|
||||
|
@ -100,7 +101,7 @@ export function shallowEquals<
|
|||
// ...
|
||||
const { access_token, site } = useAppSelector(
|
||||
(selector) => pick(selector.auth, 'access_token', 'site'),
|
||||
shallowEquals
|
||||
shallowEquals,
|
||||
);
|
||||
```
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
---
|
||||
title: "Automating My Blog With Gitea and Woodpecker"
|
||||
title: 'Automating My Blog With Gitea and Woodpecker'
|
||||
date: 2022-11-19T12:21:40-05:00
|
||||
toc: false
|
||||
images:
|
||||
tags:
|
||||
- homelab
|
||||
description: I automated my personal projects with a surprisingly simple CI/CD tool called Woodpecker. Discover how to build and deploy code from your Gitea with just Docker containers!
|
||||
---
|
||||
|
||||
I had been using Gitea for a while. If you haven't heard of it before, it's a
|
||||
|
|
|
@ -6,6 +6,7 @@ images:
|
|||
tags:
|
||||
- dev
|
||||
- web
|
||||
description: A short piece about a weird XML parsing error I had to deal with, including some Rust code.
|
||||
---
|
||||
|
||||
I've been seeing this error a lot while working on my project [Bulgur Cloud](/bulgur-cloud-intro/).
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
title: "Hosting websites without a static IP with Gandi LiveDNS"
|
||||
title: 'Hosting websites without a static IP with Gandi LiveDNS'
|
||||
date: 2022-12-29T18:11:42-05:00
|
||||
toc: false
|
||||
images:
|
||||
tags:
|
||||
- web
|
||||
- homelab
|
||||
description: Want to ditch expensive dynamic DNS and keep your home server online? I built a tool to use Gandi's free LiveDNS with lightning-fast updates – under 250ms!
|
||||
---
|
||||
|
||||
I've been hosting this website at home now for a few years. My ISP doesn't offer
|
||||
|
@ -58,6 +59,7 @@ fqdn = "kaangenc.me"
|
|||
name = "@"
|
||||
types = ["A"]
|
||||
```
|
||||
|
||||
The configuration file is a bit trimmed, but it shows the gist of everything.
|
||||
I'm updating `bgenc.net`, along with `gitea.bgenc.net`. I also update
|
||||
`kaangenc.me`, which is an old domain I was using.
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
title: "Get inferred type for a generic parameter in TypeScript"
|
||||
title: 'Get inferred type for a generic parameter in TypeScript'
|
||||
date: 2023-01-28T14:50:54-05:00
|
||||
toc: false
|
||||
images:
|
||||
tags:
|
||||
- dev
|
||||
- typescript
|
||||
description: "Dive into using TypeScript to extract inferred types, inspired by Zod's schema inference magic."
|
||||
---
|
||||
|
||||
Have you used [Zod](https://zod.dev/)? It's a very cool TypeScript library for
|
||||
|
@ -14,8 +15,8 @@ strenghts of Zod is that it can do type inference. For example,
|
|||
|
||||
```ts
|
||||
const PersonSchema = z.object({
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
name: z.string(),
|
||||
age: z.number(),
|
||||
});
|
||||
|
||||
type Person = z.infer<typeof PersonSchema>;
|
||||
|
|
|
@ -5,6 +5,7 @@ toc: false
|
|||
images:
|
||||
tags:
|
||||
- dev
|
||||
description: Let's explore how to use DevContainers to create a one-click development environment with all the tools and services you need.
|
||||
---
|
||||
|
||||
It is important to have a consistent and streamlined setup process for your
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
---
|
||||
title: Enforcing a "Do Not Merge" label with Github Actions
|
||||
date: 2023-02-18T12:33:32-05:00
|
||||
updated: 2024-05-11T12:08:00-06:00
|
||||
toc: false
|
||||
images:
|
||||
image: /img/gh-do-not-merge-action.png
|
||||
tags:
|
||||
- dev
|
||||
description: Stop accidental code deployments with a clever GitHub Actions trick using labels to prevent merging pull requests until they're truly ready.
|
||||
---
|
||||
|
||||
At my workplace, we sometimes find ourselves in situations where a PR passes all
|
||||
|
@ -103,3 +105,7 @@ This will only apply to new repositories, so you may need to add
|
|||
the label to your existing repositories. But even if you don't add the label to
|
||||
the repository, the check should not block you so you don't have to worry about
|
||||
going through all your repositories to add this label.
|
||||
|
||||
> **Update:** We have stopped using this action since. Github Action seemed to
|
||||
> occasionally get stuck, blocking us from merging PRs even after the label was
|
||||
> removed.
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
title: "Making the Slow Explicit: Dynamodb vs SQL"
|
||||
title: 'Making the Slow Explicit: Dynamodb vs SQL'
|
||||
date: 2023-02-26T15:51:19-05:00
|
||||
toc: false
|
||||
images:
|
||||
tags:
|
||||
- dev
|
||||
- web
|
||||
description: Bad SQL habits are to blame for slow databases, not NoSQL magic. Here's my thoughts on how DynamoDB forces better code for scalability.
|
||||
---
|
||||
|
||||
SQL databases like MySQL, MariaDB, and PostgreSQL are highly performant and can
|
||||
|
@ -16,7 +17,8 @@ Proponents of DynamoDB like Alex DeBrie, the author of ["The DynamoDB Book"](htt
|
|||
point to a few things for this difference: HTTP-based APIs of NoSQL databases are more efficient than TCP connections used by SQL databases,
|
||||
table joins are slow, SQL databases are designed to save disk space while NoSQL databases take advantage of large modern disks.[^1]
|
||||
|
||||
[^1]: I don't have my copy of the book handy, so I wrote these arguments from
|
||||
[^1]:
|
||||
I don't have my copy of the book handy, so I wrote these arguments from
|
||||
memory. I'm confident that I remember them correctly, but apologies if I
|
||||
misremembered some details.
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
title: 'Setting up my blog as an Onion service (Tor hidden service)'
|
||||
date: 2023-03-05T15:54:13-05:00
|
||||
toc: false
|
||||
images:
|
||||
image: /img/tor-censorship-snowflake-chart.webp
|
||||
description: See how you can use Tor and Docker to create your own hidden onion service, perfect for tech enthusiasts and privacy advocates.
|
||||
---
|
||||
|
||||
If you don't know about it, Tor is a software that helps online privacy and
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
title: 'Self Hosted Backups with Minio, Kopia, and Tailscale'
|
||||
date: 2023-04-25T00:11:31-04:00
|
||||
toc: false
|
||||
images:
|
||||
image: /img/2023-04-25.tailscale.png
|
||||
tags:
|
||||
- homelab
|
||||
description: Ditch complex SSH setups and leverage Minio's S3 compatibility with Tailscale for secure, encrypted backups.
|
||||
---
|
||||
|
||||
I've been struggling with my local backup setup for a while now.
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
---
|
||||
title: "Fully Headless Setup for Raspberry Pi"
|
||||
title: 'Fully Headless Setup for Raspberry Pi'
|
||||
date: 2023-04-27T20:40:00-04:00
|
||||
toc: false
|
||||
images:
|
||||
tags:
|
||||
- homelab
|
||||
description: Don't feel like hooking up a monitor and keyboard to your Raspberry Pi? I'll show you how to set up your Raspberry Pi for headless SSH access using just a few commands.
|
||||
---
|
||||
|
||||
I always hit this issue: I have a Raspberry Pi I want to set up, but I don't
|
||||
|
|
|
@ -6,6 +6,7 @@ images:
|
|||
tags:
|
||||
- dev
|
||||
- web
|
||||
description: Here is how to make a text area that resizes automatically and has placeholder text, with no JavaScript with this clever CSS trick!
|
||||
---
|
||||
|
||||
The HTML elements `input` and `textarea` include a `placeholder` property. This
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
title: 'Getting theme colors in JavaScript using React with DaisyUI and TailwindCSS'
|
||||
date: 2023-08-10T00:18:27-05:00
|
||||
toc: false
|
||||
images:
|
||||
image: /img/2023-08-10.chartjs.png
|
||||
tags:
|
||||
- web
|
||||
- dev
|
||||
- react
|
||||
description: This post shows you how to use JavaScript to read CSS variables and create dynamic UIs, using SWR for seamless light & dark mode switching!
|
||||
---
|
||||
|
||||
I've been building a web app using React and TailwindCSS, with DaisyUI. But
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
---
|
||||
title: "Next.js error about native node modules using bindings"
|
||||
title: 'Next.js error about native node modules using bindings'
|
||||
date: 2023-08-13T16:44:41Z
|
||||
toc: false
|
||||
images:
|
||||
tags:
|
||||
- dev
|
||||
description: Struggling to use a native node module with bindings in your Next.js app? I dive into a weird Webpack/Terser issue and a clever Next.js-specific solution to get it working smoothly.
|
||||
---
|
||||
|
||||
This might be a little niche, but I had trouble finding anyone else write about
|
||||
|
@ -35,9 +36,9 @@ bundle a module. You can configure this with the
|
|||
```js
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
experimental: {
|
||||
serverComponentsExternalPackages: ["libheif-node-dy"],
|
||||
},
|
||||
experimental: {
|
||||
serverComponentsExternalPackages: ['libheif-node-dy'],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
---
|
||||
title: "Amazon SES Production Access Approval"
|
||||
title: 'Amazon SES Production Access Approval'
|
||||
date: 2023-10-03T04:34:37Z
|
||||
toc: false
|
||||
images:
|
||||
tags:
|
||||
description: Is your Amazon SES approval getting declined? My odyssey through email services & how to finally get the green light (with code!)
|
||||
---
|
||||
|
||||
I've been setting up something for a relative who's trying to start a business,
|
||||
|
@ -86,7 +87,7 @@ guess that's the "security purposes".
|
|||
|
||||
I'm sure there are other things to consider and explain, but this worked for me.
|
||||
If you get denied, reopen the case, add even more screenshots and information
|
||||
and try again.
|
||||
and try again.
|
||||
|
||||
## Brevo
|
||||
|
||||
|
@ -97,38 +98,38 @@ is the code for that actually:
|
|||
|
||||
```ts
|
||||
export type Email = {
|
||||
sender: {
|
||||
name: string;
|
||||
email: string;
|
||||
};
|
||||
to: string;
|
||||
content: {
|
||||
text: string;
|
||||
html: string;
|
||||
};
|
||||
subject: string;
|
||||
sender: {
|
||||
name: string;
|
||||
email: string;
|
||||
};
|
||||
to: string;
|
||||
content: {
|
||||
text: string;
|
||||
html: string;
|
||||
};
|
||||
subject: string;
|
||||
};
|
||||
|
||||
export async function sendEmailBrevo({
|
||||
sender,
|
||||
to,
|
||||
content: { html: htmlContent, text: textContent },
|
||||
subject,
|
||||
sender,
|
||||
to,
|
||||
content: { html: htmlContent, text: textContent },
|
||||
subject,
|
||||
}: Email) {
|
||||
await fetch("https://api.brevo.com/v3/smtp/email", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"content-type": "application/json",
|
||||
"api-key": process.env.BREVO_API_KEY,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
sender,
|
||||
to: [{ email: to }],
|
||||
subject,
|
||||
htmlContent,
|
||||
textContent,
|
||||
}),
|
||||
});
|
||||
await fetch('https://api.brevo.com/v3/smtp/email', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
accept: 'application/json',
|
||||
'content-type': 'application/json',
|
||||
'api-key': process.env.BREVO_API_KEY,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
sender,
|
||||
to: [{ email: to }],
|
||||
subject,
|
||||
htmlContent,
|
||||
textContent,
|
||||
}),
|
||||
});
|
||||
}
|
||||
```
|
||||
|
|
|
@ -6,6 +6,7 @@ images:
|
|||
tags:
|
||||
- dev
|
||||
- rust
|
||||
description: Unleash developer zen with a Rust CLI organization trick! This post uses Clap and a custom trait to create a file system mirrored command structure, making your code a breeze to navigate and maintain.
|
||||
---
|
||||
|
||||
Here's a pattern came up with for building CLI tools in Rust using
|
||||
|
|
|
@ -9,6 +9,7 @@ tags:
|
|||
- react
|
||||
- mobile
|
||||
- typescript
|
||||
description: Love fresh sourdough bread but hate remembering to feed your starter? This post details my journey of building a mobile app (with open-source tools) to manage sourdough starter feeding schedules and take the guesswork out of perfect sourdough.
|
||||
---
|
||||
|
||||
I like baking bread. Well, most of all I love bread, and baking it myself is
|
||||
|
@ -205,4 +206,4 @@ This is getting really long (for my usual posts), so a few more quick mentions:
|
|||
I'm about done with the basic functionality I wanted for this app, so I'm hoping
|
||||
to have a version of this up on the Google Play store by the end of the week.
|
||||
|
||||
Oh and I still haven't made that sourdough starter,
|
||||
Oh and I still haven't made that sourdough starter!
|
||||
|
|
|
@ -34,13 +34,20 @@
|
|||
{#each posts as post}
|
||||
<a href={`/${post.path}`}>
|
||||
<li>
|
||||
<h5 class="post-title">
|
||||
{post.meta.title}
|
||||
</h5>
|
||||
<time datetime={post.meta.date}>
|
||||
{format(new Date(post.meta.date), 'MMMM d')}
|
||||
</time>
|
||||
<div>About {post.readingTime.text}</div>
|
||||
<div>
|
||||
<h5 class="post-title">
|
||||
{post.meta.title}
|
||||
</h5>
|
||||
{#if post.meta.description}
|
||||
<p class="description">{post.meta.description}</p>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="meta">
|
||||
<time datetime={post.meta.date}>
|
||||
{format(new Date(post.meta.date), 'MMMM d')}
|
||||
</time>
|
||||
<div>About {post.readingTime.text}</div>
|
||||
</div>
|
||||
</li>
|
||||
</a>
|
||||
{/each}
|
||||
|
@ -96,7 +103,6 @@
|
|||
padding: 0.5rem 1rem;
|
||||
}
|
||||
.post-title {
|
||||
margin-bottom: 0.3rem;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: transparent;
|
||||
transition: all var(--animation-speed) var(--animation-type);
|
||||
|
@ -123,4 +129,16 @@
|
|||
.posts {
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.9rem;
|
||||
margin-top: -1.4rem;
|
||||
}
|
||||
.meta {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -65,3 +65,8 @@ a img {
|
|||
display: inline-block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
padding: 0.1rem 1.5rem;
|
||||
border-left: 5px solid $color-primary;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue