Migrate to markdown, add rss feed
This commit is contained in:
parent
a4e952773b
commit
83a4c483c7
|
@ -1,4 +1,6 @@
|
|||
import { defineConfig } from 'astro/config'
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({})
|
||||
export default defineConfig({
|
||||
site: 'https://comfycamp.space'
|
||||
})
|
||||
|
|
36
package-lock.json
generated
36
package-lock.json
generated
|
@ -9,6 +9,7 @@
|
|||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.3.0",
|
||||
"@astrojs/rss": "^3.0.0",
|
||||
"astro": "^3.5.2",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
|
@ -128,6 +129,15 @@
|
|||
"node": ">=18.14.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@astrojs/rss": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/rss/-/rss-3.0.0.tgz",
|
||||
"integrity": "sha512-PMX8iqByk9gtOrusikten/oF5uHjOCZigL6RuXFBUu+xtdKQxXzfIohJ99V2haA4FJjVDyibDTGzXR81POBMxQ==",
|
||||
"dependencies": {
|
||||
"fast-xml-parser": "^4.2.7",
|
||||
"kleur": "^4.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@astrojs/telemetry": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.0.4.tgz",
|
||||
|
@ -2118,6 +2128,27 @@
|
|||
"node": ">=8.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-xml-parser": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.2.tgz",
|
||||
"integrity": "sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||
},
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/naturalintelligence"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"strnum": "^1.0.5"
|
||||
},
|
||||
"bin": {
|
||||
"fxparser": "src/cli/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
|
||||
|
@ -5603,6 +5634,11 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strnum": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
|
||||
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.3.0",
|
||||
"@astrojs/rss": "^3.0.0",
|
||||
"astro": "^3.5.2",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
|
|
1
public/icons/rss.svg
Normal file
1
public/icons/rss.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>RSS</title><path fill="white" d="M19.199 24C19.199 13.467 10.533 4.8 0 4.8V0c13.165 0 24 10.835 24 24h-4.801zM3.291 17.415c1.814 0 3.293 1.479 3.293 3.295 0 1.813-1.485 3.29-3.301 3.29C1.47 24 0 22.526 0 20.71s1.475-3.294 3.291-3.295zM15.909 24h-4.665c0-6.169-5.075-11.245-11.244-11.245V8.09c8.727 0 15.909 7.184 15.909 15.91z"/></svg>
|
After Width: | Height: | Size: 414 B |
|
@ -1,4 +1,28 @@
|
|||
<footer>
|
||||
<h2>Сайт</h2>
|
||||
<div class="list">
|
||||
<a href="/rss.xml">
|
||||
<img
|
||||
src="/icons/rss.svg"
|
||||
width="18px"
|
||||
height="18px"
|
||||
alt="RSS logo"
|
||||
/>
|
||||
RSS
|
||||
</a>
|
||||
<a href="https://github.com/ordinary-dev/comfycamp/issues/new" target="_blank">
|
||||
<img
|
||||
src="/icons/github.svg"
|
||||
width="18px"
|
||||
height="18px"
|
||||
alt="Github logo"
|
||||
/>
|
||||
Обратная связь
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h2>Автор</h2>
|
||||
<div class="list">
|
||||
<a href="https://m.comfycamp.space/@lumin" rel="me" target="_blank">
|
||||
<img
|
||||
src="/icons/mastodon.svg"
|
||||
|
@ -26,15 +50,7 @@
|
|||
/>
|
||||
admin@comfycamp.space
|
||||
</a>
|
||||
<a href="https://github.com/ordinary-dev/comfycamp/issues/new" target="_blank">
|
||||
<img
|
||||
src="/icons/github.svg"
|
||||
width="18px"
|
||||
height="18px"
|
||||
alt="Github logo"
|
||||
/>
|
||||
Обратная связь
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
|
@ -46,14 +62,15 @@
|
|||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
footer > a {
|
||||
.list {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
footer div > a {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
footer svg {
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
---
|
||||
interface Props {
|
||||
date: string
|
||||
title: string
|
||||
href: string
|
||||
}
|
||||
|
||||
const { date, title, href } = Astro.props
|
||||
---
|
||||
|
||||
<div class="note">
|
||||
<div class="date">{ date }</div>
|
||||
<div class="dash">—</div>
|
||||
<a href={href}>{ title }</a>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.note {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.dash {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 600px) {
|
||||
.note {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.dash {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,25 +0,0 @@
|
|||
---
|
||||
interface Props {
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
const { createdAt, updatedAt } = Astro.props
|
||||
---
|
||||
|
||||
<div class="metadata">
|
||||
<p>Дата создания: { createdAt }</p>
|
||||
{ updatedAt !== createdAt && <p>Последнее обновление: { updatedAt }</p> }
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.metadata {
|
||||
color: #aaa;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.metadata p {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
</style>
|
15
src/content/config.ts
Normal file
15
src/content/config.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { z, defineCollection } from 'astro:content'
|
||||
|
||||
const notesCollection = defineCollection({
|
||||
type: 'content',
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
pubDate: z.date(),
|
||||
updatedAt: z.date().optional(),
|
||||
}),
|
||||
})
|
||||
|
||||
export const collections = {
|
||||
'notes': notesCollection,
|
||||
}
|
34
src/content/notes/carefree-mountain.md
Normal file
34
src/content/notes/carefree-mountain.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
title: "Беспечная Гляденовская гора"
|
||||
description: "Путешествие до Гляденовской горы."
|
||||
pubDate: 2023-10-16
|
||||
---
|
||||
|
||||
Гляденовская гора - это красивый лес в Перми, за которым прячется деревянная часовня.
|
||||
Здесь не будет рассказов про историю горы, ведь я не являюсь ни историком, ни археологом,
|
||||
оставлю это занятие более компетентным людям.
|
||||
Я всего лишь хочу показать вам это место от лица обывателя,
|
||||
и, может быть, это сподвигнет вас отправиться когда-нибудь в небольшое путешествие.
|
||||
|
||||
![Фотография поля с видом на пермский аэропорт](/carefree-mountain/1.webp)
|
||||
|
||||
Гляденовская гора находится недалеко от пермского аэропорта.
|
||||
Если до него самого добраться не составит большого труда, то дальше придётся идти вдоль дороги, хоть и совсем недолго.
|
||||
Вскоре вы зайдете в лес, тогда идти станет проще и интереснее.
|
||||
|
||||
Теперь, когда мы наконец-то в лесу, где нет проезжающих мимо машин, можно расслабиться.
|
||||
Дальнейший путь представляет из себя череду деревянных ступенек.
|
||||
Спускаться по ним довольно легко, но ещё проще подскользнуться.
|
||||
|
||||
![Деревянные ступеньки в лесу](/carefree-mountain/2.webp)
|
||||
![Белочка](/carefree-mountain/3.webp)
|
||||
![Деревянные ступеньки в лесу](/carefree-mountain/4.webp)
|
||||
|
||||
В конце пути нас ожидает деревянная церковь.
|
||||
Теперь можно неспеша осмотреться вокруг.
|
||||
В таких местах почти ничего не просиходит, внешнего мира как будто не существует.
|
||||
Он остался там, за горой, здесь же царит тишина и спокойствие.
|
||||
|
||||
![Деревянная церковь в лесу](/carefree-mountain/5.webp)
|
||||
|
||||
*Удивительное рядом.*
|
119
src/content/notes/self-hosting.md
Normal file
119
src/content/notes/self-hosting.md
Normal file
|
@ -0,0 +1,119 @@
|
|||
---
|
||||
title: "Свободные альтернативы популярным сервисам"
|
||||
description: "Список самых популярных независимых альтернатив коммерческим социальным сетям и сервисам."
|
||||
pubDate: 2023-08-27
|
||||
updatedAt: 2023-10-01
|
||||
---
|
||||
|
||||
Это статья для тех, кто никогда не слышал про альтернативы современным социальным сетям и сервисам.
|
||||
|
||||
Мы стали слишком сильно полагаться на централизованные сервисы, такие как vk, youtube, telegram или twitter.
|
||||
Если у вас начали появляться претензии, будь то цензура, отсутствие нужных функций или
|
||||
пренебрежительное отношение к личным данным, я предлагаю вам обратить внимание на альтернативы,
|
||||
которые вы можете запустить на своем сервере.
|
||||
|
||||
Хотя, строго говоря, иметь свой сервер вовсе не обязательно,
|
||||
но в таком случае вы должны доверять владельцу сервера, ведь именно он будет хранить ваши данные.
|
||||
Это не особо касается социальных сетей без личных сообщений, там всё и так публично.
|
||||
Мессенджеры, поддерживающие шифрование, защищают вас от лишних глаз, можете без опасений выбирать публичный сервер.
|
||||
А вот хранить файлы у незнакомцев я бы не стал.
|
||||
|
||||
## Социальные сети - Fediverse
|
||||
|
||||
![Скриншот fediverse.party](/selfhosted/fediverse-party.webp)
|
||||
|
||||
Fediverse - это группа из нескольких социальных сетей, представляющие аналоги популярным сервисам.
|
||||
|
||||
| Оригинальный сервис | Альтернативы |
|
||||
| --- | --- |
|
||||
| Twitter | [Mastodon](https://joinmastodon.org/), [Misskey](https://misskey-hub.net/en/) и [Pleroma](https://pleroma.social/) |
|
||||
| Vk, Facebook | [Friendica](https://friendi.ca/) |
|
||||
| Instagram | [Pixelfed](https://pixelfed.org/) |
|
||||
| Reddit | [Lemmy](https://join-lemmy.org/) |
|
||||
| YouTube | [Peertube](https://joinpeertube.org/) |
|
||||
| Soundcloud | [Funkwhale](https://funkwhale.audio/) |
|
||||
|
||||
Создать свой сервер может любой желающий, и его пользователи смогут общаться с пользователями на других серверах.
|
||||
Зачастую можно общаться даже с пользователями других платформ.
|
||||
|
||||
Если вы ищете простое место для старта, рекомендую обратить внимание на mastodon.
|
||||
Выберите сервер, на котором вы хотите зарегистрироваться,
|
||||
в этом вам может помочь [каталог русскоязычных серверов](https://ru.index.community/communities).
|
||||
|
||||
Вот парочка советов:
|
||||
* Ознакомтесь с правилами сервера. На разных серверах модераторы могут запрещать разные вещи.
|
||||
* Не регистрируйтесь на больших серверах. Вы не получите от этого особых плюсов, а лишь усилите централизацию.
|
||||
|
||||
У меня есть свой [сервер mastodon](https://m.comfycamp.space), жду вас в гости!
|
||||
|
||||
## Мессенджеры - Matrix, XMPP
|
||||
|
||||
![Скриншот matirx.org](/selfhosted/matrix.webp)
|
||||
|
||||
В мире децентрализованного общения сейчас популярны 2 протокола: xmpp (ранее jabber) и matrix.
|
||||
Идея такая же, как и в случае с fediverse: любой желающий может поднять свой сервер, а пользователи разных серверов могут общаться между собой.
|
||||
|
||||
По сравнению с другими платформами, например telegram или whatsapp,
|
||||
вы получаете end-to-end шифрование на всех устройствах и множество клиентов с открытым исходным кодом,
|
||||
а для регистрации не нужно указывать номер телефона.
|
||||
|
||||
Какой из них вам понравится больше - сказать тяжело. Попробуйте оба.
|
||||
|
||||
## Облако - Nextcloud
|
||||
|
||||
![Скриншот менеджера файлов в nextcloud](/selfhosted/nextcloud.webp)
|
||||
|
||||
[Nextcloud](https://nextcloud.com/) - это больше, чем просто облако.
|
||||
Это и хранилище файлов, и календарь, и задачи, и контакты, и ещё много всего.
|
||||
Своим функционалом nextcloud способен заменить целые экосистемы.
|
||||
|
||||
При желании к nextcloud можно подключить офисный редакторор, например onlyoffice, тогда nextcloud превращается ещё и в альтернативу Google Docs.
|
||||
Это может быть очень кстати для разных компаний.
|
||||
|
||||
Список контактов, календари и задачи можно синхронизировать между своими устройствами.
|
||||
Для этого используются обычные протоколы сaldav и carddav.
|
||||
|
||||
## Стриминг - Jellyfin
|
||||
|
||||
![Скриншот jellyfin с открытой страницей фильма](/selfhosted/jellyfin.webp)
|
||||
|
||||
[Jellyin](https://jellyfin.org/) - это ваш личный стриминговый сервис.
|
||||
|
||||
Вы просто указываете путь до вашей медиатеки и получаете возможность смотреть и слушать её онлайн.
|
||||
Jellyfin поддерживает фильмы, сериалы, музыку, аудиокниги и комиксы.
|
||||
|
||||
Jellyfin может перекодировать файлы на лету, если посчитает, что ваше устройство не поддерживает исходный формат.
|
||||
Это зачастую необходимо для браузеров, так как они могут воспроизодить только самые простые форматы.
|
||||
Если вы хотите снизить нагрузку на сервер, можно найти клиенты, позволяющие транслировать видео напрямую в ваш плеер
|
||||
(см. [jellyfin-mpv-shim](https://github.com/jellyfin/jellyfin-mpv-shim).
|
||||
|
||||
Jellyfin поддерживает DLNA, а это значит, что в пару кликов можно включить любой фильм на телевизоре, который находится в той же сети, что и сервер.
|
||||
А ещё можно организовывать комнаты для просмотра и синхронизировать воспроизведение на нескольких устройствах, если вы хотите посмотреть
|
||||
что-нибудь в компании.
|
||||
|
||||
Jellyfin - это не обязательно синоним пиратства.
|
||||
Вы можете стримить легально приобретенный контент, если у вас есть такое желание.
|
||||
|
||||
## Фото и видео - Photoprism
|
||||
|
||||
![Скриншот photoprism, на котором видно несколько фотографий](/selfhosted/photoprism.webp)
|
||||
|
||||
Многие пользуются сервисами от Apple или Google, чтобы хранить личные фотографии и видео.
|
||||
Если вам становится некомфортно от мысли, что компании, владеющие рекламными сетями,
|
||||
имеют доступ к вашим фотографиям, можно обратиться к [Photoprism](https://www.photoprism.app/).
|
||||
|
||||
Photoprism позволяет легко просматривать фото в браузере, редактировать метатеги,
|
||||
и даже умеет распознавать объекты на фото (хотя, честно говоря, эта функция работает не идеально).
|
||||
Вы можете создавать альбомы и делиться ими с другими людьми.
|
||||
|
||||
Загружать фото можно через веб-интерфейс, импортировать с директории на вашем сервере или же
|
||||
загружать их по протоколу webdav с того же nextcloud.
|
||||
|
||||
Photoprism умеет конвертировать фотографии формата raw, чтобы их можно было открыть в браузере.
|
||||
Также вы без проблем можете загрузить свои видео, они при необходимости будут сконвертированы.
|
||||
|
||||
## Вместо заключения - Awesome Selfhosted
|
||||
|
||||
Один из самых популярных репозиториев на Github: [Awesome Selfhosted](https://github.com/awesome-selfhosted/awesome-selfhosted).
|
||||
Это большой список всевозможных сервисов, которые вы можете запустить на своем сервере.
|
||||
В этой статье я не покрыл и 10% доступных вариантов. Однако, надеюсь, что я смог хотя бы приоткрыть дверь в эту кроличью нору.
|
38
src/content/notes/strelka.md
Normal file
38
src/content/notes/strelka.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
title: "Мыс Стрелка"
|
||||
description: "Фотографии с путешествия на мыс Стрелка."
|
||||
pubDate: 2023-11-11
|
||||
---
|
||||
|
||||
*Некоторые вещи стоят того, чтобы встать в 6 утра в воскресенье.*
|
||||
|
||||
## Куда мы едем?
|
||||
|
||||
Мыс Стрелка - это скала, у которой соединяются 2 реки: Кама и Чусовая.
|
||||
Находится она недалеко от города, добраться до ближайших деревень можно на электричке,
|
||||
а дальше нужно пару километров пройти пешком.
|
||||
|
||||
Точную дорогу мы заранее не посмотрели и пришли не на скалу, а к её основанию.
|
||||
Подняться, как оказалось, можно и у самой скалы, в самом тяжёлом месте есть верёвка.
|
||||
|
||||
![Вершина скалы](/strelka/1.jpg)
|
||||
![Вид с мыса Стрелка](/strelka/2.jpg)
|
||||
![Вид с мыса Стрелка](/strelka/3.jpg)
|
||||
|
||||
На вершине скалы довольно ветрено, но красиво.
|
||||
Совсем рядом находятся макушки деревьев, а дальше только вода.
|
||||
|
||||
Спустившись буквально на пару шагов можно укрыться от ветра и отдохнуть.
|
||||
Если вы оказались здесь осенью, то чай будет очень кстати.
|
||||
|
||||
![Пьем чай на скале](/strelka/4.jpg)
|
||||
|
||||
## Путь обратно
|
||||
|
||||
У нас оставалось несколько часов до электрички, поэтому мы отправились изучать ближайшие деревни.
|
||||
Вроде бы мы находились совсем недалеко от города, но атмосфера уже другая.
|
||||
|
||||
![Железная дорога в деревне](/strelka/5.jpg)
|
||||
|
||||
Наши похождения закончились довольно быстро, но на станции можно спокойно дождаться электрички.
|
||||
В итоге получилось короткое, но запоминающееся путешествие.
|
1
src/env.d.ts
vendored
1
src/env.d.ts
vendored
|
@ -1 +1,2 @@
|
|||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import Note from '../components/Note.astro'
|
||||
import { getCollection } from 'astro:content'
|
||||
|
||||
const notes = await getCollection('notes')
|
||||
notes.sort((a, b) => {
|
||||
if (a.data.pubDate < b.data.pubDate) return 1
|
||||
if (a.data.pubDate > b.data.pubDate) return -1
|
||||
return 0
|
||||
})
|
||||
---
|
||||
|
||||
<Layout title="Уютный домик">
|
||||
|
@ -14,22 +21,31 @@ import Note from '../components/Note.astro'
|
|||
</p>
|
||||
|
||||
<h2>Заметки</h2>
|
||||
<Note
|
||||
date="11 ноября 2023"
|
||||
title="Мыс Стрелка"
|
||||
href="/notes/strelka/"
|
||||
/>
|
||||
<Note
|
||||
date="16 октября 2023"
|
||||
title="Беспечная Гляденовская гора"
|
||||
href="/notes/carefree-mountain/"
|
||||
/>
|
||||
<Note
|
||||
date="27 августа 2023"
|
||||
title="Свободные альтернативы популярным сервисам"
|
||||
href="/notes/self-hosting/"
|
||||
/>
|
||||
{notes.map(note => (
|
||||
<div class="note">
|
||||
<div class="date">{note.data.pubDate.toLocaleDateString("ru", {year: "numeric", month: "long", day: "numeric"})}</div>
|
||||
<div class="dash">—</div>
|
||||
<a href={`/notes/${note.slug}/`}>{ note.data.title }</a>
|
||||
</div>
|
||||
))}
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.note {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.dash {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 600px) {
|
||||
.note {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.dash {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
54
src/pages/notes/[...slug].astro
Normal file
54
src/pages/notes/[...slug].astro
Normal file
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
import { getCollection } from 'astro:content'
|
||||
import Layout from '../../layouts/Layout.astro'
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const blogEntries = await getCollection('notes')
|
||||
return blogEntries.map(entry => ({
|
||||
params: { slug: entry.slug },
|
||||
props: { entry },
|
||||
}))
|
||||
}
|
||||
const { entry } = Astro.props
|
||||
const { Content } = await entry.render()
|
||||
const formatDate = (date: Date) => {
|
||||
return date.toLocaleDateString("ru", {year: "numeric", month: "long", day: "numeric"})
|
||||
}
|
||||
---
|
||||
|
||||
<Layout title={entry.data.title} description={entry.data.description}>
|
||||
<h1>{entry.data.title}</h1>
|
||||
<div class="metadata">
|
||||
<p>Дата создания: { formatDate(entry.data.pubDate) }</p>
|
||||
{ entry.data.updatedAt !== undefined && entry.data.updatedAt !== entry.data.pubDate && (
|
||||
<p>Последнее обновление: { formatDate(entry.data.updatedAt) }</p>
|
||||
)}
|
||||
</div>
|
||||
<div class="content">
|
||||
<Content />
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.metadata {
|
||||
color: #aaa;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.metadata p {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.content {
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #eee;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,123 +0,0 @@
|
|||
---
|
||||
import Layout from '../../layouts/Layout.astro'
|
||||
import NoteMetadata from '../../components/NoteMetadata.astro'
|
||||
---
|
||||
|
||||
<Layout title="Беспечная Гляденовская гора">
|
||||
<h1>Беспечная Гляденовская гора</h1>
|
||||
|
||||
<NoteMetadata createdAt="16 октября 2023" updatedAt="16 октября 2023" />
|
||||
|
||||
<p>
|
||||
Гляденовская гора - это красивый лес в Перми, за которым прячется деревянная часовня.
|
||||
Здесь не будет рассказов про историю горы, ведь я не являюсь ни историком, ни археологом,
|
||||
оставлю это занятие более компетентным людям.
|
||||
Я всего лишь хочу показать вам это место от лица обывателя,
|
||||
и, может быть, это сподвигнет вас отправиться когда-нибудь в небольшое путешествие.
|
||||
</p>
|
||||
|
||||
<img
|
||||
src="/carefree-mountain/1.webp"
|
||||
alt="Фотография поля с видом на пермский аэропорт"
|
||||
class="wideImg"
|
||||
loading="lazy"
|
||||
/>
|
||||
<p>
|
||||
Гляденовская гора находится недалеко от пермского аэропорта.
|
||||
Если до него самого добраться не составит большого труда, то дальше придётся идти вдоль дороги, хоть и совсем недолго.
|
||||
Вскоре вы зайдете в лес, тогда идти станет проще и интереснее.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Теперь, когда мы наконец-то в лесу, где нет проезжающих мимо машин, можно расслабиться.
|
||||
Дальнейший путь представляет из себя череду деревянных ступенек.
|
||||
Спускаться по ним довольно легко, но ещё проще подскользнуться.
|
||||
<p>
|
||||
|
||||
<div class="firstPhotoGroup">
|
||||
<div>
|
||||
<img
|
||||
src="/carefree-mountain/2.webp"
|
||||
alt="Деревянные ступеньки в лесу"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
src="/carefree-mountain/3.webp"
|
||||
alt="Белочка"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
src="/carefree-mountain/4.webp"
|
||||
alt="Деревянные ступеньки в лесу"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
В конце пути нас ожидает деревянная церковь.
|
||||
Теперь можно неспеша осмотреться вокруг.
|
||||
В таких местах почти ничего не просиходит, внешнего мира как будто не существует.
|
||||
Он остался там, за горой, здесь же царит тишина и спокойствие.
|
||||
</p>
|
||||
|
||||
<img
|
||||
src="/carefree-mountain/5.webp"
|
||||
alt="Деревянная церковь в лесу"
|
||||
class="wideImg"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
<p><i>Удивительное рядом.</i></p>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.wideImg {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.firstPhotoGroup {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: 450px 310px;
|
||||
grid-template-areas:
|
||||
"first first"
|
||||
"second third";
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.firstPhotoGroup {
|
||||
grid-template-rows: 450px 450px 450px;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas:
|
||||
"first"
|
||||
"second"
|
||||
"third";
|
||||
}
|
||||
}
|
||||
|
||||
.firstPhotoGroup > div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.firstPhotoGroup > div > img {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.firstPhotoGroup > div:nth-child(1) {
|
||||
grid-area: first;
|
||||
}
|
||||
.firstPhotoGroup > div:nth-child(2) {
|
||||
grid-area: second;
|
||||
}
|
||||
.firstPhotoGroup > div:nth-child(3) {
|
||||
grid-area: third;
|
||||
}
|
||||
</style>
|
|
@ -1,258 +0,0 @@
|
|||
---
|
||||
import Layout from '../../layouts/Layout.astro'
|
||||
import NoteMetadata from '../../components/NoteMetadata.astro'
|
||||
---
|
||||
|
||||
<Layout title="Свободные альтернативы популярным сервисам">
|
||||
<h1>Свободные альтернативы популярным сервисам</h1>
|
||||
|
||||
<NoteMetadata createdAt="27 августа 2023" updatedAt="1 октября 2023" />
|
||||
|
||||
<p>
|
||||
Это статья для тех, кто никогда не слышал про альтернативы современным социальным сетям и сервисам.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Мы стали слишком сильно полагаться на централизованные сервисы, такие как vk, youtube, telegram или twitter.
|
||||
Если у вас начали появляться претензии, будь то цензура, отсутствие нужных функций или
|
||||
пренебрежительное отношение к личным данным, я предлагаю вам обратить внимание на альтернативы,
|
||||
которые вы можете запустить на своем сервере.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Хотя, строго говоря, иметь свой сервер вовсе не обязательно,
|
||||
но в таком случае вы должны доверять владельцу сервера, ведь именно он будет хранить ваши данные.
|
||||
Это не особо касается социальных сетей без личных сообщений, там всё и так публично.
|
||||
Мессенджеры, поддерживающие шифрование, защищают вас от лишних глаз, можете без опасений выбирать публичный сервер.
|
||||
А вот хранить файлы у незнакомцев я бы не стал.
|
||||
</p>
|
||||
|
||||
<h2>Социальные сети - Fediverse</h2>
|
||||
|
||||
<img
|
||||
src="/selfhosted/fediverse-party.webp"
|
||||
alt="Скриншот fediverse.party"
|
||||
loading="lazy"
|
||||
/>
|
||||
<p class="attribution">Скриншот <a href="https://fediverse.party" target="_blank">fediverse.party</a>.</p>
|
||||
|
||||
<p>
|
||||
Fediverse - это группа из нескольких социальных сетей, представляющие аналоги популярным сервисам.
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Оригинальный сервис</th>
|
||||
<th>Альтернативы</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Twitter</td>
|
||||
<td>
|
||||
<a href="https://joinmastodon.org/" target="_blank">Mastodon</a>,
|
||||
<a href="https://misskey-hub.net/en/" target="_blank">misskey</a> и
|
||||
<a href="https://pleroma.social/" target="_blank">pleroma</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Vk, facebook</td>
|
||||
<td>
|
||||
<a href="https://friendi.ca/" target="_blank">Friendica</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Instagram</td>
|
||||
<td>
|
||||
<a href="https://pixelfed.org/" target="_blank">Pixelfed</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Reddit</td>
|
||||
<td>
|
||||
<a href="https://join-lemmy.org/" target="_blank">Lemmy</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>YouTube</td>
|
||||
<td>
|
||||
<a href="https://joinpeertube.org/" target="_blank">Peertube</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Soundcloud</td>
|
||||
<td>
|
||||
<a href="https://funkwhale.audio/" target="_blank">Funkwhale</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
Создать свой сервер может любой желающий, и его пользователи смогут общаться с пользователями на других серверах.
|
||||
Зачастую можно общаться даже с пользователями других платформ.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Если вы ищете простое место для старта, рекомендую обратить внимание на mastodon.
|
||||
Выберите сервер, на котором вы хотите зарегистрироваться,
|
||||
в этом вам может помочь <a href="https://ru.index.community/communities" target="_blank">каталог русскоязычных серверов</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Вот парочка советов:
|
||||
|
||||
<ul>
|
||||
<li>Ознакомтесь с правилами сервера. На разных серверах модераторы могут запрещать разные вещи.</li>
|
||||
<li>Не регистрируйтесь на больших серверах. Вы не получите от этого особых плюсов, а лишь усилите централизацию.</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
У меня есть <a href="https://m.comfycamp.space" target="_blank">свой сервер mastodon</a>, жду вас в гости!
|
||||
</p>
|
||||
|
||||
<h2>Мессенджеры - Matrix, XMPP</h2>
|
||||
|
||||
<img
|
||||
src="/selfhosted/matrix.webp"
|
||||
alt="Скриншот matirx.org"
|
||||
loading="lazy"
|
||||
/>
|
||||
<p class="attribution">Скриншот <a href="https://matrix.org/" target="_blank">matrix.org</a>.</p>
|
||||
|
||||
<p>
|
||||
В мире децентрализованного общения сейчас популярны 2 протокола: xmpp (ранее jabber) и matrix.
|
||||
Идея такая же, как и в случае с fediverse: любой желающий может поднять свой сервер, а пользователи разных серверов могут общаться между собой.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
По сравнению с другими платформами, например telegram или whatsapp,
|
||||
вы получаете end-to-end шифрование на всех устройствах и множество клиентов с открытым исходным кодом,
|
||||
а для регистрации не нужно указывать номер телефона.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Какой из них вам понравится больше - сказать тяжело. Попробуйте оба.
|
||||
</p>
|
||||
|
||||
<h2>Облако - Nextcloud</h2>
|
||||
|
||||
<img
|
||||
src="/selfhosted/nextcloud.webp"
|
||||
alt="Скриншот менеджера файлов в nextcloud"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
<p>
|
||||
<a href="https://nextcloud.com/" target="_blank">Nextcloud</a> - это больше, чем просто облако.
|
||||
Это и хранилище файлов, и календарь, и задачи, и контакты, и ещё много всего.
|
||||
Своим функционалом nextcloud способен заменить целые экосистемы.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
При желании к nextcloud можно подключить офисный редакторор, например onlyoffice, тогда nextcloud превращается ещё и в альтернативу Google Docs.
|
||||
Это может быть очень кстати для разных компаний.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Список контактов, календари и задачи можно синхронизировать между своими устройствами.
|
||||
Для этого используются обычные протоколы сaldav и carddav.
|
||||
</p>
|
||||
|
||||
<h2>Стриминг - Jellyfin</h2>
|
||||
|
||||
<img
|
||||
src="/selfhosted/jellyfin.webp"
|
||||
alt="Скриншот jellyfin с открытой страницей фильма Taxi Driver"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
<p>
|
||||
<a href="https://jellyfin.org/" target="_blank">Jellyfin</a> - это ваш личный стриминговый сервис.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Вы просто указываете путь до вашей медиатеки и получаете возможность смотреть и слушать её онлайн.
|
||||
Jellyfin поддерживает фильмы, сериалы, музыку, аудиокниги и комиксы.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Jellyfin может перекодировать файлы на лету, если посчитает, что ваше устройство не поддерживает исходный формат.
|
||||
Это зачастую необходимо для браузеров, так как они могут воспроизодить только самые простые форматы.
|
||||
Если вы хотите снизить нагрузку на сервер, можно найти клиенты, позволяющие транслировать видео напрямую в ваш плеер
|
||||
(см. <a href="https://github.com/jellyfin/jellyfin-mpv-shim" target="_blank">jellyfin-mpv-shim</a>).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Jellyfin поддерживает DLNA, а это значит, что в пару кликов можно включить любой фильм на телевизоре, который находится в той же сети, что и сервер.
|
||||
А ещё можно организовывать комнаты для просмотра и синхронизировать воспроизведение на нескольких устройствах, если вы хотите посмотреть
|
||||
что-нибудь в компании.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Jellyfin - это не обязательно синоним пиратства.
|
||||
Вы можете стримить легально приобретенный контент, если у вас есть такое желание.
|
||||
</p>
|
||||
|
||||
<h2>Фото и видео - Photoprism</h2>
|
||||
|
||||
<img
|
||||
src="/selfhosted/photoprism.webp"
|
||||
alt="Скриншот photoprism, на котором видно несколько фотографий"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
<p>
|
||||
Многие пользуются сервисами от Apple или Google, чтобы хранить личные фотографии и видео.
|
||||
Если вам становится некомфортно от мысли, что компании, владеющие рекламными сетями,
|
||||
имеют доступ к вашим фотографиям, можно обратиться к <a href="https://www.photoprism.app/" target="_blank">Photoprism</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Photoprism позволяет легко просматривать фото в браузере, редактировать метатеги,
|
||||
и даже умеет распознавать объекты на фото (хотя, честно говоря, эта функция работает не идеально).
|
||||
Вы можете создавать альбомы и делиться ими с другими людьми.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Загружать фото можно через веб-интерфейс, импортировать с директории на вашем сервере или же
|
||||
загружать их по протоколу webdav с того же nextcloud.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Photoprism умеет конвертировать фотографии формата raw, чтобы их можно было открыть в браузере.
|
||||
Также вы без проблем можете загрузить свои видео, они при необходимости будут сконвертированы.
|
||||
</p>
|
||||
|
||||
<h2>Вместо заключения - Awesome Selfhosted</h2>
|
||||
|
||||
<p>
|
||||
Один из самых популярных репозиториев на Github: <a href="https://github.com/awesome-selfhosted/awesome-selfhosted" target="_blank">Awesome Selfhosted</a>.
|
||||
Это большой список всевозможных сервисов, которые вы можете запустить на своем сервере.
|
||||
В этой статье я не покрыл и 10% доступных вариантов. Однако, надеюсь, что я смог хотя бы приоткрыть дверь в эту кроличью нору.
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.attribution {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
font-style: italic;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table {
|
||||
margin-bottom: 20px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table, td, th {
|
||||
border: 1px solid #676767;
|
||||
}
|
||||
|
||||
td, th {
|
||||
padding: 6px 8px;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
|
@ -1,89 +0,0 @@
|
|||
---
|
||||
import Layout from '../../layouts/Layout.astro'
|
||||
import NoteMetadata from '../../components/NoteMetadata.astro'
|
||||
---
|
||||
|
||||
<Layout title="Мыс Стрелка">
|
||||
<h1>Мыс Стрелка</h1>
|
||||
|
||||
<NoteMetadata createdAt="11 ноября 2023" updatedAt="11 ноября 2023" />
|
||||
|
||||
<p>
|
||||
<i>Некоторые вещи стоят того, чтобы встать в 6 утра в воскресенье.</i>
|
||||
</p>
|
||||
|
||||
<h2>Куда мы едем?</h2>
|
||||
|
||||
<p>
|
||||
Мыс Стрелка - это скала, у которой соединяются 2 реки: Кама и Чусовая.
|
||||
Находится она недалеко от города, добраться до ближайших деревень можно на электричке,
|
||||
а дальше нужно пару километров пройти пешком.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Точную дорогу мы заранее не посмотрели и пришли не на скалу, а к её основанию.
|
||||
Подняться, как оказалось, можно и у самой скалы, в самом тяжёлом месте есть верёвка.
|
||||
</p>
|
||||
|
||||
<img
|
||||
src="/strelka/1.jpg"
|
||||
alt="Вершина скалы"
|
||||
/>
|
||||
|
||||
<div class="row">
|
||||
<img
|
||||
src="/strelka/2.jpg"
|
||||
alt="Вид с мыса Стрелка"
|
||||
/>
|
||||
<img
|
||||
src="/strelka/3.jpg"
|
||||
alt="Вид с мыса Стрелка"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
На вершине скалы довольно ветрено, но красиво.
|
||||
Совсем рядом находятся макушки деревьев, а дальше только вода.
|
||||
<p>
|
||||
|
||||
<p>
|
||||
Спустившись буквально на пару шагов можно укрыться от ветра и отдохнуть.
|
||||
Если вы оказались здесь осенью, то чай будет очень кстати.
|
||||
</p>
|
||||
<img
|
||||
src="/strelka/4.jpg"
|
||||
alt="Пьем чай на скале"
|
||||
/>
|
||||
|
||||
<h2>Путь обратно</h2>
|
||||
<p>
|
||||
У нас оставалось несколько часов до электрички, поэтому мы отправились изучать ближайшие деревни.
|
||||
Вроде бы мы находились совсем недалеко от города, но атмосфера уже другая.
|
||||
</p>
|
||||
|
||||
<img
|
||||
src="/strelka/5.jpg"
|
||||
alt="Железная дорога в деревне"
|
||||
/>
|
||||
|
||||
<p>
|
||||
Наши похождения закончились довольно быстро, но на станции можно спокойно дождаться электрички.
|
||||
В итоге получилось короткое, но запоминающееся путешествие.
|
||||
</p>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.row {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
23
src/pages/rss.xml.ts
Normal file
23
src/pages/rss.xml.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import type { APIContext } from 'astro'
|
||||
import rss from '@astrojs/rss'
|
||||
import { getCollection } from 'astro:content'
|
||||
|
||||
export async function GET(context: APIContext) {
|
||||
const notes = await getCollection('notes')
|
||||
notes.sort((a, b) => {
|
||||
if (a.data.pubDate < b.data.pubDate) return 1
|
||||
if (a.data.pubDate > b.data.pubDate) return -1
|
||||
return 0
|
||||
})
|
||||
return rss({
|
||||
title: 'Уютный домик',
|
||||
description: 'Заметки с сайта comfycamp.space',
|
||||
site: context.site || "https://comfycamp.space",
|
||||
items: notes.map((note) => ({
|
||||
title: note.data.title,
|
||||
pubDate: note.data.pubDate,
|
||||
description: note.data.description,
|
||||
link: `/notes/${note.slug}/`,
|
||||
})),
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue