Compare commits
No commits in common. "main" and "develop" have entirely different histories.
18
package.json
18
package.json
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "personal-website",
|
"name": "personal-website",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "2.0.1",
|
"version": "2.0.0",
|
||||||
"author": "Cyril Šebek <cyril@cyrilsebek.cz>",
|
"author": "Cyril Šebek <cyril@cyrilsebek.cz>",
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"start": "astro dev",
|
"start": "astro dev",
|
||||||
@ -12,12 +12,12 @@
|
|||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/mdx": "^3.1.9",
|
"@astrojs/mdx": "^3.1.2",
|
||||||
"@astrojs/node": "^8.3.4",
|
"@astrojs/node": "^8.3.2",
|
||||||
"@astrojs/react": "^3.6.2",
|
"@astrojs/react": "^3.6.0",
|
||||||
"@astrojs/rss": "^4.0.9",
|
"@astrojs/rss": "^4.0.7",
|
||||||
"@astrojs/sitemap": "^3.2.1",
|
"@astrojs/sitemap": "^3.1.6",
|
||||||
"@astrojs/tailwind": "^5.1.2",
|
"@astrojs/tailwind": "^5.1.0",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
"@radix-ui/react-scroll-area": "^1.0.5",
|
||||||
@ -28,7 +28,7 @@
|
|||||||
"@tailwindcss/typography": "^0.5.13",
|
"@tailwindcss/typography": "^0.5.13",
|
||||||
"@types/react": "^18.2.74",
|
"@types/react": "^18.2.74",
|
||||||
"@types/react-dom": "^18.2.24",
|
"@types/react-dom": "^18.2.24",
|
||||||
"astro": "^4.16.13",
|
"astro": "^4.11.5",
|
||||||
"astro-seo": "^0.8.4",
|
"astro-seo": "^0.8.4",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
|
1478
pnpm-lock.yaml
1478
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 5.0 MiB |
Binary file not shown.
Before Width: | Height: | Size: 5.9 MiB |
@ -5,13 +5,11 @@ import { ContactButton } from "./ContactButton";
|
|||||||
|
|
||||||
<!-- Contacts list -->
|
<!-- Contacts list -->
|
||||||
<div>
|
<div>
|
||||||
<div class="relative top-[95vh] left-[17vw] z-[1000] sm:fixed sm:left-2 sm:top-[38%] flex sm:flex-col text-lg text-content" >
|
<div class="relative top-[97vh] left-[26vw] z-[1000] sm:fixed sm:left-2 sm:top-[38%] flex sm:flex-col text-lg text-content" >
|
||||||
<ContactButton variant={"mail"} link={"mailto:cyril@cyrilsebek.cz"} tooltip={"Mail"} client:load/>
|
<ContactButton variant={"mail"} link={"mailto:cyril@cyrilsebek.cz"} tooltip={"Mail"} client:load/>
|
||||||
<ContactButton variant={"bluesky"} link={"https://bsky.app/profile/cyrilsebek.cz"} tooltip={"Bluesky"} client:load/>
|
|
||||||
<ContactButton variant={"telegram"} link={"https://t.me/blboun3"} tooltip={"Telegram Chat"} client:load/>
|
<ContactButton variant={"telegram"} link={"https://t.me/blboun3"} tooltip={"Telegram Chat"} client:load/>
|
||||||
<ContactButton variant={"matrix"} link={"https://matrix.to/#/@cyril:cyrilsebek.cz"} tooltip={"Matrix Chat"} client:load/>
|
<ContactButton variant={"matrix"} link={"https://matrix.to/#/@cyril:cyrilsebek.cz"} tooltip={"Matrix Chat"} client:load/>
|
||||||
<ContactButton variant={"github"} link={"https://github.com/Blboun3"} tooltip={"Github"} client:load/>
|
<ContactButton variant={"github"} link={"https://github.com/Blboun3"} tooltip={"Github"} client:load/>
|
||||||
<ContactButton variant={"git"} link={"https://git.cyrilsebek.cz/blboun3"} tooltip={"Selfhosted gitea"} client:load/>
|
<ContactButton variant={"git"} link={"https://git.cyrilsebek.cz/blboun3"} tooltip={"Selfhosted gitea"} client:load/>
|
||||||
<ContactButton variant={"mastodon"} link={"https://mastodon.cyrilsebek.cz/@blboun3"} tooltip={"Mastodon"} client:load/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
title: "Cesta k webovkám"
|
title: "Cesta k webovkám"
|
||||||
description: "Příběh vzniku těchto úžasných stránek"
|
description: "Příběh vzniku těchto úžasných stránek"
|
||||||
author: "Cyril Šebek"
|
author: "Cyril Šebek"
|
||||||
publishDate: 2024-07-06T9:34:25Z
|
publishDate: 2024-06-07T9:34:25Z
|
||||||
image:
|
image:
|
||||||
url: "/blog/post-1.png"
|
url: "/blog/post-1.png"
|
||||||
alt: "Snímek mojí osobní webové stránky"
|
alt: "Snímek mojí osobní webové stránky"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
title: "Pushing site to production"
|
title: "Pushing site to production"
|
||||||
description: "My journey of creating this amazing personal website"
|
description: "My journey of creating this amazing personal website"
|
||||||
author: "Cyril Šebek"
|
author: "Cyril Šebek"
|
||||||
publishDate: 2024-07-06T9:34:25Z
|
publishDate: 2024-06-07T9:34:25Z
|
||||||
image:
|
image:
|
||||||
url: "/blog/post-1.png"
|
url: "/blog/post-1.png"
|
||||||
alt: "Screenshot of my personal website"
|
alt: "Screenshot of my personal website"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
title: "Pushing site to production"
|
title: "Pushing site to production"
|
||||||
description: "My journey of creating this amazing personal website"
|
description: "My journey of creating this amazing personal website"
|
||||||
author: "Cyril Šebek"
|
author: "Cyril Šebek"
|
||||||
publishDate: 2024-07-06T9:34:25Z
|
publishDate: 2024-06-07T9:34:25Z
|
||||||
image:
|
image:
|
||||||
url: "/blog/post-1.png"
|
url: "/blog/post-1.png"
|
||||||
alt: "Screenshot of my personal website"
|
alt: "Screenshot of my personal website"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
title: "Pushing site to production"
|
title: "Pushing site to production"
|
||||||
description: "My journey of creating this amazing personal website"
|
description: "My journey of creating this amazing personal website"
|
||||||
author: "Cyril Šebek"
|
author: "Cyril Šebek"
|
||||||
publishDate: 2024-07-06T9:34:25Z
|
publishDate: 2024-06-07T9:34:25Z
|
||||||
image:
|
image:
|
||||||
url: "/blog/post-1.png"
|
url: "/blog/post-1.png"
|
||||||
alt: "Screenshot of my personal website"
|
alt: "Screenshot of my personal website"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
title: "Clearing matrix synapse database"
|
title: "Clearing matrix synapse database"
|
||||||
description: "Synapse is matrix homeserver implementation. Unfortunately it sometimes manages to accumulate GBs of unimportant data"
|
description: "Synapse is matrix homeserver implementation. Unfortunately it sometimes manages to accumulate GBs of unimportant data"
|
||||||
author: "Cyril Šebek"
|
author: "Cyril Šebek"
|
||||||
publishDate: 2024-07-09T12:11:32Z
|
publishDate: 2024-09-07T12:11:32Z
|
||||||
image:
|
image:
|
||||||
url: "/blog/post-2.png"
|
url: "/blog/post-2.png"
|
||||||
alt: "Screenshot of matrix-synapse diskspace janitor"
|
alt: "Screenshot of matrix-synapse diskspace janitor"
|
||||||
@ -70,7 +70,7 @@ It seems straightforward, right? You install the app - sure, there aren't detail
|
|||||||
|
|
||||||
The first issue I encountered, which has since been resolved, was logging into the app. Every time I tried to log in, the app would crash with an error in the console.
|
The first issue I encountered, which has since been resolved, was logging into the app. Every time I tried to log in, the app would crash with an error in the console.
|
||||||
|
|
||||||
```go
|
```golang
|
||||||
http: panic serving 10.69.13.200:43140: runtime error: slice bounds out of range [:10] with capacity 0
|
http: panic serving 10.69.13.200:43140: runtime error: slice bounds out of range [:10] with capacity 0
|
||||||
goroutine 54 [running]:
|
goroutine 54 [running]:
|
||||||
net/http.(*conn).serve.func1()
|
net/http.(*conn).serve.func1()
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
title: "Clearing matrix synapse database"
|
title: "Clearing matrix synapse database"
|
||||||
description: "Synapse is matrix homeserver implementation. Unfortunately it sometimes manages to accumulate GBs of unimportant data"
|
description: "Synapse is matrix homeserver implementation. Unfortunately it sometimes manages to accumulate GBs of unimportant data"
|
||||||
author: "Cyril Šebek"
|
author: "Cyril Šebek"
|
||||||
publishDate: 2024-07-09T12:11:32Z
|
publishDate: 2024-09-07T12:11:32Z
|
||||||
image:
|
image:
|
||||||
url: "/blog/post-2.png"
|
url: "/blog/post-2.png"
|
||||||
alt: "Screenshot of matrix-synapse diskspace janitor"
|
alt: "Screenshot of matrix-synapse diskspace janitor"
|
||||||
@ -70,7 +70,7 @@ It seems straightforward, right? You install the app - sure, there aren't detail
|
|||||||
|
|
||||||
The first issue I encountered, which has since been resolved, was logging into the app. Every time I tried to log in, the app would crash with an error in the console.
|
The first issue I encountered, which has since been resolved, was logging into the app. Every time I tried to log in, the app would crash with an error in the console.
|
||||||
|
|
||||||
```go
|
```golang
|
||||||
http: panic serving 10.69.13.200:43140: runtime error: slice bounds out of range [:10] with capacity 0
|
http: panic serving 10.69.13.200:43140: runtime error: slice bounds out of range [:10] with capacity 0
|
||||||
goroutine 54 [running]:
|
goroutine 54 [running]:
|
||||||
net/http.(*conn).serve.func1()
|
net/http.(*conn).serve.func1()
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
title: "Clearing matrix synapse database"
|
title: "Clearing matrix synapse database"
|
||||||
description: "Synapse is matrix homeserver implementation. Unfortunately it sometimes manages to accumulate GBs of unimportant data"
|
description: "Synapse is matrix homeserver implementation. Unfortunately it sometimes manages to accumulate GBs of unimportant data"
|
||||||
author: "Cyril Šebek"
|
author: "Cyril Šebek"
|
||||||
publishDate: 2024-07-09T12:11:32Z
|
publishDate: 2024-09-07T12:11:32Z
|
||||||
image:
|
image:
|
||||||
url: "/blog/post-2.png"
|
url: "/blog/post-2.png"
|
||||||
alt: "Screenshot of matrix-synapse diskspace janitor"
|
alt: "Screenshot of matrix-synapse diskspace janitor"
|
||||||
@ -70,7 +70,7 @@ It seems straightforward, right? You install the app - sure, there aren't detail
|
|||||||
|
|
||||||
The first issue I encountered, which has since been resolved, was logging into the app. Every time I tried to log in, the app would crash with an error in the console.
|
The first issue I encountered, which has since been resolved, was logging into the app. Every time I tried to log in, the app would crash with an error in the console.
|
||||||
|
|
||||||
```go
|
```golang
|
||||||
http: panic serving 10.69.13.200:43140: runtime error: slice bounds out of range [:10] with capacity 0
|
http: panic serving 10.69.13.200:43140: runtime error: slice bounds out of range [:10] with capacity 0
|
||||||
goroutine 54 [running]:
|
goroutine 54 [running]:
|
||||||
net/http.(*conn).serve.func1()
|
net/http.(*conn).serve.func1()
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
title: "Clearing matrix synapse database"
|
title: "Clearing matrix synapse database"
|
||||||
description: "Synapse is matrix homeserver implementation. Unfortunately it sometimes manages to accumulate GBs of unimportant data"
|
description: "Synapse is matrix homeserver implementation. Unfortunately it sometimes manages to accumulate GBs of unimportant data"
|
||||||
author: "Cyril Šebek"
|
author: "Cyril Šebek"
|
||||||
publishDate: 2024-07-09T12:11:32Z
|
publishDate: 2024-09-07T12:11:32Z
|
||||||
image:
|
image:
|
||||||
url: "/blog/post-2.png"
|
url: "/blog/post-2.png"
|
||||||
alt: "Screenshot of matrix-synapse diskspace janitor"
|
alt: "Screenshot of matrix-synapse diskspace janitor"
|
||||||
@ -70,7 +70,7 @@ It seems straightforward, right? You install the app - sure, there aren't detail
|
|||||||
|
|
||||||
The first issue I encountered, which has since been resolved, was logging into the app. Every time I tried to log in, the app would crash with an error in the console.
|
The first issue I encountered, which has since been resolved, was logging into the app. Every time I tried to log in, the app would crash with an error in the console.
|
||||||
|
|
||||||
```go
|
```golang
|
||||||
http: panic serving 10.69.13.200:43140: runtime error: slice bounds out of range [:10] with capacity 0
|
http: panic serving 10.69.13.200:43140: runtime error: slice bounds out of range [:10] with capacity 0
|
||||||
goroutine 54 [running]:
|
goroutine 54 [running]:
|
||||||
net/http.(*conn).serve.func1()
|
net/http.(*conn).serve.func1()
|
||||||
|
@ -1,109 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Self-hostování sociálních sítí"
|
|
||||||
description: "V posledních měsících začínají dříve méně známé sociální nabírat nocé uživatele ostrým tempem. Mezi nimi i některé, které jsou federované a dají se tedy self-hostovat"
|
|
||||||
author: "Cyril Šebek"
|
|
||||||
publishDate: 2024-11-22T17:42:25Z
|
|
||||||
image:
|
|
||||||
url: "/blog/post-3.png"
|
|
||||||
alt: "Logo ATproto a ActivityPub protokolů"
|
|
||||||
language: "cs"
|
|
||||||
tags: ["selfhosting", "social", "bluesky", "mastodon"]
|
|
||||||
---
|
|
||||||
|
|
||||||
Koncem června 2024 jsem, díky konci školního roku a přibývajícímu volnému času, konečně dokončil většinu nastavování na svých serverech. Vše, co jsem chtěl i potřeboval, už stabilně běželo, všechny aplikace fungovaly, jak měly, a já tak konečně získal čas si trochu hrát.
|
|
||||||
|
|
||||||
Vzhledem k tomu, že jsem nikdy předtím žádné sociální sítě moc neměl a nepoužíval, a vzhledem k situaci ohledně Twitteru/X — která mě výrazně odrazuje od zapojení na jakoukoli sociální síť — vzpomněl jsem si na projekt, který jsem objevil před pár lety: [Mastodon](https://joinmastodon.org/). Při následném průzkumu jsem narazil i na tehdy rychle rostoucí [Bluesky](https://bsky.app/).
|
|
||||||
|
|
||||||
## Mastodon
|
|
||||||
|
|
||||||
Mastodon je decentralizovaná sociální síť, což znamená, že servery nepatří jedné velké společnosti, ale členům komunity. Každý si tedy může hostovat svůj server. To dává smysl, že? Mám server, tak si samozřejmě můžu hostovat, co se mi zlíbí. Ale jaký to asi bude mít dosah? Tady přichází kouzlo federace — nezáleží na tom, který server je váš domovský, všechny servery spolu komunikují v rámci jednoho systému, tzv. *Fediverse*. Tento vesmír Mastodonu funguje na protokolu [ActivityPub](https://activitypub.rocks/), což je technologie, díky které Mastodon žije.
|
|
||||||
|
|
||||||
### Proč si self-hostovat Mastodon
|
|
||||||
|
|
||||||
Důvodů může být několik. Jednou z možností je vytvoření Mastodon serveru pro zájmové skupiny, firmy nebo korporace. Pro mě je však hlavním důvodem absolutní svoboda. Mastodon nemá centrální pravidla — každý server si může určit vlastní politiku, co je a není v pořádku.
|
|
||||||
|
|
||||||
### Self-hostování Mastodonu
|
|
||||||
|
|
||||||
Na internetu najdete mnoho zdrojů, jak si Mastodon nainstalovat, včetně [oficiální dokumentace]((https://docs.joinmastodon.org/admin/prerequisites/)). Nebudu proto podrobně rozepisovat každý krok; zájemci si vše snadno vyhledají a přizpůsobí svým potřebám.
|
|
||||||
|
|
||||||
Já jsem Mastodon nainstaloval na nový kontejner Debian 12 (LXC CT) na svém Proxmox serveru. Upozornění: Mastodon je poměrně náročný na úložný prostor, což jsem zjistil až později. Moje instance zabírá přibližně 60 GB. Doména mého serveru je [mastodon.cyrilsebek.cz]((https://mastodon.cyrilsebek.cz)); jiné domény nevlastním, takže si musím vystačit se subdoménami.
|
|
||||||
|
|
||||||
### Automatické mazání
|
|
||||||
|
|
||||||
Když jsem poprvé nainstaloval Mastodon s asi 20GB úložiště tak všechno vypadalo v pořádku. Ovšem další den se něco stalo, protože moje instance neodpovídala. Po hledání problému jsem zjistil, že problém je s místem na disku. Rozšířil jsem tedy kapacitu a všechno bylo v pořádku. Další den byl ale problém zpátky. A znovu. Když už se moje instance začala dostávat ke 100GB došlo mi, že něco asi není úplně správně.
|
|
||||||
Po nějakém hledání jsem zjistil, že Mastodon si sám žádným způsobem nečistí úložiště a stahuje toho dost, veškerá data která zobrazuje (což znamená jistou část celé sociální sítě!).
|
|
||||||
S pomocí internetu jsem tedy vytvořil skript, který maže data starší 4 dnů:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Mastodon běží hodně v Ruby, musíme tedy inicializovat Ruby prostředí
|
|
||||||
export PATH="$HOME/.rbenv/bin:$PATH"
|
|
||||||
eval "$(rbenv init -)"
|
|
||||||
|
|
||||||
# Pročištění "cizích" účtů, které nemají žádné interakce s lokálními uživateli
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl accounts prune;
|
|
||||||
|
|
||||||
# Smazání statusů "cizích" účtů, starších 4 dnů, které nemají žádné interakce s lokálními uživateli
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl statuses remove --days 4;
|
|
||||||
|
|
||||||
# Smazání medií staších 4 dnů
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl media remove --days 4;
|
|
||||||
|
|
||||||
# Smazání metadata/hlaviček starších 4 dnů
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl media remove --remove-headers --include-follows --days 0;
|
|
||||||
|
|
||||||
# Smazání náhledů odkazů starší 4 dnů
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl preview_cards remove --days 4;
|
|
||||||
|
|
||||||
# Smazání "osiřelých" souborů (nejsou spojené s žádným příspěvkem)
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl media remove-orphans;
|
|
||||||
```
|
|
||||||
|
|
||||||
a protože běží na linuxu tak jsem následně přidal do crontabu pro uživatele Mastodon, aby tento skript pustil automaticky každé asi 3 hodiny
|
|
||||||
|
|
||||||
```bash
|
|
||||||
0 */3 * * * /bin/bash /home/mastodon/purge-media.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## Bluesky
|
|
||||||
|
|
||||||
Bluesky je také sociální síť podobná twitteru/X. Je výrazně mladší než Mastodon - který vznikl v roce 2016 - Bluesky je implementace protokolu ATProto, původně z roku 2019 pod twitterem. V roce 2021 se osamostatnilo od twitteru úplně a nyní je samostatnou společností, ale ne jako Mastodon neziskovou. Aktuálně je asi nejrozšířenější alternativou k twitteru/X, mimo jiné i díky podpoře zakladatele twitteru Jacka Dorseyho. K dnešnímu datu - 22.11.2024 - má Bluesky asi 21.5 milionu uživatelů.
|
|
||||||
|
|
||||||
### Self-hostování Bluesky PDS
|
|
||||||
|
|
||||||
ATproto protokol funguje trochu odlišně od ActivityPub protokolu. Jednotlivé "homeservery" jsou označovány jako PDS - personal data server. Proces instalace je ovšem výrazně snazší než pro Mastodon - v zásadě celý instalační proces je automatický pomocí jednoho skriptu, manuální nastavování je minimální. Postup instalace je popsán na [oficiálním githubu](https://github.com/bluesky-social/pds).
|
|
||||||
|
|
||||||
### Nginx vs. Caddy
|
|
||||||
|
|
||||||
Automatický instalační skript se pří spuštění zeptá na doménu, pod kterou by měl být veřejně dostupný. Následně si sám zajistí, zda-li k oné doméně opravdu má přístup, zařídí veškeré SSL/TLS certifikáty a nastaví Caddy jako reverse proxy. Pro méně technicky zdatné uživatele je to vskutku ideální řešení. Pro mě to ovšem byl problém navíc.
|
|
||||||
Moje homelab prostředí je z venku přístupné pouze přes jednu veřejnou IP adresu a mám pouze jednu doménu, na které je dost věcí (pomocí subdomén). Od první iterace mé domácí laboratoře používám jako reverse proxy [Nginx](https://nginx.org/en/) (nebo z něj derivované [OpenResty](https://openresty.org/en/)). Přes ten mám řešené certifikáty i posílání dotazů ke správným aplikacím běžícím na různých fyzických i virtuálních strojích.
|
|
||||||
Trochu mi dalo zabrat, než jsem tento problém vyřešil, ale nginx naštěstí podporuje `stream` modul. To je druh konfigurace, při kterém Nginx pouze zjistí "komu má dotaz jít" a dál se s ním nezabývá.
|
|
||||||
Možná to není nejideálnější řešení, správně bych měl asi upravit instalační skript, Caddy úplně zrušit a nechat všechno řešit nginx. Já jsem ale líný. Takže následující nginx konfigurace je pro mě dostačující. Navíc má výhodu, že pokud bude něco dalšího, co by mělo jít bez úprav k jiným serverům, tak to bude snadné přidat.
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
stream {
|
|
||||||
# Define a map to route based on SNI
|
|
||||||
map $ssl_preread_server_name $backend {
|
|
||||||
bsky.cyrilsebek.cz 10.69.13.212:443;
|
|
||||||
default 127.0.0.1:8443; # Fallback backend or drop the connection
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 443;
|
|
||||||
listen [::]:443;
|
|
||||||
|
|
||||||
# Enable SNI inspection
|
|
||||||
ssl_preread on;
|
|
||||||
|
|
||||||
# Route traffic to the correct backend
|
|
||||||
proxy_pass $backend;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Závěr
|
|
||||||
|
|
||||||
Self-hostování ani jedné ze sítí není nijak složité, ale Bluesky je nesporně uživatelsky jednodušší a zvládnou ho selfhostovat i technicky méně zdatní uživatelé. Není také potřeba řešit problémy s místem ani ničím jiným, všechno je vyřešené a prostě funguje. Samozřejmě, ani jednu ze sítí není nutné hostovat, dají se používat i s využitím serverů poskytovaných jak společnostmi zodpovědnými za vývoj platforem, tak množství komunitních serverů. A díky federalizaci obou protokolů je ve výsledku skoro jedno, který server je váš homeserver (nebo PDS).
|
|
||||||
Co se komunit týče, nejsem žádný expert, twitter/X jsem nikdy neměl a na moc dalších sociálních sítích také úplně nejsem, ale Bluesky je výrazně aktivnější než Mastodon. Pro ty, co hledají náhradu za twitter/X je Bluesky asi lepší volba. Mastodon je ovšem také, byť možná na self-hostování technicky náročnější, možnost.
|
|
||||||
Zůstávám na obou.
|
|
@ -1,102 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Selbstgehostete soziale Netzwerke"
|
|
||||||
description: "In den letzten Monaten haben einige vorher weniger bekannte soziale Netzwerke viele neue Nutzer gewonnen. Manche davon sind föderiert und können selbst gehostet werden, was den Nutzern mehr Kontrolle gibt."
|
|
||||||
author: "Cyril Šebek"
|
|
||||||
publishDate: 2024-11-22T17:42:25Z
|
|
||||||
image:
|
|
||||||
url: "/blog/post-3.png"
|
|
||||||
alt: "Logo of ATProto and ActivityPub protocols"
|
|
||||||
language: "de"
|
|
||||||
tags: ["selfhosting", "social", "bluesky", "mastodon"]
|
|
||||||
---
|
|
||||||
|
|
||||||
**Haftungsausschluss: Dieser Artikel ist eine schlechte Übersetzung der englischen/tschechischen Version. Ich lerne gerade Deutsch und habe dies als Ausrede für ein bisschen Übung genutzt.**
|
|
||||||
|
|
||||||
## Einführung
|
|
||||||
|
|
||||||
Im Juli 2024 hatte ich endlich Zeit, meine Server vollständig einzurichten. Alle Programme liefen stabil, und ich konnte etwas Neues ausprobieren. Ich nutze soziale Netzwerke eigentlich nicht, aber wegen der Probleme mit Twitter/X wollte ich eine Alternative finden. So stieß ich auf [Mastodon](https://joinmastodon.org/) und später auf [Bluesky](https://bsky.app/).
|
|
||||||
|
|
||||||
## Mastodon
|
|
||||||
|
|
||||||
Mastodon ist ein dezentrales Netzwerk. Das bedeutet, dass die Server von Nutzern gehostet werden, nicht von einer Firma. Jeder kann einen eigenen Server betreiben, der trotzdem mit anderen Servern verbunden ist – dank des [ActivityPub-Protokolls]((https://activitypub.rocks/)). Zusammen bilden diese Server das sogenannte „Fediverse“.
|
|
||||||
|
|
||||||
### Warum Mastodon selbst hosten ?
|
|
||||||
|
|
||||||
Es gibt viele Gründe. Manche machen es für ihre Communities oder Firmen. Für mich war die größte Motivation Freiheit. Es gibt keine zentrale Kontrolle. Jeder Server legt seine eigenen Regeln fest.
|
|
||||||
|
|
||||||
### Mastodon einrichten
|
|
||||||
|
|
||||||
Die Installation von Mastodon ist nicht schwer. Es gibt [offizielle Anleitungen]((https://docs.joinmastodon.org/admin/prerequisites/)). Ich habe Mastodon auf einem Debian 12-Container in meinem Proxmox-Server installiert. Eine wichtige Lektion: Mastodon braucht viel Speicherplatz! Meine Instanz verbraucht ungefähr 60 GB. Und ich habe das auf die harter Tour gelernt.
|
|
||||||
|
|
||||||
### Speicherproblem lösen
|
|
||||||
|
|
||||||
Mastodon speichert viele Daten, löscht sie aber nicht automatisch. Ich habe ein Skript erstellt, das alte Daten löscht, um Speicherplatz zu sparen:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export PATH="$HOME/.rbenv/bin:$PATH"
|
|
||||||
eval "$(rbenv init -)"
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl accounts prune;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl statuses remove --days 4;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl media remove --days 4;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl media remove --remove-headers --include-follows --days 0;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl preview_cards remove --days 4;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl media remove-orphans;
|
|
||||||
```
|
|
||||||
|
|
||||||
Das Skript läuft automatisch alle drei Stunden
|
|
||||||
|
|
||||||
```bash
|
|
||||||
0 */3 * * * /bin/bash /home/mastodon/purge-media.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Das macht meine Konfiguration hanhabbar unter 60GB.
|
|
||||||
|
|
||||||
## Bluesky
|
|
||||||
|
|
||||||
Bluesky ist ein anderes soziales Netzwerk, ähnlich wie Twitter/X. Es ist jünger als Mastodon und basiert auf dem ATProto-Protokoll. Seit 2021 ist Bluesky unabhängig. Heute hat es über 21,5 Millionen Nutzer (Stand: November 2024).
|
|
||||||
|
|
||||||
### Bluesky selbst hosten
|
|
||||||
|
|
||||||
Bluesky ist einfacher zu hosten als Mastodon. Es gibt ein automatisches Installationsskript, das alles einrichtet. Mehr Informationen finden Sie auf [Blueskys GitHub-Seite]((https://github.com/bluesky-social/pds)).
|
|
||||||
|
|
||||||
### Nginx vs. Caddy
|
|
||||||
|
|
||||||
Eine Herausforderung bei der Einrichtung war, dass das automatische Skript davon ausgeht, dass es der einzige Dienst auf der Domain oder IP-Adresse ist. Standardmäßig verwendet das Skript Caddy als Reverse Proxy und verwaltet die SSL/TLS-Zertifikate. Für weniger technisch versierte Nutzer ist das praktisch, aber es passte nicht zu meiner Umgebung.
|
|
||||||
Ich nutze Nginx, um in meinem Homelab mehrere Dienste zu verwalten, die jeweils Subdomains verwenden. Um Konflikte zu vermeiden, habe ich [Nginx]((https://nginx.org/en/)) so konfiguriert, dass es Anfragen an die passenden Backends weiterleitet, ohne die SSL/TLS-Zertifikate zu beeinträchtigen. Hier ist meine Lösung mit dem stream-Modul von Nginx:
|
|
||||||
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
stream {
|
|
||||||
# Define a map to route based on SNI
|
|
||||||
map $ssl_preread_server_name $backend {
|
|
||||||
bsky.cyrilsebek.cz 10.69.13.212:443;
|
|
||||||
default 127.0.0.1:8443; # Fallback backend or drop the connection
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 443;
|
|
||||||
listen [::]:443;
|
|
||||||
|
|
||||||
# Enable SNI inspection
|
|
||||||
ssl_preread on;
|
|
||||||
|
|
||||||
# Route traffic to the correct backend
|
|
||||||
proxy_pass $backend;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Sowohl Mastodon als auch Bluesky sind großartige Optionen für selbstgehostete soziale Netzwerke. Allerdings ist es nicht notwendig, sie selbst zu hosten, um sie nutzen zu können.
|
|
||||||
Mastodon bietet unvergleichliche Freiheit und eine Community-gesteuerte Verwaltung, während Bluesky mit Einfachheit und leichter Einrichtung punktet.
|
|
||||||
Für Nutzer, die eine unkomplizierte Alternative zu Twitter/X suchen, ist Bluesky wahrscheinlich die bessere Wahl. Mastodon hingegen überzeugt durch seine dezentrale und föderierte Struktur, die mehr Kontrolle ermöglicht und dem Gedanken eines wirklich unabhängigen Netzwerks entspricht.
|
|
||||||
Am Ende habe ich mich entschieden, beide Plattformen zu nutzen. Jede hat ihre eigenen Stärken, und durch das Selbst-Hosting kann ich sie vollständig erkunden.
|
|
@ -1,103 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Self-hosting social networks"
|
|
||||||
description: "In recent months, previously obscure social networks have gained significant traction. Some of these platforms are federated, making them self-hostable and giving users more control over their digital interactions."
|
|
||||||
author: "Cyril Šebek"
|
|
||||||
publishDate: 2024-11-22T17:42:25Z
|
|
||||||
image:
|
|
||||||
url: "/blog/post-3.png"
|
|
||||||
alt: "Logo of ATProto and ActivityPub protocols"
|
|
||||||
language: "en"
|
|
||||||
tags: ["selfhosting", "social", "bluesky", "mastodon"]
|
|
||||||
---
|
|
||||||
|
|
||||||
In recent months, previously obscure social networks have gained significant traction. Some of these platforms are federated, making them self-hostable and giving users more control over their digital interactions.
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
At the beginning of July 2024, with some free time thanks to the school year ending, I finally finished configuring my servers. Everything I needed was running smoothly—apps and services were performing as expected. This left me with some extra time to experiment.
|
|
||||||
Despite avoiding social networks in the past, the current situation with Twitter (now known as X) made me even more certain of my disinterest. However, I remembered a project I discovered soem time ago—[Mastodon](https://joinmastodon.org/). While researching, I also came across another rapidly growing platform, [Bluesky](https://bsky.app/).
|
|
||||||
|
|
||||||
## Mastodon: Decentralized and Federated
|
|
||||||
|
|
||||||
Mastodon is a decentralized, federated social network. Unlike centralized platforms like Twitter/X, where one company owns all servers, Mastodon servers are community-driven. Anyone can host their own instance, known as a "home server," while still interacting with others through the magic of federation. This interconnected network of servers runs on the [ActivityPub protocol](https://activitypub.rocks/) and is often referred to as the *Fediverse*.
|
|
||||||
|
|
||||||
### Why self-host Mastodon
|
|
||||||
|
|
||||||
There are several reasons to self-host Mastodon. For hobby groups, communities, or companies, hosting your own instance provides autonomy and flexibility. For me, it boils down to freedom—absolute freedom. Since there’s no central authority, each server can establish its own rules about acceptable content, allowing users to enjoy an uncensored environment.
|
|
||||||
|
|
||||||
### Setting up Mastodon
|
|
||||||
|
|
||||||
Self-hosting Mastodon isn’t overly complex, and numerous guides are available online. I followed the [official guide](https://docs.joinmastodon.org/admin/prerequisites/) and created a new Debian 12 LXC container on my Proxmox server. My public domain for Mastodon is [mastodon.cyrilsebek.cz](https://mastodon.cyrilsebek.cz), and while I don’t own other domains, subdomains worked just fine.
|
|
||||||
However, I quickly discovered one major challenge — Mastodon is a disk space guzzler. Initially, I allocated 20 GB of storage, thinking it would suffice. Within a day, the site stopped loading due to lack of space. After repeatedly increasing the disk size and watching it balloon to 100 GB, I realized something is wrong and I needed a better solution.
|
|
||||||
|
|
||||||
### Automatic Deletion
|
|
||||||
|
|
||||||
Mastodon doesn’t automatically clean up its storage. It downloads and stores a significant amount of data, including content from the wider Fediverse. To manage this, I created a script that deletes unused or unnecessary data older than four days:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export PATH="$HOME/.rbenv/bin:$PATH"
|
|
||||||
eval "$(rbenv init -)"
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl accounts prune;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl statuses remove --days 4;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl media remove --days 4;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl media remove --remove-headers --include-follows --days 0;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl preview_cards remove --days 4;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl media remove-orphans;
|
|
||||||
```
|
|
||||||
|
|
||||||
I also scheduled it to run every three hours using a cron job:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
0 */3 * * * /bin/bash /home/mastodon/purge-media.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
This setup has kept my Mastodon instance stable and storage manageable at around 60GB.
|
|
||||||
|
|
||||||
## Bluesky: Modern Alternative
|
|
||||||
|
|
||||||
Bluesky is another microblogging social network similar to Twitter/X. Unlike Mastodon, Bluesky operates on the ATProto protocol and is a younger platform. Initially developed under Twitter in 2019, it became independent in 2021 following Elon Musk's acquisition of Twitter. Today, Bluesky has over 21.5 million users as of November 22, 2024, and is one of the most popular Twitter/X alternatives. It's also federated, but the protocol works a bit differently. The original idea behind it was to explore the possibilites of federated social network.
|
|
||||||
|
|
||||||
### Why self-host Bluesky
|
|
||||||
|
|
||||||
Bluesky allows users to set up their own Personal Data Server (PDS), making it simple to manage and maintain control over data. The installation process is remarkably straightforward compared to Mastodon. A single script handles most of the setup, with minimal manual configuration required. You can find the detailed guide on [Bluesky's GitHub page](https://github.com/bluesky-social/pds).
|
|
||||||
|
|
||||||
### Nginx vs. Caddy
|
|
||||||
|
|
||||||
One challenge I faced during setup was the automatic script assuming it was the sole service on the domain or IP address. By default, the script uses Caddy as a reverse proxy and manages SSL/TLS certificates. While this is convenient for less technical users, it didn’t suit my setup.
|
|
||||||
I rely on Nginx to manage multiple services on my homelab, each using subdomains. To resolve conflicts, I configured Nginx to forward requests to the appropriate backend without interfering with SSL/TLS. Here’s my solution using [Nginx](https://nginx.org/en/)'s stream module:
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
stream {
|
|
||||||
# Define a map to route based on SNI
|
|
||||||
map $ssl_preread_server_name $backend {
|
|
||||||
bsky.cyrilsebek.cz 10.69.13.212:443;
|
|
||||||
default 127.0.0.1:8443; # Fallback backend or drop the connection
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 443;
|
|
||||||
listen [::]:443;
|
|
||||||
|
|
||||||
# Enable SNI inspection
|
|
||||||
ssl_preread on;
|
|
||||||
|
|
||||||
# Route traffic to the correct backend
|
|
||||||
proxy_pass $backend;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Both Mastodon and Bluesky are excellent options for self-hostable social networks. But you don't need to selfhost either of them to use them.
|
|
||||||
Mastodon offers unparalleled freedom and community-driven management, while Bluesky shines in simplicity and ease of setup.
|
|
||||||
For users seeking a straightforward Twitter/X alternative, Bluesky is likely the better choice. However, Mastodon’s decentralized, federated structure provides more control and aligns with the ethos of a truly independent network.
|
|
||||||
Ultimately, I’ve decided to use both. Each platform has its strengths, and self-hosting gives me the freedom to explore them fully.
|
|
@ -1,103 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Réseaux sociaux auto-hébergés"
|
|
||||||
description: "Ces derniers mois, des réseaux sociaux auparavant obscurs gagnent rapidement en popularité. Certains d'entre eux sont fédérés et donc auto-hébergeables."
|
|
||||||
author: "Cyril Šebek"
|
|
||||||
publishDate: 2024-11-22T17:42:25Z
|
|
||||||
image:
|
|
||||||
url: "/blog/post-3.png"
|
|
||||||
alt: "Logos des protocoles ATProto et ActivityPub"
|
|
||||||
language: "fr"
|
|
||||||
tags: ["selfhosting", "social", "bluesky", "mastodon"]
|
|
||||||
---
|
|
||||||
|
|
||||||
In recent months, previously obscure social networks have gained significant traction. Some of these platforms are federated, making them self-hostable and giving users more control over their digital interactions.
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
At the beginning of July 2024, with some free time thanks to the school year ending, I finally finished configuring my servers. Everything I needed was running smoothly—apps and services were performing as expected. This left me with some extra time to experiment.
|
|
||||||
Despite avoiding social networks in the past, the current situation with Twitter (now known as X) made me even more certain of my disinterest. However, I remembered a project I discovered soem time ago—[Mastodon](https://joinmastodon.org/). While researching, I also came across another rapidly growing platform, [Bluesky](https://bsky.app/).
|
|
||||||
|
|
||||||
## Mastodon: Decentralized and Federated
|
|
||||||
|
|
||||||
Mastodon is a decentralized, federated social network. Unlike centralized platforms like Twitter/X, where one company owns all servers, Mastodon servers are community-driven. Anyone can host their own instance, known as a "home server," while still interacting with others through the magic of federation. This interconnected network of servers runs on the [ActivityPub protocol](https://activitypub.rocks/) and is often referred to as the *Fediverse*.
|
|
||||||
|
|
||||||
### Why self-host Mastodon
|
|
||||||
|
|
||||||
There are several reasons to self-host Mastodon. For hobby groups, communities, or companies, hosting your own instance provides autonomy and flexibility. For me, it boils down to freedom—absolute freedom. Since there’s no central authority, each server can establish its own rules about acceptable content, allowing users to enjoy an uncensored environment.
|
|
||||||
|
|
||||||
### Setting up Mastodon
|
|
||||||
|
|
||||||
Self-hosting Mastodon isn’t overly complex, and numerous guides are available online. I followed the [official guide](https://docs.joinmastodon.org/admin/prerequisites/) and created a new Debian 12 LXC container on my Proxmox server. My public domain for Mastodon is [mastodon.cyrilsebek.cz](https://mastodon.cyrilsebek.cz), and while I don’t own other domains, subdomains worked just fine.
|
|
||||||
However, I quickly discovered one major challenge — Mastodon is a disk space guzzler. Initially, I allocated 20 GB of storage, thinking it would suffice. Within a day, the site stopped loading due to lack of space. After repeatedly increasing the disk size and watching it balloon to 100 GB, I realized something is wrong and I needed a better solution.
|
|
||||||
|
|
||||||
### Automatic Deletion
|
|
||||||
|
|
||||||
Mastodon doesn’t automatically clean up its storage. It downloads and stores a significant amount of data, including content from the wider Fediverse. To manage this, I created a script that deletes unused or unnecessary data older than four days:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
export PATH="$HOME/.rbenv/bin:$PATH"
|
|
||||||
eval "$(rbenv init -)"
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl accounts prune;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl statuses remove --days 4;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl media remove --days 4;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl media remove --remove-headers --include-follows --days 0;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl preview_cards remove --days 4;
|
|
||||||
|
|
||||||
RAILS_ENV=production /home/mastodon/live/bin/tootctl media remove-orphans;
|
|
||||||
```
|
|
||||||
|
|
||||||
I also scheduled it to run every three hours using a cron job:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
0 */3 * * * /bin/bash /home/mastodon/purge-media.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
This setup has kept my Mastodon instance stable and storage manageable at around 60GB.
|
|
||||||
|
|
||||||
## Bluesky: Modern Alternative
|
|
||||||
|
|
||||||
Bluesky is another microblogging social network similar to Twitter/X. Unlike Mastodon, Bluesky operates on the ATProto protocol and is a younger platform. Initially developed under Twitter in 2019, it became independent in 2021 following Elon Musk's acquisition of Twitter. Today, Bluesky has over 21.5 million users as of November 22, 2024, and is one of the most popular Twitter/X alternatives. It's also federated, but the protocol works a bit differently. The original idea behind it was to explore the possibilites of federated social network.
|
|
||||||
|
|
||||||
### Why self-host Bluesky
|
|
||||||
|
|
||||||
Bluesky allows users to set up their own Personal Data Server (PDS), making it simple to manage and maintain control over data. The installation process is remarkably straightforward compared to Mastodon. A single script handles most of the setup, with minimal manual configuration required. You can find the detailed guide on [Bluesky's GitHub page](https://github.com/bluesky-social/pds).
|
|
||||||
|
|
||||||
### Nginx vs. Caddy
|
|
||||||
|
|
||||||
One challenge I faced during setup was the automatic script assuming it was the sole service on the domain or IP address. By default, the script uses Caddy as a reverse proxy and manages SSL/TLS certificates. While this is convenient for less technical users, it didn’t suit my setup.
|
|
||||||
I rely on Nginx to manage multiple services on my homelab, each using subdomains. To resolve conflicts, I configured Nginx to forward requests to the appropriate backend without interfering with SSL/TLS. Here’s my solution using [Nginx](https://nginx.org/en/)'s stream module:
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
stream {
|
|
||||||
# Define a map to route based on SNI
|
|
||||||
map $ssl_preread_server_name $backend {
|
|
||||||
bsky.cyrilsebek.cz 10.69.13.212:443;
|
|
||||||
default 127.0.0.1:8443; # Fallback backend or drop the connection
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 443;
|
|
||||||
listen [::]:443;
|
|
||||||
|
|
||||||
# Enable SNI inspection
|
|
||||||
ssl_preread on;
|
|
||||||
|
|
||||||
# Route traffic to the correct backend
|
|
||||||
proxy_pass $backend;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Both Mastodon and Bluesky are excellent options for self-hostable social networks. But you don't need to selfhost either of them to use them.
|
|
||||||
Mastodon offers unparalleled freedom and community-driven management, while Bluesky shines in simplicity and ease of setup.
|
|
||||||
For users seeking a straightforward Twitter/X alternative, Bluesky is likely the better choice. However, Mastodon’s decentralized, federated structure provides more control and aligns with the ethos of a truly independent network.
|
|
||||||
Ultimately, I’ve decided to use both. Each platform has its strengths, and self-hosting gives me the freedom to explore them fully.
|
|
@ -105,7 +105,7 @@ export const en = {
|
|||||||
music: {
|
music: {
|
||||||
pageTitle: "Music @ Cyril Šebek",
|
pageTitle: "Music @ Cyril Šebek",
|
||||||
title: "My compositions",
|
title: "My compositions",
|
||||||
description: "This is page listing all my published musical compositions.",
|
description: "This is page is listing all my published musical compositions.",
|
||||||
ogDescription: "This page lists all published compositions by Cyril Šebek",
|
ogDescription: "This page lists all published compositions by Cyril Šebek",
|
||||||
downloadPDF: "Download PDF",
|
downloadPDF: "Download PDF",
|
||||||
downloadMP3: "Download MP3",
|
downloadMP3: "Download MP3",
|
||||||
|
@ -21,7 +21,6 @@ const t = useTranslations(lang)
|
|||||||
<link rel="shortcut icon" href={t("favicon")}>
|
<link rel="shortcut icon" href={t("favicon")}>
|
||||||
<link rel="shortcut icon" href={t("favicon")} type="image/x-icon">
|
<link rel="shortcut icon" href={t("favicon")} type="image/x-icon">
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<link rel="me" href="https://mastodon.cyrilsebek.cz/@blboun3">
|
|
||||||
<SEO
|
<SEO
|
||||||
title={title}
|
title={title}
|
||||||
description={description}
|
description={description}
|
||||||
|
@ -38,7 +38,7 @@ const theme_light = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox
|
|||||||
</svg>`;
|
</svg>`;
|
||||||
const lang = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-6 h-6">
|
const lang = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-6 h-6">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="m10.5 21 5.25-11.25L21 21m-9-3h7.5M3 5.621a48.474 48.474 0 0 1 6-.371m0 0c1.12 0 2.233.038 3.334.114M9 5.25V3m3.334 2.364C11.176 10.658 7.69 15.08 3 17.502m9.334-12.138c.896.061 1.785.147 2.666.257m-4.589 8.495a18.023 18.023 0 0 1-3.827-5.802" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="m10.5 21 5.25-11.25L21 21m-9-3h7.5M3 5.621a48.474 48.474 0 0 1 6-.371m0 0c1.12 0 2.233.038 3.334.114M9 5.25V3m3.334 2.364C11.176 10.658 7.69 15.08 3 17.502m9.334-12.138c.896.061 1.785.147 2.666.257m-4.589 8.495a18.023 18.023 0 0 1-3.827-5.802" />
|
||||||
</svg>`;
|
</svg>`
|
||||||
const theme_auto = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
const theme_auto = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9.813 15.904 9 18.75l-.813-2.846a4.5 4.5 0 0 0-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 0 0 3.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 0 0 3.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 0 0-3.09 3.09ZM18.259 8.715 18 9.75l-.259-1.035a3.375 3.375 0 0 0-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 0 0 2.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 0 0 2.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 0 0-2.456 2.456ZM16.894 20.567 16.5 21.75l-.394-1.183a2.25 2.25 0 0 0-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 0 0 1.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 0 0 1.423 1.423l1.183.394-1.183.394a2.25 2.25 0 0 0-1.423 1.423Z" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M9.813 15.904 9 18.75l-.813-2.846a4.5 4.5 0 0 0-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 0 0 3.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 0 0 3.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 0 0-3.09 3.09ZM18.259 8.715 18 9.75l-.259-1.035a3.375 3.375 0 0 0-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 0 0 2.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 0 0 2.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 0 0-2.456 2.456ZM16.894 20.567 16.5 21.75l-.394-1.183a2.25 2.25 0 0 0-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 0 0 1.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 0 0 1.423 1.423l1.183.394-1.183.394a2.25 2.25 0 0 0-1.423 1.423Z" />
|
||||||
</svg>`;
|
</svg>`;
|
||||||
@ -46,27 +46,10 @@ const music = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill=
|
|||||||
<path fill-rule="evenodd" d="M19.952 1.651a.75.75 0 0 1 .298.599V16.303a3 3 0 0 1-2.176 2.884l-1.32.377a2.553 2.553 0 1 1-1.403-4.909l2.311-.66a1.5 1.5 0 0 0 1.088-1.442V6.994l-9 2.572v9.737a3 3 0 0 1-2.176 2.884l-1.32.377a2.553 2.553 0 1 1-1.402-4.909l2.31-.66a1.5 1.5 0 0 0 1.088-1.442V5.25a.75.75 0 0 1 .544-.721l10.5-3a.75.75 0 0 1 .658.122Z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M19.952 1.651a.75.75 0 0 1 .298.599V16.303a3 3 0 0 1-2.176 2.884l-1.32.377a2.553 2.553 0 1 1-1.403-4.909l2.311-.66a1.5 1.5 0 0 0 1.088-1.442V6.994l-9 2.572v9.737a3 3 0 0 1-2.176 2.884l-1.32.377a2.553 2.553 0 1 1-1.402-4.909l2.31-.66a1.5 1.5 0 0 0 1.088-1.442V5.25a.75.75 0 0 1 .544-.721l10.5-3a.75.75 0 0 1 .658.122Z" clip-rule="evenodd" />
|
||||||
</svg>`;
|
</svg>`;
|
||||||
|
|
||||||
const matrix = `<svg role="img" viewBox="-1 -1 26 26" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" stroke="currentColor" d="M.632.55v22.9H2.28V24H0V0h2.28v.55zm7.043 7.26v1.157h.033c.309-.443.683-.784 1.117-1.024.433-.245.936-.365 1.5-.365.54 0 1.033.107 1.481.314.448.208.785.582 1.02 1.108.254-.374.6-.706 1.034-.992.434-.287.95-.43 1.546-.43.453 0 .872.056 1.26.167.388.11.716.286.993.53.276.245.489.559.646.951.152.392.23.863.23 1.417v5.728h-2.349V11.52c0-.286-.01-.559-.032-.812a1.755 1.755 0 0 0-.18-.66 1.106 1.106 0 0 0-.438-.448c-.194-.11-.457-.166-.785-.166-.332 0-.6.064-.803.189a1.38 1.38 0 0 0-.48.499 1.946 1.946 0 0 0-.231.696 5.56 5.56 0 0 0-.06.785v4.768h-2.35v-4.8c0-.254-.004-.503-.018-.752a2.074 2.074 0 0 0-.143-.688 1.052 1.052 0 0 0-.415-.503c-.194-.125-.476-.19-.854-.19-.111 0-.259.024-.439.074-.18.051-.36.143-.53.282-.171.138-.319.337-.439.595-.12.259-.18.6-.18 1.02v4.966H5.46V7.81zm15.693 15.64V.55H21.72V0H24v24h-2.28v-.55z"/></svg>`;
|
const matrix = `<svg role="img" viewBox="-1 -1 26 26" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" stroke="currentColor" d="M.632.55v22.9H2.28V24H0V0h2.28v.55zm7.043 7.26v1.157h.033c.309-.443.683-.784 1.117-1.024.433-.245.936-.365 1.5-.365.54 0 1.033.107 1.481.314.448.208.785.582 1.02 1.108.254-.374.6-.706 1.034-.992.434-.287.95-.43 1.546-.43.453 0 .872.056 1.26.167.388.11.716.286.993.53.276.245.489.559.646.951.152.392.23.863.23 1.417v5.728h-2.349V11.52c0-.286-.01-.559-.032-.812a1.755 1.755 0 0 0-.18-.66 1.106 1.106 0 0 0-.438-.448c-.194-.11-.457-.166-.785-.166-.332 0-.6.064-.803.189a1.38 1.38 0 0 0-.48.499 1.946 1.946 0 0 0-.231.696 5.56 5.56 0 0 0-.06.785v4.768h-2.35v-4.8c0-.254-.004-.503-.018-.752a2.074 2.074 0 0 0-.143-.688 1.052 1.052 0 0 0-.415-.503c-.194-.125-.476-.19-.854-.19-.111 0-.259.024-.439.074-.18.051-.36.143-.53.282-.171.138-.319.337-.439.595-.12.259-.18.6-.18 1.02v4.966H5.46V7.81zm15.693 15.64V.55H21.72V0H24v24h-2.28v-.55z"/></svg>`
|
||||||
const telegram = `<svg role="img" viewBox="-1 -1 26 26" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" stroke="currentColor" d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg>`;
|
const telegram = `<svg role="img" viewBox="-1 -1 26 26" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" stroke="currentColor" d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg>`
|
||||||
const github = `<svg role="img" viewBox="-1 -1 26 26" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" stroke="currentColor" d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>`;
|
const github = `<svg role="img" viewBox="-1 -1 26 26" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" stroke="currentColor" d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>`
|
||||||
const git = `<svg role="img" viewBox="-1 -1 26 26" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" stroke="currentColor" d="M23.546 10.93L13.067.452c-.604-.603-1.582-.603-2.188 0L8.708 2.627l2.76 2.76c.645-.215 1.379-.07 1.889.441.516.515.658 1.258.438 1.9l2.658 2.66c.645-.223 1.387-.078 1.9.435.721.72.721 1.884 0 2.604-.719.719-1.881.719-2.6 0-.539-.541-.674-1.337-.404-1.996L12.86 8.955v6.525c.176.086.342.203.488.348.713.721.713 1.883 0 2.6-.719.721-1.889.721-2.609 0-.719-.719-.719-1.879 0-2.598.182-.18.387-.316.605-.406V8.835c-.217-.091-.424-.222-.6-.401-.545-.545-.676-1.342-.396-2.009L7.636 3.7.45 10.881c-.6.605-.6 1.584 0 2.189l10.48 10.477c.604.604 1.582.604 2.186 0l10.43-10.43c.605-.603.605-1.582 0-2.187"/></svg>`;
|
const git = `<svg role="img" viewBox="-1 -1 26 26" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" stroke="currentColor" d="M23.546 10.93L13.067.452c-.604-.603-1.582-.603-2.188 0L8.708 2.627l2.76 2.76c.645-.215 1.379-.07 1.889.441.516.515.658 1.258.438 1.9l2.658 2.66c.645-.223 1.387-.078 1.9.435.721.72.721 1.884 0 2.604-.719.719-1.881.719-2.6 0-.539-.541-.674-1.337-.404-1.996L12.86 8.955v6.525c.176.086.342.203.488.348.713.721.713 1.883 0 2.6-.719.721-1.889.721-2.609 0-.719-.719-.719-1.879 0-2.598.182-.18.387-.316.605-.406V8.835c-.217-.091-.424-.222-.6-.401-.545-.545-.676-1.342-.396-2.009L7.636 3.7.45 10.881c-.6.605-.6 1.584 0 2.189l10.48 10.477c.604.604 1.582.604 2.186 0l10.43-10.43c.605-.603.605-1.582 0-2.187"/></svg>`
|
||||||
const mail = `<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 1 23 23" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" style="--darkreader-inline-stroke: currentColor;" data-darkreader-inline-stroke=""><path stroke="none" d="M0 0h24v24H0z" fill="none" style="--darkreader-inline-stroke: none;" data-darkreader-inline-stroke=""></path><path d="M22 7.535v9.465a3 3 0 0 1 -2.824 2.995l-.176 .005h-14a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-9.465l9.445 6.297l.116 .066a1 1 0 0 0 .878 0l.116 -.066l9.445 -6.297z" stroke-width="0" fill="currentColor" style="--darkreader-inline-fill: currentColor;" data-darkreader-inline-fill=""></path><path d="M19 4c1.08 0 2.027 .57 2.555 1.427l-9.555 6.37l-9.555 -6.37a2.999 2.999 0 0 1 2.354 -1.42l.201 -.007h14z" stroke-width="0" fill="currentColor" style="--darkreader-inline-fill: currentColor;" data-darkreader-inline-fill=""></path></svg>`;
|
const mail = `<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 1 23 23" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" style="--darkreader-inline-stroke: currentColor;" data-darkreader-inline-stroke=""><path stroke="none" d="M0 0h24v24H0z" fill="none" style="--darkreader-inline-stroke: none;" data-darkreader-inline-stroke=""></path><path d="M22 7.535v9.465a3 3 0 0 1 -2.824 2.995l-.176 .005h-14a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-9.465l9.445 6.297l.116 .066a1 1 0 0 0 .878 0l.116 -.066l9.445 -6.297z" stroke-width="0" fill="currentColor" style="--darkreader-inline-fill: currentColor;" data-darkreader-inline-fill=""></path><path d="M19 4c1.08 0 2.027 .57 2.555 1.427l-9.555 6.37l-9.555 -6.37a2.999 2.999 0 0 1 2.354 -1.42l.201 -.007h14z" stroke-width="0" fill="currentColor" style="--darkreader-inline-fill: currentColor;" data-darkreader-inline-fill=""></path></svg>`
|
||||||
const mastodon = `<svg role="img" viewBox="-1 -1 26 26" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" stroke="currentColor" d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>`;
|
|
||||||
const bluesky = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" stroke="currentColor" d="M407.8 294.7c-3.3-.4-6.7-.8-10-1.3c3.4 .4 6.7 .9 10 1.3zM288 227.1C261.9 176.4 190.9 81.9 124.9 35.3C61.6-9.4 37.5-1.7 21.6 5.5C3.3 13.8 0 41.9 0 58.4S9.1 194 15 213.9c19.5 65.7 89.1 87.9 153.2 80.7c3.3-.5 6.6-.9 10-1.4c-3.3 .5-6.6 1-10 1.4C74.3 308.6-9.1 342.8 100.3 464.5C220.6 589.1 265.1 437.8 288 361.1c22.9 76.7 49.2 222.5 185.6 103.4c102.4-103.4 28.1-156-65.8-169.9c-3.3-.4-6.7-.8-10-1.3c3.4 .4 6.7 .9 10 1.3c64.1 7.1 133.6-15.1 153.2-80.7C566.9 194 576 75 576 58.4s-3.3-44.7-21.6-52.9c-15.8-7.1-40-14.9-103.2 29.8C385.1 81.9 314.1 176.4 288 227.1z"/></svg>`
|
|
||||||
|
|
||||||
export const icons = {
|
export const icons = { home: home, blog: blog, theme_dark: theme_dark, theme_light: theme_light, lang: lang, theme_auto: theme_auto, music: music, matrix: matrix, telegram: telegram, github: github, git: git, mail: mail};
|
||||||
home: home,
|
|
||||||
blog: blog,
|
|
||||||
theme_dark: theme_dark,
|
|
||||||
theme_light: theme_light,
|
|
||||||
lang: lang,
|
|
||||||
theme_auto: theme_auto,
|
|
||||||
music: music,
|
|
||||||
matrix: matrix,
|
|
||||||
telegram: telegram,
|
|
||||||
github: github,
|
|
||||||
git: git,
|
|
||||||
mail: mail,
|
|
||||||
mastodon: mastodon,
|
|
||||||
bluesky: bluesky,
|
|
||||||
};
|
|
||||||
|
@ -6,7 +6,9 @@ import Hero from "../components/Hero.astro";
|
|||||||
import About from "../components/About.astro";
|
import About from "../components/About.astro";
|
||||||
import Showcase from "../components/Showcase.astro";
|
import Showcase from "../components/Showcase.astro";
|
||||||
import FloatingLinks from "../components/FloatingLinks.astro";
|
import FloatingLinks from "../components/FloatingLinks.astro";
|
||||||
import { getLangFromUrl, useTranslations } from "../i18n/utils";
|
|
||||||
|
//@ts-ignore
|
||||||
|
import { getLangFromUrl, useTranslations } from "../../i18n/utils";
|
||||||
|
|
||||||
const t = useTranslations(getLangFromUrl(Astro.url));
|
const t = useTranslations(getLangFromUrl(Astro.url));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user