mirror of
https://codeberg.org/postscriptum/gemlog.git
synced 2026-02-18 22:12:40 +00:00
initial commit
This commit is contained in:
commit
259fee630b
127 changed files with 7811 additions and 0 deletions
|
|
@ -0,0 +1,99 @@
|
|||
# Про сервер Fediverse в альтернативних мережах
|
||||
|
||||
Я давно користуюсь Fediverse, але свій інстанс - підняв відносно недавно: приблизно пів року тому. При чому, його було створено у якості експерименту з працездатності екосистеми ActivityPub в рамках оверлейної мережі Yggdrasil як спроба не просто зробити веб-проксі, а організації локального сузір'я.
|
||||
|
||||
Так як це був перший досвід, я ще збирався організувати доступ до нього з Інтернет (повноцінну між-серверну комунікацію) але згодом усвідомив, що цей протокол на таке не розрахований і навіть якщо це технічно можливо, адміністратори інших серверів потребуватимуть додаткового шару специфікації для такої взаємодії.
|
||||
|
||||
## Сервери
|
||||
|
||||
Їх вистачає: популярний Mastodon я не ставив бо не хотів морочитись з PostgreSQL та й в цілому, його розгортання виглядає для мене ледачого складно.
|
||||
|
||||
### Snac
|
||||
|
||||
Мені своєчасно порадили сервер Snac:
|
||||
=> https://codeberg.org/grunfink/snac2
|
||||
|
||||
Він є розрахованим не тільки на маленький персональний сервер без зовнішніх реєстрацій, а ще й не потребує бази даних, при тому клієнт функціонує без JS і до цього сервера є доволі якісні теми оформлення, наприклад Pika:
|
||||
=> https://codeberg.org/voron/snac-style/raw/branch/master/pika.css
|
||||
|
||||
Написаний Snac мовою C.
|
||||
|
||||
Функціонування (слідування, коментарі, бусти, приватні повідомлення тощо) в локальній мережі було успішно перевірено на живих вузлах Yggdrasil в комбінаціях:
|
||||
|
||||
* Snac (IPv6) / Snac (IPv6)
|
||||
* Snac (IPv6) / Snac (DNS)
|
||||
* Snac (IPv6) / Mitra (IPv6)
|
||||
* Snac (IPv6) / Mitra (DNS)
|
||||
|
||||
=> personal-snac-instance-for-yggdrasil-network.gmi Налаштування Fediverse-сервера Snac для мережі Yggdrasil
|
||||
|
||||
Мій інстанс для тестування:
|
||||
|
||||
=> http://[302:68d0:f0d5:b88d::fed] 302:68d0:f0d5:b88d::fed
|
||||
* взаємний фоловінг по запиту
|
||||
|
||||
### Mitra
|
||||
|
||||
Вже після встановлення Snac, я відкрив для себе Mitra:
|
||||
=> https://codeberg.org/silverpill/mitra
|
||||
|
||||
Цей сервер вимагає PostgreSQL, JS для стандартного клієнта і включає в себе навороти для автоматичної реєстрації та (мабуть) менеджменту користувачів. Я планую його спробувати потім, мені подобається його простий інтерфейс, користуюсь для свого Інтернет-профілю, який спочатку створив для тестів а потім там й лишився "жити".
|
||||
|
||||
Окремо згадую про це програмне рішення, тому що його вдалось протестувати в між-серверній комунікації з одним з реальних користувачів мережі. Також в нього була якимось чином налаштована взаємодія з Інтернет-федерацією. Подробиці цього збочення я не знаю, але довкола нашого експерименту, розробниками рушія Mitra було проявлено цікавість і спеціально натягнуто деякі адаптації по частині IPv6, тому в контексті цей сервер вартий уваги, а кому цікаві подробиці - зконтактуйте з
|
||||
```
|
||||
@madamada@mitra.void.my
|
||||
```
|
||||
* Yggdrasil
|
||||
|
||||
### Tootik
|
||||
|
||||
Окремо з цікавих платформ, я б ще відмітив сервер для протоколу Gemini - Tootik:
|
||||
=> https://github.com/dimkr/tootik
|
||||
|
||||
Його бекенд, як і передбачає ActivityPub, взаємодіє по HTTP/JSON, але на рівні користування там буде інтерфейс Gemini на 1965 порті з TLS.
|
||||
|
||||
Згадую про дане рішення в контексті того, що альтернативними мережами часто користуються з метою приватності, тому протокол Gemini тут може мати деяку нішу користувачів.
|
||||
|
||||
## Ідентифікатори хостів
|
||||
|
||||
Власне, саме цікаве і чому взагалі я задумав написати цей матеріал.
|
||||
|
||||
Щоб створити свій інстанс, вам потрібно ідентифікувати свій вузол як `user@host`. Коли ви це зробите, то вже не зможете безпроблемно його змінити потім, бо інші сервери федерації втратять зв'язок з вами. Тому він повинен бути обраний один раз і обраний правильно.
|
||||
|
||||
### DNS
|
||||
|
||||
ActivityPub розроблявся як протокол високого рівня для комунікації засобами класичної мережі Інтернет з DNS, у той час як мережах типу Yggdrasil прийнято користуватись IPv6 а домен - вже надавати опцією.
|
||||
|
||||
Локальна мережа з ідентифікацією DNS, здавалось би дозволяє легко змінювати адреси IP або навіть перескочити з мережі Yggdrasil на Mycelium, а з того - на ще якусь нову екосистему. Але за цими перескоками повинні слідувати не тільки ваші читачі, але й сервери федерації. Тому сумнівно, якщо це не якийсь спеціалізований на одну мережу домен `.ygg` в Alfis DNS, що тільки відсіче потенційну частину сегменту, яка ним не користується.
|
||||
|
||||
### IP
|
||||
|
||||
Свій ID я вирішив робити на IPv6 адресі мережі Yggdrasil бо такий формат, на відміну від домена, не може бути протермінований з часом (так, я можу зникнути на рік два і неочікувано для себе повернутись до мережі через десять).
|
||||
|
||||
З іншого боку, перманентні IP на основі криптографії - теж не панацея: ключі можуть бути загублені, випадково опубліковані тощо, а тому можуть потенційно потребувати генерації нових.
|
||||
|
||||
### Адреси підмереж
|
||||
|
||||
В мене ще лишається відкритим питання з адресами підмереж, бо ті є менш стійкими до колізій і такими, що потенційно можуть бути випиляні з майбутніх релізів роутера. Так, вони зручні, але сьогодні я б радив користуватись основними адресами для ідентифікації сервера, коли це можливо.
|
||||
|
||||
## Гібридна маршрутизація
|
||||
|
||||
На початку матеріалу зауважив, що особисто для себе - відмовився від ідеї мульти-мережної комунікації для свого інстансу, спробувавши просто ініціювати локальне сузір'я, галактику, фізично ізольованої на рівні ActivityPub від інших мереж. Але зараз, спостерігаю занепад обраної мною мережі Yggdrasil, вже експериментую з Mycelium, і вже завтра - буду експериментувати з наступною. Тому трохи розкрию свої думки стосовно гібридного формату вузла.
|
||||
|
||||
Вже після запуску сервера в альтернативній мережі (або мережах з налаштованим проксі для між-серверного API), постане питання поширення посилань. Адже ви захочете ділитись публікаціями з друзями в Інтернет, Yggdrasil, I2P та інших екосистемах. Бажано, це має бути якесь одне канонічне посилання, яке приведе користувача до цільової сторінки в незалежності від налаштувань його маршрутизації і функціонування самої мережі на момент відвідування.
|
||||
|
||||
Були думки щодо організації центрального сервісу "коротких посилань" по типу колишнього bit.ly який підтримуватиме усі можливі альтернативи і для кожної з них надаватиме веб-проксі за форматом хосту...
|
||||
|
||||
З іншого боку, можна створити ще один "шейред роутер" як окремий протокол та програму для нього, саме з метою обслуговування гібридних сервісів без реалізації їх маршрутизації. У цьому питанні явно не вистачає сучасних рішень, адже зберігається тенденція до створення нових мереж та деградації старих, як це сталось з CJDNS і сьогодні відбувається з Yggdrasil. Технічно, діапазон IPv6 надає широке поле для експериментальних роутерів, їх кількість в умовах сучасних перешкод мережі буде тільки зростати, тому це питання є відкритим.
|
||||
|
||||
## Висновки
|
||||
|
||||
Особисто для себе, вирішив не міксувати екосистеми і вести ізольовані тематичні ресурси по кожній з них, намагаючись ділитись посиланнями в рамках тої екосистеми, для якої створено інстанс. Звичайно, на практиці виходить інакше, станом на сьогодні, назвати локальний інстанс - соціальним складно, утім якщо ви цієї соціальної взаємодії не шукаєте на глобальному рівні, то чому б не спробувати.
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://devzone.org.ua/post/pro-server-fediverse-v-alternatyvnykh-merezakh Веб-адаптація матеріалу на DevZone (з коментарями)
|
||||
|
||||
### Дивіться також
|
||||
|
||||
=> yggdrasil-is-network-with-distributed-routing.gmi Yggdrasil - мережа з децентралізованою маршрутизацією
|
||||
92
public/uk/alfis-dns-domain-registration-in-blockchain.gmi
Normal file
92
public/uk/alfis-dns-domain-registration-in-blockchain.gmi
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
# Alfis DNS - реєстрація домену в блокчейн
|
||||
|
||||
Alfis DNS - децентралізована система доменних імен з реєстром в блокчейн.
|
||||
|
||||
Резольвер та клієнтська частина написані на мові Rust.
|
||||
|
||||
Для реєстрації домену використовуються наступні зони:
|
||||
|
||||
* .anon *
|
||||
* .btn
|
||||
* .conf
|
||||
* .index
|
||||
* .merch
|
||||
* .mirror
|
||||
* .mob
|
||||
* .screen
|
||||
* .srv
|
||||
* .ygg *
|
||||
|
||||
* зарезервовані для адрес мережі Yggdrasil, що виключає випадковий перехід з таких адрес на хости Clearnet.
|
||||
|
||||
Для запобігання кіберсквотингу використовується модель PoW і частково - PoS, генерація нових доменів також обмежена добовим інтервалом, а на один персональний ключ можна згенерувати не більше 10 доменів (що насправді має досить спірне підґрунтя).
|
||||
|
||||
Тим не менше, система досить зручна для локальних мереж, оскільки дозволяє створити безкоштовний домен на один рік без необхідності реєстрації персональних даних та залежності від центрального серверу. А розмір блокчейну при поточній кількості близько 1000 доменів складає приблизно 7 Мб.
|
||||
|
||||
В середньому, на комп'ютері з процесором i5, новий домен генерується від декількох до 12 годин; утім спочатку потрібно згенерувати персональний ключ, до якого потім цей домен буде прив'язаний. До закінчення року, потрібно змайнити домен повторно, використовуючи існуючий ключ, раніше робити це не має сенсу, бо пролонгація рахується від часу створення блоку.
|
||||
|
||||
## Встановлення
|
||||
|
||||
Встановлення відбувається досить просто, є готові бінарні пакети та репозиторії.
|
||||
|
||||
При збірці, з початкового коду, доведеться встановити останню версію rustc що зручно використовуючи пакет rustup.
|
||||
|
||||
В іншому, виконується стандартна процедура cargo:
|
||||
|
||||
```
|
||||
git clone https://github.com/Revertron/Alfis.git
|
||||
cd Alfis
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
## Налаштування
|
||||
|
||||
Запускаючи клієнт з графічною оболонкою, буде автоматично налаштовано резольвер.
|
||||
|
||||
Для систем типу Ubuntu, ймовірно доведеться вивільнити зайнятий порт 53.
|
||||
|
||||
Утім, графічний інтерфейс зручно використовувати виключно для адміністрування доменів та майнінгу, а сам резольвер розмістити десь на VPS та вказати до нього конфігурацію:
|
||||
|
||||
### В системах з systemd-resolved
|
||||
|
||||
```
|
||||
#/etc/systemd/resolved.conf
|
||||
[Resolve]
|
||||
DNS=IP
|
||||
Domains=~.
|
||||
```
|
||||
|
||||
Після змін, потрібно оновити кеш командою:
|
||||
|
||||
```
|
||||
systemctl restart systemd-resolved
|
||||
```
|
||||
|
||||
### В системах без systemd
|
||||
|
||||
```
|
||||
#/etc/resolv.conf
|
||||
nameserver xx.xx.xx.xx
|
||||
```
|
||||
|
||||
### FireFox
|
||||
|
||||
Браузер нічого не знає про діапазон імен Alfis DNS, тому при введені таких адрес, буде відправляти користувача в пошук.
|
||||
|
||||
Щоб виправити це, потрібно додати в about:config, наприклад для зони .ygg - наступну опцію:
|
||||
|
||||
```
|
||||
browser.fixup.domainsuffixwhitelist.ygg = true
|
||||
```
|
||||
|
||||
## Особистий досвід
|
||||
|
||||
* Alfis DNS хоч і працює з типовими адресами, але вимагає наявності встановленого або підключеного вузла для клієнтів. З цієї ж причини виникає і проблема сертифікації, наприклад засобами Certbot / Let's Encrypt.
|
||||
* В намірах зменшити розмір блокчейну, автор явно перегнув із захистом від зловживань сквотерами, від чого іноді доводиться чекати випадкові вузли, що підпишуть блок. А сама модель захисту явно грає на руку тим, хто в сквотингу зацікавлені (володіючи відповідним обчислювальним ресурсом). Під час останнього такого "зависання" частина користувачів була обурена бездіяльністю автора у відповідь на повторний інцидент і вірогідно відмовилась від цього і до того непопулярного рішення.
|
||||
* Внутрішньої монети блокчейн немає, утім потенційно може мати деякий економічний потенціал з використанням бартеру (засобами трансферу)
|
||||
* Наразі проєкт варто сприймати виключно як експериментальну альтернативу централізованим сервісам для ентузіастів, який фактично виконує ту само роль, що й /etc/hosts
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://github.com/Revertron/Alfis Репозиторій проєкту на GitHub
|
||||
=> https://viewer.alfis.name Неофіційний експлорер
|
||||
45
public/uk/asocial-p2p.gmi
Normal file
45
public/uk/asocial-p2p.gmi
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Граблі соціального P2P
|
||||
|
||||
Це мій особистий висновок в контексті соціальних платформ, які у свій час мали на меті створити "вільний інтернет" для вільних людей, зокрема - такі вже сьогодні мертві проєкти як twister, OpenBazaar, ZeroNet та інші. А можливо завтра - не так давно анонсована перша стабільна версія децентралізованої Git платформи Radicle.
|
||||
|
||||
## BitTorrent / DHT
|
||||
|
||||
Нічого революційного за останні роки еволюції пірингових технологій не відбулось. Мабуть, єдиним напівживим піринговим напрямком, все ще лишається обмін великими файлами через BitTorrent / DHT засобами UPnP, що дозволяє клієнтові користуватись шириною каналу, а серверу - балансувати трафік на піках, допоки пір не отримає своє та не закриє додаток, видаливши потім і сам файл.
|
||||
|
||||
## Залежність від сервісних вузлів
|
||||
|
||||
Трекери, сіди, ноди ретрансляції...
|
||||
|
||||
Ніхто за дарма не буде обслуговувати серйозний об'єм даних. Звісно, можна знайти "не мамонта", хто вірить в щось, паразитувати на його ноді, деякий час..
|
||||
|
||||
Підняв віддалену ноду Radicle для експерименту на мотивації стати сідом, а та з'їла все вільне місце на диску. Можливо, є певна опція, щоб лімітувати простір чи зберігати якісь фрагментовані дані (pieces), але тоді для мережі потрібна більша кількість вузлів, що забезпечать її 100% покриття. Тут я вже мовчу про оверхед системних даних, що стосуються актуалізації даних (CRUD) і мережевої роботи самого рою.
|
||||
|
||||
Якщо мені потрібен повноцінний сервер з виділеним IP, то питання: чому просто не поставити собі Gitea?
|
||||
|
||||
## Очікування пірів
|
||||
|
||||
В пірингових мережах завжди доводиться чекати когось на роздачу, яка відбувається, коли вже й не потрібно. Або ж знову таки орендувати сервер VPN, щоб спробувати знайти контент в заблокованих глобальним фаєрволом локаціях. Хоча на практиці, IP "не сервісних" вузлів змінюється так швидко, що й VPN не є панацеєю. В сучасному світі прогрес не стоїть на місці, люди користуються мобільним інтернетом, а той еволюціонує для балансування навантажень між різними станціями, генеруючи тони мертвих через годину IP.
|
||||
|
||||
З цієї ж причини, багато даних просто втрачається. Якщо ви хочете загубити на рандомному і цілком тимчасовому пірі шматок опублікованих даних, p2p - вдалий вибір.
|
||||
|
||||
## BlockChain
|
||||
|
||||
Тут здавалось би, рішенням може стати BlockChain, але це веде у ті само ворота, коли об'єм даних розростається а вартість зберігання зростає. При цьому, сама технологія блокчейн доволі чутлива до консенсусу: будь які зміни останнього вимагають згоди від усіх учасників та оновлення ними локальних копій програмного забезпечення, що не рідко спонукає до фрагментування форками.
|
||||
|
||||
Іноді я навіть не знаю, що витратніше для процесору: періодичний майнинг чи постійна актуалізація DHT. Багато p2p платформ використовують для своєї роботи і BlockChain і окремий сервер(и) DHT, а ще SSL інтерфейс для останнього і сервер UPnP. Відповідно це піднімає вимоги до вашого знову ж таки виділеного серверу, бо на локальному, без добровольців-садомазохістів мережа просто не працюватиме. А ті, на практиці, онлайн не довго, бо знаходять собі нову забаву, що вимагає 100% віддачі ресурсів попередньої.
|
||||
|
||||
## Mesh
|
||||
|
||||
Іншим цікавим напрямком, є оверлейні мережі типу Yggdrasil, що використовують піринг для побудови автоматизованого роутингу. Здебільшого вони зручні в ситуаціях, коли з якихось причин, протокол UPnP не доступний або певний порт блокується провайдером. Але й тут, подібно до VPN, потрібні вихідні вузли, тобто є залежніть від когось, а цей хтось - по суті, звичайний сервер.
|
||||
|
||||
На мою думку, потреба в оверлейних мережах відпаде з інтеграцією ISP протоколу IPv6, що дозволить кожному отримати свою виділену адресу і закрити це питання разом з його надлишковою інфраструктурою.
|
||||
|
||||
## Висновки
|
||||
|
||||
Таким чином, поки хтось не оформить 5-10 серверів початковою вартістю хоч в одну акцію GitHub (в контексті Radicle), воно просто не функціональне, при всій моїй лояльності до напрямку p2p.
|
||||
|
||||
Вже яку тисячу разів я стаю на ці граблі: ну немає ніякої свободи у цьому, суцільні обмеження, купа зайвого трафіку, обчислень і власного часу. Воно класно тільки тим, що зручно реплікується по хешу, але знову таки якщо задача децентралізувати то простіше руками чи скриптом розкидати на client/server.
|
||||
|
||||
Все одно розподілені/анархічні ноди потребують сервер у якійсь цілком собі монархічній країні. Ще раз нагадування собі про те, що "втекти" кудись вирішує питання, у кращому випадку, тільки на деякий час.
|
||||
|
||||
Сама пірингова технологія, все ще може бути корисною у спеціалізованих сферах, для яких власне вона і розроблялась. Додаткові можливості з повітря не беруться, а свої громадянські права і свободу - досі потрібно виборювати, на жаль.
|
||||
55
public/uk/btracker-bittorrent-tracker-in-rust.gmi
Normal file
55
public/uk/btracker-bittorrent-tracker-in-rust.gmi
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# βtracker - BitTorrent агрегатор на базі Rust
|
||||
|
||||
> Даний матеріал писався як гайд для локальної спільноти адміністраторів альтернативних мереж, утім, я знайшов його потенційно цікавим для широкого загалу: зокрема для тих, хто бажає підняти свій торент трекер і автоматизований Веб-каталог з функцією пошуку та статистикою роздач на базі інфраструктури Rust.
|
||||
|
||||
βtracker (beta-tracker) - BitTorrent каталог агрегаційного типу з Веб-інтерфейсом на базі фреймворку Rocket (https://rocket.rs), що є продовженням попереднього проєкту YGGtracker але на відміну від останнього - повністю автоматизований.
|
||||
|
||||
Індекс тут складається на базі додаткових компонентів:
|
||||
=> https://github.com/YGGverse/aquatic/tree/yggverse/crates/udp форк UDP трекера "aquatic", який генерує дамп інфо-хешів у бінарному форматі API
|
||||
=> https://github.com/YGGverse/aquatic-crawler спеціально написаний кравлер "aquatic-crawler"
|
||||
|
||||
## Принцип роботи
|
||||
|
||||
Схематично, робота усієї системи зображена в README:
|
||||
```
|
||||
torrent client > aquatic_udp > infohash.bin < aquatic-crawler > * /preload/info-hash.torrent > β
|
||||
torrent client <-----------------------| * /preload/info-hash/data |
|
||||
<-------------------------------------| * /preload/.info-hash/tmp |
|
||||
<-------------------------- scrape -------------------------------|
|
||||
```
|
||||
* на схемі βtracker є останнім оператором даних справа.
|
||||
|
||||
Таким чином, торент-клієнти передають дані на UDP трекер, звідки (його форк) генерує бінарний API дамп `infohash.bin` з опціональною періодичністю, який в свою чергу читає кравлер і резольвить вказані дані (мета дані торент файлів, опціонально підвантажує зображення, txt файли, логи та інші) у спільну файлову систему, звідки їх вже читає та парсить агрегатор βtracker, тим само формуючи пошуковий індекс і Web UI.
|
||||
|
||||
Наразі вся система є раннім прототипом, і при вдалому її тестуванні, планується створити спільну бібліотеку файлової БД, зі стандартизованим API для всіх компонентів системи, що працюють з файлами асинхронно.
|
||||
|
||||
## Мережі
|
||||
|
||||
βtracker розроблявся для мережі Yggdrasil (https://yggdrasil-network.github.io), але на відміну від YGGtracker, може без проблемно працювати з Інтернет / IPv4 включно, а також споріднених оверлейних мережах типу Mycelium (https://github.com/threefoldtech/mycelium).
|
||||
|
||||
## Проблеми
|
||||
|
||||
В рамках того, що коментарі та інша мета-інформація не є частиною стандартного словника BitTorrent:
|
||||
=> https://wiki.theory.org/BitTorrentSpecification#Info_Dictionary
|
||||
ці дані втрачаються при резольві байтів торент файла з інфо-хешу.
|
||||
|
||||
Оскільки сам інфо-хеш формує унікальність роздачі, отримати такі дані з першого зустрічного піра не варіант, навіть якщо це технічно можливо при наприклад запиті такої інформації через сокет за рамками протоколу BitTorrent. Утім, й наявних даних цілком вистачає для побудови навігації та пошуку торентів (включно з індексованим вмістом) у невеличких мережах.
|
||||
|
||||
## Додаткова функціональність
|
||||
|
||||
Так як URI сторінок торентів є унікальними для усієї мережі BitTorrent інфо-хешами, технічно, можливо реалізувати для них локальні соціальні функції: коментарі, лічильник завантажень, вподобання тощо. Але наразі така функціональність не запланована, щонайменше до першої стабільної версії усіх компонентів системи.
|
||||
|
||||
## Інстанси
|
||||
|
||||
Поки, для тестування усіх компонентів системи, функціонує один інстанс Yggdrasil:
|
||||
=> http://[302:68d0:f0d5:b88d::fdb]
|
||||
=> http://tracker.ygg Alfis DNS
|
||||
|
||||
UPD. днями з'явилась реалізація серверної частини для протоколу Gemini та відповідний інстанс:
|
||||
=> https://github.com/YGGverse/btracker-gemini
|
||||
=> gemini://[302:68d0:f0d5:b88d::fdb]
|
||||
=> gemini://tracker.ygg Alfis DNS
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://github.com/yggverse/btracker GitHub
|
||||
190
public/uk/chesslablab-open-source-chess-in-php.gmi
Normal file
190
public/uk/chesslablab-open-source-chess-in-php.gmi
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
# ChesslaBlab - шахи онлайн з відкритим кодом на PHP
|
||||
|
||||
ChesslaBlab - це проєкт з відкритим кодом на PHP, для розгортання шахового серверу з нуля та клієнтська частина на Symfony - для вивчення стратегій, гри онлайн з комп'ютером або друзями.
|
||||
|
||||
Являє собою невибагливу до серверних потужностей та більш спрощену альтернативу Lichess.
|
||||
|
||||
У якості рушія штучного інтелекту використовується сервер Stockfish:
|
||||
|
||||
=> https://github.com/official-stockfish/Stockfish
|
||||
|
||||
ChesslaBlab може бути цікавим для:
|
||||
|
||||
* розробників
|
||||
* керівників освітніх програм
|
||||
* тренерів шахових клубів
|
||||
* просто бажаючих проводити власні турніри, розгорнувши багатокористувацький сервер для гри в браузері на мінімальному тарифі VPS
|
||||
|
||||
Варто зауважити, що обчислення сценаріїв виконується в режимі "server-side", тому такий проєкт оптимально підійде для ігор з невеликою кількістю одночасних сесій, наприклад в локальній мережі.
|
||||
|
||||
## Сервер
|
||||
|
||||
Для розгортання серверної частини, використовується "chess-server":
|
||||
|
||||
=> https://github.com/chesslablab/chess-server
|
||||
|
||||
що реалізує бібліотеку "php-chess":
|
||||
|
||||
=> https://github.com/chesslablab/php-chess
|
||||
|
||||
В системах Debian, спочатку потрібно встановити наступні залежності:
|
||||
|
||||
```
|
||||
apt install git php composer stockfish
|
||||
```
|
||||
|
||||
Для безпечного запуску, краще створити окремого користувача:
|
||||
|
||||
```
|
||||
useradd -m chesslablab
|
||||
su chesslablab
|
||||
```
|
||||
|
||||
Клонуємо репозиторій та оновлюємо залежності Composer:
|
||||
|
||||
```
|
||||
git clone https://github.com/chesslablab/chess-server.git
|
||||
cd chess-server
|
||||
composer update
|
||||
```
|
||||
|
||||
Копіюємо приклад конфігурації в продакшн:
|
||||
|
||||
```
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Файл містить налаштування для двох типів з'єднань:
|
||||
|
||||
1. "WSS" - захищене з'єднання через веб-сокет (потребує додатково створення сертифікатів)
|
||||
2. "WS" - незахищене, в принципі цей варіант буде простішим для початку
|
||||
|
||||
Вказуємо актуальні адреси для обраного способу, після чого відкриваємо відповідні порти в "iptables" наприклад за допомогою утиліти "ufw":
|
||||
|
||||
```
|
||||
ufw allow 8443
|
||||
ufw allow 8085
|
||||
```
|
||||
|
||||
Для кожного типу з'єднань, є свій метод (скрипт) запуску, описаний в документації:
|
||||
|
||||
=> https://chess-server.docs.chesslablab.org
|
||||
|
||||
Наприклад, для "WS", я користуюсь реалізацією сокет-сервера на PHP - Ratchet:
|
||||
|
||||
=> https://github.com/ratchetphp/Ratchet
|
||||
|
||||
і наступною командою:
|
||||
|
||||
```
|
||||
php cli/ratchet/ws.php
|
||||
```
|
||||
|
||||
Також, зручно створити сервіс "systemd":
|
||||
|
||||
``` /etc/systemd/system/chesslablab.service
|
||||
[Unit]
|
||||
Description=chesslablab
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=chesslablab
|
||||
ExecStart=/usr/bin/php /home/chesslablab/chess-server/cli/ratchet/ws.php
|
||||
StandardOutput=file:/home/chesslablab/chess-server-debug.log
|
||||
StandardError=file:/home/chesslablab/chess-server-error.log
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Після чого додати його в авто-запуск:
|
||||
|
||||
```
|
||||
systemctl daemon-reload
|
||||
systemctl enable chesslablab
|
||||
systemctl start chesslablab
|
||||
```
|
||||
|
||||
Перевірити роботу служби на порті можна командою:
|
||||
|
||||
```
|
||||
netstat -tulpn | grep LISTEN
|
||||
```
|
||||
|
||||
## Клієнт
|
||||
|
||||
За весь час розробки, проєкт мав декілька клієнтів, зокрема реалізацію на React.js, яка нещодавно була переведена у стан архівної:
|
||||
|
||||
=> https://github.com/chesslablab/react-chess
|
||||
|
||||
Наразі, актуальним клієнтом є багатосторінкова реалізація на базі фреймворку Symfony, розробка яклшл ведеться в репозиторії "website":
|
||||
|
||||
=> https://github.com/chesslablab/website
|
||||
|
||||
Встановлення досить просте, якщо додані залежності серверу, достатньо виконати наступні команди:
|
||||
|
||||
```
|
||||
cd /home/chesslablab
|
||||
git clone https://github.com/chesslablab/website.git
|
||||
cd website
|
||||
composer update
|
||||
cp assets/env.example.js assets/env.js
|
||||
php bin/console importmap:install
|
||||
```
|
||||
|
||||
Змінюємо у скопійованому файлі "assets/env.js" потрібні нам адреси відповідно до налаштувань сервера і генеруємо оптимізований кеш для веб-застосунку:
|
||||
|
||||
```
|
||||
php bin/console asset-map:compile
|
||||
```
|
||||
|
||||
При змінах в стандартному файлі Symfony ".env" в середовищі "production", скидаємо кеш командою:
|
||||
|
||||
```
|
||||
APP_ENV=prod APP_DEBUG=0 php bin/console cache:clear
|
||||
```
|
||||
|
||||
Налаштування Nginx - таке само як і для типового проєкту на Symfony.
|
||||
|
||||
Для проєкту в локальній мережі IPv6 Yggdrasil / Alfis DNS, я користуюсь наступною конструкцією "WS" (достатньо змінити домен на той, що вказано в налаштуваннях клієнта):
|
||||
|
||||
```
|
||||
server {
|
||||
listen [::]:80;
|
||||
|
||||
allow 0200::/7;
|
||||
deny all;
|
||||
|
||||
server_name chesslablab.ygg chesslablab.ygg.at;
|
||||
|
||||
root /home/chesslablab/website/public;
|
||||
|
||||
location / {
|
||||
try_files $uri /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location ~ ^/.+\.php(/|$) {
|
||||
|
||||
# php -v
|
||||
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
|
||||
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param APP_ENV prod;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Якщо все зроблено правильно, то можна відкрити сайт і почати гру з комп'ютером.
|
||||
|
||||
У разі проблем, першим ділом відкриваємо консоль браузеру "Ctrl+Shift+i" та дивимось стан підключення.
|
||||
Якщо підключення до сокету успішне, тоді потрібно дивитись безпосередньо серверні журнали за шляхами, які вказані в секціях "StandardOutput" та "StandardError" сервісу "systemd".
|
||||
|
||||
Долучайтесь до розробки на GitHub - проєкт має хороший фідбек від розробника!
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://chesslablab.org Офіційний сайт ChesslaBlab
|
||||
=> https://github.com/chesslablab Сторінка на GitHub
|
||||
32
public/uk/create-half-life-graffiti-logo-in-gimp.gmi
Normal file
32
public/uk/create-half-life-graffiti-logo-in-gimp.gmi
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Створення графіті Half-Life в GIMP
|
||||
|
||||
В мультиплеєрі Half-Life можна малювати графіті (або логотипи)
|
||||
|
||||
Для цього, в стандартній конфігурації, використовується клавіша "T".
|
||||
|
||||
Обрати таке зображення можна в меню "Multiplayer" → "Customize",
|
||||
але якщо жодне з них не відповідає бажаному, є можливість зробити його самому, в редакторі з відкритим кодом - GIMP!
|
||||
|
||||
Файл графіті / логотипу для Half-Life має бути у відтінках сірого, розміром 64х64 пікселя та у форматі "BMP".
|
||||
При тому, чорний колір у поточній схемі - буде відповідати 100% прозорості.
|
||||
Палітру можна потім обрати безпосередньо в інтерфейсі ігрових налаштувань.
|
||||
|
||||
Щоб створити логотип на базі існуючого кольорового зображення, наприклад:
|
||||
|
||||
=> https://github.com/YGGverse/hl-customs/blob/main/icons/ipv6/128x128.png?raw=true Емблема "Half-Life IPv6"
|
||||
|
||||
Відкриваємо його в редакторі GIMP і виконуємо наступні кроки:
|
||||
|
||||
1. "Image" → "Scale Image" → "64×64 / Quanlity: Linear"
|
||||
2. "Layer" → "Transparency" → "Remove Alpha Channel"
|
||||
3. "Colors" → "Components" → "Decompose"
|
||||
4. "Colors" → "Invert"
|
||||
5. "File" → "Export As.." → зберігаємо до "valve/logos/filename.bmp"
|
||||
|
||||
Якщо все зроблено правильно, результат буде наступним:
|
||||
|
||||
=> https://raw.githubusercontent.com/YGGverse/hl-customs/main/icons/ipv6/valve/logos/ipv6.bmp Логотип "Half-Life IPv6"
|
||||
|
||||
## Дивіться також
|
||||
|
||||
=> half-life-on-linux-using-xash3d-fwgs-engine.gmi Half-Life в Linux на базі рушія Xash3D / FWGS
|
||||
122
public/uk/dns-less-email-server-for-the-local-networks.gmi
Normal file
122
public/uk/dns-less-email-server-for-the-local-networks.gmi
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
# Організація поштової скриньки для локальних мереж без DNS
|
||||
|
||||
Даний матеріал є адаптацією інструкції для користувачів локальної спільноти адміністраторів альтернативних мереж. Може стати в нагоді, якщо ви бажаєте створити власний поштовий сервер для локальної групи користувачів на підприємстві або для дому, без використання сторонніх серверів Google (та інших провайдерів) для переписки засобами DeltaChat або іншого клієнта, що підтримує протоколи SMTP/IMAP.
|
||||
|
||||
Подібним займаюсь вперше, матеріал може доповнюватись, але в цілому, мій персональний сервер на базі локальної мережі Yggdrasil - вже працює. Користуючись мережами Yggdrasil та/або Mycelium, ви також можете отримувати і надсилати пошту з локального простору через їх вбудований оверлейний режим: тобто пересилати листи через Інтернет без виділеного IP за NAT.
|
||||
|
||||
* якщо плануєте користуватись Yggdrasil але для вас ця інструкція виглядає складною, зверніть увагу на більш простий, коробковий варіант для локальних мереж - Yggmail, але він є швидше месенджером з API для поштових клієнтів з лімітом на вкладення в 1 Мб, а не повноцінним поштовим сервером; до того ж реалізує не стандартні адреси, що може стати проблемою сумісності з альтернативними клієнтами, які валідують хост без підтримки локальних псевдонімів
|
||||
|
||||
## Створення користувача пошти
|
||||
|
||||
Є багато способів адміністрування скриньок, але я обрав самий простий: системний, за паролем:
|
||||
|
||||
``` bash
|
||||
useradd -m USER
|
||||
passwd USER
|
||||
echo "root: USER" >> /etc/aliases
|
||||
```
|
||||
* USER - замініть на вашого користувача
|
||||
* для логіну буде використовуватись саме "юзернейм" а не "юзернейм@хост", оскільки технічно я хочу потім зробити прийом пошти з Yggdrasil та Mycelium на один і той же системний обліковий запис
|
||||
* вказаний пароль буде також використовуватись для авторизації в клієнті
|
||||
|
||||
## Dovecot
|
||||
|
||||
``` bash
|
||||
apt install dovecot-core dovecot-imapd
|
||||
```
|
||||
|
||||
### /etc/dovecot/dovecot.conf
|
||||
|
||||
Вимикаю IPv4, але "слухаю" всі мережі IPv6 (включно з Yggdrasil та у моєму випадку - Mycelium)
|
||||
|
||||
``` /etc/dovecot/dovecot.conf
|
||||
listen = ::
|
||||
```
|
||||
|
||||
### /etc/dovecot/conf.d/10-master.conf
|
||||
|
||||
``` /etc/dovecot/conf.d/10-master.conf
|
||||
service auth {
|
||||
unix_listener /var/spool/postfix/private/auth {
|
||||
mode = 0666
|
||||
user = postfix
|
||||
group = postfix
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Postfix
|
||||
|
||||
``` bash
|
||||
apt install postfix
|
||||
```
|
||||
|
||||
### /etc/postfix/main.cf
|
||||
|
||||
``` /etc/postfix/main.cf
|
||||
myhostname = localhost
|
||||
|
||||
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
|
||||
biff = no
|
||||
append_dot_mydomain = no
|
||||
readme_directory = no
|
||||
smtpd_use_tls=no
|
||||
compatibility_level = 3.6
|
||||
|
||||
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
|
||||
|
||||
alias_maps = hash:/etc/aliases
|
||||
alias_database = hash:/etc/aliases
|
||||
|
||||
mydestination =
|
||||
relayhost =
|
||||
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
|
||||
mailbox_size_limit = 0
|
||||
recipient_delimiter = +
|
||||
inet_interfaces = all
|
||||
inet_protocols = ipv6
|
||||
|
||||
# деякі поради від спаму з мережі, але як вони працюють ще не знаю :)
|
||||
smtpd_etrn_restrictions=reject
|
||||
disable_vrfy_command = yes
|
||||
smtpd_helo_required = yes
|
||||
|
||||
# інтеграція з Dovecot
|
||||
smtpd_sasl_auth_enable = yes
|
||||
smtpd_sasl_type = dovecot
|
||||
smtpd_sasl_path = private/auth
|
||||
mailbox_command =
|
||||
```
|
||||
* опцію `smtpd_use_tls` вимкнено, тому що весь внутрішній трафік Yggdrasil (у моєму випадку) вже захищено на рівні транспорту; для класичних мереж - можна скористатись само-підписаним сертифікатом, додавши на нього виключення.
|
||||
|
||||
## Фаєрвол
|
||||
|
||||
Моя особиста конфігурація не передбачає прийом пошти від користувачів окрім локальних:
|
||||
|
||||
``` bash
|
||||
ufw allow from CLIENT_IP to any port 25 proto tcp
|
||||
ufw allow from CLIENT_IP to any port 143 proto tcp
|
||||
```
|
||||
* замінити CLIENT_IP на той, з якого підключатиметься клієнт DeltaChat
|
||||
|
||||
У випадку, якщо ви налаштовуєте скриньку для прийому відправлень від інших користувачів локальної мережі, можете додати виключення на його хост тієї мережі, якою користуєтесь, або замість фаєрволу - налаштувати доступ засобами SpamAssassin.
|
||||
|
||||
## DeltaChat
|
||||
|
||||
Налаштовуючи даний клієнт, вказуємо:
|
||||
* E-mail - у форматі RFC 5321, тобто з літералом IPv6, наприклад: user@[IPv6:xxx:xxxx:xxxx:xxxx::]
|
||||
* User SMTP/IMAP - такий як до `useradd` (без хосту)
|
||||
* Host SMTP/IMAP - ваш IPv6 в мережі Yggdrasil або Mycelium
|
||||
* Password - такий як до `passwd`
|
||||
* Обов'язково вказуємо стандартні порти, інакше DeltaChat використовує відмінні від 25/143
|
||||
* Усі види шифрування TLS вимикаємо
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://www.rfc-editor.org/rfc/rfc5321#section-4.1.3 Специфікація RFC 5321
|
||||
=> https://devzone.org.ua/post/orhanizatsiia-poshtovoyi-skrynky-e-mail-dlia-lokalnykh-merez-bez-dns Веб-адаптація матеріалу на DevZone (з коментарями)
|
||||
|
||||
### Дивіться також
|
||||
|
||||
=> yggdrasil-is-network-with-distributed-routing.gmi Yggdrasil - мережа з децентралізованою маршрутизацією
|
||||
=> yggmail-messenger-with-email-protocol.gmi Yggmail - месенджер з поштовим інтерфейсом
|
||||
37
public/uk/fail2ban-startup-failure-fix-on-debian-12.gmi
Normal file
37
public/uk/fail2ban-startup-failure-fix-on-debian-12.gmi
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Виправлення помилки запуску fail2ban в Debian 12
|
||||
|
||||
fail2ban - це утиліта на Python для захисту серверів від атак підбору паролів.
|
||||
|
||||
Вона сканує системні журнали на предмет шкідливої активності, та блокує підозрілі хости на заданий в налаштуваннях час.
|
||||
|
||||
Програма має базові налаштування і працює одразу після встановлення, стандартно блокуючи на десять хвилин доступ до "ssh" після п'ятої невдалої спроби входу.
|
||||
|
||||
Подивитись стандартні налаштування можна у файлі "/etc/fail2ban/jail.conf".
|
||||
|
||||
Утім, в системах Debian 12 існує відома проблема її запуску зі штатних репозиторіїв, що звершується з помилкою сервісу "systemd".
|
||||
|
||||
Виправити це досить просто виконавши наступні кроки.
|
||||
|
||||
1. Переконайтесь, що "python3-systemd" встановлено:
|
||||
|
||||
```
|
||||
apt install python3-systemd
|
||||
```
|
||||
|
||||
2. Додайте наступні налаштування у файл:
|
||||
|
||||
``` /etc/fail2ban/jail.local
|
||||
[DEFAULT]
|
||||
backend = systemd
|
||||
```
|
||||
|
||||
3. Перезавантажте сервіс:
|
||||
|
||||
```
|
||||
systemctl restart fail2ban
|
||||
```
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://github.com/fail2ban/fail2ban Офіційна сторінка fail2ban на GitHub
|
||||
=> https://github.com/fail2ban/fail2ban/issues/3292 Звіт про помилку #3292
|
||||
260
public/uk/first-impressions-of-cpp.gmi
Normal file
260
public/uk/first-impressions-of-cpp.gmi
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
# Мої перші враження від C++
|
||||
|
||||
Я займаюсь професійною Веб розробкою вже близько п'ятнадцяти років, переважно у сфері back-end. Починаючи свій шлях в програмуванні, мав іншу вищу освіту і в мене не було змоги попрацювати та оцінити C++ в рамках класичної навчальної програми. Тому цей огляд являє собою точку зору аматора, для якого хоббі стало професією; хто вивчав програмування самостійно і починав це вивчення з мови високого рівня а не навпаки (у моєму випадку - PHP)
|
||||
|
||||
## Передмова
|
||||
|
||||
Так сталось, що у сферу Веб потратив через особистий інтерес до її потенціалу на ті часи, а домінантною мовою в мережі - була PHP, тому і обрав саме її. Так і вивчав: спочатку різні CMS, потім CMF, почав створювати власні самописи, потім бібліотеки.
|
||||
|
||||
Звісно, в процесі довелось працювати і з суміжними мовами та їх технологіями (HTML, CSS, JS), так і альтернативи back-end, такі як Python. Оскільки вже більше 10 років користуюсь Linux, час від часу вносив невеличкі контрибуції в проєкти на C/C++, відповідно працював з інструментами компіляції, встановлюючи останні версії таких програм. Також в мене був деякий досвід програмування контролерів Arduino, наприклад обробка сигналів різноманітних датчиків, взаємодія з GSM та інше.
|
||||
|
||||
Отже заочно з мовами групи C знайомий, оскільки постійно натикався в роботі. Але мені ніколи не доводилось писати на ній щось нове, наприклад з використанням графічного інтерфейсу GTK або для роботи з мережевими інструментами. Одного разу, я знайшов собі таку задачу на вільний час - браузер для протоколу Gemini. Спочатку, написав прототип на вже знайомій мові PHP, з використанням бібліотек PHP-CPP та PHP-GTK3, але виявив що в останній - багато методів GTK не реалізовані, тому витрачав багато часу на доробку бібліотеки, а не створення самої програми. До того ж на момент роботи, актуальною версією була GTK 4 і тут потрібно було або писати бібліотеку з нуля, або створити для браузера окрему гілку і писати його тією мовою, якою написаний сам фреймворк. Власне так і почався мій фундаментальний квест у світ C++
|
||||
|
||||
## Перехід з PHP
|
||||
|
||||
PHP має C-подібний синтаксис, а об'єктне програмування - вже давно стало мейнстрім стандартом а не опцією, тому особисто мій перехід на C++ був відносно легким. Можна швидко переписати скелет програми, при цьому трохи видозмінивши оголошення класів, додавши їх до заголовкових файлів.
|
||||
|
||||
Утім, присутня й кардинальна відмінність у моделі роботи з пам'яттю, яка може змусити переписати з нуля майже всю програму.
|
||||
|
||||
## Вказівники і посилання
|
||||
|
||||
Деякі складнощі можуть виникнути з "новими" типами даних: зокрема вказівниками та посиланнями. Це швидко стає зрозумілим, як тільки отримаєте segmentation fault і почнете копати причину вже на реально написаній програмі а не теорії.
|
||||
|
||||
Оскільки C++ не використовує рантайм, як PHP, Python чи Go - написати програму без прямого звернення до пам'яті ви просто не зможете, а отже - доведеться трохи розібратись з тим, як для кожного з типів даних програма буде виділяти пам'ять та звільняти її.
|
||||
|
||||
Що добре - в мережі просто навалом як документації, так і авторських уроків, зокрема відео курсів українською мовою. Тому зупинятись не будемо, в цілому в мене тема зайняла пару днів поки засвоїлась практично. Трохи ускладнюють тему так звані "розумні вказівники", які потягнуть за собою шаблони функцій та ще купу всього. Плюс вони мають різні реалізації в різних бібліотеках, але я вивчав їх послідовно - коли вирішував окремі проблеми вже потім, зрозумівши основи.
|
||||
|
||||
Від себе тільки додам, що вказівник та посилання - це по суті "ярлик" на певну частину пам'яті, а не копія його об'єкту. Оскільки ваша програма не створює копії об'єктів кожного разу при їх зверненні, це і робить C/C++ таким швидким. З іншого боку, доступ до спільних ресурсів знижує стабільність бо є ризик перезаписати одні і ті само дані в різних частинах програми. Цим і займаються "розумні вказівники" тож по суті - все просто!
|
||||
|
||||
## Конструкції if та switch
|
||||
|
||||
Стосовно конструкцій if - в C++ всі вони повинні оперувати з типами true / false. В принципі, те само й для інших мов, просто там корекція типу відбувається автоматично, а тут неправильний тип викличе помилку компіляції. Я завжди намагаюсь оголошувати типи даних в PHP, тому в C++ це нарешті для мене велика перевага, а ніж незручність.
|
||||
|
||||
Також є певні відмінності при роботі зі switch - тут ви не можете просто закинути в умову будь що, а повинні створити або число або попередньо оголошений enum (також є числом). Не зважаючи на те, що С++ синтаксично схожа на PHP, все таки це "найбільш низькорівнева мова високорівневих" (с)
|
||||
|
||||
## Робота з типами даних
|
||||
|
||||
На від міну від C та їй подібних мов, C++ має розширений інструментарій для роботи з висхідними і низхідними операціями перетворення даних: const_cast, static_cast, dynamic_cast, reinterpret_cast. На перший погляд, додаткові конструкції заплутують на етапі вивчення та ускладнюють читабельність коду - потім, але на практиці, дозволяють регулювати співвідношення продуктивності та стабільності програми. Характерні для C перетворення типу (MyClass*) не бажані, оскільки є найменш стабільними, у чому особисто я переконався доволі швидко :)
|
||||
|
||||
## Перезавантаження функцій
|
||||
|
||||
Якось рефакторив код і випадково помітив, що у моєму класі лишилось два однойменні методи, при чому а ні аналізатор, а ні компілятор - не видавали помилок:
|
||||
|
||||
``` cpp
|
||||
void MyClass::method1();
|
||||
void MyClass::method1(int x, int y);
|
||||
```
|
||||
|
||||
Наткнувся випадково і швиденько видалив, щоб не соромитись. Але згодом зрозумів, що це є фіча, якої мені давно не вистачало в інших мовах! Таким чином, можна створити багато методів з різним API, а не оголошувати значення атрибутів за замовчуванням та не придумувати якісь ініціальні дані для обов'язкових атрибутів. Тут є своя специфіка, але в цілому - дуже зручна опція!
|
||||
|
||||
## Рядкові типи даних
|
||||
|
||||
Де я спочатку застряг - так це на рядках. По-перше їх можна оголосити (і зберігати у пам'яті) різними способами:
|
||||
|
||||
``` cpp
|
||||
char str[] = "C++";
|
||||
char str[4] = {'C','+','+','\0'};
|
||||
const char* str = "C++";
|
||||
std::string str = "C++";
|
||||
// ...
|
||||
```
|
||||
|
||||
По-друге, якщо ви працюєте з сирим типом char, вам доведеться постійно працювати з ними як з масивом і відповідно - його довжиною, наприклад ви не зможете просто передати змінні до макросів через sprintf, для цього вам знадобиться також знати кількість символів рядка, спочатку створивши його буфер (а також збільшувати його за необхідності).
|
||||
|
||||
На щастя, в C++ багато попередньо реалізованих класів, що полегшують таку роботу. Зокрема, в своїй програмі на GTK, я оголошую рядки як об'єкти класів Glib::ustring (для STL є схожий метод) та працюю з ними так само, як в PHP:
|
||||
|
||||
``` cpp
|
||||
using Glib::ustring; // namespace для виклику sprintf
|
||||
|
||||
sprintf(
|
||||
"%s and %d",
|
||||
"substring",
|
||||
1
|
||||
);
|
||||
```
|
||||
|
||||
Тим не менше, все одно доведеться розібратись з примітивними типами даних, оскільки без цього ви просто не зможете зрозуміти як працювати з пам'яттю, не викликаючи її помилок роботи.
|
||||
|
||||
## Масиви
|
||||
|
||||
В C++ можна так само працювати зі статичними масивами, як і в інших мовах, але в більшості випадків, масиви використовуються саме для операцій з динамічними даними. Якщо в PHP можна просто накидати нові елементи масиву "в процесі роботи" програми, то в C++ потрібно спочатку подбати про виділення пам'яті, щоб випадково не зчитати взагалі дані іншої програми, коли відбувся вихід за рамки оголошеного діапазону.
|
||||
|
||||
Це непорозуміння може виникнути тому, що PHP являє собою препроцесор, по суті заголовковий файл, де вся робота з динамічними даними виконується на етапі інтерпретації до запуску програми середовищем (runtime) якого в C++ просто немає. Тому таку роботу повинен виконати програміст - або на рівні препроцесору або виділяти пам'ять безпосередньо під час роботи програми.
|
||||
|
||||
Коли стомитесь від ручного керування, дуже скоро віднайдете для себе "вектори" стандартної бібліотеки, які по суті являють собою допоміжні класи для роботи з динамічними масивами, так само як і відповідні класи для рядків - автоматизують рутинні задачі і роблять це безпечно (хоч і за рахунок зменшення швидкодії):
|
||||
|
||||
``` cpp
|
||||
std::vector<int> numbers;
|
||||
|
||||
numbers.push_back(10); // додати 10 в кінець масиву
|
||||
```
|
||||
|
||||
Вектори будуть створювати новий простір (capacity) для ваших даних автоматично, тому не потрібно цим займатись самостійно. У класу векторів є багато інших методів, якими також можна задати початковий розмір простору або алгоритм перезапису, щоб збільшити швидкодію програми за рахунок виділення більшого об'єму пам'яті заздалегідь. Тому всі ці на перший погляд "складнощі" стають перевагами тоді, коли ви розберетесь в принципах їх роботи.
|
||||
|
||||
## Класи
|
||||
|
||||
Якщо в PHP класи - це такі собі структурні одиниці, де описані всі їх компоненти то в C++ класи розділені на файли для компіляції (.cpp) та окремі заголовкові файли (.h, .hpp), що описують структуру та забезпечують попередню логіку препроцесора. По суті, функціональність програми ділиться на таку, що виконується препроцесором до компіляції і ту, яка виконується після такої.
|
||||
|
||||
### Простори імен
|
||||
|
||||
Починаючи зі стандарту C++17, працювати з класами у заголовкових файлах доволі інтуїтивно: можна використовувати як інлайн, так і вкладені конструкції, скорочуючи об'єм коду через using:
|
||||
|
||||
``` cpp
|
||||
namespace myNamespace
|
||||
{
|
||||
class myClass;
|
||||
}
|
||||
|
||||
using myNamespace::myClass;
|
||||
```
|
||||
|
||||
Щодо синтаксису файлів для компіляції - мені не дуже сподобався формат оголошення членів класу, на рівні написання їх у спільному просторі файлу замість розміщення у фігурних дужках цього класу:
|
||||
|
||||
``` cpp
|
||||
void myClass::methodOne() {}
|
||||
void myClass::methodTwo() {}
|
||||
// ...
|
||||
```
|
||||
|
||||
Звісно, я можу користуватись using для скорочення, але так як це виглядає класично - являє собою суцільні дублі назв одного й того ж класу. Поточний формат мабуть успадкував цей стиль від процедурного підходу C, або ж так зроблено для зворотної сумісності компілятора - іншого логічного пояснення не бачу.
|
||||
|
||||
Складається враження, що сучасний підхід ООП з його практиками (зокрема, 1 клас - 1 файл) відбувся вже після того, як сформувалась мова C++, тут відчувається ретро.
|
||||
|
||||
### Ключове слово this
|
||||
|
||||
Після роботи з іншими мовами ООП, може збивати з толку відсутність ключового слова this, хоча воно доступне, але на практиці майже не використовується. Може виникнути ситуація, коли ви захочете передати однойменний аргумент функції:
|
||||
|
||||
``` cpp
|
||||
class MyClass
|
||||
{
|
||||
int argument;
|
||||
|
||||
void Method(int argument);
|
||||
};
|
||||
|
||||
void MyClass::Method(int argument)
|
||||
{
|
||||
argument = argument; // :)
|
||||
}
|
||||
```
|
||||
|
||||
Звісно, ви можете зробити так:
|
||||
|
||||
``` cpp
|
||||
void MyClass::Method(int argument)
|
||||
{
|
||||
this->argument = argument;
|
||||
}
|
||||
```
|
||||
|
||||
Але такий спосіб мені здається не надійним і його робота може залежати від настрою певного компілятора, тому потрібно вигадувати якісь нові назви для однойменних змінних, у той час як стандартом C++ не рекомендується використання нижніх підкреслень на початку назви (через зарезервовані імена), а підкреслення в кінці - як правило означає назву приватного члена класу.
|
||||
|
||||
Так як this чомусь не використовується цією мовою іншими програмістами, я це питання вирішую вбиваючи для себе двох зайців: і створюю константу (класично - у верхньому реєстрі) і пришвидшую роботу з пам'яттю через роботу з посиланням:
|
||||
|
||||
``` cpp
|
||||
void MyClass::Method(const int & ARGUMENT)
|
||||
{
|
||||
argument = ARGUMENT;
|
||||
}
|
||||
```
|
||||
|
||||
### Інкапсуляція
|
||||
|
||||
Так як фішкою C++ є саме ООП, зі всіма перевагами використання класів - зокрема інкапсуляції, не так давно я стикнувся з неприємним нюансом при роботі з посиланнями, раз вже згадав про них у попередньому розділі.
|
||||
|
||||
Наприклад в мене є клас, що має певну закриту (private) структуру. Щоб пришвидшити роботу програми, я хотів би передавати її вміст за посиланням (через публічний getter) замість створення окремої копії об'єкта. Але оскільки я надаю зовнішньому компоненту посилання на "приватний" блок пам'яті, а також тип даних, який там зберігається, цей компонент може змінити дані, що розташовані в закритій структурі мого класу.
|
||||
|
||||
Тут також є дуже спірні моменти з рівнем доступа friend, що ламає звичну інкапсуляцію. Це звісно не проблема, якщо ви самі пишете код програми або маєте програмний регламент, але таким чином немає жодних гарантій, що якийсь інший блок програми не перепише приватні дані.
|
||||
|
||||
Таким чином, посилання або вказівники на дані приватних структур, які я віддаю на зовні, відкривають їх, і мені варто одразу зробити такі структури публічними, що по суті робить їх не класом а struct.
|
||||
|
||||
## Заголовкові файли
|
||||
|
||||
Для того, хто ніколи не зустрічав таких файлів (.h, .hpp) у сучасних мовах вищого рівня, постане логічне питання: навіщо вони взагалі потрібні. Наскільки мені вдалось усвідомити для себе, оскільки в C++ все повинне мати оголошений тип (для відповідного виділення пам'яті) це свого роду мета-інформація для зовнішнього об'єкту, без підключення безпосередньо його логіки (.cpp).
|
||||
|
||||
Наприклад, коли потрібно використати певний тип даних з іншого файлу, що підключається - компілятор нічого не знає про цей тип, адже його не було оголошено раніше у поточному файлі. Саме тому, через заголовковий файл, передається структура доданого об'єкту і те, скільки і яким чином потрібно виділити під цей тип даних пам'яті.
|
||||
|
||||
З іншого боку, я все ще не дуже зрозумів, як саме працює варіант у прикладі нижче і чому взагалі є така можливість оголосити тип даних у поточному файлі без підключення повних заголовків відповідних класів:
|
||||
|
||||
``` hpp
|
||||
// оголошення типів даних без підключення Class1, Class2, Class3 через include
|
||||
class Class1;
|
||||
class Class2;
|
||||
class Class3;
|
||||
|
||||
void someMethod(Class1, Class2, Class3); // це не викличе помилки компіляції
|
||||
```
|
||||
|
||||
В заголовкових файлах іноді прописують допоміжну логіку для автоматичної "підстановки" даних до компіляції, наприклад - макроси або певні функції, які не потрібні безпосередньо під час виконання програми. Використання макросів, ніби як не рекомендоване в мові C++ тому я поки що не використовую в заголовках динаміку, але трішки експериментую з "пресетами", щоб не писати наприклад тексти та стандартні значення чисел в коді. Також, в цих файлах можуть бути оголошені стандартні визначення для аргументів функцій:
|
||||
|
||||
``` hpp
|
||||
void someMethod(int arg1 = 1, bool arg2 = true);
|
||||
```
|
||||
|
||||
Мені це здається не дуже зручним, оскільки стандартні значення не можна вказати у файлах cpp і я постійно про них забуваю. Доводиться постійно тримати в голові два різних файли, з різним синтаксисом, які по суті являються компонентами одного майбутнього об'єкта.
|
||||
|
||||
В іншому - по цій темі нічого складного немає, оскільки такі файли використовуються препроцесором, швидко приходить розуміння що саме в них писати, таким чином значно розвантажуючи основні файли для компіляції. Це простіше зрозуміти, якщо сприймати заголовки у якості "спільної" інформації для всієї програми, свого роду "абстракції". Структури, оголошені в заголовках, ще називають "прототипами".
|
||||
|
||||
Додам ще декілька слів стосовно мого вибору назви розширення для заголовків. Адже можна назвати файл .h, а можна .hpp. Особисто я користуюсь в проєкті C++ саме .hpp тому що розробники, які читатимуть код потім, можуть сприйняти .h як сумісний формат також для компіляції C. Це моя суто теоретична логіка.
|
||||
|
||||
## Багатофайлові проєкти
|
||||
|
||||
Типовий для PHP include має аналогічний спосіб підключення файлів в C++ через #include який по суті копіює код донора в цільовий файл.
|
||||
|
||||
Якщо потрібно використати include_once, то в C для цього використовується директива #pragma once. Можна також користуватись перевіркою визначення констант через #ifdef.
|
||||
|
||||
* include C++ є аналогом require в PHP, оскільки виконується на етапі передкомпіляції
|
||||
|
||||
Оскільки в PHP я користуюсь пакетним менеджером та засобами autoload, мені здалось трішки не зручним роботи це знову вручну. Більше того, потрібно в ідеалі спроєктувати програму так, щоб не користуватись #pragma once взагалі, оскільки це зайва робота для процесору на етапі збірки, яка виконується постійно.
|
||||
|
||||
## Пакетний менеджер
|
||||
|
||||
Поки що, мені не доводилось користуватись такими бібліотеками, яких немає в репозиторії мого дистрибутиву. Звісно, розумію що одного разу такий момент настане, тим паче, що я звик виносити окремі функції програми на рівень маленьких бібліотек і працювати з ними через пакетний менеджер, замість використання моделей.
|
||||
|
||||
В принципі, такі пакетні менеджери для C++ є, наприклад Conan (https://conan.io). Але мене трохи засмучує, що немає якось єдиного хабу по типу Composer/Packagist для PHP. В цьому плані, звісно дуже виграє Rust з його пакетним менеджером Cargo. Хотілось би подібний набір для C++ з коробки, але все таки це мова, яка сформована спільнотою і має трохи іншу, свого роду анархічну парадигму в своїй основі, яка не прив'язує вас до конкретного рішення і дає повну свободу дій.
|
||||
|
||||
Коли дійдуть руки до організації сторонніх бібліотек, в першу чергу, хочу спробувати варіант з git submodule. Це не зовсім пакетний менеджер, але у даного рішення є таке поняття як "теги", з яких формуються безпосередньо версії, git також перед-встановлено на більшості систем тих, хто займається збіркою і не доведеться вимагати від користувача додаткових рухів. Тому наразі це рішення для мене виглядає найбільш привабливим, перш за все - з точки зору універсальності.
|
||||
|
||||
## Автоматична збірка
|
||||
|
||||
До цього поки що не дійшов, але історія обіцяє бути веселою. Не кажучи про мобільні платформи, наразі я розробляю та запускаю програму на Debian 12, але досі не можу зробити цього на Ubuntu 24.04 через конфлікти залежностей, жорстко прописані у Makefile. Окрім make існує чимало альтернативних рішень, але використовуючи їх для себе, мені також доведеться подбати і про класичний спосіб в README.
|
||||
|
||||
Вже заздрю користувачам більш сучасних екосистем Rust та Electron :)
|
||||
|
||||
## IDE
|
||||
|
||||
Однією з помилок, якої припускався, коли працював з C++ до того як взявся за справу фундаментально, була відсутність правильно налаштованого середовища для розробки. Раніше я користувався продуктами JetBrains, але потім перейшов на VSCodium, де середовище під кожну мову потрібно збирати самому.
|
||||
|
||||
Тема IDE потягне на окремий матеріал, але у будь якому випадку, перш ніж почати вивчення і практичну роботу, важливо одразу користуватись відповідними інструментами, що полегшують інтерпретацію та аналіз коду локально, до компіляції всієї програми. Інакше ви ризикуєте пропустити вивчення мови тільки по тій причині, що вона була погано інтегрована!
|
||||
|
||||
### Аналізатор коду
|
||||
|
||||
Тут, окрім вбудованої підсвітки синтаксису, важливо також довстановити аналізатор коду, який буде здійснювати попередню компіляцію і підсвітку типів даних, а також додасть підтримку вбудованої документації та авто-доповнень, що особливо зручно при роботі з наслідуванням класів.
|
||||
|
||||
Особисто я користуюсь плагіном clangd і навіть не уявляю, як раніше редагував і збирав програми без його функцій!
|
||||
|
||||
Також важливо не тільки встановити плагін, але й правильно налаштувати залежності проєкту (у даному плагіні це clangd.fallbackFlags), які використовуються в лінкері - без них, встановлений аналізатор просто не розкриє свій потенціал.
|
||||
|
||||
=> https://github.com/clangd/vscode-clangd Офіційний репозиторій vscode-clangd
|
||||
|
||||
### Дебагер
|
||||
|
||||
Іншим рішенням, без якого не обійтись - це звичайно дебагер для відлагодження коду, у моєму випадку - codelldb
|
||||
|
||||
=> https://github.com/vadimcn/codelldb Офіційний репозиторій codelldb
|
||||
|
||||
### Авто-доповнення
|
||||
|
||||
Є думки поставити плагін ШІ, наприклад Copilot, але поки що надаю перевагу Chat GPT і Claude 3 Haiku в браузері.
|
||||
|
||||
## Висновки
|
||||
|
||||
Безумовно, C++ є дуже потужним інструментом, що відкриває можливості, які ви не отримаєте від подібних мов на рантаймі. З іншого боку, не завжди такі можливості можуть бути виправдані по часовим витратам, які потрібні на забезпечення їх стабільності.
|
||||
|
||||
Це можна порівняти з ручною коробкою передач в автомобілі: у певних моментах вона виручає, але у більшості випадків ви будете робити зайві рухи, стоячи десь у заторі тільки витрачаючи зайве пальне (так само як і розробляти певні програми на потоці).
|
||||
|
||||
В плані мови для загального розвитку, як хоббі, C++ нагадує мені гру з відкритим світом, де можна робити все, що завгодно і як завгодно. Ця гра має великий всесвіт, який наврят може бути колись вивчений, навіть якщо ви працюєте з плюсами все життя. Тут немає основного сюжету чи сценарію, це динамічне середовище, яке об'єднує тільки стандарт, що оновлюється кожні три роки.
|
||||
|
||||
Подібно як з PHP - мало розуміти мову, потрібно знати певний фреймворк, патерни та інструменти, і вже тоді можна робити якісь практичні речі ефективно. Так само і тут: шлях, яким ви підете вірогідно залежатиме від бібліотеки, яку почнете вивчати, наприклад у моєму випадку - це GTK, тому я вже мабуть краще знаю API цієї групи бібліотек, ніж STL.
|
||||
|
||||
C++ це основа, свого роду класика. Я не міг пройти повз. Особисто, вивчаю її у вільний час просто тому, що мені вже не цікаво розробляти програми на тих мовах, які я знаю добре. А розуміння того, як написаний їх інтерпретатор допомагає мені також краще розуміти принцип роботи таких програм, вносити контрибуції в їх рушій, та експериментувати з новими технологіями, які працюють на пристроях з обмеженою пам'яттю або вимагають максимальної швидкодії.
|
||||
103
public/uk/first-steps-in-gtk.gmi
Normal file
103
public/uk/first-steps-in-gtk.gmi
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Мої перші кроки в GTK
|
||||
|
||||
## Передмова
|
||||
|
||||
Не так давно захотілось зробити простенький браузер для протоколу Gemini. Зокрема - додати детекцію Geo-IP капсул, власний пошук на базі Manticore та інтегрувати екосистему Yggdrasil.
|
||||
Для обраного протоколу задача виглядала тривіальною, залишалось обрати графічний фреймворк і накидати туди крутих функцій.
|
||||
|
||||
Я постійний користувач Linux, вибір був між Qt та GTK. З Qt вже поверхнево знайомий, в рамках одного з проєктів, що довелось апгрейдити і разом з тим оновлювати залежності API (досі приходить спам після обов'язкової реєстрації для завантаження останньої SDK)
|
||||
Але оскільки завжди був користувачем середовища GNOME, все таки пішов стежкою GNU і вирішив глянути що там і як робиться на практиці.
|
||||
|
||||
Забігаючи вперед, скажу що займався професійно виключно веб розробкою, добре знаю суміжні мови, патерни і фреймворки. Але це була фактично моя перша десктоп програма, яку захотілось написати з нуля.
|
||||
Заради цікавості вбив у пошук знайому для себе комбінацію "PHP-GTK" і щось там побачив! Ну, а оскільки вже чимало часу провів з PHP, то написати браузер цією мовою було доволі простою задачею - в запасі вже було з десяток власних бібліотек, лишалось їх тільки натягнути на віконний API і все.
|
||||
|
||||
Отже, знайшов пару прикладів, написав простенький код виклику вікна - працює, значить працюватиме й інше!
|
||||
|
||||
## Інформація в мережі
|
||||
|
||||
Перше з чим зіткнувся, на відміну від Qt, PHP/JS та інших поп-технологій, пошукові видачі по GTK - порівняно порожні або посилаються на якісь старі мануали по другій версії.
|
||||
|
||||
Звісно є офіційна документація, але без розуміння загальної картини.. взагалі складно.. як, що, до чого. На ютубі теж тотальний кошмар, але частково є англомовний контент в основному по двійці, якості не краще. Краще не дивитись. Довелось пробиратись через тернові хащі розуміння самому. Вирішив нарешті задати дільне питання Chat GPT і Haiku, останній до речі доволі точно і лаконічно генерує приклади на Python, C та C++
|
||||
|
||||
## Документація
|
||||
|
||||
В принципі, вона є але доволі скупа і потребує деякого рівня для входу. Але так якщо писати програму по інструкціям ШІ та паралельно читати документацію по кожному методу і його типам то по-трохи можна в'їхати що до чого.
|
||||
|
||||
## Поняття Widget
|
||||
|
||||
В якийсь момент, почав розуміти що є деякі класи, з яких подібно блокам конструктора, складається вікно і всі його елементи. Ці класи потрібно зібрати до купи в рамках іншого класу-віджету у потрібному порядку і вкладеності: копки, поля та інше - формуючи в результаті вікно і заголовок (який тут теж є віджетом).
|
||||
|
||||
Згодом, працюючи безпосередньо з компонуванням, стало відомо про такий інструмент як Glade, який дозволяє робити те само не в коді а через UI (як Unity для ігор) а проєкт експортувати - в XML, який в свою чергу, потім можна імпортувати в об'єкти - вже засобами класу Builder.
|
||||
|
||||
В принципі, використовувати Glade чи ні - справа вподобань. Особисто мені не подобається UI та XML як явище в ООП, але офіційного аналогу JSON не знайшов, тому й далі описую віджети об'єктами в коді програми. Варто зазначити, що для GTK 4, Glade вже не використовується, а на його офіційному сайті висить заглушка nginx. Тут на зміну для четвірки приходить нова і мабуть єдина, неофіційна програма і новий формат проєкту файлів - Cambalache.
|
||||
|
||||
## Ієрархія і наслідування
|
||||
|
||||
Через певний час роботи, виявиться що деяких методів в прикладах, які описує ШІ немає в документації класу і це не помилка! Як виявляється, на сторінці кожного віджету можна побачити дерево наслідування, і воно там проілюстроване не спроста.
|
||||
|
||||
Таким чином, в пошуках певної функції для тюнінгу програми, вже можна читати не тільки документацію робочого класу, але й обов'язково його батьківського елементу - де виявляється ціла купа нових методів про які на сторінці самого методу не згадується окрім клікабельної мапи наслідування. Це була суто моя, користувацька неуважність і як наслідок самостійна реалізація функціональності яка вже була давно доступна, просто з інструментами які знаходяться рівнем вище у батьківських класах та групах об'єктів.
|
||||
|
||||
Якщо пишете програму на C, то ймовірно це буде простіше, оскільки компілятор підсвітить та авто-доповнить семантику. Але в моєму випадку - був шлях пробивного першопрохідця на PHP, який ще й писав порт на PHP-CPP мабуть довше ніж сам браузер.
|
||||
|
||||
По цій темі можна ще додати наступне. Якщо ви пишете першу програму GTK на C++, то ймовірно вже віднайшли і користуєтесь враппером gtkmm (і його сателітами - glibmm і giomm) - така собі абстракція, яка спростить код але добряче заплутає в офіційній документації для C через свою абстрагованість. Частина API в gtkmm досі не реалізована. Також офіційні класи GTK 4, на відміну від GTK 3, вже оголошені як final, тобто не можуть наслідуватись і передбачають або агрегацію або композицію. Разом з тим, gtkmm все ще дозволяє наслідування (подібно Qt) і тут звісно теж не дуже зрозуміло як краще проєктувати програму згідно стандартам і що на gtkmm чекатиме завтра. Але бібліотека безумовно зручна, дійсно зменшує об'єм коду в рази три.
|
||||
|
||||
Все таки мені здається оптимальним писати першу програму GTK на С а не C++, чи наприклад Rust. C, не зважаючи на потенційно більшу кількість коду і відсутність ООП, на мою думку, буде простішим шляхом для початківця, зокрема у вивченні того як влаштована ієрархія фреймворку, події, використання пам'яті. І вже тільки потім варто переходити на абстрактний рівень вище. Для себе все ще не визначився бо все життя провів в ООП, і вже не уявляю без цього програму більшу за "hello world"
|
||||
|
||||
## Типи даних і допоміжні функції
|
||||
|
||||
Низькорівневі бібліотеки Gio/Glib містять чимало зручних функцій для роботи з кодуванням, розміткою, сокетами і взагалі все що потрібно для роботи з контентом. Багато з них наслідують STL. Коли я вперше відкрив для себе розділ функцій то чомусь мені згадалось різноманіття препроцесору розмітки PHP - де є все для роботи з веб, а у веб зараз є все.
|
||||
|
||||
Таким чином, майже не використовую STL бо майже на кожну функцію і тип даних, в середовищі GTK для них є розширена і адаптована реалізація.
|
||||
|
||||
## Робота з контентом
|
||||
|
||||
Якщо говорити про вивід даних в контексті браузеру, який у моєму випадку має відображати простий gemtext з клікабельними посиланнями, починається деякий дисонанс після роботи з JS/HTML.
|
||||
|
||||
По-перше в GTK є тільки два відомих мені віджети для тексту - Label і TextView
|
||||
|
||||
Label - це по суті блок для невеликого тексту, який може підтримувати розмітку або звичайний текст. Розмітка при цьому використовує формат Pango, тобто це такий собі мінімальний варіант HTML з декількома тегами і класами, що використовується для інших, специфічних для стаціонарних програм цілей. Поле Label не розраховане на великий об'єм даних, тому як і варто очікувати мігрантам з HTML - сенс його однозначний - невеличкий опис до якогось блоку і все. Текст на 100Кб відправить вашу програму в роздуми на 10 секунд, особливо якщо спробуєте відформатувати Layout своїм способом, наприклад перерахувати word-wrap чи відмалювати інакше шрифти перед додаванням у віджет.
|
||||
|
||||
Тим не менше, усі дороги і поради "бувалих" ведуть саме на Label, але якщо залізти у вихідний код інших, швидких GTK браузерів то побачимо, що часто використовується багаторядкове текстове поле вводу TextView. По суті, це аналог поля textarea, в якому достатньо сховати курсор засобами CSS (наприклад, через caret-color: transparent) та перевести віджет в режим readonly (через set_editable).
|
||||
Цей віджет так само підтримує розмітку, події, користувацькі теги а також буфер, в який можна складати і відмальовувати невеликий текст по мірі прокрутки контенту, як багато хто і робить що і являє собою розгадку швидкодії рендерингу великих документів.
|
||||
|
||||
Говорячи про прокрутку. Ще не знаю як в GTK 4, але в GTK 3 віджет, який не підтримує засоби скролу, потрібно додати у відповідний контейнер Viewport. Інакше ви довго не будете розуміти чому ваш текст сам по собі відскролюється на верх, наприклад при перемиканні табів Label віджету Notebook. Так само в іншому, очевидно вектор спрощення торкається не тільки візуальної але і внутрішньої частини GTK - розробник повинен сам реалізувати потрібні йому набори, навіть якщо вони здаються тривіальними і такими, що здавалось би, мають бути з коробки.
|
||||
|
||||
Щодо CSS - тут від нього тільки три літери, і мабуть три властивості в залежності від двох тегів, які оберете :)
|
||||
|
||||
Є віджет для картинки (Picture) і є для більш простої піктограми, наприклад для віджету кнопки (Image) якщо треба більше - пишіть самі. Мінімалізм.
|
||||
|
||||
## Робота з діями
|
||||
|
||||
Фреймворк базується на системі подій, тому, хто добре знайомий з JavaScript / Node.js, буде просто розібратись.
|
||||
Кожен віджет здатен оголошувати власні Action, ActionGroup, а також звертатись до глобальних рівнів вікна (win) та застосунку (app)
|
||||
|
||||
Взагалі тема велика і було б добре написати про неї окремо, але нагадаю, особливо бекендерам: на систему дій потрібно звернути увагу в першу чергу і не писати передачу викликів методами класів, ін'єкцією залежностей і т.д., як робив це я спочатку.
|
||||
|
||||
## Спільнота
|
||||
|
||||
В GNU спілньота живе десь в чистилищах своїх гітлабів та інших екзотичних місцях. На гітхабі звісно ваша присутність не допоможе, доведеться по всім баг-репортам реєструватись на окремих сайтах. А для підтримки користуватись форумом. В принципі 2-3 користувача час від часу відповідають (схоже на співробітників фундації) але особисто мені це не зовсім зручно, в порівнянні з системою Issues та PR на GitHub.
|
||||
|
||||
Для себе, в навчанні, й досі користуюсь ШІ, потім читаю документацію, пишу код, і так по колу. Пошук Google тут видає аж нічого цікавого. В принципі форум читати не дуже інформативно бо старі теми там закриваються і коментувати туди вже не можна.
|
||||
|
||||
## Висновки
|
||||
|
||||
Мені чимось подобається графічне середовище GNOME: можливо своєю простотою та однорідністю застосунків, що схоже за філософією на MacOS, користувачем і фаном якої я був довгий час. Й досі, використовую GOME на машині iMac (оскільки для розробки користуюсь Linux)
|
||||
|
||||
GTK має більш інтуїтивний, матеріальний інтерфейс, в той час як Qt та різноманітні JS/Electron чомусь асоціюються з пластиком, де кожна програма має різні властивості, таймінги відгуку, що не зручно коли фокусуєшся на роботі.
|
||||
|
||||
Звісно з трійкою все пішло мобільним шляхом, робочий стіл став пустим місцем, а програми такі прості що виконують буквально одну функцію. Все що більш менш в побуті - на Qt.
|
||||
|
||||
Власне кажучи, буду намагатись й надалі в'їхати в тему GTK, а коли все вляжеться і засвоїться то може буде сенс написати якийсь навчальний матеріал по одній конкретній темі. Ну а зараз, думаю краще поділитись тим що маю - першим враженням з точки зору розробника, і як бачу GTK під капотом в цілому після декількох місяців користування.
|
||||
|
||||
## Щодо браузера
|
||||
|
||||
Наразі в нього утворилось дві гілки:
|
||||
|
||||
=> https://github.com/YGGverse/Yoda/tree/PHP-GTK3 PHP-GTK3
|
||||
|
||||
* більш функціональна але ймовірно вже архівна версія, оскільки писати на GTK 3 не бачу сенсу а вкладатись в контрибуцію бібліотеки Zend / GTK 4 зараз не цікаво. По суті включає базові функції - відкриває файли, протоколи nex та gemini. Останньою фічею була авторизація сертифікатами TLS (коди групи 60), тому якщо комусь цікаво - може поколупатись в робочих прикладах багатопоточної реалізації PHP/GTK так і обробки протоколу Gemini зокрема.
|
||||
|
||||
|
||||
=> https://github.com/YGGverse/Yoda/tree/CPP-GTK4 CPP-GTK4
|
||||
|
||||
* остання версія, стан чернетки, в якій я досі не визначився з патерном. Можливо взагалі переключусь на C або відкрию нову гілку на Rust, який мене давно цікавить своїм пакетним менеджером.
|
||||
157
public/uk/flatpak-bundle-build.gmi
Normal file
157
public/uk/flatpak-bundle-build.gmi
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
# Створення пакунку Flatpak
|
||||
|
||||
Маю декілька улюблених програм, зокрема - пірингова платформа мікроблогів twister p2p та key/value база даних у блокчейн - KevaCoin, які доволі важко збираються на сучасних системах, тим не менше потребують нових юзерів для існування їх пірингових мереж. Раніше, робив для них локальні збірки у бінарному форматі та пакунках `deb`, але з часом вони втрачали свою актуальність через залежність від батьківського середовища. Тому врешті зібрався часом та створив два пакунки Flatpak, які обіцяють не старіти з часом, щонайменше, в осяжній перспективі.
|
||||
|
||||
Тема Flatpak - не нова, у цьому матеріалі я не стану вдаватись в детальні гайди і архітектуру, але поділюсь власним, першим досвідом роботи з цією системою пакунків, наведу декілька прикладів роботи а також спробою публікації на хостингу Flathub. Особисто мені не вистачало такого коротенького гайду, а попередні спроби перечитати документацію від і до, закінчувались нудьгою і відкладаннями на потім у моментах, які мені були одразу не очевидними без практики.
|
||||
|
||||
## Платформи
|
||||
|
||||
Екосистема Flatpak влаштована довкола так званих "рантаймів" або платформ - тобто програмних комплексів або середовищ, які характерні для певної операційної системи, на якій і засобами якої планується збірка програми:
|
||||
=> https://docs.flatpak.org/en/latest/available-runtimes.html
|
||||
|
||||
Основною платформою є Freedesktop:
|
||||
=> https://gitlab.com/freedesktop-sdk/freedesktop-sdk
|
||||
можна сказати, це такий собі голий Linux, який підійде для програм, які не потребують характерних залежностей GTK чи Qt. Для останніх, існують окремі платформи, які по суті розширюють Freedesktop:
|
||||
=> https://gitlab.gnome.org/GNOME/gnome-build-meta GNOME
|
||||
=> https://invent.kde.org/packaging/flatpak-kde-runtime KDE
|
||||
|
||||
Для програм, які використовуватимуть дані фреймворки, специфічна платформа не обов'язкова, ви можете зібрати всі необхідні залежності з початкового коду для Freedesktop, якщо у вас звісно є таке бажання.
|
||||
|
||||
Кожна з платформ має версію релізу, і оновлюється як мені здається раз на рік, тому важливо актуалізовувати вашу програму, якщо ви не бажаєте вимагати від юзера ставити додаткову "операційну систему" лише задля запуску однієї вашої програми.
|
||||
|
||||
## Інкапсуляція
|
||||
|
||||
У контексті Flatpak, мабуть, правильніше використовувати поняття "пісочниці" або Sandbox:
|
||||
=> https://docs.flatpak.org/en/latest/sandbox-permissions.html
|
||||
|
||||
Якщо описувати влаштування ізоляції програм і їх дозволів Flatpak, то воно має більше спільного з контейнеризацією, хоча автори не задумували проєкт як альтернативу Docker, орієнтуючись саме на користувачів "робочого столу" (Desktop). Чому це так, мені не відомо, адже логіка і принцип роботи Flatpak і Docker - доволі схожі, обидва проєкти мають спільні цілі і у відомих мені випадках можуть бути взаємозамінними.
|
||||
|
||||
Якщо коротко, то ізоляція програми Flatpak для кінцевого користувача зумовлена наступними базовими критеріями.
|
||||
|
||||
### Ідентифікатори програм
|
||||
|
||||
Назви програм, в екосистемі Flatpak, мають стандартизований формат назви, який запобігає конфліктам простору імен (і відповідно - файловими шляхами). Зокрема у випадку, якщо вони мають ідентичну назву, але походять від іншого постачальника або є однойменним форком існуючої програми. Повний перелік вимог до йменування є в документації Application ID:
|
||||
=> https://docs.flathub.org/docs/for-app-authors/requirements#application-id
|
||||
|
||||
Тому при створенні програми і тим більше, якщо ви плануєте її подальшу публікацію на Flathub, важливо дотримуватись специфікації та мати можливість підтвердження володіння простором імен / доменом, в якому оголошено назву.
|
||||
|
||||
### Файлова система
|
||||
|
||||
Файлова система в контексті Flatpak характеризується специфікою її розташування відносно батьківської системи. А саме - в ізольованому просторі імен, що відноситься до умовної "програми" Flatpak. Більше того, щоб зберігати файли у батьківській системі (яка по суті для пісочниці є "зовнішньою") програмі Flatpak потрібно надати відповідний доступ при оголошенні маніфесту.
|
||||
|
||||
Наприклад, якщо програма в Linux стандартно зберігає дані профілю користувача в теці:
|
||||
```
|
||||
~/.app
|
||||
```
|
||||
то в маніфесті Flatpak цей шлях має бути оголошений атрибутом `--persist=.app` і таким чином, реальний шлях до профілю буде:
|
||||
```
|
||||
~/.var/app/org.domain.app-id/.app
|
||||
```
|
||||
Те само стосується й інших каталогів (`share`, `usr` і тд), що планується використовувати із-зовні.
|
||||
|
||||
Також, аргумент `persist` вказує на те, що дані потрібно зберегти після завершення роботи програми Flatpak, оскільки всі дані (окрім виконавчих файлів програми) в процесі роботи зберігаються у тимчасовому сховищі і будуть очищені. По цій причині, ви також не зможете просто дістатись до користувацьких ресурсів пісочниці, оскільки вони зберігатимуться десь у тимчасових файлах з рандомним хешем замість назви файлу, якщо не залінкувати їх на-зовні у маніфесті.
|
||||
|
||||
Окрім згаданого вище `persist`, існують і додаткові дозволи на використання певних каталогів, таких як наприклад ~/Download. Усі ці дозволи в процесі будуть підсвічені на Flathub (чи в центрі застосунків) як необхідні для роботи програми, і надмірне їх використання без зайвої необхідності може відлякувати юзерів, що не хочуть надавати невідомому застосунку забагато прав доступу. Тому варто їх вказувати тільки тоді, коли програма того дійсно потребує.
|
||||
|
||||
### Інтерфейси
|
||||
|
||||
Як і у випадку з файловою системою, потрібно також явно оголошувати які саме інтерфейси та сокети потрібні для роботи вашої програми. У більшості випадків, програма потребуватиме доступу до Інтернет, тому для неї вказується запит на `--share=network` або `--share=ipc`, якщо потрібен доступ до спільної пам'яті. Програми на базі серверу X11, потребуватимуть `--socket=x11` і так далі. Все це є в документації:
|
||||
=> https://docs.flatpak.org/en/latest/sandbox-permissions.html#standard-permissions
|
||||
тому в рамках даного матеріалу розглядати окремо не будемо.
|
||||
|
||||
## Маніфест
|
||||
|
||||
Маніфест в контексті Flatpak - це доволі простий, але й водночас - головний текстовий файл у форматі `.yml` або `.json`, у якому вказані:
|
||||
|
||||
* платформа та її версія
|
||||
* SDK
|
||||
* дозволи програми
|
||||
* параметри зовнішніх інтерфейсів
|
||||
* аргументи запуску
|
||||
* перелік модулів та інструкції для їх послідовної збірки
|
||||
* інструкції очищення після збірки пакунку
|
||||
|
||||
По суті, цього невеличкого файлу достатньо для запуску вашої програми з будь якого пристрою, підключеного до мережі Інтернет, оскільки його зміст вказує тільки на те, звідки завантажити компоненти, яка в них очікувана контрольна сума, та як саме їх зкомпілювати на вказаному у маніфесті середовищі.
|
||||
|
||||
Щоб не писати багато, пропоную приклад двох своїх маніфестів:
|
||||
|
||||
=> https://github.com/twisterarmy/twister/blob/main/io.github.twisterarmy.twister.json twister p2p
|
||||
=> https://github.com/kvazar-network/kevacoin/blob/kvazar/io.github.kvazar_network.kevacoin-qt.json KevaCoin
|
||||
|
||||
Ви можете обрати будь який інший маніфест, що краще відповідає вимогам саме вашої програми - їх там наразі близько чотирьох тисяч:
|
||||
=> https://github.com/orgs/flathub/repositories
|
||||
|
||||
Вважаю, що розглядати опції в рамках одного матеріалу немає сенсу, оскільки все має бути інтуїтивно зрозуміло для тих, хто цікавиться даною темою. Особисто я не користуюсь жодними CLI утилітами і просто створюю цей файл в ручну для проєкту, для якого планую створити збірку Flatpak. Якщо ви проєктуєте нову програму з нуля, а не портуєте спадковий код вже існуючої програми, то рекомендую базовий патерн https://gitlab.gnome.org/World/Rust/gtk-rust-template, за допомогою якого також можете згенерувати новий десктоп проєкт з CLI утилітою на Python, що постачається в репозиторії.
|
||||
|
||||
Для компіляції та встановлення програми з валідного маніфесту, достатньо однієї команди:
|
||||
|
||||
``` bash
|
||||
flatpak-builder --force-clean build\
|
||||
--install-deps-from=flathub\
|
||||
--install\
|
||||
--repo=repo\
|
||||
--user\
|
||||
org.domain.app-id.json
|
||||
```
|
||||
* де `org.domain.app-id.json` - ваш файл маніфесту
|
||||
|
||||
## Збірка пакунку
|
||||
|
||||
Збірка, або бандл (bundle) - це зкомпільований та оптимізований в реліз файл з розширенням `.flatpak`, який по суті є аналогом пакунку встановлення `.exe` у системах Windows. На відміну від "сирого" маніфесту, де вам потрібно для запуску програми завантажувати первинний код та компілювати його на слабкому залізі, ви можете зробити те само на більш потужному пристрої і передати вже готовий файл збірки на флешці.
|
||||
|
||||
Наприклад, щоб зібрати пакунок, з теки, де знаходиться репозиторій маніфесту, виконується команда:
|
||||
|
||||
``` bash
|
||||
flatpak build-bundle repo app-id.flatpak org.domain.app-id.json
|
||||
```
|
||||
* `repo` - тека з локальним репозиторієм Flatpak
|
||||
* `app-id.flatpak` - назва пакунку для генерації
|
||||
* `org.domain.app-id.json` - файл маніфесту
|
||||
|
||||
Цільовий пристрій, на якому запускається файл інсталяції, тільки дозавантажить необхідну платформу, якщо така не була встановлена раніше (при встановленні інших Flatpak застосунків). Для встановлення в користувацький простір (є ще варіант глобального встановлення від `root`)
|
||||
|
||||
``` bash
|
||||
flatpak install --user app-id.flatpak
|
||||
```
|
||||
* де `app-id.flatpak` - назва файлу з попереднього кроку
|
||||
|
||||
Щоб не передавати пакунки `.flatpak` на флешках (і не тільки) й було створено хостинг Flathub, про який декілька слів нижче!
|
||||
|
||||
## Flathub
|
||||
|
||||
Flathub - це безкоштовний хостинг для пакунків Flatpak. Утім, не все з ним так просто як може здаватись, оскільки публікація пакунку на даній платформі потребує певного контролю якості та відповідності програми вимогам сервісу.
|
||||
|
||||
### Публікація
|
||||
|
||||
Суть публікації пакунку на Flathub полягає в створенні форку ініціативного репозиторію (https://github.com/flathub/flathub), в якому створюється окрема гілка для подальшого запиту на додавання (https://github.com/flathub/flathub/pulls).
|
||||
|
||||
До PR, окрім файлу маніфесту, додається ще один, схожий до маніфесту файл metainfo (https://docs.flathub.org/docs/for-app-authors/metainfo-guidelines) у форматі XML. Наприклад для twister p2p - він виглядає так
|
||||
=> https://github.com/twisterarmy/flathub/blob/twister-bundle/io.github.twisterarmy.twister.metainfo.xml
|
||||
* а саме: містить назву, опис проєкту, версію збірки а також скріншоти та деяку іншу інформацію, необхідну для коректного відображення на сайті каталогу.
|
||||
|
||||
Після модерації, ваш проєкт буде (або не буде) розміщено в офіційному списку постачальників (https://github.com/orgs/flathub/repositories) Flathub, а вам (у разі схвалення) - надано статус майнтейнера до відповідного репозиторію для його подальших оновлень.
|
||||
|
||||
Опціонально, постачальники можуть отримати верифікований статус та інші фічі для подальшого просування власного проєкту.
|
||||
|
||||
### Вимоги до публікації
|
||||
|
||||
Основною вимогою до усіх застосунків Flathub є наявність графічного інтерфейсу (GUI). Веб-інтерфейс (як у випадку з twister p2p) і тим паче CLI тут не проходять і модератори вимагатимуть від вас або веб-фрейм або лаунчер, по типу Steam. Веб-фрейми мені не подобаються, оскільки надаю перевагу браузеру. Саме тому, проєкт twister p2p я там досі не опублікував і пішов робити twister-control-center на базі GTK 4:
|
||||
=> https://github.com/twisterarmy/twister-control-center
|
||||
та додаткової ліби rust-twistercore-rpc:
|
||||
=> https://github.com/twisterarmy/rust-twistercore-rpc
|
||||
порт якої навіть не знаю коли завершу і чи завершу взагалі.
|
||||
|
||||
Натомість файл `.flatpak`, який просто запускає демон і відкриває вікно в браузері через `xdg-open`, було розміщено на GitHub, в розділі стабільних релізів:
|
||||
=> https://github.com/twisterarmy/twister/releases/download/0.1.1/twister.flatpak
|
||||
|
||||
По-друге, модератори можуть приколупатись до заміни інструкцій `simple` на `autotools`. Це не зважаючи на те, що майже всі вже додані до їх репозиторію проєкти мають `make` в своїй імплементації. Звісно можна переписати, але я користуюсь офіційними інструкціями для модулів і мені ані цікаво, ані виглядає краще з їх побажанками у маніфесті.
|
||||
|
||||
По-третє, якщо з вимогами GUI чи оптимізацією команд маніфесту, модераторів можна зрозуміти, то таке явище як зміна сортування деяких (не array) елементів в маніфесті - для мене загадка. Наприклад, перенести `finish-args` на початок файлу (коли до цього конструкція була внизу) або побажання доопрацювати інструкції очищення `cleanup` при цьому не надаючи конкретних файлів, які не влаштовують.
|
||||
|
||||
Коротше таке, я спробував, але не в моїх інтересах туди пробиватись, для мене головне, щоб юзер міг скачати. Наразі він може зробити це на GitHub, в розділі релізів. Можливо, повернусь до питань модерації через рік-другий :) З іншого боку, може це й на краще, оскільки проєкт Flathub орієнтований на десктоп користувачів, і це мотивує програмістів створювати більш якісний контент, аніж публікація різноманітного сміття, як це часто відбувається на crates, packagist, npm - де взагалі ручна модерація відсутня.
|
||||
|
||||
## Висновки
|
||||
|
||||
В принципі, мій перший досвід виявився позитивним, після збірки вже другої програми, подумую над іншими проєктами, оскільки перевстановлюю системи часто і кожного разу доводиться звідкись збирати чи то Berkeley DB чи то Boost, або стару версію Qt. Тут же я можу просто зібрати пакунок на робочій станції і потім легко поставити його на будь якому пристрої та операційній системі, наприклад на слабкому ноутбуці не від'їдаючи батарею.
|
||||
|
||||
Сподіваюсь, матеріал буде комусь корисним, можливо щось згадаю то доповню!
|
||||
32
public/uk/freetube-the-private-youtube-client.gmi
Normal file
32
public/uk/freetube-the-private-youtube-client.gmi
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# FreeTube - приватний перегляд YouTube
|
||||
|
||||
FreeTube - це конфіденційно-орієнтований клієнт з відкритим кодом для перегляду відео на YouTube.
|
||||
|
||||
Написаний на базі Node.js / Electron.
|
||||
|
||||
В стандартних налаштуваннях використовує випадковий проксі-сервер Invidious:
|
||||
|
||||
=> https://invidious.io
|
||||
|
||||
Це запобігає відслудковуванню користувача і формуванню персоналізованих списків.
|
||||
Метод підключення та провайдер може бути вибраний вручну, якщо така потреба є.
|
||||
|
||||
Кожен може стати таким провайдером, якщо сервер відповідає певним умовам:
|
||||
|
||||
=> https://docs.invidious.io/instances/#rules-to-have-your-instance-in-this-list
|
||||
|
||||
Окрім того, що ретрансляція відео FreeTube не містить реклами, клієнт працює у режимі створення локальних підписок, плейлистів, вподобань, які за потреби можна експортувати в JSON чи XML на інший пристрій без участі третіх сторін.
|
||||
Такий підхід дозволяє переглядати виключно той контент, який подобається саме вам, і ніхто не матиме доступу до ваших персональних колекцій.
|
||||
|
||||
Серед функцій - гнучкі налаштування інтерфейсу, зручне завантаження відео, аудіо та субтитрів локально у різній якості, щоправда максимальна якість такого відео обмежена 1080p / 60fps, чого в принципі достатньо для колекції "в дорогу" де немає Інтернет.
|
||||
|
||||
Якщо ж є потреба зберігати оригінальні відео, варто звернути увагу на утиліту yt-dlp:
|
||||
|
||||
=> https://github.com/yt-dlp/yt-dlp
|
||||
|
||||
Клієнт орієнтований в першу чергу на десктоп, може працювати в різних операційних системах та архітектурах.
|
||||
Для встановлення в Linux - доступні готові пакети "deb", "rpm", "appimage", "flatpak" та інші; також є готові збірки останніх версій для Windows і MacOS.
|
||||
|
||||
Завантажити FreeTube можна на офіційному сайті:
|
||||
|
||||
=> https://freetubeapp.io
|
||||
213
public/uk/gemini-protocol-as-http-alternative.gmi
Normal file
213
public/uk/gemini-protocol-as-http-alternative.gmi
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
# Протокол Gemini як альтернатива HTTP
|
||||
|
||||
Gemini - мережний протокол прикладного рівня, в своїй основі є спадкоємцем протоколу Gopher.
|
||||
|
||||
Орієнтований на мінімалізм та конфіденційність, оскільки виключає використання таких веб-технологій як Cookies та JavaScript, а формат даних, що передається - обмежений текстом або бінарними даними без підтримки стиснення та фонового завантаження зі сторонніх ресурсів. Обов'язковим є захист трафіку сертифікатом TLS.
|
||||
|
||||
Не зважаючи на простоту, протокол цілком багатофункціональний і дозволяє працювати з різними типами ресурсів: от як хостинг статичних файлів для персональних сторінок так і системи з авторизацією та обробкою запитів користувача - форуми, пошукові системи, організація радіо та відео стрімів, обмін зображеннями та іншими мультимедійними даними.
|
||||
|
||||
Враховуючи архітектуру, Gemini здебільшого буде цікавий тим, кого не влаштовує "роздутість" сучасного HTTP; стане в нагоді користувачам командного рядка, E-ink планшетів.
|
||||
|
||||
## Термінологія
|
||||
|
||||
Як і в багатьох екосистемах, для Gemini характерна своя термінологія, знання якої допоможе краще розуміти контекст і формувати пошукові запити:
|
||||
|
||||
* Gemini space - екосистема Gemini, подібно терміну "Fediverse" - для федеративної тематики
|
||||
* Capsule - капсула, веб-сайт - назва протоколу походить від тематики космічної програми, тому багато проєктів наслідують концепцію у своїх назвах
|
||||
* Gemlog - блог, персональна сторінка, фід профілю
|
||||
|
||||
## Розмітка
|
||||
|
||||
### Gemtext
|
||||
|
||||
Текстові ресурси gemtext - це звичайний текст (MIME text/gemini), схожий до Markdown, який опціонально містить наступні мета-теги на початку кожного рядка:
|
||||
|
||||
```
|
||||
# h1
|
||||
## h2
|
||||
### h3
|
||||
|
||||
=> gemini://geminiprotocol.net 1965-01-19 Gemini
|
||||
|
||||
> цитата
|
||||
|
||||
* елемент списку 1
|
||||
* елемент списку 2
|
||||
|
||||
` ` ` заголовок, назва файлу або розширення для підсвітки синтаксису (якщо підтримується)
|
||||
код
|
||||
` ` `
|
||||
```
|
||||
|
||||
У розмітці Gemtext не використовуються декоративні технології накшталт CSS, при цьому завдання відображення ресурсу повністю делеговане клієнтові.
|
||||
|
||||
Різні браузери по-різному декорують вміст, деякі додають відступи.
|
||||
Тому головний принцип створення крос-браузерних сторінок простий: розмітка має зручно читатися у вигляді початкового коду.
|
||||
|
||||
### Gemfeed
|
||||
|
||||
Оскільки текстовий регламент протоколу не передбачає використання мета-тегів, такі стандарти сповіщень як Atom та RSS, без зовнішніх засобів інтеграції, в Gemini - не застосовні.
|
||||
|
||||
Незважаючи на це, підписки можливі, зокрема - засобами стандарту Gemfeed для інтерпретації браузером змін документу Gemtext.
|
||||
Наприклад, в браузері Lagrange, відстежувати оновлення сторінки можна за допомогою меню Bookmarks - Subscribe to page… і обрати відповідний сторінці спосіб - підписка при зміні заголовків на сторінці або за зміною дат у посиланнях.
|
||||
|
||||
Відстеження за датою посилань
|
||||
|
||||
У цьому підході здійснюється відстеження за датою, що вказана наступною за посиланням у форматі ISO 8601 (Y-m-d), наприклад:
|
||||
|
||||
```
|
||||
# Заголовок сторінки виконує роль заголовка стрічки
|
||||
|
||||
Вміст параграфа ігнорується у відстеженні
|
||||
|
||||
=> /index.gmi будь-яке посилання, ігнорується
|
||||
|
||||
## Підрозділ сторінки, ігнорується
|
||||
|
||||
Довільний вміст підрозділу, що ігнорується
|
||||
|
||||
## Публікації
|
||||
|
||||
=> /pub1.gmi 2024-01-28 Посилання на першу публікацію - відстежується
|
||||
=> /pub2.gmi 2024-01-29 Посилання на другу публікацію - відстежується
|
||||
=> /pub3.gmi Посилання публікацію - не відстежується (оскільки не містить дати)
|
||||
=> /pub4.gmi Посилання публікацію - не відстежується (оскільки дата є частиною заголовка і розташована зправа) 2024-01-30
|
||||
```
|
||||
|
||||
Таким чином, документ не втрачає зручності для візуального сприйняття людиною і при цьому містить мета-інформацію, зрозумілу для програмного інтерпретатора в браузері, що відслідковує сторінку.
|
||||
|
||||
Єдиний недолік такого підходу полягає в тому, що оновлення не можна отримувати частіше ніж раз на добу. Як заявлено в документації, це пов'язано з часовими зонами.
|
||||
|
||||
Відстеження заголовків
|
||||
|
||||
Альтернативний підхід, який відстежує зміни у заголовках документу:
|
||||
|
||||
```
|
||||
# Мій блог, заголовок стрічки
|
||||
|
||||
Параграф опису
|
||||
|
||||
## Перша публікація - відстежується
|
||||
|
||||
Опис першої публікації
|
||||
|
||||
=> /pub1.gmi Читати
|
||||
|
||||
## Друга публікація - відстежується
|
||||
|
||||
Опис другої публікації
|
||||
|
||||
=> /pub2.gmi Читати
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
## Коди статусів
|
||||
|
||||
Протокол Gemini має власний реєстр кодів заголовків, відмінний від типових для HTTP 200, 301, 404, 500, тощо
|
||||
|
||||
* 10-19 - input expected - очікується введення - використовується для відправлення та отримання даних форм
|
||||
* 20-29 - success - код 20 аналогічний коду 200 в HTTP
|
||||
* 30-39 - redirection - діапазон переадресації, у пакеті, що відправляється, код зазвичай супроводжується мета-посиланням: code + link + \n\r + text
|
||||
* 40-49 - temporary failure
|
||||
* 50-59 - permanent failure
|
||||
* 60-69 - client certificates - у Gemini сертифікати використовуються для ідентифікації та авторизації користувачів
|
||||
|
||||
Клієнт зобов'язаний відхиляти будь-який код менше 10 або більше 69, при цьому повідомити користувача.
|
||||
У невизначених випадках пріоритет буде відданий коду з початковим значенням діапазону, наприклад, 10 для 11 або 20 для 27.
|
||||
|
||||
## Обробка запитів
|
||||
|
||||
Протокол передбачає обмін пакетами із заголовками довжиною максимум 1024 байт. У цю довжину необхідно вмістити мета інформацію про пакет - наприклад рядок URI та/або дані введення користувача.
|
||||
|
||||
Текстові дані мають бути закодовані у стандарт RFC 3986 (відомий такими функціями, як urlencode). Якоюсь мірою це скорочує корисний обсяг заголовка при використанні наприклад кирилиці.
|
||||
|
||||
Тіло пакету складається із "сирих" текстових або бінарних даних без стиснення; сервер закриває з'єднання після надсилання останнього байту.
|
||||
|
||||
За допомогою статусів групи "10", що відправляються клієнту, сервер здатний запитувати введення користувача (замість звичних форм, у браузерах Gemini - це спливаюче текстове вікно)
|
||||
Після отримання та обробки даних, сервер зазвичай повертає статус "20", статус "51" (не знайдено) або переадресацію на цільову сторінку з кодом "30".
|
||||
|
||||
Приклад типового пакету для текстової сторінки "Hello World":
|
||||
|
||||
```
|
||||
20 text/gemini; charset=utf-8; lang=en\r\nHello%20world%21
|
||||
```
|
||||
|
||||
## Клієнт
|
||||
|
||||
Щоб відкрити ресурс з адресою `gemini://` потрібен спеціальний браузер, що стандартно працює з портом `1965`
|
||||
|
||||
Користувачі GUI можуть почати з популярних Lagrange, Eva, Kristall, Castor і т.д.
|
||||
|
||||
Що варто знати:
|
||||
|
||||
* Оскільки більшість клієнтів повертають одно-текстове рядкове поле запиту, додати новий рядок можна комбінацією клавіш Shift+Enter. Розширити спосіб введення можна за допомогою суміжного протоколу Titan, але розляд цього способу вартує окремого матеріалу.
|
||||
* У протоколі Gemini немає webstorage та cookies, тому деякі інтерактивні сайти для функцій авторизації використовують сертифікати, які також можуть використовуватись для швидкої зміни облікового запису в браузері.
|
||||
* Замість фідів RSS є вбудовані в браузер інструменти відстеження контенту (див. Gemfeed)
|
||||
|
||||
## Сервер
|
||||
|
||||
Поняття "сервер" у середовищі Gemini може бути не звичним для користувачів веб, які звикли до поширених веб проксі Nginx або Apache "на всі випадки". Термін часто передбачає повноцінний сервіс для конкретного завдання, що резервує за собою окремий системний хост і порт.
|
||||
|
||||
У каталозі "awesome-gemini" представлено велику кількість таких рішень:
|
||||
|
||||
=> https://github.com/kr1sp1n/awesome-gemini
|
||||
|
||||
Наприклад, для запуску простої статики підійде сервер Agate (Rust). При цьому назва звичного index.html залежатиме від обраного рішення, для Agate - це index.gmi але для іншого серверу постфікс може відрізнятись.
|
||||
|
||||
Запуск динамічних ресурсів часто передбачає розробку власного сокет-серверу для реалізації специфіки окремо взятої програми.
|
||||
|
||||
Веб-розробникам простіше зрозуміти принцип роботи "server-side" на прикладі декількох файлів gemini-php:
|
||||
|
||||
=> https://github.com/eapl-gemugami/gemini-php
|
||||
|
||||
Утім, в нових проєктах краще використовувати більш актуальні рішення, наприклад, з простих - форк бібліотеки Titan-II:
|
||||
|
||||
=> https://github.com/YGGverse/titan-II
|
||||
|
||||
Приклади реалізації можна подивитись у коді β-Doku - Gemini-проксі для DokuWiki:
|
||||
|
||||
=> https://github.com/YGGverse/bdoku
|
||||
|
||||
Щоб запускати різні сервіси на одному IP (не змінюючи стандартний порт), зручно встановити загальний проксі-сервер, який здійснюватиме маршрутизацію запитів на відповідну адресу/інтерфейс або на зовнішній сервер.
|
||||
|
||||
Якщо в HTTP - це Apache / Nginx, то для протоколу Gemini підійдуть Gmid (C):
|
||||
|
||||
=> https://gmid.omarpolo.com/
|
||||
|
||||
або Twins (Go):
|
||||
|
||||
=> https://code.rocket9labs.com/tslocum/twins
|
||||
|
||||
З другим помічені деякі проблеми з передачею довгих текстів, тому для початківців, бажано розпочати роботу з Gmid, який наразі активно розвивається та має дружній фідбек від розробника.
|
||||
|
||||
## З чого почати
|
||||
|
||||
=> gemini://geminiprotocol.net Домашня сторінка проєкту
|
||||
=> gemini://geminispace.info Одна з пошукових систем
|
||||
=> gemini://tlgs.one Ще одна пошукова система
|
||||
=> gemini://chat.mozz.us Чат в режимі онлайн стріму (хороший тест для вашого клієнта)
|
||||
=> gemini://bbs.geminispace.org Дошка оголошень і тематичні форуми
|
||||
=> gemini://station.martinrue.com Соціальна мережа за типом twitter
|
||||
=> gemini://hd.206267.xyz Офіційний Fediverse інстанс від розробників Tootik
|
||||
=> gemini://meadow.hmmm.zt.ua Україномовний інстанс Tootik
|
||||
=> gemini://gemlog.blue Найпростіший спосіб опублікувати свої сторінки в мережі Gemini
|
||||
=> gemini://cities.yesterweb.org Хостинг з піддоменом, підтримкою Titan і WebDAV
|
||||
=> gemini://flounder.online Хостинг з агрегатором останніх записів, є SFTP
|
||||
=> gemini://astrobotany.mozz.us ASCII гра з догляду за рослинами, що затягує :)
|
||||
|
||||
## Схожі протоколи
|
||||
|
||||
* Gopher
|
||||
* Guppy
|
||||
* Nex
|
||||
* Scorpion
|
||||
* Scroll
|
||||
* Spartan
|
||||
* Text
|
||||
|
||||
## Дивіться також
|
||||
|
||||
=> gmid-server-for-gemini-protocol.gmi Gmid - багатофункціональний сервер для Gemini
|
||||
=> nex-lightweight-gemini-alternative.gmi Протокол NEX - легка альтернатива Gemini
|
||||
=> ukrainian-geminispace.gmi Український Geminispace
|
||||
122
public/uk/gmid-server-for-gemini-protocol.gmi
Normal file
122
public/uk/gmid-server-for-gemini-protocol.gmi
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
# Gmid - багатофункціональний сервер для Gemini
|
||||
|
||||
Gmid - багатофункціональний сервер з відкритим кодом для протоколу Gemini.
|
||||
|
||||
Зокрема, його зручно використовувати у якості проксі серверу для віртуальних хостів, аналогічно тому як цю функцію виконує Nginx для HTTP.
|
||||
|
||||
Це може бути зручно, коли немає змоги піднімати для кожного серверу маску підмережі, або коли сайт працює на VPS з одним виділеним IP.
|
||||
|
||||
## Встановлення
|
||||
|
||||
Написаний Gmid на "C", перед його збіркою потрібно додати наступні бібліотеки:
|
||||
|
||||
```
|
||||
apt install bison byacc
|
||||
```
|
||||
|
||||
Буде правильним запускати і відповідно виконувати процес Gmid від окремого користувача, якого спочатку створимо та виконаємо вхід:
|
||||
|
||||
```
|
||||
useradd -m gmid
|
||||
su gmid
|
||||
```
|
||||
|
||||
Клонуємо останню версію з офіційного дзеркала на GitHub і виконуємо компіляцію:
|
||||
|
||||
```
|
||||
git clone https://github.com/omar-polo/gmid.git
|
||||
cd gmid
|
||||
./configure
|
||||
make
|
||||
```
|
||||
|
||||
## Налаштування reverse-proxy
|
||||
|
||||
Нижче описаний приклад створення двох віртуальних хостів "host1.com" та "host2.com", які працюють на умовних адресах "1.2.3.4:1965" та "4.3.2.1:1965" відповідно.
|
||||
|
||||
Таким чином, файл конфігурації виглядатиме так:
|
||||
|
||||
``` /home/gmid/gmid.conf
|
||||
server "host1.com" {
|
||||
|
||||
listen on 0.0.0.0 port 1965
|
||||
|
||||
cert "/home/gmid/host/host1.com/cert.pem"
|
||||
key "/home/gmid/host/host1.com/key.rsa"
|
||||
|
||||
proxy {
|
||||
sni "host1.com"
|
||||
relay-to 1.2.3.4 port 1965
|
||||
verifyname off
|
||||
}
|
||||
}
|
||||
|
||||
server "host2.com" {
|
||||
|
||||
listen on 0.0.0.0 port 1965
|
||||
|
||||
cert "/home/gmid/host/host2.com/cert.pem"
|
||||
key "/home/gmid/host/host2.com/key.rsa"
|
||||
|
||||
proxy {
|
||||
sni "host2.com"
|
||||
relay-to 4.3.2.1 port 1965
|
||||
verifyname off
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Створення сертифікатів
|
||||
|
||||
На прикладі конфігурації вище, додамо само-підписаний сертифікат на прикладі "host1.com", необхідний для роботи протоколу Gemini.
|
||||
|
||||
Аналогічним способом створюється й сертифікат для "host2.com", замінивши значення атрибуту "CN":
|
||||
|
||||
```
|
||||
mkdir /home/gmid/host/host1.com
|
||||
cd /home/gmid/host/host1.com
|
||||
openssl req -x509 -newkey rsa:4096 -keyout key.rsa -out cert.pem -days 3650 -nodes -subj "/CN=host1.com"
|
||||
```
|
||||
|
||||
## Автозапуск
|
||||
|
||||
Щоб наш проксі-сервер стартував разом з системою і працював як процес "systemd", додамо для Gmid юніт:
|
||||
|
||||
``` /etc/systemd/system/gmid.service
|
||||
[Unit]
|
||||
Description=gmid
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=gmid
|
||||
Group=gmid
|
||||
WorkingDirectory=/home/gmid/
|
||||
ExecStart=/home/gmid/gmid/gmid -c /home/gmid/gmid.conf -f -v
|
||||
StandardOutput=file:/home/gmid/output.log
|
||||
StandardError=file:/home/gmid/debug.log
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
* "-f" - запуск в режимі "foreground", інакше процес стартуватиме у фоновому режимі без журналювання
|
||||
* "-v" - або "verbose" для звітів відлагодження
|
||||
|
||||
Оновлюємо конфігурацію та запускаємо сервер:
|
||||
|
||||
```
|
||||
systemctl daemon-reload
|
||||
systemctl enable gmid
|
||||
systemctl start gmid
|
||||
```
|
||||
|
||||
## Посилання
|
||||
|
||||
=> gemini://gmid.omarpolo.com Офіційна сторінка проєкту
|
||||
=> https://github.com/omar-polo/gmid Дзеркало на GitHub
|
||||
|
||||
## Дивіться також
|
||||
|
||||
=> gemini-protocol-as-http-alternative.gmi Протокол Gemini як альтернатива HTTP
|
||||
56
public/uk/grab-video-from-rezka-hd-mirror-with-yt-dlp.gmi
Normal file
56
public/uk/grab-video-from-rezka-hd-mirror-with-yt-dlp.gmi
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Завантаження відео з сайтів Rezka HD через плагін yt-dlp
|
||||
|
||||
Інструкція зі встановлення rezka_yt_dlp_plugin в Linux, для завантаження останніх релізів озвучки з дзеркал Rezka. Може бути корисною для створення роздач BitTorrent, або для локального перегляду відео.
|
||||
|
||||
Спочатку потрібно встановити утиліту yt-dlp зручним способом. Якщо у вас вже розгорнуто інфраструктуру Python, найпростіше це зробити з pip:
|
||||
|
||||
``` bash
|
||||
pip install yt-dlp
|
||||
```
|
||||
|
||||
Наступним кроком, встановлюємо сам плагін. Є різні способи, але я робив так:
|
||||
|
||||
``` bash
|
||||
mkdir -p ~/.config/yt-dlp/plugins
|
||||
cd ~/.config/yt-dlp/plugins
|
||||
git clone https://github.com/gnfalex/rezka_yt_dlp_plugin.git
|
||||
yt-dlp --verbose
|
||||
5. `nano ~/.config/yt-dlp/plugins/rezka_yt_dlp_plugin/yt-dlp.conf
|
||||
```
|
||||
|
||||
Перевірити успішність встановлення плагіну можна так:
|
||||
|
||||
``` bash
|
||||
yt-dlp --verbose
|
||||
```
|
||||
|
||||
Змінити стандартну конфігурацію (зокрема вказати звукову доріжку та URL) можна у файлі:
|
||||
|
||||
```
|
||||
~/.config/yt-dlp/plugins/rezka_yt_dlp_plugin/yt-dlp.conf
|
||||
```
|
||||
* або пропустити, використовуючи у такому випадку аргументи CLI
|
||||
|
||||
Відео завантажується наступним чином:
|
||||
|
||||
``` bash
|
||||
cd ~/Downloads
|
||||
yt-dlp --config-location ~/.config/yt-dlp/plugins/rezka_yt_dlp_plugin/yt-dlp.conf URL
|
||||
```
|
||||
|
||||
або ж просто наступною командою, обравши звукову доріжку вручну
|
||||
|
||||
``` bash
|
||||
yt-dlp URL
|
||||
```
|
||||
|
||||
для вибору сезону та епізоду:
|
||||
|
||||
``` bash
|
||||
--match-filter "season=1&episode=2"
|
||||
```
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://github.com/gnfalex/rezka_yt_dlp_plugin
|
||||
=> https://github.com/yt-dlp/yt-dlp#installing-plugins
|
||||
239
public/uk/half-life-on-linux-using-xash3d-fwgs-engine.gmi
Normal file
239
public/uk/half-life-on-linux-using-xash3d-fwgs-engine.gmi
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
# Half-Life в Linux на базі рушія Xash3D / FWGS
|
||||
|
||||
Half-Life - чудова гра свого часу, яка досі має чимало шанувальників та високий онлайн.
|
||||
|
||||
Вона стандартно працює в Linux через Steam, утім варто знати й про таку альтернативу рушія з відкритим кодом, як Xash3D/FWGS:
|
||||
|
||||
=> https://github.com/FWGS/xash3d-fwgs
|
||||
|
||||
## Встановлення
|
||||
|
||||
Для встановлення, знадобиться підтримка архітектури i386:
|
||||
|
||||
```
|
||||
dpkg --add-architecture i386
|
||||
apt update
|
||||
apt install build-essential gcc-multilib g++-multilib python3 libsdl2-dev:i386 libfontconfig-dev:i386 libfreetype6-dev:i386
|
||||
export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
|
||||
```
|
||||
|
||||
Після цього клонуємо репозиторій та збираємо бінарний пакет:
|
||||
|
||||
```
|
||||
git clone https://github.com/FWGS/xash3d-fwgs.git
|
||||
cd xash3d-fwgs
|
||||
./waf configure -T release
|
||||
./waf build
|
||||
```
|
||||
|
||||
Коли рушій буде скомпільовано, його можна перенести у потрібну теку, де звичайно зберігаються ігри:
|
||||
|
||||
```
|
||||
./waf install --destdir=/path/to/any/output/directory
|
||||
```
|
||||
|
||||
Наступним кроком, потрібно придбати саму гру (до якої і досі виходять оновлення).
|
||||
|
||||
Купити гру можна безпосередньо у Steam за посиланням:
|
||||
|
||||
=> https://store.steampowered.com/app/70/HalfLife
|
||||
|
||||
Зробити це найкраще у свята, коли бувають розпродажі, знижки іноді сягають 90% вартості, тому можна купити повний набір Half-Life, включно з другою версією (про запуск якої в Linux поговоримо окремо).
|
||||
|
||||
Не зважаючи на те, що в офіційній інструкції вказано просто скопіювати файли гри в директорію "valve", насправді при запуску ймовірно буде помилка:
|
||||
|
||||
```
|
||||
Host_InitError: Can't initialize cl_dlls/client.so: vgui.so: cannot open shared object file: No such file or directory
|
||||
```
|
||||
|
||||
Вирішується вона копіюванням файлу "vgui.so" безпосередньо у кореневу теку, де встановлено Xash3D (тобто рівнем вище "valve")
|
||||
|
||||
## Запуск гри
|
||||
|
||||
Запускається гра виконанням бінарного файлу Xash3D. Для цього можливо треба дати відповідні права:
|
||||
|
||||
```
|
||||
chmod +x xash3d
|
||||
xash3d -dev
|
||||
```
|
||||
|
||||
Якщо все зроблено правильно, з'явиться лаунчер рушія з відповідним меню, де можна грати в одиночну гру або в мережі з друзями.
|
||||
|
||||
Щоб відображати консоль в лаунчері, Xash3D запускається з атрибутом "-dev" або "~" під час гри.
|
||||
|
||||
## Майстер сервер
|
||||
|
||||
Майстер сервер використовується для пошуку ігрових серверів, відповідно усі сервери, що ви бачитимете в мультиплеєрі, відправляють свій статус майстер серверу, а той - клієнтові при виборі підключень.
|
||||
|
||||
За замовченням, рушій викуристовує власний майстер mentality.rip:27010
|
||||
|
||||
Змінити його можна в терміналі командами:
|
||||
|
||||
* clearmasters
|
||||
* addmaster IP:port
|
||||
* listmasters
|
||||
|
||||
Також на етапі збірки, змінити можна у вихідному коді, наприклад:
|
||||
|
||||
=> https://github.com/FWGS/xash3d-fwgs/commit/05d9cf895e180f25e6e0cbe33e3f02cd222de3b0
|
||||
|
||||
Якщо планується запуск власного майстера, наразі існує два відкритих рішення:
|
||||
|
||||
### pymaster
|
||||
|
||||
Неофіційна редакція закинутого майстер сервера на Python:
|
||||
|
||||
=> https://github.com/YGGverse/pymaster/tree/v2
|
||||
|
||||
### xash3d-master
|
||||
|
||||
Після релізу редакції pymaster, розробник опублікував офіційну версію на мові Rust:
|
||||
|
||||
=> https://git.mentality.rip/numas13/xash3d-master
|
||||
|
||||
## Ігровий сервер
|
||||
|
||||
Для запуску ігрового серверу, використовується аналогічна збірка Xash3D та файлами з грою, запуск відбувається приблизно так:
|
||||
|
||||
```
|
||||
xash3d -dedicated -port 27015 -ip xx:xx:xx:xx +maxplayers 8 +map crossfire
|
||||
```
|
||||
|
||||
При цьому звісно, не забуваємо відкрити порт:
|
||||
|
||||
```
|
||||
ufw allow 27015
|
||||
```
|
||||
|
||||
Налаштування серверу зберігаються у наступних файлах, по яким треба пройтись окремо
|
||||
|
||||
* valve/server.cfg
|
||||
* valve/skill.cfg
|
||||
* valve/listip.cfg
|
||||
* valve/banned.cfg
|
||||
|
||||
Щоб сервер став доступним у спискі обраного майстра, потрібно також вказати:
|
||||
|
||||
```
|
||||
sv_lan 0
|
||||
public 1
|
||||
```
|
||||
|
||||
### FastDL
|
||||
|
||||
FastDL - це спосіб швидкої передачі ігрових файлів клієнтові. Звичайно організується на базі веб каталогу за допомогою Nginx.
|
||||
|
||||
Структура файлів, що передається, має відповідати стандартному їх розташуванню у "valve", наприклад:
|
||||
|
||||
* /maps
|
||||
* /models
|
||||
* /sound
|
||||
* /materials
|
||||
|
||||
після чого, у файлі "server.cfg" вказується шлях до цільових файлів (де "/fastdl/half-life/" - довільний шлях, якщо корінь зайнятий):
|
||||
|
||||
```
|
||||
sv_downloadurl "http://xx.xx.xx.xx/fastdl/half-life/"
|
||||
sv_allowdownload 1
|
||||
```
|
||||
|
||||
### RESGen
|
||||
|
||||
RESGen - найпростіший спосіб згенерувати файли залежностей для ігрової мапи.
|
||||
|
||||
Наприклад, особливі моделі гравців, для подальшої передачі їх через FastDL.
|
||||
|
||||
Репозиторій проєкту доступний на GitHub:
|
||||
|
||||
=> https://github.com/kriswema/resgen
|
||||
|
||||
Для генерації текстового файлу необхідно вказати цільову мапу на сервері:
|
||||
|
||||
```
|
||||
Linux.64-bit /valve/maps/crossfire.bsp
|
||||
```
|
||||
|
||||
Після чого буде згенеровано приблизно такий файл:
|
||||
|
||||
```
|
||||
// crossfire.res - created with RESGen v2.0.3.
|
||||
// RESGen is made by Jeroen "ShadowLord" Bogers,
|
||||
// with serveral improvements and additions by Zero3Cool.
|
||||
// For more info go to http://resgen.hltools.com
|
||||
|
||||
// .res entries (9):
|
||||
halflife.wad
|
||||
sound/ambience/jetflyby1.wav
|
||||
sound/ambience/siren.wav
|
||||
sound/debris/beamstart11.wav
|
||||
sound/weapons/electro5.wav
|
||||
sound/weapons/mortarhit.wav
|
||||
sound/weapons/sbarrel1.wav
|
||||
sprites/muzzleflash1.spr
|
||||
sprites/steam1.spr
|
||||
models/player/... // тут додаємо нашу кастомну модель гравця
|
||||
```
|
||||
|
||||
Отриманий файл розміщуємо у теці "/valve/maps" поряд з однойменною мапою.
|
||||
|
||||
Таким чином (після перезапуску серверу), нові гравці, що підключились, зможуть бачити відповідні моделі замість стандартних.
|
||||
Моделі будуть завантажуватись усім учасникам гри до локальної теки "/valve/downloaded/models/player/"
|
||||
|
||||
### Боти
|
||||
|
||||
Щоб гравцям не було самотньо в очікуванні, можна додати ботів, які наприклад будуть зникати із появою онлайну.
|
||||
|
||||
Рішень насправді існує багато, але найбільш простий спосіб - це "Bot Number 10":
|
||||
|
||||
=> http://hpb-bot.bots-united.com/downloads.html
|
||||
|
||||
Є офіційна бінарна збірка для Linux:
|
||||
|
||||
=> http://hpb-bot.bots-united.com/releases/bot10_linux.tgz
|
||||
|
||||
Утім, якщо потрібно модифікувати бота, можна зібрати власну версію з початкового коду, яка є частиною "Portable Half-Life SDK":
|
||||
|
||||
=> https://github.com/FWGS/hlsdk-portable/tree/bot10
|
||||
|
||||
```
|
||||
git clone --recursive https://github.com/FWGS/hlsdk-portable.git
|
||||
git checkout bot10
|
||||
cmake -B build -S .
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
Існує також альтернативна збірка "YGGverse":
|
||||
|
||||
=> https://github.com/YGGverse/hlsdk-portable/tree/bot10-yggverse
|
||||
|
||||
Вона дозволяє запускати зконфігурованих ботів а не просто рандомних, як це зроблено в оригіналі.
|
||||
Наприклад, на цьому сервері у якості мішені використовується путін:
|
||||
|
||||
```
|
||||
connect 94.140.114.89:27015
|
||||
```
|
||||
|
||||
## Висновки
|
||||
|
||||
В цілому, хоч в Steam більший онлайн, Xash3D має свою спільноту і можливості для розробки.
|
||||
|
||||
Гра стабільно працює на стареньких PC, а для запуску власного серверу, згодиться мінімальний тариф VPS (в середньому завантаженість CPU складає 15%).
|
||||
|
||||
Якщо тема є цікавою, можливо окремо буде описано спосіб запуску Half-Life 2 на базі рушія Source, наприклад однієї з його версій у відкритому доступі:
|
||||
|
||||
=> https://github.com/nillerusr/source-engine
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://store.steampowered.com/app/70/HalfLife Придбати файли гри Half-Life в Steam
|
||||
=> https://github.com/FWGS/xash3d-fwgs Офіційна сторінка рушія Xash3D / FWGS
|
||||
=> https://www.svencoop.com/manual/server-config.html Опис параметрів конфігурації сервера
|
||||
=> https://developer.valvesoftware.com/wiki/Server_queries Серверні запити
|
||||
=> https://developer.valvesoftware.com/wiki/Master_Server_Query_Protocol Команди майстер сервера
|
||||
=> https://gamebanana.com/mods/cats/7734 Каталог скінів
|
||||
=> https://github.com/YGGverse/hl-php PHP 8 / Composer бібліотека для веб-розробників
|
||||
=> https://github.com/YGGverse/hlstate Веб рушій на Symfony для організації серверу аналітики
|
||||
|
||||
## Дивіться також
|
||||
|
||||
=> create-half-life-graffiti-logo-in-gimp.gmi Створення графіті Half-Life в GIMP
|
||||
56
public/uk/htcount-visitors-counter-for-access-log.gmi
Normal file
56
public/uk/htcount-visitors-counter-for-access-log.gmi
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# htcount: лічильник відвідувань сайту на базі access.log
|
||||
|
||||
htcount - системна утиліта, написана мовою Rust, що рахує кількість хостів / хітів на основі даних локального журналу access.log (CLF) і виводить результати у вигляді дампу JSON/API або у форматі кнопки SVG для вставки на сайт.
|
||||
|
||||
По суті, вона влаштована так само, як відомий goaccess - інструмент CLI, який дозволяє аналізувати Веб-трафік без підключення сторонніх сервісів типу Google Analytics.
|
||||
|
||||
## Встановлення
|
||||
|
||||
htcount написано мовою Rust, можливо, вам знадобиться спочатку розгорнути відповідну інфраструктуру для її збірки з початкового коду:
|
||||
=> install-latest-rust-version-on-linux.gmi Встановлення останньої версії Rust в Linux
|
||||
|
||||
``` bash
|
||||
git clone https://github.com/YGGverse/htcount.git && cd htcount
|
||||
cargo build --release
|
||||
sudo install target/release/htcount /usr/local/bin/htcount
|
||||
```
|
||||
* встановлення до `/usr/local/bin/htcount` потрібне в контексті системних дозволів systemd
|
||||
|
||||
### systemd
|
||||
|
||||
``` /etc/systemd/system/htcount.service
|
||||
[Unit]
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/htcount\
|
||||
# вкажіть актуальний шлях цільового журналу (див. також --ignore-host)
|
||||
--source /var/log/nginx/access.log\
|
||||
# вкажіть актуальний шлях для розміщення кнопки
|
||||
--export-svg /var/www/path/to/public/counter.svg\
|
||||
# я збирав пакунок від root, де в мене лежать оригінали у якості шаблону
|
||||
--template-svg /root/htcount/default/counter.svg\
|
||||
# оновлення кнопки раз на годину (3600 секунд)
|
||||
--update 3600\
|
||||
# оновлення відвідувачів раз на добу
|
||||
--match-time %%d/%%b/%%Y\
|
||||
# дебаг у файл вимкнено для заощадження терміну служби SSD
|
||||
--debug n
|
||||
# дебаг вимкнено опцією --debug n, можна вказати шлях або драйвер
|
||||
StandardOutput=null
|
||||
# збираємо усі помилки до стандартного розташування системних журналів
|
||||
StandardError=file:///var/log/htcount-error.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
* перелік доступних опцій залежить від актуальної версії: `htcount --help`
|
||||
* для інтеграції кнопки на сайт, додайте відповідний код: `<img src="/counter.svg"/>`
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://crates.io/crates/htcount
|
||||
=> https://en.wikipedia.org/wiki/Common_Log_Format
|
||||
=> https://goaccess.io
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
# Регулювання яскравості монітору iMac в Linux
|
||||
|
||||
Маю відносно старенький девайс iMac 2011 року і довий час користувався ручним регулюванням яскравості монітору командою "xrandr".
|
||||
|
||||
Наприклад, щоб зменишити яскравість до 80%:
|
||||
|
||||
```
|
||||
xrandr --output eDP --brightness 0.8
|
||||
```
|
||||
|
||||
Якось випадково мені довелось залізти в драйвер підсвітки "/sys/class/backlight/radeon"
|
||||
|
||||
Змінивши стандартне значення з "255" до комфортних "50" я був здивований наскільки контрастне відображення вдалось отримати!
|
||||
Вражає, скільки часу провів на пересвіченому моніторі!
|
||||
|
||||
Як виявилось, існує два фактори: живлення на катоди RGB та власне самої підсвітки.
|
||||
|
||||
В залежності від відеокарти, ймовірно, для інших користувачів iMac цього року випуску, команда виглядатиме так (і виконується від "root"):
|
||||
|
||||
```
|
||||
echo 50 > /sys/class/backlight/radeon_bl0/brightness
|
||||
```
|
||||
|
||||
або ж подивіться що у вас в теці (і підставте в команду вище вашу карту):
|
||||
|
||||
```
|
||||
ls /sys/class/backlight
|
||||
```
|
||||
|
||||
Зауважу, що наведена вище інструкція, у моєму випадку, вимагає зміни "acpi_backlight" в значення "native"
|
||||
|
||||
``` /etc/default/grub
|
||||
GRUB_CMDLINE_LINUX_DEFAULT="quiet acpi_backlight=native"
|
||||
```
|
||||
|
||||
Після чого потрібно оновити конфігурацію командою "update-grub" і перезавантажити систему.
|
||||
Остання дія також додасть підтримку сенсору автоматичної яскравості, зокрема в Ubuntu.
|
||||
|
||||
Сподіваюсь, дана замітка стане в нагоді!
|
||||
55
public/uk/index.gmi
Normal file
55
public/uk/index.gmi
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
> ...якщо цьому є творець, то він - революціонер,
|
||||
> експериментатор або вигнанець системи
|
||||
> споконвічного спокою.
|
||||
|
||||
### Ласкаво просимо
|
||||
|
||||
=> traditional-craft-masters-of-ukraine.gmi 2024-06-16 Майстри традиційних ремесел України
|
||||
=> ukrainian-geminispace.gmi 2024-06-16 Український Geminispace
|
||||
|
||||
### Статті
|
||||
|
||||
=> rssto-convert-rss-feeds-into-multiple-formats.gmi 2025-09-03 rssto: конвертація фідів у різні формати
|
||||
=> misfin-postal-satellite-of-geminispace.gmi 2025-09-02 Misfin - поштовий сателіт Geminispace
|
||||
=> htcount-visitors-counter-for-access-log.gmi 2025-08-30 htcount: лічильник відвідувань сайту на базі access.log
|
||||
=> about-fediverse-server-in-the-alternative-networks.gmi 2025-08-30 Про сервер Fediverse в альтернативних мережах
|
||||
=> thoughts-on-tls-on-yggdrasil-and-mycelium-networks.gmi 2025-08-28 Думки стосовно TLS в мережах Yggdrasil та Mycelium
|
||||
=> grab-video-from-rezka-hd-mirror-with-yt-dlp.gmi 2025-08-28 Завантаження відео з сайтів Rezka HD через плагін yt-dlp
|
||||
=> btracker-bittorrent-tracker-in-rust.gmi 2025-08-27 βtracker - BitTorrent агрегатор на базі Rust
|
||||
=> dns-less-email-server-for-the-local-networks.gmi 2025-08-26 Організація поштової скриньки для локальних мереж без DNS
|
||||
=> install-flarum-v2-on-linux.gmi 2025-08-24 Встановлення Flarum v2 в Linux
|
||||
=> prepare-steam-half-life-game-asset-for-the-bittorrent-release.gmi 2025-08-13 Підготовка ігрових файлів Half-Life (Steam) для релізу BitTorrent
|
||||
=> install-aquatic-open-tracker-on-linux.gmi 2025-06-02 Встановлення BitTorrent трекера Aquatic в Linux
|
||||
=> safe-yggdrasil-websites-browsing-with-yggstack.gmi 2025-05-23 Безпечний перегляд сайтів Yggdrasil з Yggstack
|
||||
=> my-thoughts-on-the-modern-uanet-it-segment.gmi 2025-05-17 Мої думки про сучасний ІТ сегмент UANET
|
||||
=> personal-snac-instance-for-yggdrasil-network.gmi 2025-05-11 Налаштування Fedi-сервера Snac для мережі Yggdrasil
|
||||
=> asocial-p2p.gmi 2025-05-09 Граблі соціального P2P
|
||||
=> flatpak-bundle-build.gmi 2025-04-20 Створення пакунку Flatpak
|
||||
=> twister-p2p-decentralized-microblogging-platform.gmi 2025-04-13 twister - децентралізована платформа мікроблогів
|
||||
=> my-first-impressions-of-rust.gmi 2024-10-26 Мої перші враження від Rust
|
||||
=> libhandy-tab-as-gtk-notebook-widget-alternative.gmi 2024-09-07 HdyTab як альтернатива віджету вкладок GtkNotebook
|
||||
=> inefficient-internet-privacy.gmi 2024-09-07 Про конфіденційність в мережі Інтернет
|
||||
=> first-impressions-of-cpp.gmi 2024-09-03 Мої перші враження від C++
|
||||
=> memory-management-in-gtkmm-4.0.gmi 2024-09-02 Керування пам'яттю в gtkmm-4.0
|
||||
=> memory-management-in-gtk-applications.gmi 2024-09-01 Керування пам'яттю в програмах GTK
|
||||
=> first-steps-in-gtk.gmi 2024-08-31 Мої перші кроки в GTK
|
||||
=> nex-lightweight-gemini-alternative.gmi 2024-06-20 Протокол NEX - легка альтернатива Gemini
|
||||
=> mushroom-ink-for-calligraphy.gmi 2024-06-16 Грибні чорнила для каліграфії
|
||||
=> manticore-as-primary-database-usage-issues.gmi 2024-03-25 Незручні моменти в роботі Manticore як основної БД
|
||||
=> chesslablab-open-source-chess-in-php.gmi 2024-03-25 ChesslaBlab - шахи онлайн з відкритим кодом на PHP
|
||||
=> openlegends-tes-legends-card-game-open-source-alternative.gmi 2024-03-20 Розробка карткової гри з відкритим кодом OpenLegends
|
||||
=> fail2ban-startup-failure-fix-on-debian-12.gmi 2024-03-09 Виправлення помилки запуску fail2ban в Debian 12
|
||||
=> gmid-server-for-gemini-protocol.gmi 2024-03-09 Gmid - багатофункціональний сервер для Gemini
|
||||
=> install-latest-rust-version-on-linux.gmi 2024-03-09 Встановлення останньої версії Rust в Linux
|
||||
=> install-latest-golang-version-on-debian-linux.gmi 2024-03-09 Встановлення останньої версії Go в Debian
|
||||
=> yggmail-messenger-with-email-protocol.gmi 2024-03-09 Yggmail - месенджер з поштовим інтерфейсом
|
||||
=> freetube-the-private-youtube-client.gmi 2024-03-08 FreeTube - приватний перегляд YouTube
|
||||
=> imac-monitor-backlight-brightness-setup-in-linux.gmi 2024-03-08 Регулювання яскравості монітору iMac в Linux
|
||||
=> ukrainian-latin-keyboard-layout-on-linux.gmi 2024-03-08 Розкладка української латинки в Linux
|
||||
=> kevacoin-decentralized-database-in-blockchain.gmi 2024-03-08 KevaCoin - децентралізована база даних в блокчейн
|
||||
=> manticore-as-modern-alternative-to-sphinx-search.gmi 2024-03-07 Manticore як сучасна альтернатива Sphinx
|
||||
=> create-half-life-graffiti-logo-in-gimp.gmi 2024-03-07 Створення графіті Half-Life в GIMP
|
||||
=> half-life-on-linux-using-xash3d-fwgs-engine.gmi 2024-03-07 Half-Life в Linux на базі рушія Xash3D / FWGS
|
||||
=> alfis-dns-domain-registration-in-blockchain.gmi 2024-03-06 Alfis DNS - реєстрація домену в блокчейн
|
||||
=> yggdrasil-is-network-with-distributed-routing.gmi 2024-03-06 Yggdrasil - мережа з децентралізованою маршрутизацією
|
||||
=> gemini-protocol-as-http-alternative.gmi 2024-03-06 Протокол Gemini як альтернатива HTTP
|
||||
81
public/uk/inefficient-internet-privacy.gmi
Normal file
81
public/uk/inefficient-internet-privacy.gmi
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# Про конфіденційність в мережі Інтернет
|
||||
|
||||
У кожного своє розуміння конфіденційності та інформаційної безпеки у рамках різних контекстів. Дехто не переймається темою взагалі, нехтуючи поширенням своїх персональних даних, аргументуючи це їх повною "непотрібністю". Наочним історичним прикладом колись "нікому непотрібної" інформації, був виток облікових даних єврейських громад у фашистській Германії - коли водномить, здавалось би побутові дані стали питанням виживання мільйонів людей. З іншого боку, такі сфери, як об'єктивна журналістика та альтернативні технології зазнають тиску, і сусідня диктатура - тому наглядний приклад, де недавно бравий анон сьогодні боїться писнути "проти" в глибинах своїх мешів.
|
||||
|
||||
Цей матеріал не має на меті когось навчати, переконувати чи нав'язувати паранойю. Тут немає практичних рекомендацій для жодного з тлумачень приватної інформації. Це виключно моя суб'єктивна фіналізація отриманого досвіду по темі інформаційної гігієни, з точки зору користувача, а не спеціаліста сфери безпеки: зокрема, до вивчення того, як працюють мережеві технології та після.
|
||||
|
||||
## Передісторія
|
||||
|
||||
Мій шлях в мережі Інтернет почався незадовго до популяризації його в Україні. Не зважаючи на той факт, що технологія вже давно була поширена на заході, в Україні, мережа все ще сприймалась не серйозно і через тодішню швидкість, слугувала більше для пошуку друкованої інформації (смішно задувати, але вже тоді Інтернет був джерелом прибутку для піратського передруку брошур на ринку "Караваєві Дачі") тоді ніхто не міг подумати, що Інтернет стане основою сучасної торгівлі, міжнародного зв'язку, а через вартість послуг провайдерів і обмеження швидкості - колись Youtube замінить телевізор чи ПК.
|
||||
|
||||
В мене відносно пізно з'явився комп'ютер, я закінчував вищу медичну освіту на якому і серед багатьох хобі, у вільний час писав статті та різні замітки в MS Word, зберігаючи їх просто до локальної теки. Але Інтернет змінив все, з його появою, нарешті я міг ділитися своїми роботами з іншими та більше того - не шукати потрібну інформацію в книжкових магазинах, де якість технічної літератури була просто водою у порівнянні з авторськими блогами; дуже скоро почав робити свої сайти і в мене почали з'являтись перші комерційні замовлення. Так, поступово став поглиблюватись в технічну сферу, яка почала займати 100% мого студентського часу, а потім й стала основною роботою.
|
||||
|
||||
Оскільки через тодішню швидкість мережі ринок е-комерсу в Україні розвивався повільно, час від часу мені траплялись замовлення на закордонному рику, де крім того, що фігурували інші суми, попиту було в рази більше. Але була одна проблема: фінансові операції з країнами пост-СНД, зокрема через популярний на заході Paypal, були не доступні. Тоді я вперше зіткнувся з якимось невидимим бар'єром, не справедливістю, що відгороджує мою країну з тими ямами на дорогах від цивілізованого світу. Тоді прийшло розуміння, що Україна перебуває в штучній ізоляції, через корупцію та "договірняки" з російською мафією, з'явився певний дух протесту до системи, а з ним - вивчення технологій, що близькі свободі по духу: зокрема - OpenSource.
|
||||
|
||||
## Децентралізовані технології
|
||||
|
||||
Одного разу, я прочитав новину про реліз Bitcoin - свого роду розрахункову систему накшталт електронної пошти, тільки для грошей. При чому, на відміну від тодішніх централізованих контор типу WebMoney (з яких також на закордонному ринку було мало толку) нова криптовалюта не мала центрального органу та не мала географічних обмежень. Це для мене стало просто з'їздом даху в плані потенційних можливостей. Але була одна проблема: вона була нікому не відомою і щоб отримати 50 доларів, доводилось пояснювати кожному замовникові як встановити гаманець, де обміняти "реал" в його країні та як переказати гроші :)
|
||||
|
||||
Тим не менше, це мало певний результат, а схожі технології, що базувались на Bittorrent - стали дружніми та цікавими, оскільки в ньому не було такого поняття як "модерація" та "злий адмін" взагалі. У ті часи з'являлось багато інших цікавих проєктів по типу Twister, ZeroNet, IPFS і т.д., які також мали на меті свободу поширення та доступу до інформації та свободи слова. Відбувся свого роду бум, тоді альтернативні, зокрема соціальні мережі отримали великий імпульс і затягнули мене своєю цікавістю в "діпвеби", де й провів багато часу на ентузіазмі, зокрема ще й тому, що інтелектуальний рівень їх користувачів був у рази вищим за "юзвірів" популярних порталів. Було чому повчитись.
|
||||
|
||||
Але пройшло більше десяти років і можна побачити, що майже всі альтернативні платформи мертві. Bitcoin, замість вільного засобу розрахунків, став чорною дірою для спекуляцій, де лох - не мамонт. Пам'ятаю єдиний магазин в Києві, де колись офлайн за біткоїн можна було купити пончик, вже мабуть закрився. Звісно, сьогодні вам продадуть пончик в іншому місці, але це не те, чим мало стати взагалі. Чому так? Давайте розглянемо технічну сторону цього питання, адже для інших сфер - є свої спеціалісти.
|
||||
|
||||
На мою думку, ахіллесовою п'ятою всіх революційних технологій, є використання ними класичних засобів зв'язку. Так, на прикладі Bitcoin, щоб підключити свій гаманець до спільного реєстру блокчейн, потрібно з'єднатись з певним сервером DNS, який надасть вам IP адреси публічних вузлів Bittorrent, через які вже можна отримати дані поточних блоків. Оскільки весь ланцюг з'єднання не включає принципово нових засобів комунікації, такі канали можуть бути повністю заблоковані, а їх провайдери - притягнуті до відповідальності, як тільки певна "революційна" технологія почне заважати централізованій економіці (будь вона легальною або корумпованою).
|
||||
|
||||
Іншим моментом децентралізованих рішень, є складна архітектура їх роботи. Оскільки децентралізовані засоби зв'язку від початку були створені для військового застосування, вони не передбачали роботи з такими об'ємами даних та кількістю користувачів, яких сьогодні вимагає цивільна сфера. Наприклад, якщо запустити сервер IPFS, можна побачити, що фонове споживання трафіку складає приблизно 1 мегабіт на секунду і це "на холостих" і при відносно малій кількості пірів у мережі - близько 400. Можна уявити, що станеться з каналом та акумулятором мобільного пристрою (де кожен пір являє собою хеш-суму) при підключенні до рою мільйонів користувачів одночасно. По цій причині, користувачі криптовалют часто користуються "легкими" гаманцями, тобто такими, які підключаються до мережі на стороні серверу, а користувачеві видають ті само порожні цифри балансу, які надає по суті звичайний банк.
|
||||
|
||||
З точки зору приватності, на мою думку, децентралізовані рішення є парадоксально найбільш вразливими, оскільки в таких мережах користувач надає свою адресу усім її учасникам. Тому якщо справа стосується публікації "чутливих" даних, то потрібно спочатку приховати адресу і вже після того підключатись. Насправді зробити це не так просто, оскільки звичайний VPN може працювати тільки для певного інтерфейсу програми, у той час як на фоні, вона також може підключатись до сторонніх сервісів, використовуючи інший протокол чи зовнішню бібліотеку службового сервісу, через білий IP. Для рядового користувача, це майже непосильне завдання, оскільки потребує окремої збірки операційної системи на певному залізі.
|
||||
|
||||
## Fediverse
|
||||
|
||||
Таким чином, еволюційно на зміну повній "децентралізації" як такої, приходять "федеративні" рішення: коли багато невеличких серверів формують одну спільну екосистему Fediverse, яку ще називають "сузір'ям". Яскравим і найбільш вдалим прикладом реалізації є соціальна мережа мікро-блогінгу Mastodon.
|
||||
|
||||
Обраний сервер, або "провайдер", тут виступає вже у якості проксі та свого роду, додатковим "фаєрволом" для користувача. Можна збитись в певні кучі по інтересам і тусити у вузьких колах однодумців, де в інших місцях вас за такі само пости виженуть або заблокують, але знову таки до тих пір, допоки адміну це подобається або його чи його хостинг-провайдера разом з вашими персональними даними не візьмуть за дупу в юрисдикції тієї країни, де він знаходиться.
|
||||
|
||||
Недоліком федеративних рішень є також відсутність будь яких гарантій зі сторони їх "диких" провайдерів, ваш контент може бути так само легко втрачено як і додано. Це знову таки, рішення "зробити сервіс нікому не потрібним, для того, щоб його не заблокували". Ну і звісно, приватні кампанії фізичних осіб рідко слідують наданим ними самими угодам обробки персональних даних користувача, на відміну від медіа холдингів США, таких як Twitter, Facebook тощо.
|
||||
|
||||
## Mesh, TOR, Onion
|
||||
|
||||
Існують різні технології тунелювання (TOR), заплутування маршрутів (Onion) та віртуальних мереж (CJDNS, Yggdrasil та інших). Всі їх об'єднує спільна риса: вам потрібно якось до них підключитись. Так, можна орендувати VPN, або ланцюг з таких, але кожен з них буде посилатись на початковий вузол, а маскування трафіку під "443" (через наприклад Shadowsocks) можна легко викрити шляхом перебору протоколів під час рукостискання (handshake) з публічним вузлом. Тому отримання таких даних - це просто "питання потреби"
|
||||
|
||||
Як варіант, можна взагалі не використовувати Інтернет і уявити собі світ, де кожен юзер роздає по Wi-Fi точку Yggdrasil, але цього мабуть ніколи не станеться. У разі гіпотетичної катастрофи, ймовірно будуть так само використовуватись стандартні протоколи IPv4/IPv6 для побудови класичних локальних мереж, допоки вони знову не з'єднаються і не утворять собою нову мережу Інтернет.
|
||||
|
||||
Ще однією стороною проблеми анонімізації, яку обіцяють технології вище - неможливість приховування такими засобами цифрового відбитку. Наразі, вже кожному доступні технології штучного інтелекту, які наочно демонструють можливості конверсії будь яких аналогових об'єктів, у нашому випадку з копірайтингом - це може бути як використання діалекту чи навіть пунктуації.
|
||||
|
||||
## Альтернативні протоколи
|
||||
|
||||
Альтернативний протокол передбачає фундаментально новий підхід в обміні інформацією, який невідомий моніторинговій стороні. Це може бути наприклад аналоговий радіо-зв'язок, що використовується в морі.
|
||||
|
||||
Тут мало що можу сказати, маю досвід перебування в окупації, де не те щоб сховатись серед іншого трафіку, але й випромінювати будь який сигнал - самовбивство. Навіть за відсутності радіо передавання, звичайний дріт чи обладнання знищується тими, хто проводить зачистки. Якщо у вас потім знайдуть взагалі будь який пристрій, якого не мало бути - про наслідки думайте самі.
|
||||
|
||||
Отже колись романтична віра в технології пост-апокаліпсису, на ділі виявилась плацебо. У форс-мажорних обставинах, ви не зможете користуватись зв'язком як таким, без ризику бути виявленим, навіть якщо це ліхтарик та азбука морзе. Тож цивільний формат зв'язку забезпечується виключно екосистемою тієї країни, де ви знаходитесь і ймовірно користуватись іншими засобами, окрім ліцензованих - не зможете.
|
||||
|
||||
### Веб протоколи
|
||||
|
||||
Щодо цивільного середовища, не так давно, відкрив для себе старі-нові Веб-протоколи, зокрема Gemini. Пам'ятаю, як задумувався про щось схоже, але було чим займатись окрім як розвивати цю думку матеріально. Але не так давно, знову наткнувся на концепцію мінімалізму, яку пропонує протокол Gemini у якості інструмента приватності. Тут немає JS, Cookies та всього того, з чим бореться Ublock та не намагається зовсім - жоден із сучасних Веб-браузерів.
|
||||
|
||||
Утім, на практиці, досить рідко відвідую Geminispace, оскільки по-перше мало україномовного контенту, в той час як англомовний мені просто не цікавий (бо спільнота живе там своїм життям, до якої мені просто наразі немає діла). З іншого боку, сторінки Geminispace всі однотипні, не зручно читати великі тексти, а щоб переглянути зображення, потрібно спочатку підвантажити, натиснувши на посилання (приватність, все таки).
|
||||
|
||||
Стосовно конфіденційності користування - це звичайний собі веб з моделлю "клієнт-сервер", з тією лише різницею, що всі колись асинхронні запити та завантаження зображень ви будете робити вручну. Такий собі формат "я знаю, на що йду" (хоча не варто забувати про DNS на шляху таких "знань")
|
||||
|
||||
## Класичний Інтернет
|
||||
|
||||
Колись так обожнюваний мною Інтернет, після вивчення базових принципів його роботи, сьогодні для мене виглядає як технологічне решето: через браузер відбувається купа фонових JavaScript звернень до третіх серверів без мого відому, Cookies, з їх імпотентними банерами на кожному сайті.
|
||||
|
||||
Частина Веб-контенту взагалі еволюціонувала у збереження десь в базах даних месенджерів по типу Телеграм, але тут окрім IP, зливається невідомо кому ще й номер телефону а також, в залежності від застосунку - гео-дані та мета дані пристрою. Я не розумію, яким чином в умовах війни, в Телеграм присутні офіційні державні агентства, зокрема такі, що функціонують на окупованих територіях, адже це просто самовбивство для потенційних громадян що там лишились але за відсутності досвіду в сфері ІТ, користуються даним інструментом за офіційними рекомендаціями.
|
||||
|
||||
## Висновки
|
||||
|
||||
Підсумовуючи сказане, на мою думку, будь який пристрій, що підключається до мережі - як не крути, є потенційною дірою. Навіть користуючись вільною операційною системою з відкритим кодом, не виключається ймовірність наявності неописаних багів, що мовчки використовуються відповідними спеціалістами. Постійно фігурують випадки контрибуцій з вразливостями, які потім спішно видаляють з репозиторіїв, але мільйони користувачів вже встигли встановити такі оновлення.
|
||||
|
||||
Конфіденційності для пристрою, підключеного до мережі Інтернет, на мою думку, просто не існує. Немає такого інструменту, що дозволить бути 100% впевненим. Приховати дані у сучасній мережі Інтернет, можна тільки не маючи жодних зв'язків з цією технологією, зробивши еволюційний крок назад, у часи палеоліту.
|
||||
|
||||
### Переосмислення
|
||||
|
||||
Будь які інструменти анонімізації, ймовірно, суперечать початковим концепціям Інтернет, а використання таких просто створює ізоляційний вакуум та обмежує доступність інформації.
|
||||
|
||||
Щодо інструментів без цензури та вільної журналістики, анонімні джерела мають мінімальну вагу, оскільки не є перевіреними. Публікація ноунейма у хащах діпвебу просто не викличе резонансу. У цій справі, як і в інших сферах - потрібна відповідальність та мужність.
|
||||
|
||||
Свобода вибору, захист персональних даних, авторського права, надання гарантованих послуг (таких як хостинг для VPN), можливість висловлювати точку зору та користування перевагами цивілізованого світу, є результатом злагодженої роботи в багатьох сферах - оборонної, дипломатичної, соціально-ініціативної. Конфіденційність, сама по собі, як функціональна одиниця - не ефективна, особливо в такому середовищі як Інтернет. Мабуть, тут вона і не потрібна.
|
||||
|
||||
Цінуймо справжню свободу, а не імітацію. Допомагаймо тим, хто її боронить!
|
||||
226
public/uk/install-aquatic-open-tracker-on-linux.gmi
Normal file
226
public/uk/install-aquatic-open-tracker-on-linux.gmi
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
# Встановлення BitTorrent трекера Aquatic в Linux
|
||||
|
||||
Давно збирався підняти BitTorrent трекер відкритого типу для мережі Yggdrasil.
|
||||
|
||||
Детальніше про мережу Yggdrasil читайте тут:
|
||||
=> yggdrasil-is-network-with-distributed-routing.gmi Yggdrasil - мережа з децентралізованою маршрутизацією
|
||||
|
||||
Вже існує відомий сервер з 15-річною історією розробки OpenTracker:
|
||||
=> https://erdgeist.org/arts/software/opentracker
|
||||
|
||||
Але останнім часом, я надаю перевагу більш сучасній мові Rust, тим паче що потім планую реалізувати цією мовою додатковий веб-агрегатор на базі кешованих трекером інфо-хешів у зв'язці з бібліотекою rqbit:
|
||||
=> https://github.com/ikatson/rqbit
|
||||
|
||||
Трохи полиставши результати пошуку на GitHub, віднайшов сервер Aquatic:
|
||||
=> https://github.com/greatest-ape/aquatic
|
||||
|
||||
Він також підтримує IPv4/IPv6, протоколи UDP, HTTP, WS і так само зберігає дані в оперативній пам'яті, не зношуючи своєю роботою носій SSD. Нижче опишу покрокову інструкцію збірки, встановлення та налаштування для мережі Yggdrasil, можливо даний матеріал буде цікавий початківцям, зокрема - для використання в класичній мережі Інтернет.
|
||||
|
||||
## Підготовка системи
|
||||
|
||||
### Створення системного користувача
|
||||
|
||||
Як і для інших програм Linux, що збираються з початкового коду і запускаються через systemd, я створюю окремого системного користувача з домашньою текою, для ізоляції прав доступу:
|
||||
``` bash
|
||||
useradd -m aquatic
|
||||
```
|
||||
|
||||
### Встановлення системних залежностей
|
||||
|
||||
Програмне середовище Rust у мене розгорнуте в профілі root, куди я клоную репозиторії і засобами профілю якого проводжу збірку всіх бінарних файлів.
|
||||
Якщо в системі не встановлена інфраструктура Rust (rustc, cargo та інше) тоді вам сюди:
|
||||
=> https://rustup.rs
|
||||
|
||||
Стандартна інсталяція залежностей Rust вимагає близько 2 Гб дискового простору. Якщо ви, як і я, користуєтесь VPS - встановіть лише необхідні для роботи компілятора пакунки.
|
||||
|
||||
Для цього при першому запуску команди:
|
||||
``` bash
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
```
|
||||
|
||||
* оберіть пункт "Customize installation"
|
||||
* замість стандартного "Profile (which tools and data to install)" вкажіть "minimal"
|
||||
|
||||
Це пропустить встановлення зайвих на сервері пакунків, що зменшить розмір до ~600 Мб.
|
||||
|
||||
У випадку використання "minimal", для збірки також важливо до-встановити глобальні залежності:
|
||||
``` bash
|
||||
apt install clang libclang-dev
|
||||
```
|
||||
* при інсталяції rustup в режимі "default", цей крок можна пропустити.
|
||||
|
||||
Якщо rustup вже встановлено, також переконаймось, що використовуються актуальні версії:
|
||||
``` bash
|
||||
rustup update
|
||||
cargo update
|
||||
```
|
||||
|
||||
### Встановлення Aquatic
|
||||
|
||||
Виконуємо наступні кроки у послідовності:
|
||||
``` bash
|
||||
su root && cd ~
|
||||
```
|
||||
* логінимось (якщо досі не root) і переходимо в домашню теку
|
||||
``` bash
|
||||
git clone https://github.com/greatest-ape/aquatic.git && cd aquatic
|
||||
```
|
||||
* клонуємо вихідний код і переходимо до теки проєкту
|
||||
``` bash
|
||||
cargo build --release -p aquatic_udp
|
||||
```
|
||||
* збираємо оптимізовані бінарні пакети aquatic_udp
|
||||
|
||||
Таким чином, бінарники будуть доступні у теці "/root/aquatic/target/release/*". Оскільки ми будемо використовувати сервіс systemd, де відсутні змінні середовища, важливо скопіювати (або залінкувати) необхідні пакети в системне розташування, де вони матимуть відповідні права на виконання:
|
||||
``` bash
|
||||
install /root/aquatic/target/release/aquatic_udp /usr/local/bin/aquatic_udp
|
||||
```
|
||||
* мені потрібен тільки сервер UDP, якщо ви хочете підняти HTTP та WS, виконайте аналогічні кроки для інших саб-крейтів;
|
||||
* тут ми використовуємо команду "install" замість "cp", оскільки вона автоматично встановлює правильні права доступу;
|
||||
* якщо не плануєте оновлення, на даному етапі можна видалити не потрібні вихідні коди.
|
||||
|
||||
### Файл конфігурації
|
||||
|
||||
Сервер Aquatic можна запускати зі стандартним набором опцій, без аргументів (отримати поточні налаштування для конфігурації сервера можна командою:
|
||||
``` bash
|
||||
aquatic_udp -p
|
||||
```
|
||||
|
||||
Оскільки мій сервер буде працювати в режимі Yggdrasil-only, як скопіюю цей вивід у спільний системний файл конфігурації командою:
|
||||
``` bash
|
||||
aquatic_udp -p > /etc/aquatic.toml
|
||||
```
|
||||
* приклад systemd нижче буде використовувати цей модифікований файл
|
||||
|
||||
Стандартна конфігурація передбачає запуск на всіх інтерфейсах і порті 3000, я ж змінюю на окрему адресу підмережі Yggdrasil і більш типовий для відкритих UDP трекерів порт 6969. Детальніше про те, як створити адресу підмережі Yggdrasil - читайте тут:
|
||||
=> http://[222:a8e4:50cd:55c:788e:b0a5:4e2f:a92c]/yggdrasil:subnet_setting
|
||||
|
||||
``` aquatic.toml
|
||||
# вимикаю IPv4, так як цей інтерфейс в Yggdrasil не обслуговується
|
||||
use_ipv4 = false
|
||||
# "[xxx:xxxx:xxxx:xxxx::fdb]" - актуальна адреса IPv6,
|
||||
# "fdb" - це імпровізований постфікс у діапазоні A-f0-9 типу "file database"
|
||||
address_ipv6 = "[xxx:xxxx:xxxx:xxxx::fdb]:6969"
|
||||
```
|
||||
|
||||
Якщо потрібен вивід публічної веб-статистики, вказуємо також:
|
||||
``` aquatic.toml
|
||||
write_html_to_file = true
|
||||
# шлях може бути іншим
|
||||
# * у цьому випадку створіть каталог командою `mkdir /var/www/aquatic`
|
||||
# * надайте відповідні права `chown aquatic:aquatic /var/www/aquatic`
|
||||
html_file_path = "/var/www/aquatic/index.html"
|
||||
```
|
||||
|
||||
В конфігурації хосту nginx додаємо наступне (не забуваємо також відкрити 80 порт):
|
||||
``` /etc/nginx/default.conf
|
||||
server {
|
||||
# актуальна адреса IPv6
|
||||
listen [xxx:xxxx:xxxx:xxxx::fdb]:80;
|
||||
server_name xxx:xxxx:xxxx:xxxx::fdb;
|
||||
|
||||
root /var/www/aquatic;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Не забуваємо застосувати зміни:
|
||||
``` bash
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
```
|
||||
|
||||
Інші налаштування лишаю стандартними.
|
||||
|
||||
### Налаштування Firewall
|
||||
|
||||
В системах Debian, в режимі UDP / Yggdrasil-only (трекер прийматиме та віддаватиме виключно внутрішньомережеві адреси пірів) я використовую наступне перманентне правило ufw для iptables:
|
||||
``` bash
|
||||
ufw allow from 0200::/7 to 0200::/7 port 6969 proto udp
|
||||
```
|
||||
* якщо в конфігурації вказано інший порт, використовуйте його замість 6969
|
||||
|
||||
Якщо у вас звичайний Інтернет трекер, можна просто додати дозвіл на всі вхідні типи підключень:
|
||||
``` bash
|
||||
ufw allow 6969
|
||||
```
|
||||
|
||||
Не забуваємо по аналогії відкрити порт на веб-статистику, якщо така використовується у конфігурації вище!
|
||||
|
||||
### Сервіс systemd
|
||||
|
||||
Від root створюємо новий файл конфігурації:
|
||||
``` bash
|
||||
nano /etc/systemd/system/aquatic.service
|
||||
```
|
||||
|
||||
У цьому файлі, на моєму прикладі, вказано тільки запуск сервера UDP, якщо буде потрібно, додам й інші протоколи в рамках спільного сервісу (послідовністю команд групи "exec" або додатковим скриптом). Ви можете створити для себе окремі юніти типу "aquatic_ws.service", але на мою думку це не зручно і краще адмініструвати спільною короткою командою.
|
||||
|
||||
Зміст файлу приблизно такий:
|
||||
``` /etc/systemd/system/aquatic.service
|
||||
[Unit]
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=aquatic
|
||||
Group=aquatic
|
||||
ExecStart=/usr/local/bin/aquatic_udp -c /etc/aquatic.toml
|
||||
StandardOutput=file:/home/aquatic/debug.log
|
||||
StandardError=file:/home/aquatic/error.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Керування процесом відбувається наступними командами:
|
||||
|
||||
``` bash
|
||||
systemctl daemon-reload
|
||||
```
|
||||
* оновлюємо конфігурацію systemd
|
||||
``` bash
|
||||
systemctl enable aquatic
|
||||
```
|
||||
* автостарт при запуску системи
|
||||
``` bash
|
||||
systemctl start aquatic
|
||||
```
|
||||
* запуск
|
||||
|
||||
## Тестування
|
||||
|
||||
Після запуску сервісу, перевіряємо наявність активного процесу:
|
||||
``` bash
|
||||
netstat -tulpn | grep aquatic_udp
|
||||
```
|
||||
|
||||
На прикладі моєї конфігурації Yggdrasil, має бути щось типу такого:
|
||||
``` bash
|
||||
udp6 0 0 xxx:xxxx:xxxx:xxxx:6969 :::* 123456/aquatic_udp
|
||||
```
|
||||
|
||||
Також дивимось журнали:
|
||||
* /home/aquatic/debug.log
|
||||
* /home/aquatic/error.log
|
||||
|
||||
На стороні клієнта, створюємо новий торент (в qBittorrent це "Tools" -> "Torrent Creator")
|
||||
і вказуємо адресу нашого нового трекеру, після чого перевіряємо оновлення веб-статистики;
|
||||
так само, можна додати трекер до наявної роздачі.
|
||||
|
||||
В принципі, це все, якщо комусь цікаво - мій сервер розташований тут:
|
||||
```
|
||||
udp://[302:68d0:f0d5:b88d::fdb]:6969
|
||||
```
|
||||
|
||||
Веб-статистика:
|
||||
=> http://[302:68d0:f0d5:b88d::fdb]
|
||||
=> http://tracker.ygg
|
||||
|
||||
Також, на разі працюю над агрегатором Aquatic для підписок на оновлення трекеру в різних форматах:
|
||||
=> https://github.com/YGGverse/aquatic-crawler aquatic-crawler (Rust)
|
||||
252
public/uk/install-flarum-v2-on-linux.gmi
Normal file
252
public/uk/install-flarum-v2-on-linux.gmi
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
# Встановлення Flarum v2 в Linux
|
||||
|
||||
Днями було засновано українську спільноту адміністраторів альтернативних мереж. У якості платформи для спілкування - обрано рушій Flarum:
|
||||
=> https://flarum.org
|
||||
|
||||
Цей матеріал - адаптована версія локальної інструкції для Debian/Linux, якщо комусь потрібно буде швидко розгорнути дану платформу, адже в документації не вказані деякі, на мою думку, важливі моменти.
|
||||
|
||||
## Системні залежності
|
||||
|
||||
``` bash
|
||||
apt install composer php-fpm\
|
||||
php-curl php-dom php-sqlite3\
|
||||
php-fileinfo php-gd php-json php-mbstring\
|
||||
php-openssl php-pdo php-tokenizer php-zip php-session
|
||||
```
|
||||
* в `apt` має вискочити нотайс з пропозицією поставити `php-common`, тому `php-dom`, `php-fileinfo` здається можна випиляти зі списку, але залишу цю команду з історії bash, як вона є.
|
||||
|
||||
## Рушій
|
||||
|
||||
> В рамках спільноти, також було створено спеціалізований форк, для роботи з локальними поштовими скриньками без DNS. Нижче описана збірка для звичайної (офіційної) версії.
|
||||
|
||||
``` bash
|
||||
cd /var/www
|
||||
```
|
||||
|
||||
``` bash
|
||||
composer create-project flarum/flarum:^2.0.0 --stability=beta
|
||||
```
|
||||
* встановлюємо через Composer
|
||||
|
||||
``` bash
|
||||
cd /var/www
|
||||
```
|
||||
* переходимо до теки проєкту
|
||||
|
||||
``` bash
|
||||
composer update
|
||||
```
|
||||
* оновлюємо реєстр залежностей PHP
|
||||
|
||||
``` bash
|
||||
php flarum install
|
||||
```
|
||||
* встановлюємо базу даних (тут створюється файл `config.php`)
|
||||
* при виконанні команди `php flarum install` і використання драйвера SQLite, важливо вказати `name.sqlite` (з розширенням)
|
||||
* назву хосту лишаємо як є (`localhost`)
|
||||
|
||||
Так як форум спільноти працює на обидві мережі Yggdrasil/Mycelium, у файлі `config.php` я вказав:
|
||||
|
||||
``` config.php
|
||||
'url' => null,
|
||||
```
|
||||
* є ще рецепт: `'url' => '//' . $_SERVER['HTTP_HOST']`, але у такому разі компіляція статики не можлива, щонайменше на версії 2 буде помилка JS, ну і якщо робити то якось так, щоб не було warning при скиданні кешу: `'url' => isset($_SERVER['HTTP_HOST']) ? '//' . $_SERVER['HTTP_HOST'] : null`
|
||||
* якщо встановити `null` то можливі проблеми з редіректами, наприклад, при виході з акаунту
|
||||
* якщо ставите форум для класичного Інтернет - просто вкажіть свій основний хост
|
||||
|
||||
Далі вийшов з цієї теки та надав права веб-серверу, згідно інструкції:
|
||||
|
||||
``` bash
|
||||
chown -R www-data:www-data /var/www/flarum
|
||||
```
|
||||
* тут треба придумати щось з групами, бо від `root` менеджер `composer` пускати не рекомендовано (наприклад, для оновлень рушія чи встановлення нових пакунків)
|
||||
|
||||
## Nginx
|
||||
|
||||
``` /etc/nginx/default
|
||||
server {
|
||||
# для спільноти 443 порт не використовується,
|
||||
# натомість є два "дзеркала" IPv6
|
||||
listen [xxx:xxxx:xxxx:xxxx::x1]:80;
|
||||
listen [xxx:xxxx:xxxx:xxxx::x2]:80;
|
||||
|
||||
server_name xxx:xxxx:xxxx:xxxx::x1 xxx:xxxx:xxxx:xxxx::x2;
|
||||
|
||||
access_log /var/log/nginx/flarum.access.log;
|
||||
root /var/www/flarum/public;
|
||||
index index.php index.html;
|
||||
|
||||
include /var/www/flarum/.nginx.conf;
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
|
||||
include fastcgi_params;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
```
|
||||
* версію сокета PHP замініть актуальною (`php -v`)
|
||||
* шляхи `/var/www/flarum` я пишу з пам'яті, бо довго тягав локацію туди-сюди і зараз вона в мене розташована на окремому, примонтованому диску, тому перевірте відповідність у себе актуальну щоб не було конфузів
|
||||
* тут я ще думаю залочити локацію `/admin` по IP
|
||||
|
||||
## Локалізація
|
||||
|
||||
Пакети локалізації постачаються українською спільнотою Joomla-UA:
|
||||
=> https://github.com/flarum-lang/ukrainian
|
||||
* але схоже що тільки для першої версії, бо встановлення ніяк не позначилось на поточному інтерфейсі.
|
||||
|
||||
## Бекапи
|
||||
|
||||
Я зберігаю дані в SQLite, тому тут все просто:
|
||||
|
||||
``` bash
|
||||
mkdir -p /path/to/flarum/daily
|
||||
crontab -e
|
||||
@daily /usr/bin/rsync -av --delete /path/to/flarum.sqlite /path/to/flarum/daily-dir
|
||||
```
|
||||
* аватарки та інші медіа ще не вияснив де зберігаються, доповню
|
||||
|
||||
## Виявлені проблеми
|
||||
|
||||
### Швидкість
|
||||
|
||||
Рушій здивував своєю тормознутістю. Особливо в режимі дебаг. Можливо, це залежить від SSD та кількості PHP файлів Symfony, які потребують кешування препроцесору. Або ж in-memory драйвера сесій замість БД. Це питання я ще для себе вирішую. Якщо знаєте відповідь - поділіться в коментарях!
|
||||
|
||||
### Зовнішні запити
|
||||
|
||||
Згодом, в консолі були помічені зовнішні запити до кешуючих серверів:
|
||||
|
||||
```
|
||||
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://cdn.jsdelivr.net/gh/s9e/hljs-loader@1.0.36/loader.min.js. (Reason: CORS request did not succeed). Status code: (null).
|
||||
```
|
||||
|
||||
Для Веб - це "нормально", але для оверлейних мереж і спільноти, виправлено так:
|
||||
вимикаємо або вказуємо локальне дзеркало "CDN mirror address" для додатка `flarum-emoji`
|
||||
|
||||
### Тривалість сесій авторизації
|
||||
|
||||
Форум викидає користувача через відносно короткий час (120 хвилин). Рішення проблеми:
|
||||
|
||||
``` php
|
||||
// File <flarum>/extend.php
|
||||
|
||||
use Flarum\Extend;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
|
||||
class MyServiceProvider extends AbstractServiceProvider {
|
||||
public function register () {
|
||||
$this->container->make(ConfigRepository::class)->set('session.lifetime', 43829); // In minutes. Default is 120
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
// Register extenders here to customize your forum!
|
||||
(new Extend\ServiceProvider)
|
||||
->register(MyServiceProvider::class),
|
||||
];
|
||||
```
|
||||
=> https://discuss.flarum.org/d/21562-login-session-timeout/15 є ще один приклад для старішої версії
|
||||
* UPD: згодом змінив `session.lifetime` на `session.gc_maxlifetime`
|
||||
* щоб не робити окремий файл-костиль, можна просто вказати ці опції в `/etc/php/{VERSION}/fpm/php.in` та оновити налаштування командою `systemctl restart php{VERSION}-fpm`
|
||||
|
||||
### Пошта
|
||||
|
||||
Класична електронна пошта (e-mail) - ядро системи облікових записів для цього рушія. В принципі, можна вимкнути (точніше перемкнути пересилання листів до журналу) і вручну створювати користувачів, але вони не зможуть навіть скинути свій пароль без пересилання їм коду підтвердження.
|
||||
|
||||
#### Sendmail
|
||||
|
||||
Підійде у разі, якщо ви плануєте роботу з поштою але у вас з якихось причин немає повноцінного SMTP провайдера, наприклад Google.
|
||||
|
||||
##### Postfix / SMTP
|
||||
|
||||
Нижче показано самий простий приклад, який передбачає опцію налаштування "sendmail" що вказується в адмінці. У цьому випадку, 25 порт є закритим на фаєрволі.
|
||||
|
||||
``` bash
|
||||
apt install postfix
|
||||
```
|
||||
Мій файл конфігурації `/etc/postfix/main.cf` саме для цього ресурсу виглядає так:
|
||||
|
||||
``` /etc/postfix/main.cf
|
||||
myhostname = localhost
|
||||
|
||||
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
|
||||
biff = no
|
||||
append_dot_mydomain = no
|
||||
readme_directory = no
|
||||
smtpd_use_tls=no
|
||||
compatibility_level = 3.6
|
||||
|
||||
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
|
||||
|
||||
alias_maps = hash:/etc/aliases
|
||||
alias_database = hash:/etc/aliases
|
||||
|
||||
mydestination =
|
||||
relayhost =
|
||||
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
|
||||
mailbox_size_limit = 0
|
||||
recipient_delimiter = +
|
||||
inet_interfaces = all
|
||||
inet_protocols = ipv6
|
||||
|
||||
smtpd_etrn_restrictions=reject
|
||||
disable_vrfy_command = yes
|
||||
smtpd_helo_required = yes
|
||||
```
|
||||
* тут `myhostname` - ваш актуальний хост
|
||||
* `inet_protocols` можна вказати як "all" якщо планується взаємодія з Інтернет
|
||||
|
||||
##### Тестування
|
||||
|
||||
Якщо бачите повідомлення:
|
||||
> Flarum currently does not send emails. This can be due to the selected driver, or errors in its configuration.
|
||||
|
||||
то це не критично, в мене відправлення працює й з ним, головне, щоб не було помилок в поп-апах.
|
||||
|
||||
#### Варіант без пошти
|
||||
|
||||
В адмінці є варіант "log" тобто пошта буде надсилатись у файл-заглушку, з якого можна пересилати різні лінки верифікації та інші запити - руками або скриптом. Знаходиться цей журнал в `flarum/storage/logs/*.log`
|
||||
|
||||
Вимкнути самі поля форм без патчу не вийде, в адмінці доступна лише опція вимкнення реєстрацій, таким чином, користувач бачитиме помилку при надсиланні відповідного запиту (так як це зараз).
|
||||
|
||||
## Розробка
|
||||
|
||||
Так як форум спільноти має деякі не характерні фічі, що не мають сенсу для загального PR, з'явився певний досвід по розгортанню власної версії а також може стати в нагоді, якщо вам потрібно використовувати цей рушій для локальної розробки. Як і у випадку з основною документацією, тут є свої нюанси, про які в офіційній версії не сказано:
|
||||
=> https://docs.flarum.org/contributing/#setting-up-a-local-codebase
|
||||
|
||||
Якщо коротко то вам потрібен "скелет" форуму:
|
||||
=> https://github.com/flarum/flarum
|
||||
та сам фреймворк, якийсь в документації також називається "monorepo" і включає в себе різні модулі (для яких також є окремі read-only репозиторії)
|
||||
=> https://github.com/flarum/framework
|
||||
|
||||
Пакунок `framework` у даному прикладі - є вашим об'єктом модифікації, ви також можете забрати його копію з офіційного апстріму.
|
||||
|
||||
Збірка версії рушія для розробників виглядає наступним чином:
|
||||
|
||||
``` bash
|
||||
cd /var/www
|
||||
git clone https://github.com/flarum/framework.git
|
||||
cd framework
|
||||
git checkout yggverse
|
||||
cd ..
|
||||
git clone https://github.com/flarum/flarum.git
|
||||
cd flarum
|
||||
composer config repositories.0 path "/var/www/framework/*/*"
|
||||
composer config minimum-stability dev
|
||||
composer install
|
||||
php flarum install
|
||||
php flarum cache:clear
|
||||
php flarum assets:publish
|
||||
```
|
||||
* замість `php flarum install` може бути `php flarum migrate`
|
||||
|
||||
При оновленні компонентів фронт-енд, важливо (після `yarn install`) зкомпілювати оптимізовані файли дистрибутиву командою `yarn build` для подальшого встановлення в рамках компонента `flarum`. Але для цього вам треба додати такий фікс:
|
||||
=> https://github.com/YGGverse/flarum-framework/commit/b02e3b3600d5c0ff1a613590e72d91e7b9d88111
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://flarum.org Офіційний сайт рушія Flarum
|
||||
=> https://docs.flarum.org/2.x/install Офіційна документація зі встановлення Flarum v2
|
||||
=> https://github.com/YGGverse/flarum-framework/tree/yggverse Редакція фреймворку Flarum від спільноти YGGverse
|
||||
74
public/uk/install-latest-golang-version-on-debian-linux.gmi
Normal file
74
public/uk/install-latest-golang-version-on-debian-linux.gmi
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Встановлення останньої версії Go в Debian
|
||||
|
||||
Це коротенька інструкція для початківців, яка дозволить швидко розгорнути останню версію інфраструктури Go в Debian для збірки програм.
|
||||
|
||||
## Видалення попередньої версії
|
||||
|
||||
Спочатку перевірте, чи не встановлена певна версія Go за допомогою пакетного менеджеру:
|
||||
|
||||
```
|
||||
go version
|
||||
```
|
||||
|
||||
Якщо так, попередньо переконайтесь, що її не використовують інші програми, після чого її можна видалити із системи:
|
||||
|
||||
```
|
||||
apt remove golang
|
||||
```
|
||||
|
||||
Додатково можна перевірити наявність старих файлів та зачистити їх:
|
||||
|
||||
```
|
||||
rm -rf /usr/lib/go-ВЕРСІЯ
|
||||
```
|
||||
|
||||
## Встановлення останньої версії
|
||||
|
||||
Наступні кроки виконуються в домашній теці користувача, від якого планується запуск програми:
|
||||
|
||||
```
|
||||
cd ~
|
||||
```
|
||||
|
||||
Переходимо на сторінку завантажень Go:
|
||||
|
||||
=> https://go.dev/dl
|
||||
|
||||
і обираємо версію для потрібної архітектури:
|
||||
|
||||
```
|
||||
wget https://go.dev/dl/go1.22.1.linux-amd64.tar.gz
|
||||
```
|
||||
|
||||
Розпаковуємо архів, після чого його можна видалити:
|
||||
|
||||
```
|
||||
tar -xzf go1.22.1.linux-amd64.tar.gz
|
||||
rm go1.22.1.linux-amd64.tar.gz
|
||||
```
|
||||
|
||||
Переміщуємо файли до системної теки від "root":
|
||||
|
||||
```
|
||||
sudo mv go /usr/local/
|
||||
```
|
||||
|
||||
Створимо робочу теку Go для поточного користувача:
|
||||
|
||||
```
|
||||
mkdir ~/go
|
||||
```
|
||||
|
||||
Налаштуємо шляхи та перезавантажимо профіль:
|
||||
|
||||
```
|
||||
export PATH=$PATH:/usr/local/go/bin
|
||||
export GOPATH=~/go
|
||||
source .bashrc
|
||||
```
|
||||
|
||||
Перевіряємо результат:
|
||||
|
||||
```
|
||||
go version
|
||||
```
|
||||
50
public/uk/install-latest-rust-version-on-linux.gmi
Normal file
50
public/uk/install-latest-rust-version-on-linux.gmi
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Встановлення останньої версії Rust в Linux
|
||||
|
||||
Користувачі дистрибутивів з довгостроковою підтримкою, ймовірно зіткнуться з проблемою збірки програм з версією Rust, що міститься в репозиторіях.
|
||||
|
||||
Вирішується це досить просто, за допомогою утиліти Rustup:
|
||||
|
||||
=> https://www.rust-lang.org/tools/install
|
||||
|
||||
Спочатку потрібно видалити встановлені раніше версії, разом з їх залежностями.
|
||||
|
||||
Наприклад, в Debian:
|
||||
|
||||
```
|
||||
apt remove cargo rustc
|
||||
apt autoremove
|
||||
```
|
||||
|
||||
Наступні кроки виконуються від того користувача, від якого планується подальша робота з програмою.
|
||||
|
||||
Таким чином, пакети будуть стандартно встановлюватись до теки "~/.cargo"
|
||||
|
||||
Після введення наступної команди слідуємо підказкам:
|
||||
|
||||
```
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
```
|
||||
|
||||
Оновити пакети до останньої версії можна командою:
|
||||
|
||||
```
|
||||
rustup update
|
||||
```
|
||||
|
||||
Якщо потрібно зібрати будь яку програму на більш ранній версії, наприклад "1.63" - достатньо встановити її поряд з іншою:
|
||||
|
||||
```
|
||||
rustup install 1.63
|
||||
```
|
||||
|
||||
Для збірки програми, вказуємо відповідну версію компілятора:
|
||||
|
||||
```
|
||||
cargo +1.63 build --release
|
||||
```
|
||||
|
||||
Щоб видалити встановлені таким чином "cargo", "rustc" включно з "rustup":
|
||||
|
||||
```
|
||||
rustup self uninstall
|
||||
```
|
||||
123
public/uk/kevacoin-decentralized-database-in-blockchain.gmi
Normal file
123
public/uk/kevacoin-decentralized-database-in-blockchain.gmi
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
# KevaCoin - децентралізована база даних в блокчейн
|
||||
|
||||
Існує багато криптовалютних проєктів, серед яких часто лишаються непоміченими дійсно цікаві реалізації.
|
||||
|
||||
Одним з таких є децентралізована перманентна база даних key/value типу - KevaCoin:
|
||||
|
||||
=> https://kevacoin.org
|
||||
|
||||
## Статус
|
||||
|
||||
Забігаючи вперед, скажу що монета KevaCoin (KVA) у її поточному статусі і ймовірних перспективах - не буде цікавою трейдерам, оскільки була давно знята з торгівлі, проєкт покинули інвестори, а останній коміт датується 20 березня 2020 року.
|
||||
|
||||
Щодо розробника є лише наступна інформація:
|
||||
|
||||
=> https://bitcointalk.org/index.php?topic=5213046.msg63660824#msg63660824
|
||||
|
||||
> According to the information disclosed by the community moderator, the developer/administrator of the project lost contact completely after the outbreak of COVID-19.
|
||||
|
||||
Середній хешрейт мережі складає приблизно 300 kH/s, кілька років тому - близько 700 kH/s, тому зберігається впевнена тенденція до колапсу (хоча деякі монети в такому стані можуть перебувати досить довго, поки не знайдуть новий імпульс).
|
||||
|
||||
Частка потужності мережі при цьому іноді сягає 100% для одного пулу, що має застерегти від використання її у якості стабільного продукту.
|
||||
|
||||
## Застосування
|
||||
|
||||
Чому я вирішив написати про KevaCoin?
|
||||
|
||||
Не зважаючи на свій статус і спірне майбутнє, це доволі цікавий проєкт, з основною метою зробити зберігання даних в блокчейн максимально простим і універсальним.
|
||||
|
||||
Монета може бути цікавою розробникам децентралізованих продуктів у якості пісочниці, в певній мірі може використовуватись як NFT - оскільки реалізує функції трансферу, може зробити ваш проєкт "блокчейновим" у якості маркетингового ходу з реальним підгрунтям. Також, враховуючи практично нульову вартість і доступність майнингу на звичайному ПК, може стати альтернативним сховищем для зашифрованих бекапів, або як мікроблог для відкритих текстів, де наприклад "namespace" - може бути назвою, "key" - заголовком, а "value" - текстом публікації.
|
||||
|
||||
Стосовно останнього варіанту, здебільшого вона так і використовується - у якості мікроблогів, тому недавно було прийняте рішення оновити закинутий експлорер контенту і читати мало кому відомі дописи, які не зустрінеш у класичному веб-просторі.
|
||||
|
||||
Детальніше про експлорер на сторінці репозиторію:
|
||||
|
||||
=> https://github.com/kvazar-network/webapp Версія для перегляду в браузері HTTP
|
||||
=> https://github.com/kvazar-network/geminiapp Версія для протоколу Gemini
|
||||
|
||||
Недавно також "just-for-fun" було творено чат в блокчейн:
|
||||
|
||||
=> https://github.com/orgs/kevachat/repositories KevaChat
|
||||
|
||||
Утім, з появою спамерів, повідомлення стали платними, що з цього вийде - не знаю, але буває і таке :)
|
||||
|
||||
## Архітектура
|
||||
|
||||
По суті, це форк LiteCoin, на базі PoW алгоритму RandomX, орієнтованого на майнинг з використанням CPU.
|
||||
|
||||
Ядро включає в себе розширення стандартного способу зберігання повідомлень в транзаціях у форматі base58, схоже до того, як це реалізовано в BitCoin.
|
||||
|
||||
Не зважаючи на те, що база даних декларується як CRUD, фактично до нових блоків додається лише мета-маркер "KEVA_PUT", "KEVA_DELETE" тощо, а дані в попередніх блоках - клієнтами не видаляються. Таким чином, щось відправивши до блокчейну KevaCoin, це залишиться там перманентно.
|
||||
|
||||
Довжина ключа складає 255 байт, значення - обмежене 3072 байтами.
|
||||
В принципі, за допомогою спеціальних утиліт, можна "зклеювати" дані різних транзакцій в один файл, таким чином зберігати зображення і навіть відео.
|
||||
Утім, максимальна кількість транзакцій від одного публічного ключа на блок становить 20, тому це якоюсь мірою запобігає зловживанням, навіть при мінімальній вартості - шляхом обмеження відправника у часі.
|
||||
|
||||
Окрім графічного інтерфейсу, з гаманцем можна взаємодіяти за допомогою командного рядка:
|
||||
|
||||
=> https://kevacoin.org/documentation.html
|
||||
|
||||
Повний список команд можна отримати командою "help":
|
||||
|
||||
```
|
||||
...
|
||||
keva_delete "namespace" "key"
|
||||
keva_filter ("namespaceId" ("regexp" ("from" ("nb" ("stat")))))
|
||||
keva_get "namespace" "key"
|
||||
keva_group_filter ("namespaceId" ("initiator" "regexp" ("from" ("nb" ("stat")))))
|
||||
keva_group_get "namespace" "key" "initiator"
|
||||
keva_group_join "my_namespace" "other_namespace"
|
||||
keva_group_leave "my_namespace" "other_namespace"
|
||||
keva_group_show ("namespaceId" ("regexp" ("from" ("nb" ("stat")))))
|
||||
keva_list_namespaces
|
||||
keva_namespace "display_name"
|
||||
keva_pending ("namespace")
|
||||
keva_put "namespace" "key" "value" "address"
|
||||
...
|
||||
```
|
||||
|
||||
Для створення веб додатків, є бібліотека для PHP:
|
||||
|
||||
=>https://github.com/kevachat/kevacoin-php
|
||||
|
||||
## Майнинг
|
||||
|
||||
Монету легко отримати шляхом майнингу, наприклад з використанням майнеру xmrig:
|
||||
|
||||
=> https://xmrig.com
|
||||
|
||||
Типова команда виглядає так:
|
||||
|
||||
```
|
||||
xmrig --url pool.hashvault.pro:80 --user WALLET --pass x --donate-level 0 --tls --tls-fingerprint ID
|
||||
```
|
||||
|
||||
Щоб використовувати нульову комісію через "--donate-level 0", потрібно зібрати "xmrig" самостійно, попередньо змінивши відповідний код:
|
||||
|
||||
```
|
||||
#src/donate.h
|
||||
constexpr const int kDefaultDonateLevel = 0;
|
||||
constexpr const int kMinimumDonateLevel = 0;
|
||||
```
|
||||
|
||||
Якщо не планується використання пулу, цілком досяжно здобувати монету в режимі соло, скориставшись офіційною інструкцією:
|
||||
|
||||
=> https://kevacoin.org/tutorial_solo_mining.html
|
||||
|
||||
## Висновки
|
||||
|
||||
Шансів у цієї монети "захопити світ" немає, утім вона і досі виконує свою базову функцію - децентралізоване зберігання даних.
|
||||
Навідміну від Ethereum, де враховуючи вартість, транзакції замінюються централізованими прошарками веб сервісів, тут можна зберігати дані фактично безкоштовно.
|
||||
|
||||
Після кількох років цікавості до проєкту з точки зору децентралізації, а не трейдингу, маю свою критику, яка нажаль лишилась без відповідей.
|
||||
Іноді повертаються думки зробити форк, знявши ліміти на значення, або збільшити їх до 1 Мб. Це б дозволило наприклад використовувати блокчейн для бекапу веб сторінок, які нажаль з плином часу зникають з мережі.
|
||||
|
||||
Можливо, історія знайде продовження а можливо - часи цікавих децентралізованих проєктів просто минули.
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://kevacoin.org Офіційний сайт
|
||||
=> https://github.com/kevacoin-project/kevacoin Проєкт KevaCoin на GitHub
|
||||
=> https://miningpoolstats.stream/kevacoin Активні пули
|
||||
=> https://keva.one Експлорер блоків
|
||||
=> https://kvazar.duckdns.org Експлорер контенту
|
||||
172
public/uk/libhandy-tab-as-gtk-notebook-widget-alternative.gmi
Normal file
172
public/uk/libhandy-tab-as-gtk-notebook-widget-alternative.gmi
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
# HdyTab як альтернатива віджету вкладок GtkNotebook
|
||||
|
||||
Якщо ви тільки починаєте своє знайомство з фреймворком GTK, можливо, як і в мене, виникне схожа дилема з обмеженою функціональністю класичного віджету для створення вкладок GtkNotebook. В мережі віднайшов матеріал 2021 року "Reinventing tabs", в якому розглядається проблема стандартного віджету та пропонується альтернатива - HdyTab.
|
||||
|
||||
* даний матеріал є остаточно застарілим, якщо ви досі шукаєте заміну GtkNotebook, зверніть увагу на бібліотеку Libadwaita, зокрема віджет TabBar, який реалізує клас TabView
|
||||
|
||||
## Переосмислення вкладок
|
||||
|
||||
В робочому столі GNOME 40, браузер Epiphany має нову панель вкладок. Це не просто переробка старої панелі, а повне її переписування з нуля:
|
||||
|
||||
=> https://blogs.gnome.org/alicem/files/2021/03/ephy.png Вигляд вкладок в Epiphany
|
||||
|
||||
Але для чого це було необхідно?
|
||||
|
||||
## Статичні та динамічні вкладки
|
||||
|
||||
Додатки GNOME використовують вкладки різними способами, їх можна приблизно поділити на дві категорії.
|
||||
|
||||
### Статичні вкладки
|
||||
|
||||
=> https://blogs.gnome.org/alicem/files/2021/03/dialog-tabs-768x645.png Вигляд статичних вкладок в різних діалогах
|
||||
|
||||
Вони є повністю незмінними, кожна вкладка містить заголовок і зазвичай не прокручуються. Зазвичай їх використовують у діалогових вікнах, але, наприклад, їх також можна використовувати в панелях інструментів. Сучасні додатки часто використовують GtkStackSwitcher або HdyViewSwitcher, але GtkNotebook все ще досить поширений.
|
||||
|
||||
### Динамічні вкладки
|
||||
|
||||
=> https://blogs.gnome.org/alicem/files/2021/03/browser-tabs.png Вигляд gedit, GNOME Terminal, Nautilus, GNOME Editor, Devhelp, Sysprof, Epiphany
|
||||
|
||||
Цей тип вкладок використовується в багатовіконних додатках, таких як веб-браузери, текстові редактори, файлові менеджери або емулятори терміналів. Вони відкриваються та закриваються користувачем, їх можна змінювати за порядком, перетягувати між вікнами або в нові вікна, скидаючи їх на робочий стіл. Вони часто реалізують прокрутку. Завжди мають заголовок і кнопку закриття, іноді іконку та/або індикатор завантаження. Вони ховаються, коли присутня лише одна вкладка. Їх можна закривати середнім кліком або через контекстне меню. Іноді, динамічні вкладки можна закріпити або додати іконку, наприклад, індикатор відтворення аудіо чи діалог підтвердження закриття.
|
||||
|
||||
### Чому така різниця?
|
||||
|
||||
Хоча статичні вкладки в GNOME досить однорідні, динамічні вкладки в різних додатках виглядають і відчуваються дуже по-різному.
|
||||
|
||||
Наприклад:
|
||||
|
||||
* в GNOME Text Editor - кнопки закриття є лише в обраних вкладках, або при наведені на вкладку, тоді як в інших додатках - вони присутні на всіх вкладках
|
||||
* деякі додатки дозволяють вкладкам зменшуватися до малого розміру перед прокруткою, інші починають прокручуватися раніше
|
||||
* Devhelp - взагалі не прокручує вкладки, а просто виводить їх за межі вікна
|
||||
* Epiphany та GNOME Terminal - мають спливаючі вікна, що показують усі ці вкладки; GNOME Terminal використовує GtkMenu, тоді як Epiphany - використовує GtkPopover
|
||||
* хоча це не видно на скріншоті, лише Epiphany та gedit підтримують перетягування вкладок між вікнами
|
||||
* У Epiphany та Nautilus - можна перетягувати файли на вкладки. У Nautilus, якщо ви наведете курсор на вкладку під час перетягування файлу, вона автоматично переключиться на цю вкладку, але не в Epiphany
|
||||
* У gedit та GNOME Terminal - неможливо зосередити фокус на активній вкладці, натискаючи на неї знову
|
||||
* У GNOME Text Editor та Epiphany - при переході на іншу вкладку, коли відбувся фокус на активну вкладку, її фокус буде здійснено на сторінку а не саму вкладку
|
||||
|
||||
### GtkNotebook
|
||||
|
||||
Усі додатки вище використовують віджет під назвою GtkNotebook. Він реалізує загальний вигляд з вкладками та показує вкладки з одного боку, причому кожна вкладка має заголовок, який можна замінити на будь-який інший віджет.
|
||||
|
||||
За замовчуванням GtkNotebook не має засобів прокручування, має простий заголовок на кожній вкладці, не розширює вкладки та не дозволяє жодного виду перетягування.
|
||||
|
||||
Іншими словами, він орієнтований виключно на статичні вкладки. Це не означає, що його неможливо використовувати для динамічних вкладок, але вам доведеться вручну реалізувати наступне:
|
||||
|
||||
* макет вкладок з кнопкою закриття, заголовком, іконкою та, можливо, індикатором беззвучного режиму, а також встановити розумну мінімальну ширину для прокрутки
|
||||
* гарячі клавіші, такі як Ctrl+Tab або Alt+[1–9] для перемикання між вкладками
|
||||
* обробку контекстного меню, включаючи гарячі клавіші та жест довгого натискання для сенсорних екранів
|
||||
* закриття середнім кліком
|
||||
* логіку відкриття/закриття вкладок - наприклад, яку вкладку вибрати, коли активна вкладка закрита
|
||||
* якщо потрібно скинути дані на вкладки, обробник для цього. Також потрібно бути обережним, щоб випадково не зламати тайм-аут наведення в процесі, як це робить Epiphany у версії 3.38.x
|
||||
* авто-приховування контейнера, коли в ньому присутня лише одна вкладка
|
||||
|
||||
Ці вимоги підкреслюють, що хоча GtkNotebook може бути використаний для динамічних вкладок, його базова реалізація не підтримує багато функцій, які користувачі очікують від сучасних динамічних вкладок, і вимагає значних зусиль для налаштування.
|
||||
|
||||
Звісно, кожен додаток реалізує ці функції трохи по-різному. А ще є проблеми, які не можна повністю вирішити з боку додатка, наприклад, відсутня функціональність закріплених вкладок або інтерфейс загалом не дуже відшліфований - не має анімації при відкритті або закритті вкладок.
|
||||
|
||||
Також, прокручування вкладок є дискретним, це виглядає заплутано і викликає інші проблеми:
|
||||
* динамічні вкладки можуть мати велику кількість вкладок, натискання стрілок для прокрутки по одній вкладці за раз не є ефективним у цьому випадку
|
||||
* пов'язано з цим, неможливо взяти вкладку і перемістити її на великі відстані; вам доведеться перемістити її на кілька вкладок вперед, натиснути кнопки прокрутки кілька разів, знову перемістити тощо
|
||||
* при відкритті нової вкладки вона може опинитися за межами екрану без очікування такої поведінки
|
||||
* мінімальна ширина вкладки змінюється в залежності від розміру вікна
|
||||
|
||||
Ці проблеми підкреслюють, що, хоча додатки можуть намагатись реалізувати динамічні вкладки засобами статичного віджета GtkNotebook, існують обмеження, які ускладнюють створення зручного та інтуїтивно зрозумілого досвіду для користувачів.
|
||||
|
||||
Деякі з таких проблем можна замаскувати:
|
||||
|
||||
* Epiphany імітує закріплені вкладки, примусово переміщуючи їх на початок, використовуючи компактний макет, не розширюючи їх і не дозволяючи закривати. Однак ці вкладки все ще можуть бути прокручені, і те, що ширина вкладок змінюється в залежності від розміру вікна, особливо негативно впливає на зовнішній вигляд:
|
||||
|
||||
=> https://blogs.gnome.org/alicem/files/2021/03/ephy-pinned1-768x82.png Вигляд компактно закріплених вкладок
|
||||
=> https://blogs.gnome.org/alicem/files/2021/03/ephy-pinned2-768x78.png Вигляд закріплених вкладок при зміні ширини вікна
|
||||
|
||||
* Epiphany вручну підсвічує стрілки, коли вкладка була відкрита за межами екрана, також надає спливаюче вікно для швидкого перемикання між вкладками, коли вони не вміщаються на екрані, однак це не допомагає з перестановкою вкладок
|
||||
* динамічний віджет DynamicNotebook (https://valadoc.org/granite/Granite.Widgets.DynamicNotebook.html) в Elementary реалізує затримку зміни розміру після закриття вкладки, але це не завжди надійно; наприклад, він стрибає, коли стрілки прокрутки зникають, крім того, він може працювати лише тоді, коли вкладки не розширені
|
||||
* ці особливості підкреслюють, що, хоча обидва додатки намагаються покращити управління вкладками, існують обмеження, які можуть призвести до непередбачуваного або незручного досвіду для користувачів; підсвічування стрілок може бути корисним, але не вирішує основну проблему з доступом до вкладок, які відкриваються за межами екрана; затримка зміни розміру може бути корисною, але її ненадійність може викликати додаткові труднощі
|
||||
|
||||
Крім того, GtkNotebook містить як вміст, так і вкладки, і вони є невід'ємними, тому неможливо мати вкладки у повноекранному режимі, автоматично приховуючи їх з заголовної панелі, або розміщувати їх у панелі заголовку, відокремлено від вмісту.
|
||||
|
||||
Нарешті, GtkNotebook не є адаптивним, і Epiphany наразі має вбудовану реалізацію мобільного перемикача вкладок, яку кожен додаток, що прагне адаптивного інтерфейсу з вкладками - повинен буде копіювати.
|
||||
|
||||
Ці обмеження підкреслюють, що GtkNotebook не забезпечує гнучкості, необхідної для сучасних інтерфейсів, які можуть адаптуватися до різних розмірів екранів і режимів використання. Відсутність можливості розміщення вкладок окремо від вмісту обмежує можливості дизайну, а необхідність копіювати реалізацію мобільного перемикача вкладок може призвести до дублювання зусиль і несумісності між додатками.
|
||||
|
||||
## HdyTab як рішення
|
||||
|
||||
=> https://blogs.gnome.org/alicem/files/2021/03/tab-view-demo-768x128.png Як виглядають вкладки HdyTab
|
||||
|
||||
Отже, з урахуванням усіх цих обмежень, я вважаю, що найчистішим шляхом вперед є створення абсолютно нового віджета, який реалізує специфічно динамічні вкладки. Таким чином, також зможемо відокремити перегляд вкладок і панель вкладок в окремі віджети, так само як GtkStack і GtkStackSwitcher - є окремими.
|
||||
|
||||
Реалізацію цього віджета було розпочато незадовго до GUADEC 2020 року, і до кінця вересня, на момент долучення до Purism (https://puri.sm) - її було майже завершено. У січні ми з Адрієном нарешті знайшли час, щоб закінчити, переглянути та впровадити його.
|
||||
|
||||
Результатом цього стали два віджети під назвою HdyTabView і HdyTabBar, доступні в бібліотеці libhandy, починаючи з версії 1.2:
|
||||
|
||||
=> https://gnome.pages.gitlab.gnome.org/libhandy
|
||||
|
||||
HdyTabView містить GtkStack і надає API, більш придатний для динамічних вкладок: наприклад, сторінки відтепер строго впорядковані, є API для перестановки вкладок тощо. Крім того, кожна сторінка має велику кількість метаданих, вона використовує об'єкти сторінок, схожі на GTK 4 (HdyTabPage), навіть у GTK 3, замість властивостей дочірніх елементів, які було б досить незручно використовувати без прив'язок властивостей.
|
||||
|
||||
HdyTabBar є панеллю вкладок, яка підключається до HdyTabView і відображає його сторінки, реалізуючи цей дизайн:
|
||||
|
||||
=> https://gitlab.gnome.org/Teams/Design/os-mockups/-/raw/master/tabs/tabs.png Вигляд різних реалізацій вкладок
|
||||
|
||||
### Що він забезпечує
|
||||
|
||||
* вкладки мають іконку, заголовок, підказку, кнопку закриття, контекстне меню, і вони можуть показувати індикатор завантаження замість іконки
|
||||
* закріплені вкладки: вони компактні, розташовані на початку панелі вкладок і не прокручуються разом з іншими вкладками; вони також впливають на поведінку гарячих клавіш, таких як Ctrl+Home/End
|
||||
* індикатори непрочитаних повідомлень, включаючи випадки, коли непрочитана вкладка прокручується за межі екрану:
|
||||
|
||||
=> https://blogs.gnome.org/alicem/files/2021/03/tabs-unread-1024x156.png Вигляд непрочитаних вкладок
|
||||
|
||||
* піктограми-індикатори: кожна сторінка може мати одну піктограму-індикатор на вкладці, опціонально доступну для натискання; Epiphany використовує його як індикатор відтворення аудіо/кнопку вимкнення звуку, а індикатори - розташовані зліва від вкладки, щоб не конфліктувати з кнопками закриття:
|
||||
|
||||
=> https://blogs.gnome.org/alicem/files/2021/03/tabs-indicators-768x109.png Вигляд індикаторів вкладок
|
||||
|
||||
Коли вкладка закріплена, індикатор відображається замість іконки, а не поруч з нею, як це було раніше в Epiphany. Щоб забезпечити легкий вибір вкладки, вона є клікабельною лише тоді, коли вкладка вже вибрана, тому перший клік вибирає вкладку, а другий клік - активує індикатор.
|
||||
|
||||
* автоматичне приховування, коли присутня лише одна вкладка
|
||||
* при закритті вкладок за допомогою вказівника, вони не заповнюють порожній простір одразу, натомість вони змінюють розмір так, щоб кнопка закриття наступної вкладки перемістилася точно туди, де була остання, що дозволяє закривати кілька вкладок поспіль, не переміщуючи вказівник
|
||||
* автоматичне позиціонування для нових вкладок, хоча також є API для вставки вкладок у довільні позиції, якщо це потрібно
|
||||
* повноцінна підтримка перетягування: вкладки можна переставляти, переміщувати між вікнами, скидувати на робочий стіл для створення нового вікна, вкладки можуть приймати довільні дані, які скидаються; якщо панель вкладок має лише одну вкладку, вона автоматично відображається, коли починається перетягування, і приховується, коли воно закінчується, тому все ще можливо скинути іншу вкладку в неї
|
||||
* вкладки також можна скинути на перегляд вкладок; в цьому випадку вони додаються в кінець панелі вкладок
|
||||
* підтримка сенсорних екранів: перетягніть панель вкладок, щоб прокрутити її, довге натискання і перетягування для перестановки вкладки, довше натискання для відкриття контекстного меню
|
||||
* API підтвердження закриття
|
||||
* відкриття, закриття, перестановка вкладок, перетягування, показування та приховування панелі вкладок - анімовані
|
||||
* гарячі клавіші:
|
||||
|
||||
- Ctrl+Tab і Shift+Ctrl+Tab - перемикання між вкладками з обгортанням
|
||||
- Ctrl+PageUp/PageDown/Home/End - те ж саме, але без обгортання
|
||||
- Ctrl+Shift+PageUp/PageDown/Home/End - перестановка вкладок
|
||||
- Alt+[1–9] - перемикання на одну з перших 9 вкладок
|
||||
|
||||
Той факт, що вони є окремими, дозволяє Epiphany зберігати свою панель вкладок у повноекранному режимі в 40 версії:
|
||||
|
||||
=> https://blogs.gnome.org/alicem/files/2021/03/ephy-fullscreen-768x51.png Вигляд вкладок у повноекранному режимі Epiphany
|
||||
|
||||
### Чого він не забезпечує
|
||||
|
||||
* фіксований макет вкладок: HdyTabView не дозволяє встановлювати довільний віджет як макет вкладок; макет є фіксованим і керується виключно через властивості HdyTabPage
|
||||
* відсутність окремого управління перестановкою та від'єднанням: не можна окремо включати або вимикати перестановку та від'єднання для різних вкладок; вкладки завжди можна переставляти та від'єднувати, і немає можливості це вимкнути
|
||||
* відсутність контролю над розширенням вкладок: аналогічно, немає можливості зробити так, щоб певні вкладки розширювалися або не розширювалися, усі вкладки (за винятком закріплених) за замовчуванням - розширені
|
||||
* кнопки закриття: у всіх вкладках (за винятком закріплених) є кнопки закриття, їх не можна видалити, хоча можливо затримати та/або відхилити запит на закриття, наприклад, якщо додаток хоче показати діалог підтвердження при закритті вкладки
|
||||
* видимість кнопок закриття: кнопки закриття видимі лише на вибраних та/або наведені вкладки, немає можливості показувати їх на всіх вкладках
|
||||
* вертикальні вкладки: HdyTabBar є строго горизонтальним, і його макет не має сенсу у вертикальному вигляді, однак він має API для спостереження за своїми сторінками через GListModel (GtkSelectionModel у GTK 4), тому дуже легко створити вертикальний список замість цього
|
||||
|
||||
Оскільки Epiphany також використовується в Elementary OS, HdyTabBar надає API для вимкнення автоматичного приховування та розширення вкладок, а також для зміни місцями кнопки закриття та індикатора. Усі ці параметри все ще працюють так, як і раніше.
|
||||
|
||||
=> https://blogs.gnome.org/alicem/files/2021/03/tabs-elementary-768x84.png Вигляд Epiphany в "елементарному режимі"
|
||||
|
||||
### Адаптивність
|
||||
|
||||
HdyTabBar поки що не є адаптивним. Epiphany все ще постачає той самий мобільний інтерфейс у версії 40. Однак це значно полегшує реалізацію альтернативних комутаторів, наприклад, нижній аркуш на основі HdyFlap:
|
||||
|
||||
=> https://blogs.gnome.org/alicem/files/2021/03/tabs-adaptive-gtk3-603x1024.png Вигляд перемикача вкладок для мобільних пристроїв у стадії розробки
|
||||
|
||||
Однак це дуже базово. Мобільні перемикачі вкладок часто є дуже складними, наприклад, використовують 3D-стеки карток, сітки, каруселі або списки з попередніми переглядами. На жаль, усе це недоступно в GTK 3. Однак у GTK 4 це можливо, і ми можемо робити такі речі:
|
||||
|
||||
=> https://blogs.gnome.org/alicem/files/2021/03/tabs-overview-carousel-603x1024.png Вигляд вкладок у режимі каруселі
|
||||
=> https://blogs.gnome.org/alicem/files/2021/03/tabs-overview-grid-1024x904.png Вигляд вкладок у режимі сітки
|
||||
|
||||
Маючи належний огляд, можна буде нарешті видалити спливаюче вікно вкладок з Epiphany, яке все ще є в 40 версії, але показується лише тоді, коли панель вкладок починає прокручуватися.
|
||||
|
||||
Порт HdyTabView і HdyTabBar на GTK 4 завершено і повністю працює, і йому просто потрібен код-рев'ю, хоча огляд ще не є повторно використовуваним віджетом.
|
||||
|
||||
## Джерело
|
||||
|
||||
=> https://blogs.gnome.org/alicem/2021/03/13/reinventing-tabs/ Reinventing tabs, Alice Mikhaylenko's GNOME blog
|
||||
162
public/uk/manticore-as-modern-alternative-to-sphinx-search.gmi
Normal file
162
public/uk/manticore-as-modern-alternative-to-sphinx-search.gmi
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
# Manticore як сучасна альтернатива Sphinx
|
||||
|
||||
Багато хто використовує для повнотекстового пошуку рушій Sphinx:
|
||||
|
||||
=> https://sphinxsearch.com
|
||||
|
||||
і пише для нього нові програми, але мало хто знає про його сучасну реалізацію - Manticore:
|
||||
|
||||
=> https://manticoresearch.com
|
||||
|
||||
Класично, сферою застосування може бути пошуковий сервер для сайту, будь якої бази даних, індекс кравлера інтернет / локальної мережі, тощо.
|
||||
|
||||
У той час, як Sphinx версії 3 і досі має закритий код, Manticore - це форк відкритого Sphinx 2.
|
||||
|
||||
Наразі активно розробляється, має живу спільноту і містить багато доповнень, особливо в плані автоматизації real-time індексів.
|
||||
|
||||
Окрім того, Manticore у більшості випадків може замінити собою сервер PostgreSQL або MySQL, де не потрібна гнучка вибірка і багаторівневі запити.
|
||||
|
||||
Таким чином, можна значно заощадити на використанні жорсткого диску для великих індексів а також зменшити використання пам'яті у разі, якщо MySQL використовується просто як основна база даних (до якої раніше звертався Sphinx через document ID).
|
||||
|
||||
## Встановлення
|
||||
|
||||
На прикладі Debian / Linux, доволі просте:
|
||||
|
||||
```
|
||||
wget https://repo.manticoresearch.com/manticore-repo.noarch.deb
|
||||
dpkg -i manticore-repo.noarch.deb
|
||||
apt update
|
||||
apt install manticore manticore-extra
|
||||
```
|
||||
|
||||
Сервіс одразу буде доступний через systemd:
|
||||
|
||||
```
|
||||
systemctl start manticore
|
||||
```
|
||||
|
||||
Працює сервер на порті `9308`
|
||||
|
||||
Після запуску, можна підключитись клієнтом MySQL і взаємодіяти через SphinxQL:
|
||||
|
||||
=> http://sphinxsearch.com/docs/current/sphinxql-reference.html
|
||||
|
||||
```
|
||||
mysql -P9306 -h0
|
||||
```
|
||||
|
||||
В рамках цього матеріалу не будуть описані детальні мануали.
|
||||
|
||||
Оскільки проєкт має хорошу документацію:
|
||||
|
||||
=> https://manual.manticoresearch.com/Introduction
|
||||
|
||||
досвідчені користувачі Sphinx з легкістю можуть застосовувати свій досвід, наприклад в "searchd", що має таку само архітектуру, а синтаксис "extended-search":
|
||||
|
||||
=> https://sphinxsearch.com/docs/latest/extended-syntax.html
|
||||
|
||||
- повністю сумісний.
|
||||
|
||||
## Інтеграція
|
||||
|
||||
Проєкт має багато офіційних бібліотек для різних мов програмування, зокрема:
|
||||
|
||||
=> https://github.com/manticoresoftware/manticoresearch-php
|
||||
|
||||
Розгортання програми з цією бібліотекою настільки просте, що зводиться до наступних рядків:
|
||||
|
||||
```
|
||||
composer require manticoresoftware/manticoresearch-php
|
||||
```
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
$client = new \Manticoresearch\Client(
|
||||
[
|
||||
'host' => 'localhost',
|
||||
'port' => 9308
|
||||
]
|
||||
);
|
||||
|
||||
$index = $client->index(
|
||||
'test_index'
|
||||
);
|
||||
|
||||
$index->create(
|
||||
[
|
||||
'field1' => [ 'type' => 'text' ],
|
||||
'field2' => [ 'type' => 'text' ]
|
||||
]
|
||||
);
|
||||
|
||||
$index->addDocument(
|
||||
[
|
||||
'field1' => 'hello',
|
||||
'field1' => 'world'
|
||||
],
|
||||
);
|
||||
|
||||
$results = $index->search('hello')->get();
|
||||
```
|
||||
|
||||
Кому цікавий приклад програми на PHP, може відвідати сторінку дуже простого кравлера з веб інтерфейсом:
|
||||
|
||||
=> https://github.com/YGGverse/Yo
|
||||
|
||||
який був написаний в максимально короткі терміни для мережі Yggdrasil:
|
||||
|
||||
=> yggdrasil-is-network-with-distributed-routing.gmi Yggdrasil - мережа з децентралізованим роутингом
|
||||
|
||||
Наразі містить в своєму індексі близько 10.000.000 сторінок, при цьому потужності серверу рівні мінімальному тарифу VPS за 5 євро.
|
||||
|
||||
Для роботи з SphinxSQL в PHP зручно використовувати "php-pdo"; для фільтрації деяких текстових полів - можна додати окреме поле "crc32", також такий підхід можна використовувати у якості Document ID (коли є потреба зменшити об'єм даних і не критична точність)
|
||||
|
||||
## Бекапи
|
||||
|
||||
Декілька слів про бекапи: їх традиційно два типи - логічний і фізичний.
|
||||
|
||||
### Логічний бекап
|
||||
|
||||
Дозволяє генерувати SQL дампи, які наприклад зручно читати, але вони потребують більше ресурсів, тому виконуються рідше.
|
||||
Приклад створення дампу буде таким:
|
||||
|
||||
```
|
||||
mysqldump -h0 -P9306 manticore test_index > test_index.sql
|
||||
```
|
||||
|
||||
### Фізичний бекап
|
||||
|
||||
Такий спосіб передбачає копіювання "сирих" даних до відповідної теки, не вимагає багато ресурсів на обчислення і виконується частіше:
|
||||
|
||||
```
|
||||
/usr/bin/manticore-backup --backup-dir=/home/backup/manticore
|
||||
```
|
||||
|
||||
## Власний досвід
|
||||
|
||||
Працювати з Manticore одне задоволення, що доводилось робити руками в Sphinx - вже працює з коробки.
|
||||
|
||||
Маю не перший проєкт на цьому рушії, подекуди повністю відмовився від MySQL, хоч іноді це не дуже зручно, але більше не витрачаю свій час на
|
||||
проєктування в MySQL Workbench і віддаю перевагу прямому зберіганню даних до індексу в менш нормалізованому, але більш швидкому в розробці форматі.
|
||||
|
||||
Єдине, що б радив зробити, це перемкнути стандартний рівень "binlog" в режим "1", інакше можуть бути незручності з останніми транзакціями, якщо ваш сервер чутливий до вимикання світла, а транзакції відбуваються часто (у разі ймовірності отримати збій в посeкундному режимі зберігання).
|
||||
|
||||
Зробити це можна у файлі:
|
||||
|
||||
``` /etc/manticoresearch/manticore.conf
|
||||
searchd {
|
||||
...
|
||||
binlog_flush = 1
|
||||
}
|
||||
```
|
||||
|
||||
=> https://manual.manticoresearch.com/Logging/Binary_logging#Binary-flushing-strategies Детальніше про стратегії бінарних журналів
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://manticoresearch.com Офіційний сайт Manticore
|
||||
|
||||
## Дивіться також
|
||||
|
||||
=> manticore-as-primary-database-usage-issues.gmi Незручні моменти в роботі з Manticore як основної БД
|
||||
107
public/uk/manticore-as-primary-database-usage-issues.gmi
Normal file
107
public/uk/manticore-as-primary-database-usage-issues.gmi
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# Незручні моменти в роботі Manticore як основної БД
|
||||
|
||||
В роботі з Manticore, є деякі моменти, які можуть відверто вибісити особливо тих, хто планує повний перехід з класичних баз даних типу MySQL та використання її, як основної бази даних.
|
||||
|
||||
## Відсутність нормалізації даних
|
||||
|
||||
Перше, з чим може зіткнутись користувач реляційних баз даних, який вирішив зберігати усі дані в Manticore - неможливість розбити дані по таблицям таким чином, щоб збудувати їх подальші нормалізовані зв'язки по зовнішнім ключам.
|
||||
|
||||
Здебільшого вся база конкретного проєкту буде зберігатись в одній таблиці і варто бути готовим сприймати цю базу як по-рядкове сховище "key/value".
|
||||
А всі дані в ній - простий масив індексів.
|
||||
|
||||
Тому на етапі проєктування, можна забути про UML, та MySQL Workbench - логіка бази даних в результаті буде повністю відрізнятись від класичної моделі зберігання SQL.
|
||||
|
||||
## Вибірка тексту
|
||||
|
||||
В Manticore / SphinxQL неможливо просто зробити чітку вибірку тексту, таку як:
|
||||
|
||||
```
|
||||
SELECT * FROM table WHERE field = 'value'
|
||||
```
|
||||
|
||||
Якщо потрібно фільтрувати рядки по тексту, потрібно спочатку створити числовий атрибут, наприклад хеш CRC32 і фільтрувати по ньому. Інакше запит завершиться помилкою.
|
||||
|
||||
## Вибірка підрядка
|
||||
|
||||
Можливо, є сторонні рушії, але стандартно ви не зможете наприклад додавати записи по умові PHP "strpos" та потім зробити таку само вибірку підрядка назад:
|
||||
|
||||
```
|
||||
SELECT * FROM table WHERE MATCH ('@field "*alu*"')
|
||||
```
|
||||
|
||||
До того ж, результат такої вибірки залежатиме від налаштувань "min_word_len" і "min_prefix_len".
|
||||
|
||||
Обходити цю проблему доводиться в циклі пошукових результатів, з додатковою перевіркою "strpos".
|
||||
|
||||
## Спеціальні символи SphinxQL
|
||||
|
||||
Наприклад, на стадії індексації, ви заігнорили URL з символами "#" (якорі), "?" (динамічні параметри) через умову PHP.
|
||||
|
||||
При спробі видалити такі старі записи в індексах запитом:
|
||||
|
||||
```
|
||||
DELETE FROM table WHERE MATCH ('@url "#"')
|
||||
```
|
||||
|
||||
- сервер видалить будь який URL, оскільки у цьому випадку "#" буде вважатись порожньою строкою "''" :)
|
||||
|
||||
## Операції UPDATE
|
||||
|
||||
Якось з'явилась ідея оптимізувати об'єм бази, перенесенням одного з полів CRC32 в індексну колонку "ID".
|
||||
|
||||
Зробити це типовою командою SQL не вдалось:
|
||||
|
||||
```
|
||||
UPDATE table SET id = crc32field;
|
||||
```
|
||||
|
||||
Наступною спробою, була попередня вибірка і обробка скриптом в циклі результатів, але звісно, рушій не дав того зробити, тому довелось створювати індексну таблицю повторно.
|
||||
|
||||
## Лімітування результатів
|
||||
|
||||
Стандартно рушій лімітує вибірку до 1000 рядків, це не завжди зручно і потрібно додавати атрибут "cutoff".
|
||||
|
||||
## Містика пагінації
|
||||
|
||||
Приклад вибірки на одному з проєктів з використанням manticoresearch-php:
|
||||
|
||||
=> https://github.com/manticoresoftware/manticoresearch-php
|
||||
|
||||
```
|
||||
echo $index->search('')->maxMatches(10000)->offset(1000)->limit(1000)->get()->getTotal();
|
||||
> 5000
|
||||
```
|
||||
|
||||
## Журналювання SELECT
|
||||
|
||||
Наприклад у вашому скрипті пошуку інтернет сторінок, використовується перевірка наявності поля по його URL, таким чином, відбуваються мільйони перевірок і всі вони потрапляють до "query.log" як пошукові запити користувача.
|
||||
|
||||
Це неймовірний треш, рішення якого так і не знайшов.
|
||||
|
||||
## Стандартна стратегія Binlog
|
||||
|
||||
Користувачі серверів з нестабільним підключенням до мережі живлення, можуть зіткнутись з проблемою крашу "binlog", після чого сервіс Manticore просто не стартує при завантаженні системи.
|
||||
|
||||
Вирішується спочатку це видаленням "/var/lib/manticore/binlog/*" і ручним рестартом сервісу.
|
||||
|
||||
Коли проблема набридає, виявляється, що стандартно Manticore зберігає транзакції по-секундно, замість збереження після кожної транзакції:
|
||||
|
||||
=> https://manual.manticoresearch.com/Logging/Binary_logging#Binary-flushing-strategies
|
||||
|
||||
Можливо, щоб зробити маркетинговий ефект швидкості в роботі, але по факту доставить вам мороки в пошуках причини.
|
||||
|
||||
До того ж ця проблема є причиною "загадкових зникнень" деяких записів з бази (бо власне ви отримали server fault та скинули журнал відновлення "binlog")
|
||||
|
||||
## Інше
|
||||
|
||||
Відсутність більшості вбудованих функцій, типових для серверів SQL, можливість комбінованих запитів JOIN та інше - все це знайома ситуація, але на замітку.
|
||||
|
||||
Будьте готові до емуляції SQL а не повної заміни.
|
||||
|
||||
Створення проєкту на базі Manticore у якості основної бази - виправдано тільки для пошукових систем з великим об'ємом тимчасових даних, які перш за все не потребують чіткої вибірки, де MySQL є тягарем.
|
||||
|
||||
Можливо будуть додані рішення, якщо такі будуть знайдені.
|
||||
|
||||
## Дивіться також
|
||||
|
||||
=> manticore-as-modern-alternative-to-sphinx-search.gmi Manticore як сучасна альтернатива Sphinx
|
||||
385
public/uk/memory-management-in-gtk-applications.gmi
Normal file
385
public/uk/memory-management-in-gtk-applications.gmi
Normal file
|
|
@ -0,0 +1,385 @@
|
|||
# Керування пам'яттю в програмах GTK
|
||||
|
||||
Доповнений переклад PDF документу CSci493.70 "Introduction to Memory Management in GTK+" Стюарта Вайса - доцента Кафедри інформатики Гантерського коледжу Міського університету, Нью-Йорк.
|
||||
|
||||
## Нотатки
|
||||
|
||||
* Термін GTK+ було змінено на більш сучасний варіант назви - GTK
|
||||
* Переклад включає локальну адаптацію: корекцію семантики, мої доповнення та скорочення (де це можливо) також видалені не робочі посилання
|
||||
* Передбачається, що користувач володіє мінімальним досвідом програмування C++ та розуміє принцип роботи об'єктів, поінтерів та посилань
|
||||
|
||||
## Вступ до керування пам'яттю в GTK
|
||||
|
||||
Метою цієї статті є допомога в розумінні того, як саме керується пам'ять у GTK, щоб ваші програми не мали витоків пам'яті і не завершували роботу аварійно.
|
||||
|
||||
В основному, існують лише два способи, якими ви можете зіпсувати програму стосовно того, як вона працює з пам'яттю:
|
||||
|
||||
* програма не звільняє пам'ять, яка їй більше не потрібна, що призводить до витоку (наприклад, використання new без delete)
|
||||
* програма намагається звернутися до об'єкта, який більше не існує за адресою або цю адресу було змінено іншим компонентом програми
|
||||
|
||||
### Витоки пам'яті
|
||||
|
||||
Типовим випадком для витоку пам'яті є створення нових об'єктів на кучі (heap) без подальшого їх видалення після використання:
|
||||
|
||||
``` cpp
|
||||
MyClass* object = new MyClass;
|
||||
|
||||
// попрацювали...
|
||||
|
||||
// delete object; < забули
|
||||
```
|
||||
|
||||
Можна подумати, що сьогодні не так вже й важливо запобігати витокам пам'яті. Адже на сучасних комп'ютерах її в досталь, а операційна система сама очистить виділені ресурси після завершення програми. Але якщо ви намагаєтеся бути професіоналом у своїй роботі, то не повинні використовувати більше пам'яті, ніж потрібно, оскільки це знижує продуктивність самої програми, а також впливає на продуктивність системи та енергоспоживання пристрою. Тим паче, якщо витік буде достатньо масштабним, то аварійно зупиниться не тільки програма, але й операційна система!
|
||||
|
||||
### Недійсні посилання
|
||||
|
||||
Недійсні посилання неминуче призводять до збоїв, і їх однозначно потрібно уникати.
|
||||
|
||||
Звісно, вам не потрібно турбуватися, якщо ваша програма ніколи не виділяє пам'ять динамічно - у разі, якщо кожна змінна зберігається в стеку (під час виконання певної області видимості), або в статичній пам'яті (знаходиться в глобальній області видимості), тоді зв'язок адреси зі змінною є фіксованим.
|
||||
|
||||
Проте в GTK практично все, що створює програма, робиться через вказівники: віджети, списки та дерева, рядки тощо. І це не спроста, тому що такий підхід дозволяє не створювати зайві копії об'єктів та не розподіляти для них нову пам'ять - що значно прискорює швидкість та економічність програми.
|
||||
|
||||
Розглянемо простий приклад створення віджету кнопки:
|
||||
|
||||
``` cpp
|
||||
GtkWidget *button;
|
||||
button = gtk_button_new();
|
||||
```
|
||||
|
||||
* тут бачимо, що пам'ять для кнопки виділяється засобами GTK, а функція gtk_button_new() лише повертає вказівник на область пам'яті, де було збережено цей об'єкт. Таким чином, ваша програма отримує повний доступ до області пам'яті через цей вказівник, і тому існує ймовірність, що ви можете зіпсувати її роботу, якщо не розумієте, як саме керується пам'ять на рівні фреймворку GTK.
|
||||
|
||||
## Облік посилань
|
||||
|
||||
Якщо ви зрозумієте, що таке облік посилань в C++, у вас не буде жодних проблем з керуванням пам'яттю. Якщо ж ні - то ідея в основному схожа з тим, яким чином UNIX керує дисковим сховищем для файлів для ефективного використання дискового простору. Якщо ви не знайомі з цим, то зараз саме час розібратися.
|
||||
|
||||
### Приклад посилань I-node
|
||||
|
||||
В системах UNIX, файл представляється так званою і-нодою (i-node, або в перекладі - вузли). По суті, це структура мета-даних, що містить всю важливу інформацію про фізичний файл:
|
||||
|
||||
* хто є його власником
|
||||
* хто має право робити з ним
|
||||
* який його розмір
|
||||
* де розташовані його блоки, а також багато іншого.
|
||||
|
||||
І-нода також містить лічильник посилань для кожного екземпляру імені файлу. Таким чином, одні й ті само фізичні файли можуть мати багато імен, в одному або різних каталогах і навіть файлових системах, при цьому дані самого файлу зберігаються в єдиному екземплярі.
|
||||
|
||||
Уявімо, хтось інший створює ім'я для файлу в іншому місці, лічильник посилань збільшиться. Кожного разу, коли ім'я видаляється, лічильник посилань зменшується. Якщо лічильник посилань досягає нуля, i-нода видаляється, а також звільняється пам'ять файлу.
|
||||
|
||||
Користувачам Windows буде простіше зрозуміти суть посилань на прикладі ярлика програми - який може мати багато екземплярів у різних розташуваннях але при цьому посилатись на один і той само об'єкт - з робочого столу, меню чи будь якої іншої теки. Проте саме приклад і-ноди в UNIX описує, що являють собою посилання в C++ та їх лічильники в GTK.
|
||||
|
||||
### Організація посилань в GTK
|
||||
|
||||
В об'єктах GTK також використовується облік посилань. У той час як в UNIX лічильник посилань - це кількість імен, які має файл, то в GTK це кількість власників об'єкта.
|
||||
|
||||
Таким чином, кожен новий об'єкт GTK створюються з лічильником посилань і спочатку дорівнює одному. Кожного разу, коли створюється нове посилання на об'єкт, його лічильник посилань збільшується. Коли посилання видаляється, лічильник зменшується. Якщо лічильник досягає нуля, пам'ять об'єкта звільняється. Акт звільнення пам'яті, пов'язаної з об'єктом, називається фіналізацією об'єкта.
|
||||
|
||||
Концепція була б досить простою для розуміння, якби не той факт, що існує два різних типи об'єктів:
|
||||
|
||||
* ті, що походять від GInitiallyUnowned
|
||||
* ті, що не походять
|
||||
|
||||
Нагадаємо, що верхня частина ієрархії об'єктів виглядає так:
|
||||
|
||||
```
|
||||
GObject
|
||||
+----GInitiallyUnowned
|
||||
+----GtkObject
|
||||
+----GtkWidget
|
||||
```
|
||||
|
||||
На прикладі вище можемо бачити, що GtkWidget походить від GtkObject, який, у свою чергу, походить від GInitiallyUnowned. Ц означає, що кожен віджет непрямо походить від GInitiallyUnowned.
|
||||
|
||||
Що ж, можливо, ви запитаєте, що ж таке GObject, який не є GInitiallyUnowned?
|
||||
Цей список досить довгий і включає загальновживані об'єкти, зокрема такі:
|
||||
|
||||
* GtkAction
|
||||
* GtkListStore
|
||||
* GtkPrinter
|
||||
* GtkStyle
|
||||
* GtkTextBuffer
|
||||
* GtkTextMark
|
||||
* GtkTextTag
|
||||
* GtkTreeSelection
|
||||
* GtkTreeStore
|
||||
* GtkUIManager
|
||||
|
||||
Саме об'єкти, які походять від GObject, стануть нашою відправною точкою, оскільки їх легше зрозуміти.
|
||||
|
||||
## Прямі нащадки GObject
|
||||
|
||||
Об'єкти, що напряму походять від GObject, створюються з початковим лічильником посилань, рівним одному.
|
||||
Тож давайте створимо декілька таких об'єктів, щоб продемонструвати їх принцип роботи.
|
||||
|
||||
GtkListStore використовується в GTK доволі часто, наприклад з віджетами дерева у форматі рядків і стовпців, подібно до того, як це робить файловий браузер.
|
||||
|
||||
Створити його можна наступним чином:
|
||||
|
||||
``` c
|
||||
GtkListStore* store = gtk_list_store_new();
|
||||
```
|
||||
|
||||
Функція створює об'єкт та повертає нам вказівник в змінну store з лічильником посилань, рівним одному.
|
||||
|
||||
Документація зазначає:
|
||||
|
||||
> The caller of the function takes ownership of the data, and is responsible for freeing it.
|
||||
|
||||
Це значить, що створений таким чином об'єкт - володітиме посиланням, тому лічильник лічильник посилань для store можна збільшити, викликавши g_object_ref():
|
||||
|
||||
``` c
|
||||
g_object_ref(G_OBJECT( store ) );
|
||||
```
|
||||
|
||||
відповідно, щоб зменшити лічильник об'єкта store:
|
||||
|
||||
``` c
|
||||
g_object_unref( G_OBJECT( store ) );
|
||||
```
|
||||
|
||||
Уявімо, що у нас є певний об'єкт дерева treeview, який вже було створено раніше.
|
||||
|
||||
Спробуємо додати до нього наш список store:
|
||||
|
||||
``` c
|
||||
gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(store));
|
||||
```
|
||||
|
||||
Після того, як функція gtk_tree_view_set_model() повернулася, віджет дерева treeview відтепер тепер буде володіти посиланням на store.
|
||||
|
||||
Такі методи як gtk_tree_view_set_model() - набувають право власності на об'єкт і самостійно збільшують лічильник посилань об'єкта, використовуючи g_object_ref() у своїх реалізаціях. Якщо б вони цього не робили, то програма коли-небудь викликала g_object_unref(), лічильник посилань знизився б до нуля і був би фіналізований. У цей момент з'явилося б посилання на недійсну пам'ять, і при наступній спробі доступу до даних програмою - стався б збій. Отже, після того, як список зберігань був доданий до віджета дерева, його лічильник посилань становитиме два.
|
||||
|
||||
|
||||
Спосіб, яким GtkTreeView робить GtkListStore тим, що він відображає, подібний до того, як віджет GtkTextView робить текстовий буфер тим, що він відображає, використовуючи gtk_text_view_set_buffer():
|
||||
|
||||
``` c
|
||||
gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), GTK_TEXT_BUFFER(buffer));
|
||||
```
|
||||
|
||||
Оскільки текстовий віджет набуває право власності на buffer, gtk_text_view_set_buffer() збільшує лічильник посилань buffer. Якщо buffer був створений якою-небудь іншою частиною коду, яка набула право власності під час створення, лічильник посилань buffer становитиме два, що вказує на те, що buffer має двох власників (текстові буфери можуть бути спільними для кількох віджетів текстового перегляду)
|
||||
|
||||
Повернемося до обговорення нашого списку зберігань store. Уявімо, що нашій програмі більше не потрібно отримувати доступ до списку зберігань, як тільки він буде доданий до віджета дерева. Адже увесь доступ до списку зберігань тепер відбувається через віджет дерева. Тому, як тільки вона викликала gtk_tree_view_set_model(), вона повинна відмовитися від свого посилання та зменшити лічильник посилань за допомогою g_object_unref() таким чином:
|
||||
|
||||
``` c
|
||||
gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(store));
|
||||
g_object_unref(G_OBJECT(store));
|
||||
```
|
||||
|
||||
Це забезпечує зниження лічильника посилань до одного. Тому, коли віджет дерева treeview буде знищено і він звільнить усі свої дочірні об'єкти, лічильник посилань store стане нульовим, і він також буде фіналізований.
|
||||
|
||||
Підсумовуючи, коли ваша програма створює об'єкт, що безпосередньо походить від GObject, як тільки вона приєднала його до певного об'єкта або віджета GTK, який припускає право власності, ви повинні викликати g_object_unref() на об'єкті, який вам більше не потрібен.
|
||||
|
||||
## Об'єкти, що походять від GInitiallyUnowned
|
||||
|
||||
Ситуація зовсім інша для об'єктів, що походять від GInitiallyUnowned. Усі такі об'єкти створюються з плаваючим лічильником посилань, рівним одному. Плаваюче посилання - це посилання, яке не належить нікому. Саме тому клас називається GInitiallyUnowned, оскільки спочатку об'єкти цього класу не мають власника. Плаваюче посилання можна розглядати як особливий вид посилання, яке не асоційоване з жодним власником. Технічно це прапорець у структурі GObject який вказує, чи є початкове посилання плаваючим чи ні; коли він встановлений, об'єкт має плаваюче посилання, а коли він очищений, посилання є неплаваючим.
|
||||
|
||||
Необхідність плаваючих посилань має дві причини. Одна з причин полягає в тому, що вони є способом зберегти об'єкти після їх створення, але до того, як вони будуть приєднані до батьківських контейнерів. Інша причина полягає у тому, як функції можуть бути викликані в C.
|
||||
|
||||
Розглянемо наступний код:
|
||||
|
||||
``` c
|
||||
container = create_container();
|
||||
container_add_child (container, create_child());
|
||||
```
|
||||
|
||||
Припустимо, що container - це якийсь тип контейнерного віджета, а container_add_child() додає дочірній віджет до даного контейнера а також додає посилання на даний дочірній об'єкт, який у цьому випадку є об'єктом, створеним викликом create_child().
|
||||
|
||||
Припустимо, що плаваючих посилань не існує і що create_child() створює об'єкт з лічильником посилань, рівним одному. Оскільки контейнер набуває посилання на об'єкт і збільшує лічильник посилань, після виклику, дочірній об'єкт матиме лічильник посилань рівний двом.
|
||||
|
||||
Однак, оскільки значення, що повертається create_child(), не присвоюється жодному об'єкту, немає об'єкта, на якому можна викликати g_object_unref(), щоб зменшити лічильник до одного. Коли контейнер знищується, він може "звільнити" дочірній об'єкт, але лічильник дочірнього об'єкта знизиться до одного, а не до нуля, і це призведе до витоку пам'яті, оскільки пам'ять дочірнього об'єкта не може бути звільнена, поки лічильник посилань не досягне нуля.
|
||||
|
||||
Щоб запобігти витоку пам'яті за відсутності плаваючих посилань, можна використати наступний код:
|
||||
|
||||
``` c
|
||||
Child *child;
|
||||
container = create_container(); // container ref-count = 1
|
||||
child = create_child(); // child ref-count = 1
|
||||
container_add_child (container, child); // child ref-count = 2
|
||||
g_object_unref (child); // child ref-count = 1
|
||||
```
|
||||
|
||||
Різниця тут полягає в тому, що результат create_child() зберігається в child, тому до нього можна застосувати g_object_unref(). Однак, оскільки можливо написати код у C і в першому варіанті, бібліотека GObject була спроєктована так, щоб такий код працював правильно.
|
||||
|
||||
Ідея плаваючих посилань дозволяє наведеному коду бути вільним від витоків пам'яті, але лише якщо контейнер має можливість набути право власності та перетворити плаваюче посилання на стандартне посилання, яке називається звичайними посиланням.
|
||||
|
||||
Відмінність наступна:
|
||||
|
||||
* функція, яка створює віджет, така як create_child() вище - створює його з плаваючим лічильником посилань рівним одному. Тобто таким, що спочатку не має власника, а не зі стандартним лічильником посилань, рівним одному.
|
||||
* функція, яка додає дочірній віджет до контейнера, така як container_add_child() вище - не викликає g_object_ref() на дочірньому об'єкті, оскільки це не очистить прапорець плаваючого посилання. Натомість вона викликає g_object_ref_sink()
|
||||
|
||||
``` c
|
||||
g_object_ref_sink(object);
|
||||
```
|
||||
|
||||
Функція g_object_ref_sink() існує для того, щоб, коли віджет додається до батьківського контейнера, контейнер міг зробити дві речі:
|
||||
|
||||
* видалити плаваюче посилання
|
||||
* набути власне стандартне посилання на віджет
|
||||
|
||||
Вона працює наступним чином: якщо object мав плаваюче посилання, то викликаючи її:
|
||||
|
||||
* програма володіє посиланням на object
|
||||
* лічильник посилань залишається не змінним
|
||||
* прапорець плаваючого посилання очищується
|
||||
|
||||
Якщо object не мав плаваючого посилання, то g_object_ref_sink() має той само ефект, як і g_object_ref(), а саме - збільшує лічильник посилань.
|
||||
|
||||
Функціонально, операцію "Sink" можна розглянути як:
|
||||
|
||||
``` c
|
||||
if ( was_floating(object) )
|
||||
clear( object->floating_flag );
|
||||
else
|
||||
g_object_ref(object);
|
||||
```
|
||||
|
||||
Тепер повернемось до прикладу вище:
|
||||
|
||||
``` c
|
||||
container = create_container();
|
||||
container_add_child (container, create_child());
|
||||
```
|
||||
|
||||
create_child() створює віджет з плаваючим лічильником посилань, рівним одному, а функція container_add_child() викликає g_object_ref_sink() на анонімному дочірньому віджеті (як об'єкті, звичайно), набуваючи право власності на нього, що надає йому стандартний лічильник посилань, рівний одному. Коли контейнер знищується, він звільнить дочірній об'єкт, зменшуючи його лічильник до нуля, що призведе до фіналізації дочірнього об'єкта без необхідності для програми викликати g_object_unref() на дочірньому об'єкті після container_add_child().
|
||||
|
||||
Усі віджети, за винятком вікон верхнього рівня, починають своє життя з плаваючим посиланням. Вікна верхнього рівня відрізняються, оскільки вони ніколи не поміщаються в контейнери - вони є коренями дерев контейнерів. Коли вікно верхнього рівня створюється, бібліотека GTK відразу ж "зливає" його плаваюче посилання і набуває право власності на нього, тому, коли воно передається вашій програмі, його стандартний лічильник посилань становитиме один.
|
||||
|
||||
Якщо програма створює віджет, який не є вікном верхнього рівня, то в якийсь момент його, ймовірно, буде упаковано в контейнер. Усі функції, які упаковують віджети в контейнери, автоматично "зливає" плаваюче посилання віджета і надають контейнеру право власності на дочірній об'єкт. Таким чином, коли батьківський віджет зрештою отримує сигнал знищення і звільняє свої дочірні об'єкти, їх стандартні лічильники посилань знизяться до нуля, і вони будуть фіналізовані (якщо в програмі, з тієї чи іншої причини, код не викликає g_object_ref() на будь-якому з дочірніх об'єктів без відповідного g_object_unref(), що призведе до витоку пам'яті!).
|
||||
|
||||
Щоб було зрозуміло, коли ваша програма створює віджет типу foo, використовуючи конструктор, такий як gtk_foo_new(), і додає цей віджет до контейнера, їй ніколи не потрібно робити нічого іншого для керування пам'яттю віджета; GTK подбає про звільнення пам'яті, коли віджет буде знищено.
|
||||
|
||||
Спробуймо розкрити суть на прикладі:
|
||||
|
||||
``` c
|
||||
GtkWidget *gadget = gtk_gadget_new ();
|
||||
gtk_widget_destroy (gadget);
|
||||
```
|
||||
|
||||
Можна очікувати, що конструкція просто створить gadget, виділяючи пам'ять для нього, а потім відразу ж її звільнить. На перший погляд це здається логічним. Але насправді це призведе до витоку пам'яті!
|
||||
|
||||
Це тому, що коли gadget створюється, він має лише плаваюче посилання, а виклик gtk_widget_destroy() еквівалентний конструкції:
|
||||
|
||||
``` c
|
||||
gtk_object_destroy (GTK_OBJECT(gadget));
|
||||
```
|
||||
|
||||
Документація для gtk_object_destroy стверджує, що:
|
||||
|
||||
> Пам'ять для самого об'єкта не буде видалена, поки його лічильник посилань фактично не знизиться до нуля; gtk_object_destroy() лише просить власників посилань звільнити свої посилання, вона не звільняє об'єкт.
|
||||
|
||||
Іншими словами, gtk_widget_destroy() не звільняє об'єкт; вона лише просить власників посилань відпустити свої посилання. Оскільки gadget не був упакований у жоден контейнер, а отже g_object_ref_sink() - не був викликаний на ньому, він не належить жодному власнику посилань, і тому gtk_widget_destroy() не звільнить пам'ять, що утримується gadget. В результаті виникає витік пам'яті.
|
||||
|
||||
Звичайно, немає жодної причини писати код таким чином. Але в разі якоїсь причини, коли ви хочете створити віджет, а потім знищити його і звільнити пам'ять, яку він утримує (не додаючи його до контейнера) вам потрібно буде звільнити його самостійно:
|
||||
|
||||
``` c
|
||||
GtkWidget *gadget = gtk_gadget_new();
|
||||
g_object_ref_sink(G_OBJECT(gadget)); // перетворити плаваюче посилання на стандартне
|
||||
gtk_widget_destroy(gadget); // знищити зовнішні посилання
|
||||
g_object_unref(G_OBJECT(gadget)); // зменшити лічильник посилань до 0
|
||||
```
|
||||
|
||||
Знову таки, немає особливої причини створювати віджет, який ви не маєте наміру додавати до контейнера, за винятком вікна верхнього рівня. Метою попереднього прикладу лише пояснення, як працює облік посилань стосовно об'єктів, таких як віджети, зокрема які походять від GInitiallyUnowned.
|
||||
|
||||
## Типи рядків GLib
|
||||
|
||||
Існують різні типи рядків, які можна використовувати в програмі GTK на мові C:
|
||||
|
||||
``` c
|
||||
char *string1; // стандартний рядок C
|
||||
gchar *string2; // ідентичний стандартному рядку C, оскільки gchar є типом даних для char
|
||||
GString string3; // розширення рядків C, яке може автоматично зростати
|
||||
```
|
||||
|
||||
При використанні стандартних функцій рядків, таких як g_strdup(), g_strnfill(), або g_strdup_printf(), загальне правило полягає в тому, якщо документація говорить, що функція повертає новостворений рядок, то повернутий рядок слід звільнити за допомогою g_free().
|
||||
|
||||
Зазвичай документація чітко вказує, що повернутий рядок повинен бути звільнений за допомогою g_free(). Якщо жодне з цих тверджень не міститься, то не викликайте g_free() на рядок, якщо не хочете, щоб програма зазнала збою.
|
||||
|
||||
### Об'єкти GString
|
||||
|
||||
Об'єкт GString по суті є структурою, пам'яттю якої керує бібліотека GLib:
|
||||
|
||||
``` c
|
||||
typedef struct {
|
||||
gchar *str;
|
||||
gsize len;
|
||||
gsize allocated_len;
|
||||
} GString;
|
||||
```
|
||||
|
||||
Внутрішній вказівник str є адресою текстового буфера і може переміщатися в міру зростання та зменшення рядка.
|
||||
|
||||
Об'єкт GString створюється за допомогою однієї з функцій сімейства g_string_new(). Після роботи з об'єктом, для вивільнення пам'яті використовується функція g_string_free(). Якщо ви хочете, щоб сама структура та вміст її буфера були звільнені, то виклик буде таким:
|
||||
|
||||
``` c
|
||||
gchar* buf = g_string_free(string, TRUE);
|
||||
```
|
||||
|
||||
* другий аргумент вказує, чи буде звільнено також дані рядка, або лише обгорткова структура, тобто якщо передано TRUE, значення, що повертається, буде NULL але якщо другий аргумент - FALSE, то буфер не буде звільнений, і значення, що повертається - буде вказівником на буфер, який потрібно звільнити, коли код закінчить роботу з ним, використовуючи g_free()
|
||||
|
||||
## Структури GdkPixbuf
|
||||
|
||||
Структури GdkPixbuf безпосередньо походять від GObject і відповідно - реалізують облік посилань. Вони створюються з лічильником посилань, рівним одному. Програма може ділитися одним піксельним буфером pixbuf з багатьма частинами учасниками. Коли певна частина програми має утримувати вказівник на піксельний буфер, вона повинна додати посилання на нього, викликавши g_object_ref(). Коли вона закінчить з піксельним буфером, їй слід викликати g_object_unref(), щоб зменшити лічильник посилань. Піксельний буфер буде знищено, коли його лічильник посилань знизиться до нуля. Лічильник посилань загалом повинен бути рівним кількості різних вказівників на об'єкт.
|
||||
|
||||
* діалоги не додаються до контейнерів, але знищуються за допомогою gtk_widget_destroy(), і це відбувається тому, що діалоги є вікнами верхнього рівня, а GTK має посилання на них. Сигнал знищення викликає у GTK виклик unref на діалозі, звільняючи його пам'ять.
|
||||
|
||||
Правила, описані вище для рядкових типів C, загалом застосовуються і до pixbuf. Деякі функції, які повертають вказівник на GdkPixbuf, акцентують у своїй документації той факт, що вони повертають новий об'єкт pixbuf з лічильником посилань (що дорівнює одному). Якщо ви використовуєте таку функцію, то ви повинні викликати g_object_unref() після того, як закінчите використовувати новозалучений pixbuf. Якщо pixbuf використовувався як джерело для створення нового pixbuf (у випадку з gdk_pixbuf_add_alpha(), який створює модифіковану версію pixbuf, і оригінал більше не потрібен) то потрібно викликати g_object_unref(), щоб відмовитися від володіння і зменшити лічильник посилань.
|
||||
|
||||
Останнє, про що вам потрібно знати щодо pixbuf, це те, що якщо ви використовуєте gdk_pixbuf_new_from_data() для створення pixbuf з даних, які вже зберігаються в пам'яті (наприклад, масив значень пікселів) то вам також може знадобитися створити функцію, яка "знає, як" звільнити пам'ять, що належить цим даним, коли лічильник посилань на pixbuf зменшується до нуля.
|
||||
|
||||
## Списки і деревоподібні сховища
|
||||
|
||||
Інтерфейс GtkTreeModel визначає загальний інтерфейс дерева для використання віджетом GtkTreeView. Це абстрактний інтерфейс, який розроблений для використання з будь-якою структурою даних, яка йому відповідає.
|
||||
|
||||
GtkTreeStore та GtkListStore - це вже реалізовані моделі дерева GTK. Вони надають структуру даних, а також усі відповідні інтерфейси дерева. Заповнення їх даними вимагає використання або методу gtk_list_store_set(), або методу gtk_tree_store_set() відповідно. Існують варіації цих двох функцій, але зауваження нижче є вірними незалежно від того, яка варіація використовується.
|
||||
|
||||
Обидві функції gtk_list_store_set() і gtk_tree_store_set() приймають змінну кількість аргументів, які по суті є впорядкованими парами, як у прикладі нижче:
|
||||
|
||||
``` c
|
||||
void gtk_tree_store_set (GtkTreeStore *store,
|
||||
GtkTreeIter *iter,
|
||||
..., -1);
|
||||
```
|
||||
|
||||
Відсутніми аргументами є пари (column_id, value), де column_id - це ціле число, а value може бути будь-якого типу: рядком, цилим числом, pixbuf або вказівником на довільні структури. Що потрібно знати, так це те, чи передані цій функції дані копіюються у рядок сховища або вони доступні за посиланням.
|
||||
|
||||
В наступному прикладі, питання полягає в тому, чи фактичні рядкові дані (які зберігаються в змінній name) копіюються в рядок, на який вказує ітератор, чи в рядок копіюється саме вказівник на name:
|
||||
|
||||
``` c
|
||||
gchar name[] = "Groucho";
|
||||
gtk_tree_store_set (store, &iter, NAME_ID, name, -1);
|
||||
```
|
||||
|
||||
Коротка відповідь: вам не потрібно турбуватися про виділення та звільнення пам'яті для даних, які потрібно зберігати, оскільки підсистема GLib/GObject GType і GValue автоматично займається більшістю керування пам'яттю. Наприклад, якщо ви зберігаєте рядок у стовпці рядка, модель зробить копію рядка і зберігатиме цю копію. Якщо ви пізніше зміните стовпець на новий рядок, модель автоматично звільнить старий рядок і знову зробить копію нового рядка та зберігатиме цю копію.
|
||||
|
||||
Довга відповідь полягає в тому, що коли дані додаються до списку або дерева за допомогою будь-якої з функцій gtk_*_store_set() те, як вони обробляються GTK, залежить від того, до якої з трьох категорій даних вони належать:
|
||||
|
||||
1. Якщо дані є GObject (тобто об'єктом, що безпосередньо походить від GObject), то сховище бере на себе право власності на нього, викликаючи g_value_dup_object() для об'єкта та відмовляючись від права власності на раніше утримуваний об'єкт, якщо він замінює старе значення.
|
||||
|
||||
2. Якщо дані є простим скалярним типом даних, таким як числовий, логічний або перерахунковий тип, або вказівником, то сховище робить копію даних. Зверніть увагу, що копіюється вказівник, а не дані, на які він вказує.
|
||||
|
||||
3. Якщо дані є рядком або упакованою структурою, то сховище дублює рядок або упаковану структуру та зберігає вказівник на неї. Якщо дані замінюють існуючі дані, то в цьому випадку рядок або упакована структура спочатку звільняються за допомогою g_free() або g_boxed_free() відповідно (GBoxed - це механізм обгортки для довільних структур C, вони обробляються як непрозорі шматки пам'яті)
|
||||
|
||||
Це може залишити вас з питаннями як GTK обробляє pixbuf. Як зазначалося вище, pixbuf безпосередньо походить від GObject і підпадає під першу категорію. Коли ви отримуєте дані зі сховища за допомогою gtk_tree_model_get() або будь-якої з його варіацій, вам потрібно бути обізнаним про те, як GTK обробляє різні типи даних, щоб знати, чи потрібно звільняти пам'ять, коли ви закінчите з ними:
|
||||
|
||||
1. Якщо отримувані дані є GObject, функція збільшує його лічильник посилань, оскільки вона надає ще одне посилання на нього для нас.
|
||||
2. Якщо дані є простим скалярним типом даних, таким як числовий, логічний або перерахунковий тип, або вказівником, функція робить копію і повертає цю копію.
|
||||
3. Якщо дані є рядком або упакованим типом, функція копіює їх і повертає вказівник на дані.
|
||||
|
||||
Це означає, що потрібно викликати g_object_unref() для об'єктів, отриманих зі сховища, коли роботу з ними завершено, а також - звільнити дані, які ми отримали зі сховища, якщо це рядкові або упаковані типи даних (використовуючи g_free() або g_boxed_free() відповідно).
|
||||
|
||||
Щодо pixbuf, то такі дані належать до першої категорії, тому спочатку необхідно відмовитися від права власності, отримане зі сховища за допомогою gtk_tree_model_get(), за допомогою unref. Усі інші дані не потребують додаткового керування пам'яттю.
|
||||
|
||||
## Висновки
|
||||
|
||||
Коли ви налагоджуєте свою програму, хорошою ідеєю буде увімкнення системного монітора та спостереження за використанням віртуальної та фізичної пам'яті програми. Якщо ви бачите, що використання пам'яті постійно зростає під час роботи програми, це означає, що у вас десь є витік пам'яті. Моя порада - повторювати дії, такі як багаторазове натискання на один і той же пункт меню, щоб виявити, що є винуватцем.
|
||||
|
||||
Якщо ви звільнили пам'ять, яку не повинні були звільняти, це буде очевидно, оскільки програма аварійно завершить роботу. Вам слід переглянути звіт про помилку, виданий вашим менеджером вікон (наприклад, Gnome), і подивитися на трасу стека. Починаючи з верхньої частини стека, спускайтеся, поки не знайдете першу функцію, код якої належить вам, а не бібліотеці GTK. Саме там знаходиться код, який спричинив проблему.
|
||||
|
||||
Існують більш формальні методи виявлення витоків пам'яті, але попередній підхід є відносно простим у виконанні і не вимагає вивчення нового інструменту. Якщо ви хочете навчитися користуватися спеціалізованим інструментарієм, спробуйте Valgrind (http://valgrind.org) - безкоштовне програмне забезпечення з відкритим кодом для аналізу та керування пам'яттю.
|
||||
|
||||
## Джерело
|
||||
|
||||
=> https://www.cs.hunter.cuny.edu/~sweiss/course_materials/csci493.70/lecture_notes/GTK_memory_mngmt.pdf Introduction to Memory Management in GTK+
|
||||
|
||||
## Читайте також
|
||||
|
||||
=> memory-management-in-gtkmm-4.0.gmi Керування пам'яттю в gtkmm-4.0
|
||||
152
public/uk/memory-management-in-gtkmm-4.0.gmi
Normal file
152
public/uk/memory-management-in-gtkmm-4.0.gmi
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
# Керування пам'яттю в gtkmm-4.0
|
||||
|
||||
gtkmm - вільний об'єктно-орієнтований C++ інтерфейс для популярного фреймворку GTK, що поширюється за ліцензією LGPL
|
||||
|
||||
Основне призначення gtkmm полягає в отриманні усіх переваг ООП:
|
||||
|
||||
* наслідуванні класів GTK
|
||||
* скороченні конструкцій коду програми (порівняно з ієрархічною реалізацією аналогічної функціональності засобами C)
|
||||
* вбудованих інструментів перевірок та корекції типізації нащадків дерева віджетів та багато іншого.
|
||||
|
||||
Оскільки це абстрагований прошарок над стандартною бібліотекою GTK, при його використанні, може виникнути питання як саме працювати з пам'яттю відповідно документації, адже API gtkmm має певні поліморфічні відмінності, без розуміння яких, є ризик витоку пам'яті або помилок при зверненнях.
|
||||
|
||||
Даний матеріал являє собою доповнений переклад офіційної сторінки
|
||||
|
||||
=> https://gnome.pages.gitlab.gnome.org/gtkmm-documentation/chapter-memory.html Chapter 25. Memory management
|
||||
|
||||
і базується на двох окремих статтях, які представлені розділами нижче:
|
||||
|
||||
=> https://gnome.pages.gitlab.gnome.org/gtkmm-documentation/chapter-memory.html#sec-memory-widgets Widgets - Робота з віджетами
|
||||
=> https://gnome.pages.gitlab.gnome.org/gtkmm-documentation/sec-memory-shared-resources.html Shared resources - Робота зі спільними ресурсами
|
||||
|
||||
Сподіваюсь, він допоможе краще зрозуміти базові принципи роботи перед початком роботи з фреймворком: не писати зайві велосипеди для керування пам'яттю і не отримати замість переваг - segmantation fault.
|
||||
|
||||
## Робота з віджетами
|
||||
|
||||
* Важливою відмінністю gtkmm-4.0 у порівнянні зі старішими версіями є те, що знищення батьківського віджета GTK більше не призводить до знищення його дочірнього віджета, коли той знаходиться в його контейнері. Якщо ви покладалися на таку поведінку в старішій версії, то відтепер, в gtkmm-4.0 потрібно спочатку видалити дочірній віджет, наприклад, через деструктор.
|
||||
|
||||
Робота з фреймворком GTK здебільшого передбачає створення та видалення контейнерів та їх дочірніх віджетів. Відповідно, розробник повинен самостійно контролювати виділення пам'яті, а також її очищення після того, як певний її віджет більше не використовується.
|
||||
|
||||
Бібліотека gtkmm надає додаткові інструменти, які спрощують таку роботу.
|
||||
|
||||
Нижче розглянемо, які способи керування пам'яттю взагалі доступні в gtkmm
|
||||
|
||||
### Класичне керування пам'яттю C++
|
||||
|
||||
Загалом, gtkmm дозволяє програмісту контролювати тривалість життя будь-якого віджета так само, як і будь-якого іншого об'єкта C++. Ця гнучкість дозволяє використовувати:
|
||||
|
||||
* оператори new і delete для створення та знищення динамічних об'єктів
|
||||
* звичайні деструктори (які виконуються автоматично, коли клас знищується)
|
||||
* локальні екземпляри об'єктів (які знищуються, коли екземпляр виходить з області видимості)
|
||||
|
||||
Існують й інші способи, але розглянемо тільки деякі з них нижче.
|
||||
|
||||
#### Віджети в області класу
|
||||
|
||||
Якщо не потрібне динамічне виділення пам'яті, можна використовувати дочірні віджети в області батьківського класу. Однією з переваг таких віджетів є те, що керування пам'яттю зосереджено локально. Програма не ризикує отримати витоки пам'яті тому, що дані зберігаються на стеку.
|
||||
|
||||
``` cpp
|
||||
#include <gtkmm/button.h>
|
||||
#include <gtkmm/window.h>
|
||||
class Foo : public Gtk::Window
|
||||
{
|
||||
private:
|
||||
Gtk::Button theButton; // буде знищений, коли об'єкт Foo буде знищений
|
||||
};
|
||||
```
|
||||
|
||||
Недоліком використання віджетів в області класу - є розкриття реалізації класу раніше, ніж його інтерфейсу в заголовковому файлі.
|
||||
|
||||
#### Віджети в області функції
|
||||
|
||||
Якщо програмісту не потрібен віджет в області всього класу, можна також використовувати віджет в локальній області функції. Перевагою такого підходу полягає у підвищеній прихованості даних та зменшенні залежностей:
|
||||
|
||||
``` cpp
|
||||
{
|
||||
Gtk::Button aButton;
|
||||
aButton.set_visible(true);
|
||||
...
|
||||
app->run();
|
||||
}
|
||||
```
|
||||
|
||||
Однак ця техніка рідко буває корисною. Більшість віджетів не можуть бути безпечно створені до того, як програма буде зареєстрована або активована. Їх також не можна безпечно видалити після того, як програму було активовано типовими для GTK 4 засобами Gtk::Application::run() або Gtk::Application::make_window_and_run().
|
||||
|
||||
#### Динамічна алокація з new і delete
|
||||
|
||||
Доволі часто, у програмах використовується динамічна пам'ять:
|
||||
|
||||
``` cpp
|
||||
auto pButton = new Gtk::Button("Test");
|
||||
// зробіть щось корисне з pButton
|
||||
delete pButton;
|
||||
```
|
||||
|
||||
* на цьому прикладі, програміст вручну видаляє pButton, після використання, щоб запобігти витоку пам'яті.
|
||||
|
||||
Але у даному випадку, краще надати перевагу автоматичному способу знищення батьківським класом своїх нащадків, створюючи їх за допомогою інтегрованого методу Gtk::make_managed().
|
||||
|
||||
Це не є обов'язковим, оскільки також можна використовувати оператори new і delete, але сучасний стиль C++ відмовляється від них на користь більш безпечних моделей керування пам'яттю, тому краще створювати віджети за допомогою спеціального метода і потім видалити їх автоматично.
|
||||
|
||||
Тому gtkmm й надає метод, який об'єднує створення та позначення в один крок, дозволяючи не писати new взагалі і чітко для інших розробників виразити намір створити саме віджет з автоматизованим керуванням пам'яті.
|
||||
|
||||
Таким чином, коли за допомогою Gtk::make_managed() ми надаємо певному класу керування його дочірнім віджетом, такий батьківський клас називається "керуючим віджетом"
|
||||
|
||||
### Керуючий віджет
|
||||
|
||||
Оскільки тепер ми використовуємо Gtk::make_managed(), то можемо дозволити контейнеру віджета контролювати, коли його дочірній віджет буде знищено.
|
||||
|
||||
Розглянемо практичне використання на прикладі append()
|
||||
|
||||
#### Динамічна алокація з make_managed() і append()
|
||||
|
||||
Щоб делегувати керування тривалістю життя віджета його контейнеру, просто створімо його за допомогою Gtk::make_managed() і спакуймо результат до батьківського контейнера за допомогою Gtk::Box::append()
|
||||
|
||||
``` cpp
|
||||
MyContainer::MyContainer()
|
||||
{
|
||||
auto pButton = Gtk::make_managed<Gtk::Button>("Test");
|
||||
append(*pButton); // додати *pButton до MyContainer
|
||||
}
|
||||
```
|
||||
|
||||
* замість Gtk::Box::append() можна використовувати й інші методи
|
||||
|
||||
Коли об'єкти типу MyContainer знищуються, кнопка також буде видалена. Більше не потрібно видаляти pButton, щоб звільнити пам'ять; контроль за її видаленням було делеговано об'єкту MyContainer.
|
||||
|
||||
=> https://github.com/YGGverse/Yoda/commit/02b0b42b6b03f707242f461b0bea8bb4be16d16c Приклад переходу програми на модель керуючих віджетів
|
||||
|
||||
## Робота зі спільними ресурсами
|
||||
|
||||
Деякі об'єкти, такі як Gdk::Pixbufs і Pango::Fonts, отримуються зі спільного сховища. Тому ви не можете створювати свої власні екземпляри. Ці класи зазвичай успадковуються від Glib::Object.
|
||||
|
||||
Gdk::Pixbuf може бути створено лише за допомогою функції create()
|
||||
|
||||
``` cpp
|
||||
auto pixbuf = Gdk::Pixbuf::create_from_file(filename);
|
||||
```
|
||||
|
||||
Замість того, щоб вимагати від вас посилатися на ці об'єкти, gtkmm повертає розумний вказівник (smart-pointer) Glib::RefPtr<>
|
||||
|
||||
* наприклад, бібліотека cairomm - має свій власний розумний вказівник Cairo::RefPtr<>
|
||||
|
||||
У даному прикладі, pixbuf є розумним вказівником, тому ви можете працювати з ним так само як зі стандартним вказівником std::shared_ptr
|
||||
|
||||
``` cpp
|
||||
auto width = 0;
|
||||
if(pixbuf)
|
||||
{
|
||||
width = pixbuf->get_width();
|
||||
}
|
||||
```
|
||||
|
||||
Таким чином, коли pixbuf виходить з області видимості, у фоновому режимі відбудеться unref(). А оскільки немає оператора new - отже, немає і delete.
|
||||
|
||||
В оригіналі наведено список літератури, щоб дізнатися більше про вказівники:
|
||||
|
||||
* Bjarne Stroustrup, "The C++ Programming Language" Fourth Edition - section 34.3
|
||||
* Nicolai M. Josuttis, "The C++ Standard Library" - section 4.2
|
||||
|
||||
## Читайте також
|
||||
|
||||
=> memory-management-in-gtk-applications.gmi Керування пам'яттю в програмах GTK
|
||||
203
public/uk/misfin-postal-satellite-of-geminispace.gmi
Normal file
203
public/uk/misfin-postal-satellite-of-geminispace.gmi
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
# Misfin - поштовий сателіт Geminispace
|
||||
|
||||
Про Misfin я знаю давно, декілька разів намагався ним користуватись, але жодного - не доводилось перевірити його в реальних умовах. Зокрема, через брак аудиторії та відсутність такої необхідності в сучасному світі месенджерів, де й сама пошта є епохою динозаврів. Утім, хотілось би трішки освітити цю альтернативну технологію для широкого загалу, адже з протоколом Gemini - знайомий не заочно і вже встиг написати для його специфікації, у свій час, повнофункціональний GTK браузер Yoda (https://github.com/YGGverse/Yoda) а також ряд інших серверних рішень різними мовами.
|
||||
|
||||
Протокол доставки повідомлень Misfin - названо на честь космічного проєкту MSFN, Manned Space Flight Network (https://en.wikipedia.org/wiki/Manned_Space_Flight_Network) і є спробою реалізації мінімалістичної альтернативи класичній пошті e-mail, за образом і подобою концепції протоколу Gemini.
|
||||
|
||||
З офіційного маніфесту:
|
||||
|
||||
> Електронна пошта така ж погана, як і Інтернет. Вона стала складною, захищеною лише за допомогою інших протоколів, які до неї приєднані, і підтримує всі неприємні функції, які є в Інтернеті, такі як Cookies та трекінгові маячки. Ще гірше, що вона зазнає активної ворожості з боку великих гравців Інтернету. Більшість провайдерів блокують трафік на порту 25, і ви не можете доставити пошту до жодного з великих сервісів (як-от Gmail), не проходячи через численні перешкоди - і навіть тоді це лотерея.
|
||||
|
||||
Від себе зауважу, що вбачаю в ньому не стільки боротьбу з корпораціями та монополією, але опцію отримувати чистий вміст листів: без розмітки та зайвих заголовків, що можуть бути частиною трекінгу, характерних для сучасного Веб 2.0 та вище. Якщо ж у вас є проблеми з 25 портом чи бажаєте розгорнути повноцінну локальну пошту за NAT, ознайомтесь з наступними матеріалами:
|
||||
=> dns-less-email-server-for-the-local-networks.gmi Організація поштової скриньки для локальних мереж без DNS
|
||||
=> yggmail-messenger-with-email-protocol.gmi Yggmail - месенджер з поштовим інтерфейсом
|
||||
|
||||
## Реалізація
|
||||
|
||||
На мою думку, Misfin не є альтернативною класичній пошті, це швидше модифікація протоколу відправки даних Titan для Gemini або NPS для Nex, але на відміну від таких, не є частиною інфраструктури жодного з них.
|
||||
|
||||
Про концепції протоколів Gemini та Nex читайте тут:
|
||||
=> gemini-protocol-as-http-alternative.gmi Протокол Gemini як альтернатива HTTP
|
||||
=> nex-lightweight-gemini-alternative.gmi Протокол NEX - легка альтернатива Gemini
|
||||
|
||||
Схоже до Titan, Misfin використовує сервер, який працює на сокеті з виділеним портом (стандартно 1958) і отримує на нього дані через обов'язковий шар шифрування TLS. Окрім тексту повідомлень, дані містять мінімалістичні заголовки (які на відміну від Titan, орієнтовані не на файл, а текст) і включають:
|
||||
|
||||
* USER_ID - поштова скринька користувача (admin, santa і тд.)
|
||||
* COMMON_NAME - псевдонім користувача
|
||||
* SUBJECT_ALT_NAME - хост (example.com, misfin.org, тощо)
|
||||
|
||||
Сирий рядок запиту виглядатиме як:
|
||||
|
||||
```
|
||||
misfin://mailbox@hostname.com \r\n
|
||||
Текст повідомлення
|
||||
```
|
||||
|
||||
Відповідь сервера-отримувача при цьому буде такою:
|
||||
|
||||
```
|
||||
20 <fingerprint of the recipient>\r\n
|
||||
```
|
||||
|
||||
Хто вже працював з Gemini, може побачити, що ці протоколи - між собою схожі, як дві краплі води. Більш детально вдаватись в специфікацію тут не буду, вона описана на офіційному сайті (посилання внизу сторінки). Занотую тільки, що наразі існує три базові редакції протоколу:
|
||||
* A - сама перша, рання офіційна редакція (наразі вважається архівною)
|
||||
* B - друга офіційна редакція протоколу, що включає корекції A
|
||||
* C - після релізу версії B його автор (Лем) на деякий час пропав з радарів, тому декілька користувачів не дочекавшись випустили версію C, яка збільшує довжину повідомлення та включає деякі оптимізації, що стосуються формату заголовків
|
||||
|
||||
Наскільки мені відомо, автор редакцій A і B згодом вийшов на зв'язок та збирався включити редакцію C в офіційну специфікацію, але потім зник. Формально, протоколи B і C є сумісними, а новий софт пишеться з урахуванням специфікації останнього. Тому майте на увазі цю невеличку історію. Більше про неї вам розкаже один з учасників цього процесу:
|
||||
=> gemini://bbs.geminispace.org/u/clseibold
|
||||
|
||||
## Програмне забезпечення
|
||||
|
||||
Для доставки пошти, передбачається класична пара клієнт-сервер, при цьому для сервера відкривається вказаний в його налаштуваннях порт TCP:
|
||||
|
||||
``` bash
|
||||
ufw allow 1958/tcp
|
||||
```
|
||||
|
||||
### Сервер
|
||||
|
||||
Є різні реалізації, але мені відомо про щонайменше дві стабільні:
|
||||
|
||||
=> https://sr.ht/~nixgoat/estampa/ estampa (Rust)
|
||||
=> https://gitlab.com/clseibold/misfin-server misfin-server (Go)
|
||||
|
||||
### estampa
|
||||
|
||||
Так як сам розробляю програмне забезпечення на Rust, для себе обрав estampa.
|
||||
|
||||
Встановлюється цей сервер наступним чином:
|
||||
|
||||
``` bash
|
||||
git clone https://git.sr.ht/~nixgoat/estampa
|
||||
cd estampa
|
||||
cargo build --release
|
||||
install target/release/estampa /usr/local/bin/estampa
|
||||
```
|
||||
* останньою командою ми встановлюємо estampa до канонічного розташування бінарних програм Linux, для подальшої роботи сервера з systemd; якщо ви не плануєте встановлення, цей крок можна пропустити і пускатись через звернення до target/release/estampa
|
||||
|
||||
=> install-latest-rust-version-on-linux.gmi Встановлення останньої версії Rust в Linux
|
||||
|
||||
У поточній теці проєкту, відкриваємо файл конфігурації:
|
||||
|
||||
``` bash
|
||||
nano config.toml
|
||||
```
|
||||
|
||||
та додаємо в кінці поштові скриньки користувачів:
|
||||
|
||||
``` config.toml
|
||||
[mailbox.user1]
|
||||
enabled = true
|
||||
name = "user 1"
|
||||
|
||||
[mailbox.user2]
|
||||
enabled = true
|
||||
name = "user 2"
|
||||
```
|
||||
|
||||
Опціонально, можна вказати й інші налаштування (якщо плануєте запуск від systemd) але я лишив стандартно.
|
||||
|
||||
Далі, сервер запускається командою:
|
||||
|
||||
``` bash
|
||||
estampa
|
||||
```
|
||||
|
||||
При першому запуску, у поточній теці буде згенеровано сертифікат сервера TLS (такий само як для сервера Gemini)
|
||||
|
||||
* server.crt
|
||||
* server.key
|
||||
|
||||
у цій же теці буде створено файлову структуру для збереження листів, за адресою в прикладі це буде:
|
||||
|
||||
```
|
||||
store/
|
||||
certs/
|
||||
user1.pem
|
||||
user2.pem
|
||||
priv/
|
||||
user1.pem
|
||||
user2.pem
|
||||
mbox/
|
||||
user1/
|
||||
user2/
|
||||
```
|
||||
* таким чином, при отриманні листів, вони будуть зберігатись у теці store/mbox/user/*.gmi
|
||||
|
||||
### systemd
|
||||
|
||||
Публічний сервер можна (і варто) запускати від окремого користувача:
|
||||
|
||||
``` bash
|
||||
useradd -m estampa
|
||||
```
|
||||
|
||||
```
|
||||
# /etc/systemd/system/estampa.service
|
||||
[Unit]
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
|
||||
User=estampa
|
||||
Group=estampa
|
||||
|
||||
# Якщо в конфігурації вказано відносні шляхи до сховища та сертифікатів,
|
||||
# важливо вказати шлях до робочої теки:
|
||||
# WorkingDirectory=/path/to/working/directory
|
||||
|
||||
ExecStart=/usr/local/bin/estampa
|
||||
|
||||
# Вкажіть null якщо не бажаєте журналювати роботу сервера
|
||||
StandardOutput=file:/home/estampa/debug.log
|
||||
StandardError=file:/home/estampa/error.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
* systemctl daemon-reload - оновити конфігурацію
|
||||
* systemctl enable estampa - автостарт при запуску системи
|
||||
* systemctl start estampa - запуск
|
||||
* systemctl status estampa - перевірити статус
|
||||
|
||||
### Клієнт
|
||||
|
||||
Наразі, я не знайшов готового рішення для десктоп чи мобільних платформ, як це можна було б очікувати від класичного поштового клієнта. Натомість, листи що надходять - складаються в файлову систему, з розширенням .gmi (Gemtext) і читаються вручну. Можливо, колись таки зроблю повноцінний GUI клієнт зі сповіщеннями і .. блек джеком, а наразі - маємо наступні опції:
|
||||
|
||||
### Lagrange
|
||||
|
||||
Найпростіший спосіб відправити листа - зробити це через GUI браузер Lagrange, адже з вірогідністю 99% ви будете користуватись саме ним:
|
||||
=> gemini://skyjake.fi/lagrange/
|
||||
|
||||
Для цього потрібно відкрити в основному меню "Identity" > "Import" та додати сертифікат (я просто зкопіював туди згенерований сертифікат одного з юзерів store/certs). Після цього, в боковому меню "Identities" (Ctrl+4) на імпортованому сертифікаті правою кнопкою миші відкриваємо контекстне меню та шукаємо пункт "Send mail as...", пишемо адресу отримувача, повідомлення та тиснемо кнопку "Send".
|
||||
|
||||
### CLI
|
||||
|
||||
У якості клієнта, можна використовувати командний рядок, засобами утиліт, які розраховані на відправлення даних через сокет з шаром SSL:
|
||||
|
||||
``` bash
|
||||
echo -e "misfin://test@localhost < test@localhost2 Test \r\nMESSAGE" | openssl s_client\
|
||||
-connect localhost:1958\
|
||||
-cert /path/to/cert.pem\
|
||||
-key /path/to/key.pem\
|
||||
-ign_eof
|
||||
```
|
||||
* вказуючи "< test@localhost2" ми додаємо відправника і підписуємо його "/path/to/cert.pem" і "/path/to/key.pem" з набору згенерованих скриньок, що вказані в прикладі estampa, тобто лежать вони в "store/certs/*" (запуск окремого сервера `localhost2` не потрібен)
|
||||
* згідно специфікації, тут важливо зберегти пробіл перед "\r\n" інакше буде помилка "59"
|
||||
* нагадаю, що для сумісності з редакцією B, максимальна довжина повідомлення разом із заголовком, повинна складати не більше 2048 байт
|
||||
* при успішному відправленні, як і у випадку з Gemini, має бути код "20" (успішно)
|
||||
|
||||
## Висновки
|
||||
|
||||
Як писав вище, я не вважаю даний експеримент заміною e-mail, адже той пройшов довгий шлях еволюції і став таким в умовах промислової реальності. Misfin, може стати хіба що базою для альтернативного обміну повідомленнями в локальних мережах або для спілкування з друзями-гіками, особливо, якщо ви - користувач CLI (хоча й тут, мабуть я обрав би комунікацію сокетами через telnet або IRC).
|
||||
|
||||
Тут також немає відповіді на питання щодо боротьби з потенційним спамом, але як вказано на офіційному сайті, в Geminispace це явище майже відсутнє через малу популярність протоколів сімейства Gopher.
|
||||
|
||||
## Посилання
|
||||
|
||||
=> gemini://misfin.org/ Офіційний сайт
|
||||
=> gemini://bbs.geminispace.org/s/misfin Група Misfin на BBS
|
||||
=> https://devzone.org.ua/post/misfin-poshtovyy-satelit-geminispace Веб-адаптація цього матеріалу з коментарями на сайті DevZone
|
||||
22
public/uk/mushroom-ink-for-calligraphy.gmi
Normal file
22
public/uk/mushroom-ink-for-calligraphy.gmi
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Грибні чорнила для каліграфії
|
||||
|
||||
В літню пору можна приготувати саморобні чорнила з диких грибів
|
||||
|
||||
Готове чорнило - відтінку сепії з характерною фактурою мікро-волокон,
|
||||
подібно водяним знакам, робить твір унікальним та неповторним!
|
||||
|
||||
Приготувати чорнила просто:
|
||||
|
||||
* збираємо десяток грибів Coprinopsis atramentaria до чистої банки
|
||||
* залишаємо ємність закритою у темному місці кімнатної температури, допоки гриби не пустять достатньо соку
|
||||
* відфільтровуємо сік марлею - він і буде пігментом для чорнильної суміші
|
||||
* далі потрібно зібрати з дерев смолу (камідь) в якості згущувача - це може бути абрикоса або вишня
|
||||
* смолу попередньо розмочуємо у воді, попередньо перемішуючи паличкою
|
||||
* співвідношення смоли до пігменту визначається експериментально, залежить від інструменту і техніки письма
|
||||
|
||||
Якщо пропорції підібрані правильно, суміш добре тримається на пері та не розтікається на папері.
|
||||
|
||||
Після висихання, пігмент довго тримає колір, а напис лишається стійким до стирання навіть при потраплянні води.
|
||||
|
||||
Описаний спосіб передбачає роботу з сирим матеріалом без консервації,
|
||||
тому готовий твір можна вважати й артефактом певного сезону!
|
||||
232
public/uk/my-first-impressions-of-rust.gmi
Normal file
232
public/uk/my-first-impressions-of-rust.gmi
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
# Мої перші враження від Rust
|
||||
|
||||
У попередній публікації, я описував свій перший досвід переходу з PHP на C++, в рамках створення браузеру для протоколу Gemini. Не зважаючи на те, що у цьому напрямку було виконано багато роботи, все таки, виникли певні труднощі з ручним контролем пам'яті, а саме - в рамках відсутності досвіду, просто не розумів де саме я міг допустити помилку, як довго живуть мої об'єкти і про які з них міг забути. Іншою проблемою стала збірка програми на різних пристроях, і врешті останнім фактором - стало встановлення додаткових залежностей, адже в C++ немає єдиної екосистеми пакунків, накшталт npm, Composer і т.п. З іншого боку, можна поставити Conan, але мені просто не захотілось в тому розбиратись, оскільки ця система вимагає використання Python, що якось трохи дисонує в плані подальших перспектив такого симбіозу.
|
||||
|
||||
Орієнтовно через місяць роботи з плюсами, вирішив не витрачати час, та спробувати сучасну і давно розхайповану мову Rust, від якої, чесно сказати, довго морозився. Від самого початку, всі дороги вели сюди і врешті перевів реалізацію браузеру в окрему гілку, почавши розробку спочатку.
|
||||
|
||||
## Синтаксис
|
||||
|
||||
Синтаксис Rust, мені здається якимось мультяшним, мовою, яка пишеться з права на ліво, своєю екзотичністю чимось нагадує Ruby, якою бувши розробником PHP, в принципі цікавився але в так і не дійшли руки. З іншого боку, нагадує JS, який особисто мені чомусь ніколи не подобався (як і Python). Тобто я був таким собі консерватором і варився в C-подібній екосистемі, нічого не хотів змінювати, але тут сталось те, що описано вище - тепер мені не хотілось розбиратись в додаткових інструментах контролю пам'яті а не синтаксисі.
|
||||
|
||||
### Стилістика
|
||||
|
||||
Стиль коду горизонтально-орієнтований, у той час як мені подобається писати і читати короткі рядки, віддаючи пріоритет скролу.
|
||||
|
||||
Якщо використовувати стандартну конфігурацію rustfmt, то написаний в ручному форматуванні код перетворюється в таку собі склеєну горизонтально кашу, яку реально важко читати, але вирішив не відмовлятись від загально-прийнятого оформлення і адаптуватись, максимально розділяючи об'ємні структури коду на окремі моди, про що згодом.
|
||||
|
||||
### Відсутність NULL
|
||||
|
||||
Що мене постійно манило в Rust - це сучасні підходи програмування, які виключають застарілі конструкції через їх спірне застосування. NULL - це тип даних, який по суті може означати і нічого і все водночас, цим часто зловживають розробники, використовуючи її як false або як тип, що має бути визначений батьківським елементом. Особисто в мене проблем з цим не було, оскільки й не такого бачив в * кодах PHP. З іншого боку, в C++ постійно доводилось працювати з nullptr, що в принципі знову натякає на давню потребу модернізації цього типу даних.
|
||||
|
||||
Rust від самого початку вимагає певного стилю програмування з коробки, таким чином ведучи за руку "праведною стезею" сучасних підходів, виключаючи помилки та власну хвору фантазію. Більше інформації про спірність використання NULL можна знайти в мережі, окремо описувати подробиці тут не буду.
|
||||
|
||||
### Option, Result, match
|
||||
|
||||
Продовжуючи тему сучасних конструкцій, на заміну NULL, в Rust присутні такі типи нумерованих списків як Option та Result, а також новий оператор match для роботи з ними і не тільки.
|
||||
|
||||
Option, на прикладі двох змінних, перша з яких повертає "рядок" а друга - "нічого":
|
||||
|
||||
``` rust
|
||||
let some_string: Option<String> = Some(String::from("Hello, Rust!"));
|
||||
let none_string: Option<String> = None;
|
||||
|
||||
match some_string {
|
||||
Some(string) => println!("деяке значення: {string}"),
|
||||
None => println!("значення відсутнє"),
|
||||
}
|
||||
```
|
||||
|
||||
* по аналогії можна додати необмежену кількість варіантів
|
||||
|
||||
Якщо певній функції потрібно повернути статус успішної операції або помилки, у таких випадках доречніше використовувати тип Result:
|
||||
|
||||
``` rust
|
||||
// Функція повертає i32 суми або String з описом помилки
|
||||
fn sum(a: i32, b: i32, max: i32) -> Result<i32, String> {
|
||||
let sum = a + b;
|
||||
if sum > max {
|
||||
return Err(format!("sum > max"));
|
||||
}
|
||||
Ok(sum)
|
||||
}
|
||||
|
||||
// якщо 1 + 2 менше максимального значення - друкуємо результат,
|
||||
// якщо більше - викликаємо паніку або делегуємо обробку помилки
|
||||
match sum(1, 2, 10) {
|
||||
Ok(value) => println!("sum: {value}"),
|
||||
Err(reason) => panic!("{reason}"),
|
||||
}
|
||||
```
|
||||
|
||||
Це звісно дуже спрощений приклад, для наочної демонстрації обробки результатів без NULL - такий підхід забезпечує коректність виконання програми вже на етапі її написання.
|
||||
|
||||
Оператор match дозволяє замінити собою класичні конструкції if та switch, і подібно до останнього, створювати вкладене розгортання умов.
|
||||
|
||||
### Лаконічність
|
||||
|
||||
Як і в Ruby, для Rust характерна лаконічність коду, про це, зокрема, натякають максимально скорочені ключові слова функцій (fn) та інших елементів. Кажучи безпосередньо про опис логіки, в нагоді початківцям стане такий інструмент як Clippy, який окрім пошуку помилкових конструкцій (типу зайвих референсів) запропонує замінити такий код:
|
||||
|
||||
``` rust
|
||||
match result.get(0) {
|
||||
Some(value) => Some(value.row),
|
||||
None => None,
|
||||
}
|
||||
```
|
||||
|
||||
на такий:
|
||||
|
||||
``` rust
|
||||
result.first().map(|value| value.row)
|
||||
```
|
||||
|
||||
Скорочення зустрічаються тут всюди, наприклад замість if/else або його цукру
|
||||
|
||||
```
|
||||
a = b > c ? true : false
|
||||
```
|
||||
|
||||
можна зустріти мабуть найкоротший варіант умови "?", яка поверне стандартну відповідь дочірнього компонента батьківському при невідповідності результату:
|
||||
|
||||
``` rust
|
||||
// зупинка циклу з select return при помилці
|
||||
for record in select(&tx)? {
|
||||
// зупинка циклу з update return при помилці
|
||||
update(&tx, record.id, false, record.time, record.name)?;
|
||||
}
|
||||
```
|
||||
* тут варто зазначити, що тип, який повертається повинен відповідати патерну return батька
|
||||
|
||||
### Не мутабельність за замовченням
|
||||
|
||||
Про синтаксис Rust можна багато чого розповісти. Наприклад, усі змінні (variables) є незмінними за замовченням. Якщо дані, що зберігаються в змінній повинні бути мутабельними, вона повинна бути від початку оголошена ключовим словом "mut":
|
||||
|
||||
``` rust
|
||||
let var_1: bool = true;
|
||||
let mut var_2: bool = true;
|
||||
|
||||
var_1 = false; // помилка, оскільки змінна var_1 оголошена як не мутабельна
|
||||
var_2 = false // ok
|
||||
```
|
||||
|
||||
Це може здаватись дивним, але на практиці дані у пам'яті для оголошених змінних доволі рідко змінюються. Наприклад, в плюсах я майже всюди оголошував константи, щоб у великій програмі не забути переписати значення випадково там, де це не було заплановано.
|
||||
|
||||
Не мутабельність насправді здебільшого потрібна для коректної роботи компілятора та контролера посилань (borrow-checker), які забезпечують безпечну роботу з пам'яттю за рахунок виконання однієї простої умови: може бути або одне мутабельне посилання або не обмежена кількість не мутабельних. Тут немає збірника сміття та рантайм-середовища, але є ось такі принципи.
|
||||
|
||||
### Вказівники
|
||||
|
||||
В Rust є і вказівники (pointers) і посилання (references). Перші в сирому вигляді тут зустрічаються доволі рідко, можливо просто саме я не зустрічав їх в екосистемі gtk-rs. Пояснити це можна безпекою роботи з пам'яттю. Для роботи з вказівниками, в Rust використовуються обгортки (wrappers) типу Box, Rc, Arc, та інші, - по суті вони є аналогами таких класів як std::shared_ptr в C++. Різняться сферою застосування: одні гарантують унікальність копії, інші - безпечну роботу в асинхронних потоках, або реалізують внутрішній лічильник посилань, або прапор блокування, тощо.
|
||||
|
||||
Зупинятись не буду, це просто величезна тема, вказівники тут є, але в режимі safe (окрім типів, що копіюються) майже всі вони обгорнені класом, формуючи такі "страшні" для незнайомців конструкції як
|
||||
``` rust
|
||||
Rc<Mutex<Option<i32>>>
|
||||
```
|
||||
* де "Rc" просто надає можливість клонування вказівника на ту саму область пам'яті, "Mutex" забезпечує мутабельність, а про "Option" - ви вже знаєте ;)
|
||||
|
||||
## Організація файлів
|
||||
|
||||
В Rust файли проєкту організовані у вигляді модів (mods) та крейтів (crates).
|
||||
|
||||
Моди - це по суті окремі файли, або модулі, з яких складається крейт, тобто фінальний пакунок програми. В проєкті також може бути декілька крейтів а також локальні їх форки, оголошені наприклад через наступну конструкцію:
|
||||
|
||||
``` Cargo.toml
|
||||
[dependencies.crate_1]
|
||||
package = "crate_1"
|
||||
version = "0.1.0"
|
||||
|
||||
[patch.crates-io]
|
||||
crate_1 = { path = "crate_1" } // локальний форк crate_1
|
||||
```
|
||||
|
||||
Кастомізація Cargo.toml - також окрема тема, тут не будемо зупинятись, скажу тільки що це головний файл маніфесту кожного для крейту, в той час як моди - просто файли, які підключаються до основного контролеру main.rs
|
||||
|
||||
Окрім цього, в екосистемі присутнє поняття бібліотек (lib.rs) - це по суті бібліотечні файли, які не можуть бути виконані без батьківського main.rs. Кожен крейт може включати в себе бібліотеку або тільки реалізовувати її, тоді такий крейт буде вважатись бібліотекою.
|
||||
|
||||
На відміну від C/C++, в Rust немає заголовкових файлів (headers), файли підключаються через ключове слово use засобами неймспейсів. Не потрібно руками резольвити файлові залежності, що найменше такий підхід є опціональним (з використанням анотацій). При стандартному підході, є два способи організації файлів:
|
||||
|
||||
* Старий (2015) - це коли файл моду має зарезервовану назву mod.rs та розміщується в теці з назвою модуля
|
||||
* Новий (2018) - файл моду має довільну назву, але такий підхід вимагає розміщення дочірніх модів у теці з однойменною назвою.
|
||||
|
||||
Новий підхід з'явився по причині того, що при відкритті багатьох файлів в IDE, всі таби будуть мати назву mod.rs, що погодьтесь, не дуже зручно в плані навігації, коли мізки зайняті кодом а не пошуком файлу в дереві.
|
||||
|
||||
Детальніше про тему йменування файлів, можна почитати тут:
|
||||
=> https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html
|
||||
|
||||
У цілому, мені знадобився певний час, щоб розібратись в темі. Тут немає такої файлової "анархії", яка є в інших мовах. Це на мою думку і добре, оскільки інші розробники зможуть розібратись у вашому коді (якщо ви не реорганізуєте проєкт анотаціями чи специфічними оголошеннями залежностей в Cargo.toml)
|
||||
|
||||
## Cargo
|
||||
|
||||
Що дійсно круто в Rust - це система пакунків Cargo та її репозиторій crates.io. Основна система пакунків тут одна, вона офіційна і є стандартною. Більше немає солянки з різних компіляторів, пакункових менеджерів, аналізаторів та іншого. Все зібрано довкола Cargo.
|
||||
|
||||
Легко ставити, легко майнтейнити. Єдине, якщо залежності програми вимагають бібліотек C, тим паче, як в моєму випадку - інтеграція з робочим столом GNOME, будьте готові змінювати дистрибутив на Fedora чи компілювати самому пів системи, ну або щось думати з контейнерами. Чудес не буває. А ось руками резольвити дикі бібліотеки під різні платформи не треба, Cargo все зробить сам - перевірить залежності, оновить код, перевірить помилки, скомпілює та опублікує ваш пакунок.
|
||||
|
||||
До речі, опублікований пакунок на crates.io видалити не вийде, оскільки такий підхід гарантує доступність версії для залежних пакунків, що встигли її включити в свій склад. Є опція "Yank" але будьте готові що вона просто позначить версію як відхилену, а не видалить її з репозиторію. Оновити пакунок можна тільки додавши нову його версію.
|
||||
|
||||
Варто окремо згадати про дані, які вивантажує команда "cargo publish". Спочатку я забув ознайомитись з документацією, але пощастило, що Cargo вже про все подбав і заігнорив сенситивні дані типу .vscode, .git та інші теки в корені проєкту. Перевірити, чи це дійсно так, можна командою в корені проєкту до його публікації:
|
||||
|
||||
```bash
|
||||
cargo package --list
|
||||
```
|
||||
|
||||
Оптимальним способом встановити Rust і Cargo останніх версій, є утиліта rustup:
|
||||
=> https://rustup.rs
|
||||
|
||||
## Інше
|
||||
|
||||
### Наслідування
|
||||
|
||||
Як відомо, Rust не реалізує класичну систему наслідування класів, оскільки така парадигма вважається застарілою. В той час, як мій браузер вже було написано з використанням обгортки gtkmm, тут мені довелось знову проєктувати скелет програми з нуля. Хоча я довго намагався натягувати логіку наслідування на ненатягуване.
|
||||
|
||||
По нормальному, програму все таки краще писати тією мовою і парадигмою, на якій і засобами якої реалізовано обраний фреймворк. Тоді, мій вибір на плюси пав саме тому, що не хотів руками реалізовувати всі ті низькорівневі обгортки для C, які є в плюсовому gtkmm і так вийшло що з Rust я отримав подвійний трабл - це і новий підхід і новий синтаксис як бонус :)
|
||||
|
||||
Загадавши процедурний підхід, в останній раз користувався ним років 15 тому, коли тільки починав програмувати. Потім купив книгу з патернами, перейшов на ООП і повністю забув що це таке, не знаю взагалі які реалізації зараз. Тобто для мене ієрархічні конструкції та функції - це дрімучий ліс, всі нейрони побудовані під об'єктно орієнтоване мислення, з наслідуванням та іншими плюхами, деякі з яких, до речі, сьогодні критикують через їх надмірні залежності від батьківських класів.
|
||||
|
||||
Поки що вивчаю цю тему на практиці, думаю розібратись з патернами на основі enum, тому додати нічого. Якщо вирішили йти шляхом Rust - це не про ООП та розширення батьківських класів. Іноді, мені доводиться наслідувати певні класи GObject в GTK обхідними шляхами, але нативно, майже все обертається довкола трейтів (trait) та їх імплементацій (impl).
|
||||
|
||||
### Лайфтайми
|
||||
|
||||
Тут хочу згадати тільки про один, не зручний особисто для мене момент: якщо ви працюєте з C-ішними обгортками типу gkt-rs, можуть виникнути певні непорозуміння з часом життя об'єктів, оскільки в Rust, автоматично відбувається зменшення лічильника посилань (або знищення), при виході об'єкта з області видимості.
|
||||
|
||||
Наприклад, в GTK є такий клас як SocketConnection, а також асинхронні методи як наприклад SocketClient::connect_async, який виконується на стороні фреймворку, але й живе в основному потоці програми Rust. Якщо ви НЕ передасте клон SocketConnection в цей метод, то компілятор автоматично зменшить для нього лічильник посилань, при виході за область видимості, а GTK, у свою чергу - видалить, коли його значення сягне нуля. Таким чином, отримаємо закрите з'єднання ще до того, як воно буде оброблене через асинхронний метод. Саме ця проблема, вирішується передачею клону вказівника SocketConnection в тред, який триматиме кількість посилань до свого завершення, або передаватиме його володіння дочірнім структурам. На практиці - це не завжди зручно і часто стає причиною непорозумінь в пошуках багів, коли сторона C очікує від вас ручного видалення посилання, а Rust робить це автоматично.
|
||||
|
||||
Тему керування пам'яттю в GTK засобами C та C++, описував окремо у попередніх публікаціях:
|
||||
|
||||
=> memory-management-in-gtk-applications.gmi Керування пам’яттю в програмах GTK
|
||||
=> memory-management-in-gtkmm-4.0.gmi Керування пам'яттю в gtkmm-4.0
|
||||
|
||||
### Асинхронність
|
||||
|
||||
Асинхронність тут є, така опція була не від самого початку (як наприклад в Go) але все таки її згодом впровадили. Як писав вище, я особисто не користуюсь штатними засобами Rust, оскільки API мого фреймворку надає зручні методи роботи в основному потоці, за рахунок вбудованої системи подій.
|
||||
|
||||
Багато хто сварить штатну реалізацію за використання "await", тут не скажу. Особисто для себе, зрозумів що асинхронні методи працюють з асинхронними методами, тобто якщо програма вимагає асинхронних потоків, така функціональність повинна бути закладена в її архітектуру на етапі проєктування. Звісно тут з'являється додатковий thread-safe інструментарій та відповідні розумні вказівники такі як Arc, блокувальники Mutex, RwLock та інше.
|
||||
|
||||
Особисто на практиці, намагався тільки швидкоруч натягнути інтеграційні тести (оскільки вже встиг написати зовнішню бібліотеку, щоб розвантажити основний код програми), але поки відклав це заняття, оскільки схоже, що для моєї задачі потрібна інтеграція Tokio.
|
||||
|
||||
## Спільнота
|
||||
|
||||
Спільнота Rust - супер жива та активна. Наразі є два форуми - безпосередньо для розробників мови та для користувачів, де вам не тільки оперативно допоможуть з будь яким питанням, а також безкоштовно проведуть аудит якості коду у відповідній гілці.
|
||||
|
||||
Є також чати, але мені вони не цікаві, тому у будь якому разі ком'юніті є і воно дружнє до новачків.
|
||||
|
||||
Щодо соціальних мереж, на YouTube мені подобається один канал Bitωise:
|
||||
=> https://www.youtube.com/@bitwiseuwu
|
||||
|
||||
Якщо ви початківець в програмуванні, можу порекомендувати також хоч не про Rust, але якісний україномовний канал Blogan, зокрема курс теорії C++ який стане в нагоді для розуміння основ організації пам'яті для різних типів даних:
|
||||
=> https://www.youtube.com/@BloganProgramming
|
||||
|
||||
Існує проєкт української локалізації Rust Book на GitHub:
|
||||
=> https://github.com/rust-lang-ua
|
||||
|
||||
## Висновки
|
||||
|
||||
В цілому, я задоволений своїм новим вибором. Чи готовий повертатись на плюси чи скажімо на C - не знаю, на плюси точно ні, а до C в мене якісь перманентно теплі почуття, не знаю чому. Можливо, ця мова є найпростішою, можливо тому що програмував на Arduino та рахував там доступну пам'ять. Тим не менше, я не можу довіряти собі настільки, щоб впевнено писати чистий код без витоків пам'яті у програмах більших за контролер датчика температури. Враховуючи кількість підказок компілятора та аналізатора Clippy, ця впевненість остаточно зникла а ставити додатковий софт чи витрачати час на вивчення такого, коли є все готове - не хочу.
|
||||
|
||||
Звісно, чутки про те, що швидкість Rust дорівнює C не зовсім однозначні. Тут можуть використовуватись зовсім інші підходи для однакових конструкцій. Без розуміння що діється під капотом компілятора, написати такий само швидкий код навряд чи можливо, але на першому місці тут саме безпека роботи з пам'яттю, швидкість для мене і не є пріоритетною. Впевнений, що копнувши тему оптимізацій, дізнаюсь ще багато нового.
|
||||
|
||||
Кажучи про сфери застосування і комерс, тут поки що далі свого аматорського проєкту діло не зайшло. Просто не знаю, де шукати ті замовлення окрім декількох контор на доу, які щось ляпають там англійською по лекалу. До того ж саму мову знати мало, тут більше відіграє роль екосистеми під яку ви пишете. У моєму випадку це малопопулярні GTK, Glib, Gio та інші, цих бібліотек мені вистачає як для роботи з сокетами, сертифікатами TLS, мультимедійними даними, так і для реалізації графічного інтерфейсу, але всі вони написані у свою чергу на C, на Rust я поки що не знайшов такого фреймворку, який би мені сподобався (egui і tauri - це трохи не те що мені підходить)
|
||||
|
||||
Чи замінить ця мова C - не знаю, як писав на початку, мені вона здається якоюсь мультяшною та іграшковою, хоча цією іграшкою можна бити горіхи. У той час як C - це скло, класика, на якій написане мабуть все сучасне програмне забезпечення. Як мені відповіли на одному з форумів GTK, ніхто не буде просто так переписувати мільйони строк відтестованого коду, тому я думаю що нинішній тренд пройде і залишить Rust на тому ж рівні та в ніші, у яких сьогодні знаходиться Ruby.
|
||||
|
||||
Ось мабуть і все, чим хотів поділитись, можливо згадаю ще щось - доповню.
|
||||
|
||||
Кому цікаві результати роботи над вищезгаданим браузером, прошу:
|
||||
=> https://github.com/YGGverse/Yoda
|
||||
15
public/uk/my-thoughts-on-the-modern-uanet-it-segment.gmi
Normal file
15
public/uk/my-thoughts-on-the-modern-uanet-it-segment.gmi
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Мої думки про сучасний ІТ сегмент UANET
|
||||
|
||||
Це один з моїх дописів з персонального блогу Yggdrasil, який я все-таки вирішив опублікувати широкому загалу, задля вирішення описаної на мою думку, проблеми. Допис невеличкий, але сподіваюсь вартує декілька хвилин читання і уваги.
|
||||
|
||||
Так вже вийшло, що в сучасному UANET зовсім немає серйозних ІТ ресурсів. Історично так склалось, що прогресивні спеціалісти кучкувались на просторі СНД, допоки володар мафіозної структури не здурів від старості і задля збереження своєї чортової династії не стикнув колишніх однодумців лобами.
|
||||
|
||||
Тепер же парадокс полягає у тому, що більшість кращих спеціалістів та контентщиків мусять виконувати фронтову роботу.
|
||||
|
||||
Простір пустує, окрім російськомовного OpenNET, де більшість юзерів досі не зрозуміли "за что" - читати нічого. Мабуть найбільша українська спільнота DOU - для мене завжди була заплавою для офісного планктону, до якого досі не спромоглись завезти темну тему оформлення, що тільки підтверджує мої претензії й до контенту і аудиторії.
|
||||
|
||||
Якось був заради RedoxOS зареєструвався на Matrix, знайшов там айтішну групу, з пари юзерів, але й там адмін виявився прищавим хлопчиком, якому постійно муляли мої не тематичні дописи щодо цілком суміжних до Linux тем, тому закрив цей чатик для себе на завжди. Багато новеньких, з невідомих мені причин, відкривають групи Telegram, от як наприклад - українська спільнота Rust. З мобільними чатами виходить так, що це ручно, але далеко не для тих, хто працює з клавіатурою, що знову таки говорить про рівень і орієнтованість таких ресурсів.
|
||||
|
||||
Було б добре, якби хтось організував сучасний проєкт по олдовим мотивам, найняв би пару копірайтерів для моніторингу і перекладу новин із сектору OpenSource. Для будь якого інформаційного ресурсу, важлива активність і не менш важлива - якість контенту, правильно налаштовані економічні питання для забезпечення цього. Я не знаю, чим зайняті наші діячі культури, що отримують за свою роботу і гроші і тендерний капітал.
|
||||
|
||||
Є очевидні речі і не закрита конкурентна ніша, можливо хтось, у кого є на те час, розум та ресурси, зможуть організувати контент без води з цими новомодними трендами; ресурс, який було б дійсно цікаво читати рідною мовою щодня.
|
||||
128
public/uk/nex-lightweight-gemini-alternative.gmi
Normal file
128
public/uk/nex-lightweight-gemini-alternative.gmi
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
# Протокол NEX - легка альтернатива Gemini
|
||||
|
||||
Останнім часом стало поширеним явище інтеграції TLS навіть там, де використання цієї технології може бути зайвим.
|
||||
|
||||
Сьогодні майже кожна програма включає в себе параноїдальні пресети безпеки з коробки, через які результуючий трафік часто шифрується в декілька шарів, тим само створюючи часові затримки на handshake і вичерпуючи заряд мобільних акумуляторів при передачі великих об'ємів даних.
|
||||
|
||||
Звісно, протокол Gemini - не про великий трафік. Тим не менше, хотілось би поділитись його спрощеною альтернативою - NEX, яка може згодитись деяким гікам, зокрема - в альтернативних мережах Onion та Yggdrasil, де канальний трафік між двома підключеннями вже зашифрований а використання класичного DNS вважається поганим тоном.
|
||||
|
||||
## Архітектура
|
||||
|
||||
NEX - це мережний протокол прикладного рівня, сімейства Gopher / Gemini.
|
||||
|
||||
Окрім відсутності характерних для Gemini вимог шифрування, протокол також зручний для прямих підключень по IP без використання DNS та SNI. Не потрібно генерувати сертифікати, слідкувати за їх актуалізацією та вигадувати костилі для Certbot.
|
||||
|
||||
Подібно до Gemini, взаємодія між клієнтом і сервером відбувається шляхом надсилання пакету через сокет. Для ідентифікації типу підключення клієнтом використовується схема nex://, а замість порту 1965 стандартним є 1900.
|
||||
|
||||
NEX не містить жодних мета заголовків: переадресацій, кодів статусів, MIME типів та іншого. Тим не менше, він підтримує і дозволяє працювати з різними типами даних, включно з Gemtext і медіа форматами.
|
||||
|
||||
Ось кілька простих правил з документації:
|
||||
|
||||
* формат файлу визначається клієнтом (браузером) за його розширенням у назві
|
||||
* якщо розширення не вказано, клієнт буде інтерпретувати документ як text/plain
|
||||
* якщо адреса закінчується слешем, таку адресу прийнято вважати директорією
|
||||
|
||||
Специфікація протоколу не декларує власного типу даних і розмітки за виключенням синтаксису посилань, формат яких подібний до Gemtext - тобто посилання можна робити клікабельними, використовуючи префікс => в text/plain.
|
||||
|
||||
## Клієнти
|
||||
|
||||
Протокол NEX є поширеним і давно відомим в середовищі Geminispace - такий популярний браузер як Lagrange підтримує його з коробки, тому не потрібно шукати додаткових рішень для GUI чи змінювати улюблений клієнт.
|
||||
|
||||
Говорячи про CLI, на мою думку, NEX є зручнішим і більше орієнтованим для користувачів саме командного рядка: для взаємодії можна використовувати такі базові утиліти як telnet, nc, ncat тощо:
|
||||
|
||||
``` bash
|
||||
telnet nightfall.city 1900
|
||||
nex
|
||||
```
|
||||
|
||||
## NPS
|
||||
|
||||
Подібно Titan для Gemini, NPS є сателітом для відправки даних на сервер - для NEX.
|
||||
|
||||
### Специфікація
|
||||
|
||||
Стандартним для NPS є порт 1915.
|
||||
|
||||
В залежності від реалізації та специфіки конкретного сервера, клієнт може відправляти команди і типи даних в порядку що відрізняється і так, як задумано автором конкретного сервісу.
|
||||
|
||||
Зокрема, сервіс чату в блокчейн KevaChat NPS - вимагає на першому кроці введення ASCII каптчі, вибору кімнати і вже потім - безпосередньо відбувається надсилання текстового повідомлення.
|
||||
|
||||
Кожен сервер має власний гайд в режимі CLI, та як правило посилання на документацію.
|
||||
|
||||
Характерною рисою NPS є одна крапка в останньому рядку повідомлення, яка також сигналізує кінець з'єднання.
|
||||
|
||||
### Приклади
|
||||
|
||||
Розберемо приклад надсилання оголошення на сервіс classifieds серверу nightfall.city
|
||||
|
||||
``` bash
|
||||
nc nightfall.city 1915
|
||||
classifieds
|
||||
YOUR MESSAGE GOES HERE
|
||||
.
|
||||
```
|
||||
|
||||
Якщо коротко розібрати команди:
|
||||
|
||||
* першим кроком відбувається підключення до серверу по DNS чи IP на 1915 порт
|
||||
* далі слідує вибір URI ресурсу, у даному випадку classifieds
|
||||
* після цього надсилається тіло повідомлення по одному рядку
|
||||
* якщо рядок містить тільки крапку - пакет вважається надісланим
|
||||
|
||||
Згідно такому принципу, можна надсилати і багато-строкові дані з певного файлу:
|
||||
|
||||
створимо file.txt з наступним вмістом:
|
||||
|
||||
``` file.txt
|
||||
classifieds
|
||||
___
|
||||
/\__\ ___
|
||||
/:/ / /\ \
|
||||
/:/__/ \:\ \
|
||||
/::\ \ ___ /::\__\
|
||||
/:/\:\ /\__\ __/:/\/__/
|
||||
\/__\:\/:/ / /\/:/ /
|
||||
\::/ / \::/__/
|
||||
/:/ / \:\__\
|
||||
/:/ / \/__/
|
||||
\/__/
|
||||
.
|
||||
```
|
||||
|
||||
тепер надішлемо його на сервер nightfall.city, до розділу classifieds (що вказаний у першому рядку) за допомогою утиліти cat
|
||||
|
||||
``` bash
|
||||
cat file.txt | nc nightfall.city 1915
|
||||
```
|
||||
|
||||
* сервер у прикладі не є тестовим, експериментуючи дотримуйтесь мережної етики і змістовності повідомлень
|
||||
* для коректної роботи nc в середовищі IPv6, можливо доведеться встановити пакет netcat-openbsd
|
||||
* варто зауважити, що деякі емулятори терміналу (наприклад, Tilda) можуть некоректно обробляти кириличні символи командою backspace, через відсутність підтримки мультибайтового UTF-8. Виправити це можна зміною емулятора або спробувавши вказати stty iutf8 в ~/.bashrc
|
||||
|
||||
## Проксі
|
||||
|
||||
Потокові дані легко проксуються через сервер Nginx, простий приклад з nginx.conf:
|
||||
|
||||
``` /etc/nginx/nginx.conf
|
||||
stream {
|
||||
server {
|
||||
listen 1915;
|
||||
proxy_pass [IP]:1915;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Висновки
|
||||
|
||||
Протокол NEX - це окремий різновид взаємодії та спілкування у мережі. Його переваги здебільшого стають зрозумілими після досвіду роботи з Gemini, який не дивлячись на лаконічність, все ж має ряд особливостей, які у певних випадках бувають зайві.
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://nightfall.city/nex/info/specification.txt Специфікація NEX
|
||||
=> https://nightfall.city/nps/info/specification.txt Специфікація NPS
|
||||
=> https://nightfall.city Офіційний сайт
|
||||
|
||||
## Читайте також
|
||||
|
||||
=> gemini-protocol-as-http-alternative.gmi Протокол Gemini як альтернатива HTTP
|
||||
=> yggdrasil-is-network-with-distributed-routing.gmi Yggdrasil - мережа з децентралізованим роутингом
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
# Розробка карткової гри з відкритим кодом OpenLegends
|
||||
|
||||
OpenLegends - це проєкт реалізації вільного рушія та інтерфейсу багатокористувацької карткової гри The Elder Scrolls Legends:
|
||||
|
||||
=> https://bethesda.net/en/game/legends
|
||||
|
||||
Репозиторій OpenLegends розташований за адресою:
|
||||
|
||||
=> https://github.com/openlegends
|
||||
|
||||
В першу чергу орієнтований на браузерний формат з використанням технологій HTML5.
|
||||
|
||||
Початковий код реалізовано мовою PHP 8, з використанням пакетного менеджеру Composer.
|
||||
|
||||
Причина обрання цієї мови - наявний досвід та переконання що її можливостей буде цілком достатньо для опису ігрової логіки.
|
||||
Також це зручно для інтеграції веб-застосунків.
|
||||
|
||||
В процесі, можливо буде виконано адаптацію для десктопів на Rust з використанням QT, хоча більш ймовірною буде імплементація на Electron.
|
||||
Окремо хотілось би мати альтернативу для CLI та протоколу Gemini, з підтримкою графіки ASCII.
|
||||
|
||||
Враховуючи функціональний потенціал оригінальної гри - завдання для однієї людини не тривіальне, утім мені все ж цікаво спробувати здійснити цю давню мрію і можливо, за цей час хтось також долучитися до розробки!
|
||||
|
||||
## Структура
|
||||
|
||||
Проєкт ділиться на три окремі гілки розробки:
|
||||
|
||||
* core-php - базовий інтерфейс рушія та набір абстрактних класів ядра
|
||||
* asset-php - ігрові набори: карти, ігрові режими та інші компоненти, що реалізують "core-php"
|
||||
* сервер - багатокористувацький інтерфейс, ймовірно буде реалізований на базі фреймворку Symfony
|
||||
|
||||
## Ядро (core-php)
|
||||
|
||||
Мета core-php - відділити будь яку ігрову логіку від базової структури рушія, що дозволить робити менш часті оновлення ядра (зберігаючи сумісність як найдовше) та активніше розвивати зовнішні інтерфейси - наприклад, ігрові набори, що реалізують певну його версію.
|
||||
|
||||
Наразі перебуває на стадії прототипу, для встановлення використовується остання версія з репозиторія:
|
||||
|
||||
```
|
||||
composer require openlegends/core:dev-main
|
||||
```
|
||||
|
||||
Відповідно, розробка ведеться в репозиторії:
|
||||
|
||||
=> https://github.com/openlegends/core-php
|
||||
|
||||
З появою першої стабільної версії, буде виконано перехід на Семантичні версії релізів:
|
||||
|
||||
=> https://semver.org/lang/uk
|
||||
|
||||
По суті, ядро складається з класів двох типів - Інтерфейси, що власне описують можливості класів карт, гравців, ігрових сесій тощо, та Абстрактні класи - що частково реалізують ці інтерфейси та використовуються ігровими наборами.
|
||||
|
||||
Здебільшого, Абстрактні класи містять у собі такі універсальні методи як Сетери і Гетери, але можуть містити й інші - для запобігання їх дублікації в наборах (де наприклад тільки карткових класів може бути близько тисячі).
|
||||
|
||||
Також, Абстрактні класи містять деякі абстрактні методи, які вимагають специфічної реалізації окремим класом: наприклад, дія карти, або гравця, яка в TES Legends мало чи не описує всю ігрову логіку в одному акті.
|
||||
|
||||
## Ігрові набори (asset-php)
|
||||
|
||||
Ігрові набори описують ігрову логіку і представлені в репозиторії asset-php:
|
||||
|
||||
=> https://github.com/openlegends/asset-php
|
||||
|
||||
Сюди входить оголошення та опис кожної карти, гравця, режиму гри та їх можливостей.
|
||||
Ігровий набір у своєму релізі має орієнтуватись на певну версію ядра "core-php", що спростить розробку та супровід окремо взятого ігрового серверу, який реалізує певний набір.
|
||||
|
||||
В розробці використовується остання версія репозиторія, яка наразі не містить стабільних релізів:
|
||||
|
||||
```
|
||||
composer require openlegends/asset:dev-main
|
||||
```
|
||||
|
||||
Дана версія включає тільки умовний тестовий набір класів, що складається з декількох умовних гравців (Player1, Player2), карт (Mouse, Goblin, Rat) та режиму гри з двох гравців (Arena), достатніх для тестування `core-php` та майбутнього ігрового серверу.
|
||||
|
||||
Після появи першого стабільного релізу ядра, можна буде створити реальний набір, оскільки ядро перебуває в розробці а інтерфейс може змінюватись, у тестовому наборі не доречно обслуговувати більше трьох карт.
|
||||
|
||||
Говорячи про реліз, ймовірно, це буде копія набору TES Legends, оскільки розрахунок нового ігрового балансу - це окреме завдання з області математики і потребує немало часу, включно з тестуванням реальними гравцями.
|
||||
|
||||
Ігрові дані для набору можна подивитись на сайті "TES Legends Pro" (можна запозичити звідти графіку):
|
||||
|
||||
=> https://teslegends.pro
|
||||
|
||||
По зрозумілим причинам, розмістити такі дані в рамках проєкту OpenLegends не є доречним, але це не заважає розгорнути альтернативний форк "asset-php".
|
||||
|
||||
## Приклади роботи
|
||||
|
||||
Завдання будь якого рушія - спрощення розробки гри.
|
||||
Отже, для кращого розуміння що собою являє рушій OpenLegends, розглянемо декілька найпростіших прикладів на основі тестового набору.
|
||||
|
||||
У даному матеріалі не будуть описані інші можливості, враховуючи що вони можуть різнитися в новіших версіях.
|
||||
|
||||
### Взаємодія карт
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
$mouse = new \OpenLegends\Asset\Test\Card\Mouse();
|
||||
$rat = new \OpenLegends\Asset\Test\Card\Rat();
|
||||
|
||||
$damage = new \OpenLegends\Asset\Test\Card\Action\Damage(
|
||||
$mouse
|
||||
);
|
||||
|
||||
$damage->card(
|
||||
$rat
|
||||
);
|
||||
|
||||
var_dump(
|
||||
$rat->getHealth()
|
||||
);
|
||||
```
|
||||
|
||||
### Картковий пул
|
||||
|
||||
Використовується здебільшого в ігрових сесіях для організації карткових наборів, наприклад колоди, стопки скиду, карт в руці, тощо:
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
$deck = new \OpenLegends\Asset\Test\Pool\Card();
|
||||
|
||||
$deck->addCard(
|
||||
new \OpenLegends\Asset\Test\Card\Goblin()
|
||||
);
|
||||
|
||||
$deck->addCard(
|
||||
new \OpenLegends\Asset\Test\Card\Mouse()
|
||||
);
|
||||
|
||||
$deck->addCard(
|
||||
new \OpenLegends\Asset\Test\Card\Rat()
|
||||
);
|
||||
|
||||
var_dump(
|
||||
$deck->getRandomCard()->getName()
|
||||
);
|
||||
```
|
||||
|
||||
### Ігрова сесія
|
||||
|
||||
На реальному сервері, гра та її учасники створюються інтерактивно через запити графічного інтерфейсу.
|
||||
|
||||
У тестовому наборі, сесія кожного гравця ініціюється в конструкторі його класу автоматично, наприклад:
|
||||
|
||||
=> https://github.com/openlegends/asset-php/blob/main/src/Test/Game/Player/Player1.php
|
||||
|
||||
При цьому, реалізується абстрактний клас ядра "\OpenLegends\Core\Abstract\Game\Player", що у свою чергу - включає готові набори карт, об'єкт користувача, статус здоров'я тощо.
|
||||
|
||||
Після того, як об'єкт або клас гравця з картковими пулами було створено, ці дані передаються до об'єкту нової гри та ініціюється сценарій старту:
|
||||
|
||||
```
|
||||
<?php
|
||||
|
||||
$game = new \OpenLegends\Asset\Test\Game\Arena();
|
||||
|
||||
$game->addPlayer(
|
||||
new OpenLegends\Asset\Test\Game\Player\Player1()
|
||||
);
|
||||
|
||||
$game->addPlayer(
|
||||
new OpenLegends\Asset\Test\Game\Player\Player2()
|
||||
);
|
||||
|
||||
$game->start();
|
||||
```
|
||||
|
||||
Інші приклади (зокрема специфіка роботи ігрового серверу через веб-сокети, взаємодія з базами даних, створення інтерактивних інтерфейсів) вартують розгляду в рамках окремої гілки серії.
|
||||
|
||||
Сподіваюсь, на даному етапі, мені вдалось описати загальний статус та принципи розробки проєкту.
|
||||
|
||||
Кому цікава реалізація TES Legends з відкритим кодом - ласкаво просимо до OpenLegends!
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://github.com/openlegends Сторінка проєкту на GitHub
|
||||
336
public/uk/personal-snac-instance-for-yggdrasil-network.gmi
Normal file
336
public/uk/personal-snac-instance-for-yggdrasil-network.gmi
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
# Налаштування Fedi-сервера Snac для мережі Yggdrasil
|
||||
|
||||
Невдовзі, після своїх попередніх роздумів про p2p, вирішив спробувати підняти власний експериментальний інстанс Fediverse. При чому, зробити це засобами оверлейної мережі Yggdrasil, оскільки я не планую купувати для цієї іграшки виділений IP чи VPS, натомість буду хоститись з модему, одноплатника або взагалі з ПК, коли буваю онлайн, з динамічною адресою за NAT.
|
||||
|
||||
Пишу цю нотатку в першу чергу - для себе, а також, вона може бути корисною для тих, хто як і я тільки починає свої експерименти у сфері адміністрування власного вузла Fediverse і цікавиться альтернативними мережами, в контексті Linux.
|
||||
|
||||
* Згодом, мною було виявлено, що Snac автоматично видаляє "старі" пости (Issue #406) я це намагався виправити (PR #409) але згодом відмовився від даного сервера на користь Mitra. Якщо ви плануєте перманентно зберігати у себе на інстансі якісь історичні записи, документацію тощо, зверніть увагу на цю особливість рушія та налаштуйте резервні копії вашої теки storage для уникнення втрати даних!
|
||||
|
||||
## Що таке Snac
|
||||
|
||||
Snac (https://codeberg.org/grunfink/snac2) - це мінімалістична, JS-less, написана мовою C альтернатива серверу Mastodon, яка також не потребує інсталяції PostgreSQL, натомість зберігає усі дані профілю у файлах JSON. Нещодавно, до цього серверу було додано підтримку IPv6 (PR#256), а отже - він буде працювати й з діапазоном Yggdrasil `0200::/7`
|
||||
|
||||
Оскільки Yggdrasil дозволяє безкоштовно генерувати не обмежену кількість статичних IP (на базі приватного ключа Ed25519), тут немає звичної потреби в DNS. Хоча, можна опціонально прикрутити Alfis (https://github.com/Revertron/Alfis), але особисто я цим ділом не користуюсь (зокрема, й через досі не вирішену проблему #364, тому також не хочу нав'язувати його в рамках протоколу ActivityPub - буде просто формат `username@IPv6`, який мені не потрібно а ні оновлювати, а ні майнити потім.
|
||||
|
||||
## Встановлення
|
||||
|
||||
1. Точний перелік пакетів для Debian я не знаю, оскільки моя система не нова і вже має встановлені раніше пакунки. Як вказано в README, я тільки встановив `libssl-dev` і `libcurl4-openssl-dev` (для Fedora має бути приблизно те само з постфіксом `*-devel`)
|
||||
|
||||
2. Далі, створюємо окремого системного юзера, щоб ізолюватись від потенційних вразливостей:
|
||||
|
||||
``` bash
|
||||
useradd -m snac
|
||||
```
|
||||
|
||||
3. Змінюємо для зручності середовище на `bash` у файлі `/etc/passwd`
|
||||
4. Логінимось через `su snac` і переходимо в домашню директорію цього юзера: `cd`
|
||||
5. Завантажуємо останній вихідний код: `git clone https://codeberg.org/grunfink/snac2.git`
|
||||
6. Заходимо в робочу директорію `cd snac2`
|
||||
7. Компілюємо `make && sudo make install` і встановлюємо з відповідними правами
|
||||
8. Ініціалізуємо серверне сховище: `snac init /home/snac/storage`
|
||||
9. І додамо до нього нашого першого юзера `snac adduser /home/snac/storage`
|
||||
10. Далі продовжуємо від `root` виконавши команду `exit`
|
||||
|
||||
## Налаштування
|
||||
|
||||
Я вже маю встановлений і налаштований вузол Yggdrasil, якщо комусь цікавий процес встановлення, скористайтесь попередньою публікацією:
|
||||
|
||||
=> yggdrasil-is-network-with-distributed-routing.gmi Yggdrasil - мережа з децентралізованим роутингом
|
||||
|
||||
або офіційною документацією:
|
||||
|
||||
=> https://yggdrasil-network.github.io/documentation.html
|
||||
|
||||
### Адреса підмережі Yggdrasil
|
||||
|
||||
Можна пропустити цей крок і використовувати основну адресу `2*`, якщо порти `80` чи `8001` не зайняті. Але зауважте, що в рамках API протоколу ActivityPub, сервер Snac надаватиме вашу адресу хосту іншим нодам, а ті - її кешуватимуть, як частину ID і оскільки локально адреса хосту зберігається по файлам, а не в БД, потім буде важко її замінити. Тому краще виділити окрему, особливо - якщо це продакшн:
|
||||
|
||||
1. `yggdrasilctl getself` - дізнаємось свій айпішник, зокрема вивід `IPv6 subnet`
|
||||
2. `ifconfig lo inet6 add IP` - замість IP вказуємо довільну адресу для отриманого діапазону, наприклад `3xx:xxxx:xxxx:xxxx::fed/64` , де `fed` - така собі гра слів в рамках "словника" IPv6 (0-9A-F).
|
||||
* варто зауважити, що дані маршрутизації `ifconfig` не зберігаються після ребуту системи, для цього потрібно додати відповідний запис (команду з пункту 2), наприклад до `/etc/netplan/01-ygglo.yaml`, `/etc/network/interfaces`, або безпосередньо до systemd `yggdrasil.service` (секція `ExecStartPost=`) - залежно від операційної системи.
|
||||
|
||||
### Проксі Nginx
|
||||
|
||||
На моєму сервері вже встановлено веб-сервер Nginx, який займає порт `80`, я поки не хочу нічого змінювати, а також не хочу мати публічні адреси Snac з його стандартним портом `8001`. Тому, оскільки вже маю виділену адресу підмережі, просто запроксую API на `80` порт через новий віртуальний хост, частково використавши оригінальний приклад конфігурації:
|
||||
|
||||
=> https://codeberg.org/grunfink/snac2/src/branch/master/examples/nginx-alpine-ssl/default.conf default.conf
|
||||
|
||||
``` /etc/nginx/sites-available/default
|
||||
server {
|
||||
listen [3xx:xxxx:xxxx:xxxx::fed]:80;
|
||||
server_name 3xx:xxxx:xxxx:xxxx::fed;
|
||||
|
||||
location @proxy {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_redirect off;
|
||||
proxy_connect_timeout 90;
|
||||
proxy_send_timeout 90;
|
||||
proxy_read_timeout 90;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Proxy "";
|
||||
proxy_pass_header Server;
|
||||
proxy_buffering on;
|
||||
tcp_nodelay on;
|
||||
proxy_pass http://[3xx:xxxx:xxxx:xxxx::fed]:8001;
|
||||
proxy_set_header Host $http_host;
|
||||
}
|
||||
|
||||
location /.well-known/webfinger {
|
||||
try_files $uri @proxy;
|
||||
}
|
||||
|
||||
location /.well-known/nodeinfo {
|
||||
try_files $uri @proxy;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri @proxy;
|
||||
}
|
||||
|
||||
location /fedi/ {
|
||||
try_files $uri @proxy;
|
||||
}
|
||||
}
|
||||
```
|
||||
* `systemctl reload nginx` - застосовуємо зміни
|
||||
* можливо, захочете створити окремий конфігураційний файл для Nginx, замість `default` - в мене він один на всі хости.
|
||||
* опціонально, засобами Nginx, можна закрити окремі локації по IP
|
||||
|
||||
Як бачите, на прикладі вище не вказано порт `443`, а також немає сертифікатів SSL. Це зроблено спеціально, оскільки Yggdrasil вже має захищений канал, і я не хочу створювати тут зайвий шар.
|
||||
|
||||
Оскільки клієнтські підключення Yggdasil також мають статичну адресу, я вирішив обмежити доступ до адміністративного API (адмінка усіх акаунтів + `oauth`) по IP. Наскільки це ефективно і чи не забув про інші адреси - я не знаю, але додам свій приклад регулярного виразу для `location`:
|
||||
|
||||
``` default
|
||||
location ~ /([^\/]+/admin|oauth) {
|
||||
allow ADMIN_IP;
|
||||
deny all;
|
||||
try_files $uri @proxy;
|
||||
}
|
||||
```
|
||||
|
||||
### Конфігурація Snac
|
||||
|
||||
Відредагуємо раніше згенерований командою `snac init` файл `/home/snac/storage/server.json`:
|
||||
|
||||
``` server.json
|
||||
{
|
||||
"host": "[3xx:xxxx:xxxx:xxxx::fed]",
|
||||
"prefix": "",
|
||||
"address": "3xx:xxxx:xxxx:xxxx::fed",
|
||||
"port": 8001,
|
||||
"layout": 2.7,
|
||||
"dbglevel": 0,
|
||||
"queue_retry_minutes": 2,
|
||||
"queue_retry_max": 10,
|
||||
"queue_timeout": 6,
|
||||
"queue_timeout_2": 8,
|
||||
"cssurls": [
|
||||
""
|
||||
],
|
||||
"def_timeline_entries": 50,
|
||||
"max_timeline_entries": 50,
|
||||
"timeline_purge_days": 120,
|
||||
"local_purge_days": 0,
|
||||
"min_account_age": 0,
|
||||
"admin_email": "",
|
||||
"admin_account": "",
|
||||
"title": "",
|
||||
"short_description": "",
|
||||
"short_description_raw": false,
|
||||
"protocol": "http",
|
||||
"fastcgi": false
|
||||
}
|
||||
```
|
||||
* звертаю увагу, що протокол у моєму прикладі змінено на `http`
|
||||
|
||||
### Доступи iptables
|
||||
|
||||
Конфігурація у прикладах не передбачає доступу до ноди з мережі Інтернет, тому я відкрив порт тільки для Yggdrasil, щоб інші вузли в рамках цієї мережі могли взаємодіяти між собою на івентах типу фоловінгу (обидва вузли мають бути онлайн для транзакції):
|
||||
|
||||
``` bash
|
||||
ufw allow from 0200::/7 to any port 80
|
||||
```
|
||||
* якщо не користуєтесь Nginx, або сервер має стандартний чи інший порт, просто замість `80` вкажіть актуальний, наприклад `8001`;
|
||||
* якщо обмежуєте трафік по діапазону `0200::/7`, також зверніть увагу на зауваження щодо приватного режиму, про який описано нижче.
|
||||
|
||||
### Налаштування systemd
|
||||
|
||||
Є готовий офіційний приклад конфігурації:
|
||||
|
||||
=> https://codeberg.org/grunfink/snac2/src/branch/master/examples/snac.service snac.service
|
||||
|
||||
але я його трохи доповнив:
|
||||
|
||||
``` /etc/systemd/system/snac.service
|
||||
[Unit]
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=snac
|
||||
Group=snac
|
||||
ExecStart=/usr/local/bin/snac httpd /home/snac/storage
|
||||
StandardOutput=file:/home/snac/debug.log
|
||||
StandardError=file:/home/snac/error.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
* `systemctl daemon-reload` - оновлюємо конфігурацію `systemd`
|
||||
* `systemctl enable snac` - автостарт при запуску системи
|
||||
* `systemctl start snac` - запуск
|
||||
* `systemctl status snac` - перевіряємо статус
|
||||
|
||||
### Резервне копіювання
|
||||
|
||||
Оскільки база даних Snac зберігається у файловому форматі, досить просто бекапити профіль по лише одній локації.
|
||||
|
||||
Я роблю це засобами `rsync` для різних часових інтервалів наступною командою `crontab -e`:
|
||||
|
||||
``` bash
|
||||
@daily /usr/bin/rsync -av --delete /home/snac/storage /path/to/snac/daily
|
||||
@weekly /usr/bin/rsync -av --delete /home/snac/storage /path/to/snac/weekly
|
||||
@monthly /usr/bin/rsync -av --delete /home/snac/storage /path/to/snac/monthly
|
||||
```
|
||||
|
||||
## Користування
|
||||
|
||||
Після запуску Snac командою `snac httpd /home/snac/storage` або через сервіс `systemd`, можна спробувати відкрити у браузері `http://[3xx:xxxx:xxxx:xxxx::fed]`.
|
||||
|
||||
### Тестування взаємодії (API)
|
||||
|
||||
Щоб перевірити взаємодію з іншим вузлом Yggdrasil, повторюємо для нього ті само дії і робимо тестовий фоловінг чи переписку між користувачами через Web UI або підключений зовнішній клієнтський застосунок.
|
||||
|
||||
### Тюнінг браузеру
|
||||
|
||||
Якщо вперше користуєтесь сайтами Yggdrasil у Firefox, можливо знадобиться оптимізувати обробку "сирих" IPv6 адрес в `about:config`:
|
||||
* `browser.fixup.fallback-to-https`:`false` - вимкнути редірект `http` -> `https`
|
||||
* `browser.fixup.alternate.enabled`:`false` - викнути автоматичну обробку префіксу `www`
|
||||
|
||||
### Теми Web UI
|
||||
|
||||
В README є перелік посилань на CSS теми, за допомогою яких можна кастомізувати веб-інтерфейс Snac на власне вподобання:
|
||||
|
||||
=> https://codeberg.org/grunfink/snac2#incredibly-awesome-css-themes-for-snac
|
||||
|
||||
Спочатку я не зрозумів, як підключати нові теми, і додав посилання на умовний файл `/theme.css` до `/home/snac/storage/server.json`, а також створив на нього аліас локального шляху в Nginx (щоб задовольнити браузерну політику CORS):
|
||||
|
||||
``` default
|
||||
location /theme.css {
|
||||
alias /var/www/snac/theme.css;
|
||||
}
|
||||
```
|
||||
* цей приклад я лишаю на випадок, якщо ви захочете підключити додаткові `cssurls`
|
||||
|
||||
Але згодом виявилось, що при створенні інстансу, генерується стандартний файл у теці `/home/snac/storage/style.css` який буде конфліктувати з новою темою (адже він підключатиметься окремо від масиву конфігурації `cssurls`).
|
||||
|
||||
Таким чином, потрібно просто переписати вміст стандартного файлу `/home/snac/storage/style.css` обраною темою, а масив `cssurls` потрібен тільки для тюнінгу поточної теми, без правки її оригіналу. Щоб повернутись до оригінальної теми - достатньо видалити цей файл, після чого згенерується стандартний файл Snac.
|
||||
|
||||
### Специфіка клієнтських застосунків
|
||||
|
||||
Особисто, я встиг перевірити тільки Tuba (https://tuba.geopjr.dev/). Як виявилось, даний клієнт має захардкожену обробку схеми HTTPS, тому якщо користуєтесь цим застосунком, доведеться налаштувати окремий інтерфейс Nginx на порті `443` з використанням сертифікату, хоч у випадку з Yggdrasil - це зайва "капуста" і мабуть поки що лишусь на веб-інтерфейсі або зроблю і викладу потім патч.
|
||||
|
||||
UPD 1. розробник виявився супер-оперативним і вже створив гілку з патчем:
|
||||
|
||||
=> https://github.com/GeopJr/Tuba/tree/refs/heads/experiment/ignore-dom-secure
|
||||
|
||||
Flatpak є в Артефактах:
|
||||
|
||||
=> https://github.com/GeopJr/Tuba/actions/runs/14965019143
|
||||
|
||||
Єдине що - тільки поки не "завезли" валідацію IPv6 у вікно авторизації, тому я тимчасово користуюсь аліасом в `/etc/hosts` і вказую у якості URL авторизації `http://alias`. Можливо, у наступних випусках додатка це вже буде не актуально.
|
||||
|
||||
UPD 2. Наразі, гілка об'єднана з `main`, тому замість аліасів для IPv6, можна просто додати при запуску `TUBA_SKIP_STRICT_VALIDATION=1`, наприклад:
|
||||
|
||||
``` bash
|
||||
TUBA_SKIP_STRICT_VALIDATION=1 'builddir/dev.geopjr.Tuba'
|
||||
```
|
||||
|
||||
UPD 3. Особисто я користуюсь окремою гілкою, де ця нубська валідація HTTP випиляна, і жодних додаткових флагів для запуску не потрібно:
|
||||
|
||||
=> https://github.com/YGGverse/Tuba/tree/multiprotocol-address-support
|
||||
|
||||
Для збірки з форку:
|
||||
|
||||
1. `git clone https://github.com/YGGverse/Tuba.git`
|
||||
2. `git checkout multiprotocol-address-support`
|
||||
3. `make && make install`
|
||||
* або для Flatpak:
|
||||
``` bash
|
||||
flatpak-builder --force-clean build\
|
||||
--install-deps-from=flathub\
|
||||
--install\
|
||||
--repo=repo\
|
||||
--user\
|
||||
build-aux/dev.geopjr.Tuba.Devel.json
|
||||
```
|
||||
|
||||
Також, в мене є ще одна, окрема гілка, де я форсовано застосовую українську локаль для постів, адже сервер Snac має певний недопил по синхронізації даного API:
|
||||
|
||||
=> https://codeberg.org/grunfink/snac2/issues/388
|
||||
|
||||
Скачати All-in-One збірку можна тут, виконавши для встановлення кроки вище, але з `git checkout ps`:
|
||||
|
||||
=> https://github.com/YGGverse/Tuba/tree/ps
|
||||
|
||||
### Моніторинг трафіку
|
||||
|
||||
Оскільки сервер Snac не передбачає користування JS інтерфейсом, а поточна конфігурація використовує Nginx, можу по ходу справи також порадити goaccess (https://goaccess.io/) - CLI утиліту для зручного моніторингу статистики користувацького трафіку, якщо такий буде:
|
||||
|
||||
``` bash
|
||||
goaccess /var/log/nginx/access.log
|
||||
```
|
||||
|
||||
### Приватний режим
|
||||
|
||||
Yggdrasil дозволяє маскувати реальний IP, якщо ви користуєтесь власним вихідним вузлом (https://publicpeers.neilalexander.dev/). Звісно, таку можливість не варто розглядати в контексті анонімізації окремо без додаткових шарів, оскільки використання протоколу Yggdrasil без наприклад таких проксі, як Shadowsocks - легко виявляється.
|
||||
|
||||
Ця тема виходить за рамки матеріалу, але зверну увагу на деякі аспекти, якщо ви плануєте користуватись збіркою Snac + Yggdrasil в режимі "інкогніто". Протокол ActivityPub передбачає "спілкування" між серверами для обміну івентами. Тобто потенційний фоловер може відправити запит підписки на на дозволений у фаєрвол інтерфейс `0200::/7`, але вказати в заголовках події ActivityPub - зворотній DNS на вузол в мережі Інтернет. Таким чином, ваша система здійснить транзакцію з білого IP через системний резольвер або без нього, використовуючи локальний Curl API від Snac.
|
||||
|
||||
Потенційних сценаріїв витоку можна придумати багато. Я переглянув вихідний код Snac, та не знайшов у ньому жодних фільтрів взаємодії на вихідні підключення. Тому, як і для іншого не спеціалізованого софту, для цієї мети бажано використовувати ізоляцію роутера з контейнера Docker, LXC або віртуалізуватись засобами QEMU.
|
||||
|
||||
При використанні клієнтського API, окремого аудиту потребуватиме обробка віддаленого вмісту постів, аватарів та іншого. У цьому напрямку, є перші кроки, зокрема по частині Web UI (PR#394), але я не впевнений, що буду пиляти це довгий час, адже з цих причин, давно користуюсь протоколом Gemini. Якщо вам все таки цікавий напрямок "довіри" HTML/HTTP, можете спробувати цю гілку, або продовжити її розробку для себе:
|
||||
|
||||
=> https://codeberg.org/postscriptum/snac2/src/branch/enhanced-webui-privacy
|
||||
|
||||
Можна, в принципі, додати правило на "останній рубіж" `iptables`, наприклад заблокувати вихідні пакети в Інтернет:
|
||||
|
||||
``` bash
|
||||
ufw default deny outgoing
|
||||
```
|
||||
|
||||
Якщо користуєтесь Yggdrasil в оверлейному режимі (тобто через публічний пір), важливо додати його до білого списку:
|
||||
|
||||
``` bash
|
||||
ufw allow out to PUBLIC_PEER_IP
|
||||
```
|
||||
* якщо вузлів декілька, додаємо їх послідовно
|
||||
* перевірити поточні правила `ufw` можна командою `ufw status verbose`
|
||||
* можливо, для тесту конекту варто перезапустити сервіс `systemctl restart yggdrasil`
|
||||
* також, не забудьте про системний DNS резольвер та інші служби, що залежать від вихідних з'єднань!
|
||||
|
||||
На останок, дозволяємо локальні запити для взаємодії між вузлами:
|
||||
|
||||
``` bash
|
||||
ufw allow out to 0200::/7 from 0200::/7
|
||||
```
|
||||
|
||||
### Багато-мережевий режим
|
||||
|
||||
Можливо, згодом (коли розберусь) окремо опишу, як запустити інстанс у різних мережах одночасно, наприклад Інтернет + Yggdrasil, але наскільки бачу по коду Snac, його файлова реалізація сховища дозволяє працювати тільки в рамках однієї мережі / хосту.
|
||||
|
||||
Думаю, тут можна буде погратись з проксуванням з авто-заміною, організувати реплікацію або використанням "білого" DNS і записів `A`/`AAAA` відповідно до типу з'єднання клієнтського резольвера. Так чи інакше, це - вже зовсім інша історія!
|
||||
|
||||
## Посилання
|
||||
|
||||
Інших вузлів я поки не зустрічав, тому підписуйтесь на мій - для тестів і спілкування:
|
||||
|
||||
=> http://[302:68d0:f0d5:b88d::fed]/ps
|
||||
|
||||
Можливо, колись сформується нове локальне сузір'я :)
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# Підготовка ігрових файлів Half-Life (Steam) для релізу BitTorrent
|
||||
|
||||
Так як в мережі Yggdrasil з'явився старий-новий сервер Half-Life 1 (App ID 70),
|
||||
вирішив підтримати дану ініціативу виклавши асети придбаної гри на новоспечений агрегатор βtracker.
|
||||
|
||||
Чому я викладаю даний асет? Тому що не кожен сьогодні може собі дозволити офіційну купівлю гри.
|
||||
Тому роблю це для таких як сам, а не для того, щоб якось зашкодити разрабам чи бути "піратом".
|
||||
|
||||
У даному рецепті, я не гарантую повного видалення чутливих даних, але пишу замітку, так як не хочу щоразу пригадувати ці кроки. Якщо згадаю про якусь пропущену фільтрацію, то доповню.
|
||||
|
||||
## Видалення даних профілю
|
||||
|
||||
Щоб не шарити свої персональні дані:
|
||||
|
||||
``` bash
|
||||
grep -r "USERNAME" /path/to/Half-Life
|
||||
```
|
||||
* де USERNAME - логін Steam, змінюємо на guest чи інший
|
||||
|
||||
## Видалення ідентифікаторів збереження профілю
|
||||
|
||||
``` bash
|
||||
grep -r "accountid" /path/to/Half-Life
|
||||
```
|
||||
* видаляємо знайдені файли або заміняємо ID на фіктивний
|
||||
|
||||
## Посилання
|
||||
|
||||
=> half-life-on-linux-using-xash3d-fwgs-engine.gmi Інструкція по встановленню Half-Life в Linux на базі рушія Xash3D / FWGS
|
||||
=> yggdrasil-is-network-with-distributed-routing.gmi Yggdrasil - мережа з децентралізованою маршрутизацією
|
||||
=> https://store.steampowered.com/app/70/HalfLife/ Придбати офіційні ігрові асети на Steam
|
||||
=> http://[302:68d0:f0d5:b88d::fdb]/5dc7fad742310aa688970f9574bb6297b55de8d2 Завантажити ігрові асети через BitTorrent на βtracker
|
||||
=> http://[222:a8e4:50cd:55c:788e:b0a5:4e2f:a92c]/yggdrasil:games:half_life_on_xash3d Грати онлайн в мережі Yggdrasil (скрол в самий низ)
|
||||
132
public/uk/rssto-convert-rss-feeds-into-multiple-formats.gmi
Normal file
132
public/uk/rssto-convert-rss-feeds-into-multiple-formats.gmi
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
# rssto: конвертація фідів у різні формати
|
||||
|
||||
Днями, мене знову зацікавила тема RSS додатку для браузера, але нічого путнього для себе не знайшов. Звісно, є різні розширення і програми, але в мене є ряд вимог для них:
|
||||
|
||||
* мінімалізм - мені не потрібен комбайн з вбудованим браузером, а лише список заголовків з певного сайту
|
||||
* приватність - сучасні рішення мало приділяють уваги цьому питанню і часто пропускають сторонні запити навіть при використанні проксі
|
||||
* агрегація - можливість читати новини в зручних для мене форматах
|
||||
|
||||
Будучи розробником, мені простіше написати власну реалізацію аніж шукати існуючу і виявляти в ній недоліки. Забігаючи на перед скажу, що подібних прототипів у мене багато (пошукати можна по відповідному тегу в репозиторіях YGGverse), але rssto - це останній з них, тим паче що сьогодні я випустив для нього оновлення 0.2
|
||||
|
||||
rssto - CLI утиліта конвертації RSS фідів з оцією кравлера, що дозволяє слідкувати за різними RSS фідами і зберігати їх у заданому форматі, зокрема HTML або Gemtext.
|
||||
|
||||
Іншими словами, дана програма читає фід по URL та зберігає його сутності у статичний файл згідно патерну, звідки його потім можна відкрити зручною для себе програмою. Особисто я користуюсь протоколом Gemini, точніше Nex, при чому експортовані дані знаходяться на локальному сервері Yggdrasil. На зображенні до цього матеріалу - зображена саме така конфігурація (на базі браузеру Yoda), тому ви бачите саме такі UI та URL, сама ж утиліта rssto - просто форматує заданий XML фід у статичний файл, із заданими налаштуваннями експорту.
|
||||
|
||||
=> rssto-convert-rss-feeds-into-multiple-formats/rssto-yoda.png Скріншот
|
||||
|
||||
## Встановлення
|
||||
|
||||
Програма написана мовою Rust, тому тут все стандартно:
|
||||
|
||||
``` bash
|
||||
git clone https://github.com/YGGverse/rssto.git
|
||||
cd rssto
|
||||
cargo build --release
|
||||
sudo install target/release/rssto /usr/local/bin/rssto
|
||||
```
|
||||
=> install-latest-rust-version-on-linux.gmi Встановлення останньої версії Rust в Linux
|
||||
|
||||
## Налаштування
|
||||
|
||||
Я спеціально навів приклад встановлення з репозиторію, тому що версія 0.2 ще не опублікована в стабільних релізах на crates.io. По цій же причині, не буду детально описувати усі доступні опції (можна подивитись з `rssto --help`), але в цілому розповім про базові принципи конфігурації.
|
||||
|
||||
### Файл конфігурації
|
||||
|
||||
Перше, що потрібно зробити - це створити файл конфігурації, який потрібен для запуску rssto. Актуальний приклад конфігурації доступний в репозиторії:
|
||||
=> https://github.com/YGGverse/rssto/blob/main/config/example.toml
|
||||
|
||||
``` example.toml
|
||||
# якщо дана опція вказана, rssto працюватиме як фоновий демон а не разовий конвертор,
|
||||
# тобто оновлюватиме фіди із заданим інтервалом в секундах
|
||||
update = 60
|
||||
|
||||
# налаштування каналу для імпорту даних
|
||||
[[feed]]
|
||||
# URL шлях до фіду, як на прикладі
|
||||
url = "https://assets.censor.net/rss/censor.net/rss_uk_news.xml"
|
||||
|
||||
# куди зберігати експортовані дані
|
||||
storage = "./public/censor.net/rss_uk_news"
|
||||
|
||||
# у яких форматах експортувати дані
|
||||
# ці теки є в репозиторії, ви можете вказати на базі них власні патерни, використовуючи макроси
|
||||
templates = ["./template/html","./template/gmi"]
|
||||
|
||||
# обмежити кількість сутностей в каналі (стандартно стільки, скільки є у фіді)
|
||||
list_items_limit = 20
|
||||
|
||||
# формати часу для відповідних макросів, залежатимуть від вашої локалі
|
||||
pub_date_format = "%Y/%m/%d %H:%M:%S %z"
|
||||
last_build_date_format = "%Y/%m/%d %H:%M:%S %z"
|
||||
time_generated_format = "%Y/%m/%d %H:%M:%S %z"
|
||||
|
||||
# додайте інші канали по аналогії
|
||||
#[[feed]]
|
||||
```
|
||||
|
||||
Опція "templates" дозволяє використовувати свої шаблони для експорту але тут важливо зберігати поточну структуру їх файлової системи, утім можна довільно змінювати сам вміст шаблонів. Нище наводжу перелік макросів що підтримуються даною версією:
|
||||
|
||||
### index
|
||||
|
||||
=> https://www.w3schools.com/xml/rss_tag_title_link_description_channel.asp {title}
|
||||
=> https://www.w3schools.com/xml/rss_tag_title_link_description_channel.asp {description}
|
||||
=> https://www.w3schools.com/xml/rss_tag_link.asp {link}
|
||||
=> https://www.w3schools.com/xml/rss_tag_language.asp {language}
|
||||
=> https://www.w3schools.com/xml/rss_tag_pubdate.asp {pub_date}
|
||||
* {last_build_date} - час генерації статичної версії фіду (програмою)
|
||||
* {items} - список сутностей каналу, кожну з яких відформатовано за шаблоном "index/item"
|
||||
|
||||
### index/item
|
||||
|
||||
=> https://www.w3schools.com/xml/rss_tag_title_link_description_item.asp {title}
|
||||
=> https://www.w3schools.com/xml/rss_tag_title_link_description_item.asp {description}
|
||||
=> https://www.w3schools.com/xml/rss_tag_title_link_description_item.asp {link}
|
||||
=> https://www.w3schools.com/xml/rss_tag_pubdate_item.asp {pub_date}
|
||||
|
||||
### Systemd
|
||||
|
||||
Програмою rssto можна користуватись як конвертором, але особисто в мене вона працює на фоні, постійно оновлюючи фіди з інтервалом в одну годину (update = 3600).
|
||||
|
||||
Щоб запустити її на фоні, створімо новий сервіс:
|
||||
|
||||
```
|
||||
#/etc/systemd/system/rssto.service
|
||||
[Unit]
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
|
||||
User=rssto
|
||||
Group=rssto
|
||||
|
||||
# розкоментуйте та вкажіть актуальний рівень дебагу
|
||||
# опція "NO_COLOR" вимикає ANSI сутності для запису журналу у файл
|
||||
# Environment="RUST_LOG=DEBUG"
|
||||
# Environment="NO_COLOR=1"
|
||||
|
||||
ExecStart=/usr/local/bin/rssto -c /path/to/config.toml
|
||||
|
||||
# якщо не використовується, можна вказати null
|
||||
StandardOutput=file:///home/rssto/debug.log
|
||||
StandardError=file:///home/rssto/error.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
* значення "User" і "Group" та шляхів у мене вказані як користувач створеного мною сервера [Nexy](https://github.com/YGGverse/nexy), але якщо він у вас окремим обліковим записом, то створіть його командою `useradd -m rssto`
|
||||
* `sudo systemctl enable rssto` - додати до авто-запуску при старті системи
|
||||
* `sudo systemctl start rssto` - запуск
|
||||
* `sudo systemctl status rssto` - перевірка статусу
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://github.com/YGGverse/rssto проєкт на GitHub
|
||||
=> https://devzone.org.ua/post/rssto-konvertatsiia-fidiv-u-rizni-formaty Веб версія цього документу з коментарями на DevZone
|
||||
|
||||
### Читайте також
|
||||
|
||||
=> gemini-protocol-as-http-alternative.gmi Протокол Gemini як альтернатива HTTP
|
||||
=> nex-lightweight-gemini-alternative.gmi Протокол NEX - легка альтернатива Gemini
|
||||
=> yggdrasil-is-network-with-distributed-routing.gmi Yggdrasil - мережа з децентралізованою маршрутизацією
|
||||
152
public/uk/safe-yggdrasil-websites-browsing-with-yggstack.gmi
Normal file
152
public/uk/safe-yggdrasil-websites-browsing-with-yggstack.gmi
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
# Безпечний перегляд сайтів Yggdrasil з Yggstack
|
||||
|
||||
Враховуючи специфіку сучасних Веб-стандартів, при відвідуванні веб-сайтів у мережі Yggdrasil, можливі та цілком ймовірні фонові запити до зовнішніх Інтернет вузлів. Таким чином, може не контрольовано здійснюватись підвантаження сторонніх ресурсів сторінки, зокрема - графіки, стилів, скриптів та іншого. Наприклад, це може бути випадкове або спеціальне звернення веб сайту до ресурсів у зоні .ru (зокрема Yandex) або ви просто не бажаєте витоків персональної IP адреси до не відомих вузлів Інтернет, наприклад - користуючись Yggdrasil в режимі меш / інкогніто.
|
||||
|
||||
Нижче наведу невеличку нотатку, про те, як особисто я фільтрую не бажаний вихідний трафік з використанням SOCKS5 проксі Yggstack і окремо налаштованим браузером для навігації в мережі - LibreWolf, на прикладі системи Fedora Linux.
|
||||
|
||||
Звертаю увагу, що дана інструкція не включає кроки з анонімізації - маскування відбитків, оновлення сесій, cookies, вимкнення акселерації та іншого. Це окрема тема, для якої існують спеціалізовані софт, додатки і навички. Тут ми просто контролюємо вихідний трафік в режимі Yggdrasil-only.
|
||||
|
||||
## Yggstack
|
||||
|
||||
Yggstack - це невеличкий проксі сервер на базі Netstack, який містить в собі ізольований екземпляр вузла Yggdrasil (так само, як Yggmail) і надає локальний інтерфейс для підключення до нього браузеру (чи іншого додатку) для проксування в діапазоні адрес IPv6 0200::/7.
|
||||
|
||||
### Залежності
|
||||
|
||||
Тут достатньо встановити актуальну версію Go. Про це я вже писав у матеріалі:
|
||||
=> install-latest-golang-version-on-debian-linux.gmi Встановлення останньої версії Go в Debian
|
||||
|
||||
### Встановлення
|
||||
|
||||
Збираємо приблизно так само, як Yggdrasil:
|
||||
|
||||
``` bash
|
||||
git clone https://github.com/yggdrasil-network/yggstack.git && cd yggstack
|
||||
./build
|
||||
sudo ./yggstack -genconf > /etc/yggstack.conf
|
||||
sudo mv yggstack /usr/local/bin/yggstack
|
||||
sudo restorecon -rv /usr/local/bin/yggstack
|
||||
```
|
||||
* остання команда корегує доступи на виконання yggstack, який пускається не від root.
|
||||
|
||||
Якщо користуєтесь в оверлеї, також не забудьте вказати вихідний вузол до "Peers":
|
||||
=> https://publicpeers.neilalexander.dev
|
||||
|
||||
### Systemd
|
||||
|
||||
Оскільки Yggstack, в принципі, створений щоб запускатись без tun інтерфейсу і root, створимо системний сервіс з виконанням від поточного системного користувача. Якщо не плануєте його запускати на фоні, можна цей крок пропустити і запуститись командою:
|
||||
|
||||
``` bash
|
||||
/usr/local/bin/yggstack -useconffile /etc/yggstack.conf -socks 127.0.0.1:1080
|
||||
```
|
||||
|
||||
Або ж створіть файл конфігурації командою:
|
||||
``` bash
|
||||
sudo nano /etc/systemd/system/yggstack.service
|
||||
```
|
||||
|
||||
та додайте наступний вміст:
|
||||
``` yggstack.service
|
||||
[Unit]
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
|
||||
# вкажіть актуальні значення для User/Group
|
||||
User=USER
|
||||
Group=GROUP
|
||||
|
||||
ExecStart=/usr/local/bin/yggstack -useconffile /etc/yggstack.conf -socks 127.0.0.1:1080
|
||||
|
||||
# можна вказати як "file:///path/to/log", якщо потрібне ведення журналу у файл
|
||||
StandardOutput=null
|
||||
StandardError=null
|
||||
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Керування процесом відбувається наступними командами:
|
||||
|
||||
``` bash
|
||||
systemctl daemon-reload
|
||||
```
|
||||
* оновлюємо конфігурацію systemd
|
||||
``` bash
|
||||
systemctl enable yggstack
|
||||
```
|
||||
* автостарт при запуску системи
|
||||
``` bash
|
||||
systemctl start yggstack
|
||||
```
|
||||
* запуск
|
||||
|
||||
## Браузер
|
||||
|
||||
Як писав вище, для цієї мережі я користуюсь окремим браузером LibreWolf з трохи іншим пресетом, для зручності. В принципі, ті само кроки актуальні й для FireFox.
|
||||
|
||||
* Переходимо в "Settings" -> "Proxy" / "Network settings" -> "Manual proxy configuration"
|
||||
* В "SOCKS Host" пишемо наші локальні хост "127.0.0.1" і порт "1080"
|
||||
* Переконуємось, щоб версія була відмічена як "SOCKS v5"
|
||||
|
||||
### Конфігурація PAC
|
||||
|
||||
Я додатково користуюсь файлом PAC:
|
||||
=> https://en.wikipedia.org/wiki/Proxy_auto-config
|
||||
|
||||
Цей формат дозволяє в рамках браузеру перенаправити запити, що не відносяться до мережі Yggdrasil на "фіктивний" проксі, щоб швидше прискорити відвантаження сторінки.
|
||||
Інакше браузер буде намагатись відрезольвити ресурси з незначною затримкою.
|
||||
|
||||
Цього можна не робити, адже Yggstack і так відфільтрує подібні запити, оскільки він працює виключно в діапазоні IPv6 0200::/7. Але приклад нижче лишу, якщо ви захочете користуватись цим проксі на обидві мережі, закоментувавши останній "return":
|
||||
|
||||
``` yggstack.pac
|
||||
function FindProxyForURL(url, host)
|
||||
{
|
||||
// спрямовуємо трафік Yggdrasil по регулярному виразу на проксі Yggstack
|
||||
// цей приклад також включає доменні зони .ygg якщо користуєтесь Alfis DNS
|
||||
if (/^0{0,1}[2-3][a-f0-9]{0,2}:/.test(host) || /\.ygg$/.test(host)) {
|
||||
return 'SOCKS5 127.0.0.1:1080';
|
||||
}
|
||||
return 'PROXY 127.0.0.1:123'; // видаліть цей рядок, якщо хочете працювати з усіма мережами
|
||||
// або вкажіть окремий проксі для цих мереж, наприклад Tor
|
||||
}
|
||||
```
|
||||
* шлях до даного файлу потрібно вказати у полі "Automatic proxy configuration URL" у форматі "file:///path/to/file" (якщо зберігаєте його локально)
|
||||
* також, в мене працює окремий від Yggstack інтерфейс Yggdrasil для інших служб, таких як Bittorrent і радіо Rhythmbox. Тому для основного браузеру Firefox, я створив інший файл PAC з протилежними умовами, щоб виключити випадкове звернення до ресурсів Yggdrasil з нього.
|
||||
* щоб не плутатись в обох клонах Firefox, я користуюсь різними темами оформлення.
|
||||
|
||||
### Тестування підключення
|
||||
|
||||
Відкриваємо будь який сайт з каталогу, наприклад:
|
||||
=> http://[21e:a51c:885b:7db0:166e:927:98cd:d186]
|
||||
і переходимо в режим дебагу "Ctrl+Shift+i" та дивимось заблоковані запити, якщо такі є.
|
||||
|
||||
Щоб вимкнути форсований редірект "HTTP" > "HTTPS", в "about:config" встановіть опцію:
|
||||
```
|
||||
browser.fixup.fallback-to-https:false
|
||||
```
|
||||
|
||||
Для аналізу пакетного трафіку, мені подобається TUI iptraf (iptraf-ng), або GUI etherape, Sniffnet, тощо.
|
||||
|
||||
## Фаєрвол
|
||||
|
||||
### Debian
|
||||
|
||||
Варто додати правила на iptables, особливо це зручно, якщо користуєтесь окремим контейнером:
|
||||
|
||||
``` bash
|
||||
ufw default deny outgoing
|
||||
ufw allow out to PUBLIC_PEER_IP
|
||||
ufw allow out to 0200::/7 from any
|
||||
```
|
||||
|
||||
### Fedora
|
||||
|
||||
Для firewalld я надаю перевагу GUI firewall-config.
|
||||
|
||||
## Читайте також
|
||||
|
||||
=> yggdrasil-is-network-with-distributed-routing.gmi Yggdrasil - мережа з децентралізованою маршрутизацією
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# Думки стосовно TLS в мережах Yggdrasil та Mycelium
|
||||
|
||||
В середовищі оверлейних мереж чомусь прийнято вважати, що якщо ключі вузлів перманентні, а з'єднання між вузлами захищене TLS, то додатковий шар SSL ніби як і не потрібний. Утім, останнім часом я починаю у цьому сумніватись.
|
||||
|
||||
## Компрометація ключа
|
||||
|
||||
В мережах типу Yggdrasil / Mycelium немає рівня складності при генерації приватних ключів, а отже теоретично (хоча і малоймовірно) можлива колізія. З цієї причини, рекомендується використовувати основні адреси замість підмереж, а останні - розробники планують, але ніяк не випиляють. Та й зручні вони в рамках шейред хосту. Все одно це не виключає можливості випадкового видобутку копії, або не випадкового, враховуючи потенційні можливості криптоіндустрії, питання лише доречності використання супер-комп'ютера для цієї мети; скільки ці мережі включатимуть користувачів і якого статку, для потенційних атак на роутинг, що базується на фіксованому алгоритмі побудови дерева з peer ID.
|
||||
|
||||
## Подвійний шар
|
||||
|
||||
Технічно, протокол транспорту Yggdrasil бере на себе роль шифрування трафіку тоді, коли це може бути не потрібно. Наприклад, у випадках:
|
||||
* заощадження електроенергії та ресурсів CPU при пересиланні великих медіа файлів
|
||||
* коли вже використовується шар SSL / HTTPS на програмному рівні - з метою уникнення перехоплень логіну/паролю чи просто конфіденційного пересилання запитів GET через проксі
|
||||
|
||||
Практичний приклад: вимога щодо шифрування трафіку Gemini в рамках Yggdrasil, бо перший хоче бути захищеним для Інтернет, але я використовую його не там, де задумував автор. З цієї причини, певний час користувався альтернативою Nex, але згодом усвідомив, що деякі дані потенційно можуть таки потребувати сертифікату, а отже, мені потрібна стара-добра модель HTTP+HTTPS на чутливих формах.
|
||||
|
||||
Якщо від клієнта до сервера передаються чутливі дані, то на мою думку, варто користуватись сертифікатом SSL, що слугуватиме запобіжником, але маршрутизатор вже про все "подбав", створивши зайві проблеми.
|
||||
|
||||
## Сертифікація в локальних мережах
|
||||
|
||||
Через ізоляцію локальних мереж, в Yggdrasil є проблемою налаштувати валідний сертифікат, наприклад з Let's Encrypt. Але у випадку протоколу Gemini - центри сертифікації не використовуються взагалі. Натомість застосовується принцип TOFU (Trust on First Use), що значно зменшує ризик перехоплення даних у часі - до моменту виявлення витоку. В мене навіть були такі думки стосовно організації внутрішньомережного центру сертифікації, чому б і ні; чому б навіть не зробити такий сервіс платним?
|
||||
|
||||
## Висновки
|
||||
|
||||
Коли і як саме шифрувати дані - має вирішувати користувач / адміністратор мережі, на ті потоки / дані які того потребують. Тоді як Yggdrasil та Mycelium - роблять це "добровільно-примусово" як, власне, й інші новомодні софти з лейблом "абсолютно сек'юрно". Сучасний софт, розробники якого конкурують за право називатись "захищеним" нагадує крипто-капусту з коефіцієнтом безпеки було == стало.
|
||||
|
||||
Чомусь вже починає дратувати, коли за мене щось хтось починає вирішувати там, де того не просять. Маркетинг - маркетингом, слогани - слоганами, але досвідчені юзери йдуть через подібний дискомфорт, а туристи й без того не затримуються.
|
||||
|
||||
А ще висновки такі, що ефективні мережні рішення були винайдені повоєнними спеціалістами пів сторіччя тому, які мусили виживати, а не гратись в комерційні експерименти. Нічого принципово нового за цей час не винайдено. Можливо, наступним проривом стане квантова передача даних, а не подібний нонсенс: прокладати автоматичний маршрут до ймовірно компрометованих вузлів, при цьому шифрувати тони сміття, що ходить ним.
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://devzone.org.ua/post/dumky-stosovno-tls-v-merezakh-yggdrasil-ta-mycelium Веб-версія цього матеріалу з коментарями на DevZone
|
||||
=> https://en.wikipedia.org/wiki/Trust_on_first_use
|
||||
=> https://github.com/threefoldtech/mycelium
|
||||
|
||||
### Дивіться також
|
||||
|
||||
=> yggdrasil-is-network-with-distributed-routing.gmi Yggdrasil - мережа з децентралізованою маршрутизацією
|
||||
=> nex-lightweight-gemini-alternative.gmi Протокол NEX - легка альтернатива Gemini
|
||||
44
public/uk/traditional-craft-masters-of-ukraine.gmi
Normal file
44
public/uk/traditional-craft-masters-of-ukraine.gmi
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Українські майстри традиційних ремесел
|
||||
|
||||
Цей матеріал присвячений вагомим носіям культури - майстрам традиційних ремесел України
|
||||
|
||||
## Кузні
|
||||
|
||||
### Петро Федоряка
|
||||
|
||||
=> traditional-craft-masters-of-ukraine/petro-fedoryaka.jpg Фото
|
||||
|
||||
Петро Вікторович Федоряка народився 15 вересня 1964 року у селі Нова Гребля Сумської області.
|
||||
|
||||
Учитель вищої категорії, колишній голова обласної і член Всеукраїнської комісії з перевірки завдань учнівських олімпіад із інформатики.
|
||||
|
||||
Заснував "Кузню Петра", з численними учнями та послідовниками.
|
||||
|
||||
Був постійним учасником міжнародних фестивалів, конкурсів, виставок.
|
||||
Кував ножі та клинки періоду вікінгів, Середньовіччя, Київської Русі.
|
||||
|
||||
Загинув на Донбасі 31 липня 2014 року.
|
||||
|
||||
=> http://bladesmith.com.ua
|
||||
|
||||
## Лучна справа
|
||||
|
||||
### Сергій Толочко
|
||||
|
||||
Український майстер лучної справи з Чернігівщини.
|
||||
|
||||
В 2011 році заснував компанію "KadysBows" яка на даний час є одним із лідерів з виробництва традиційних луків.
|
||||
|
||||
Вироби широко використовуються в країнах колишнього СНГ, а також у Польщі, Туреччині, Греції, Китаї, Південній Кореї, Кіпрі, Малайзії, Мальті, США.
|
||||
|
||||
Мені не вдалось віднайти більше інформації, але маю честь користуватись 40-фунтовим шедевром його майстерні.
|
||||
|
||||
=> https://kadysbows.com
|
||||
|
||||
## Магазини
|
||||
|
||||
### Ножар
|
||||
|
||||
Все для створення ножів: клинки українських майстрів, Enzo, Lauri, Helle, дамасські клинки, ножові сталі AEB-L, RWL-34, матеріали для руків'я, інструменти, шкіра та готові ножі. Офіційний дилер BRISA Finland.
|
||||
|
||||
=> https://knifemaker.com.ua
|
||||
107
public/uk/twister-p2p-decentralized-microblogging-platform.gmi
Normal file
107
public/uk/twister-p2p-decentralized-microblogging-platform.gmi
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# twister - децентралізована платформа мікроблогів
|
||||
|
||||
twister (твістер, пишеться саме в нижньому регістрі) - це альтернативна пірингова платформа мікроблогів, створена на базі технологій BitTorrent і Bitcoin у 2013 році @miguelfreitas (https://github.com/miguelfreitas) як альтернатива колишньому централізованому сервісу twitter.com
|
||||
|
||||
У 2020 році, розробник платформи припинив (http://twister.net.co/archives/617) її супровід, але оскільки мережа є децентралізованою, вона все ще продовжує функціонувати у своєму первинному стані, хоча кількість користувачів значно знизилась і наразі мережа близька до колапсу.
|
||||
|
||||
Дана стаття носить історично-інформаційний характер, і створена більше для зацікавлених у децентралізованих мережах користувачів з базовим досвідом програмування мовою C++ та адміністрування Linux, а ніж рядових споживачів сучасного контенту. Також нижче я не описуватиму весь досвід, оскільки наразі працюю над книгою (https://twisterarmy.github.io/book/) у форматі документації mdBook (англійською мовою), яку планую також в релізі перекласти українською.
|
||||
|
||||
## Принцип роботи
|
||||
|
||||
Як згадувалось вище, ядро twister (twister-core) по своїй суті є форком bitcoin-core з видаленням фактору цифрової валюти як такої, а блокчейн приведено до моделі схожої з Namecoin - де користувачі зберігають свої дані, замість платіжних транзакцій. Оскільки мережа twister не має власної валюти, мотивація майнингу передбачала рекламні повідомлення у якості "нагороди". Окрім рекламних повідомлень майнерів, блокчейн зберігає мета дані DHT контенту користувачів у часовому порядку та прийняті майнерами запити на їх реєстрацію з рою (swarm).
|
||||
|
||||
Таким чином, і реєстрація і користування для кінцевого користувача задумувалось зробити безкоштовними і такими, що не потребуватимуть жодних обчислювальних потужностей, окрім хіба що, обчислення хешів торентів самих повідомлень (як це працює у звичайному біт-торент клієнті).
|
||||
|
||||
## Випробовування часом
|
||||
|
||||
Звісно, десять років - це не той термін, по якому можна робити висновки, хоча у просторі ІТ й цього часу буває достатньо: у зв'язку зі своєю не прибутковістю та не спекулятивністю, майнинг від самого початку відбувався силами ентузіастів а рекламні повідомлення - містили пусті за змістом дублікати тексту, лише задля підтримки цієї мережі небайдужими користувачами.
|
||||
|
||||
Більшість критики була об'єктивною ще від релізу цієї платформи:
|
||||
|
||||
* відсутність анонімізації, що ставило під сумнів можливість використання цієї платформи у країнах з цензурою без додаткових обгорток;
|
||||
* низький рівень стійкості до спам атак на мережу - у зв'язку з цим був різкий пік сквотингу юзернеймів, з подальшими намаганнями виправити вразливість на швидкоруч; оскільки атака була здійснена невідомим користувачем, лише задля тестів, ймовірно вона була зупинена лише ним самим а не вирішенням фундаментальних недоліків архітектури;
|
||||
* повідомлення користувачів зберігаються у DHT, щоб зробити блокчейн мінімально "легким" з іншого боку, багато контенту просто втрачено з часом, оскільки блокчейн містить лише мета дані (info-hash) і щоб повідомлення зберігалось на іншому пірі, цей пір (користувач) повинен бути підписаним фоловером.
|
||||
|
||||
В ті роки, мав місце певний "бум" децентралізованих рішень, зокрема з'являлись і такі проєкти як OpenBazaar та ZeroNet. Останній, окрім мікроблогінгу, дозволяв хостити свої веб-сторінки у децентралізованому форматі (по типу того, як це було згодом реалізовано в IPFS). Деякі користувачі перебрались на ZN, інші віддавали перевагу більш захищеним платформам накшталт RetroShare.
|
||||
|
||||
У свій час, я теж переключився на інші заняття та коли повернувся - помітив що з усього рою, лишилась буквально пара користувачів. Ще через декілька років, вже не вдавалось під'єднатись, оскільки майже всі сервісні DNS були офлайн.
|
||||
|
||||
## Як спробувати
|
||||
|
||||
Наразі, мабуть з великим запізненням, спільнотою розробляється пакунок Flatpak, мабуть це самий простий спосіб запустити twister в сандбокс-режимі, щоб просто подивитись що це таке. Варто зауважити, що при користуванні Flatpak, клієнт падатиме при спробі запустити функцію майнингу, все інше наче працює добре.
|
||||
|
||||
Для тих, хто хоче нативно зібрати повністю робоче ядро під свою систему, напишу коротеньку інструкцію для Linux (на базі матеріалу з книги), оскільки мережа все ще жива і можливо буде комусь цікавою у якості постійної іграшки.
|
||||
|
||||
Наразі є дві опції встановлення: офіційний twister-core (https://github.com/miguelfreitas/twister-core) та twister-core від спільноти (https://github.com/twisterarmy/twister-core). Різниця полягає лише в тому, що останній має додаткові DNS вузли для ініціалізації та підтримку IPv6, тобто є більш конективним. Замініть адресу репозиторію у прикладі нижче - вашим вибором!
|
||||
|
||||
### Системні залежності
|
||||
|
||||
Debian / Ubuntu
|
||||
|
||||
``` bash
|
||||
sudo apt install git autoconf automake build-essential libtool\
|
||||
libboost-all-dev libssl-dev libdb++-dev libminiupnpc-dev
|
||||
```
|
||||
|
||||
Fedora
|
||||
|
||||
``` bash
|
||||
sudo dnf install git autoconf automake libtool make\
|
||||
boost-devel openssl-devel libdb-cxx-devel miniupnpc-devel
|
||||
```
|
||||
|
||||
### Збірка
|
||||
|
||||
Даний приклад підходить для встановлення на localhost, при використанні архітектури amd64, тому кроки спрощено.
|
||||
|
||||
1. Ядро twister-core
|
||||
|
||||
``` bash
|
||||
git clone https://github.com/twisterarmy/twister-core.git
|
||||
cd twister-core
|
||||
./autotool.sh
|
||||
./configure
|
||||
make
|
||||
```
|
||||
|
||||
2. Клієнт: застосунок не має GUI, тому ставимо twister-html (Web-UI)
|
||||
|
||||
``` bash
|
||||
mkdir ~/.twister
|
||||
echo -e "rpcuser=user\nrpcpassword=pwd\nrpcallowip=127.0.0.1" > ~/.twister/twister.conf
|
||||
chmod 600 ~/.twister/twister.conf
|
||||
git clone https://github.com/twisterarmy/twister-html.git ~/.twister/html
|
||||
```
|
||||
* для локального запуску вузла, складні логін/пароль не потрібні
|
||||
|
||||
3. Запуск
|
||||
|
||||
``` bash
|
||||
./twisterd
|
||||
```
|
||||
* відкриваємо у браузері http://127.0.0.1:28332
|
||||
* вказуємо стандартні `user` і `pwd` - це спадкова реалізація оригінального Bitcoin RPC API
|
||||
* далі з'являється діалог вітання із запрошенням перейти на сторінку мережі, де відбуватиметься завантаження блокчейну
|
||||
|
||||
### Користування
|
||||
|
||||
Коли завантажиться блокчейн, можна спробувати зареєструвати вільний юзернейм (який буде записано в кінець цього ж блокчейну, тобто коли будь який майнер отримає ваш запит і включить його до блоку). Після створення юзера, зберігаємо згенерований приватний ключ, він знадобиться тільки для доступу до акаунту з іншого пристрою або для подальшого бекапу.
|
||||
|
||||
Реєстрація займатиме деякий час. Також, з урахуванням поточного онлайну, деякий час (пара хвилин або навіть годин чи днів) може зайняти ініціалізація DHT процесів, тримайте це діло увімкненим допоки на сторінці мережі не засвітиться зелений індикатор.
|
||||
|
||||
У списку останніх користувачів, спробуйте знайти активного та щось йому написати. Або напишіть у свою стрічку вітання.
|
||||
|
||||
## Перспективи
|
||||
|
||||
Не стану давати оцінку суб'єктивним поглядом на подальші перспективи цього проєкту, оскільки мережа twister є повністю децентралізованою і жодним чином не залежить від моїх висновків. Особисто, мені - вона подобається, на відміну від централізованих сервісів, у тому числі Fediverse, де по суті інстанси є такими само сервер-орієнтованими платформами, але з більш сучасним форматом агрегації (ActivityPub замість RSS).
|
||||
|
||||
Дана мережа обіцяла колапсувати ще п'ять років тому: деякі юзери раз на рік надсилають свій меседж про існування і зникають ще на стільки само. Тим не менше, вона існує у власному вимірі, і коли всі монітори пишуть офлайн, через деякий час виявляється що до неї ще можна під'єднатись.
|
||||
|
||||
Якщо вам подобаються різні екзотичні проєкти по типу twister, та ви бажаєте підтримати дану пірингову мережу - доєднуйтесь до користування, щоб продовжити її функціонування - мабуть, в тому і суть роботи по справжньому не залежних соціальних платформ!
|
||||
|
||||
## Посилання
|
||||
|
||||
=> http://twister.net.co/ Офіційний сайт
|
||||
=> https://twisterarmy.github.io/ Сайт спільноти
|
||||
=> https://twisterarmy.github.io/network Публічні піри онлайн
|
||||
=> https://twisterarmy.github.io/book/ The twister P2P Book
|
||||
29
public/uk/ukrainian-geminispace.gmi
Normal file
29
public/uk/ukrainian-geminispace.gmi
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Український Geminispace
|
||||
|
||||
Колекція україномовних ресурсів Gemini
|
||||
|
||||
## Персональні сторінки
|
||||
|
||||
### Піратський дирижабль
|
||||
|
||||
=> gemini://piratezeppel.in/
|
||||
|
||||
### Житнє поле
|
||||
|
||||
=> gemini://zhyto.cities.yesterweb.org/
|
||||
|
||||
### Byte's corner
|
||||
|
||||
=> gemini://hideout.cyber-cave.org/
|
||||
|
||||
## Соціальні мережі
|
||||
|
||||
### BBS
|
||||
|
||||
=> gemini://bbs.geminispace.org/s/Ukrainians
|
||||
|
||||
### Fediverse
|
||||
|
||||
Зоряний луг
|
||||
|
||||
=> gemini://meadow.hmmm.zt.ua/
|
||||
94
public/uk/ukrainian-latin-keyboard-layout-on-linux.gmi
Normal file
94
public/uk/ukrainian-latin-keyboard-layout-on-linux.gmi
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# Розкладка української латинки в Linux
|
||||
|
||||
Кому цікава тема української латинки, хочу поділитися способом швидкого налаштування клавіатури для Linux.
|
||||
|
||||
Варіант передбачає роботу з абеткою версії Максима Прудеуса з наступним набором символів:
|
||||
|
||||
```
|
||||
A B Cц Čч D E F G Ĝґ H I Jй K L M N O P R S Šш T U V Yи Z Žж '
|
||||
```
|
||||
|
||||
Як програміст, користуюсь версією клавіатури, що базується на мапі "us", оскільки вона дозволяє не змінювати розкладку під час написання коду і відвідування англомовних ресурсів.
|
||||
|
||||
В прикладі використовується код `ul` - обрати вільний слот можна в таблиці ISO_639:
|
||||
|
||||
=> https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes#Table_of_all_possible_two_letter_codes
|
||||
|
||||
``` /usr/share/X11/xkb/symbols/ul
|
||||
partial alphanumeric_keys
|
||||
|
||||
xkb_symbols "ul" {
|
||||
|
||||
include "us(basic)"
|
||||
|
||||
name[Group1]= "Adaptive (UL)";
|
||||
|
||||
key <AB01> { [z, Z, zcaron, Zcaron] };
|
||||
key <AB03> { [c, C, ccaron, Ccaron] };
|
||||
key <AC02> { [s, S, scaron, Scaron] };
|
||||
key <AC05> { [g, G, gcircumflex, Gcircumflex] };
|
||||
|
||||
include "level3(ralt_switch)"
|
||||
};
|
||||
```
|
||||
|
||||
У версії Прудеуса, допускається використання літери "ï" замість "ji".
|
||||
Стандартно ця літера не є частиною вказаної абетки, але на практиці, без неї не зручно писати і читати такі слова як наприклад "jiji".
|
||||
|
||||
Додати літеру до розкладки можна так:
|
||||
|
||||
```
|
||||
key <AD08> { [i, I, idiaeresis, Idiaeresis ] };
|
||||
```
|
||||
|
||||
Після редагування файлів "/usr/share/X11/xkb/symbols/*" потрібно застосувати зміни командою без атрибутів:
|
||||
|
||||
```
|
||||
setxkbmap
|
||||
```
|
||||
|
||||
В середовищі i3, мною використовується конфігурація на правий "alt":
|
||||
|
||||
``` ~/.xinitrc
|
||||
setxkbmap -layout ul,ua
|
||||
setxkbmap -option 'grp:alt_shift_toggle'
|
||||
```
|
||||
|
||||
На Fedora 42 / GNOME, я виконую наступні кроки:
|
||||
|
||||
1. Додаю до файлу `/usr/share/X11/xkb/rules/evdev.xml` (після `<layoutList>`):
|
||||
|
||||
``` /usr/share/X11/xkb/rules/evdev.xml
|
||||
<layout>
|
||||
<configItem>
|
||||
<name>ul</name>
|
||||
<shortDescription>ul</shortDescription>
|
||||
<description>Ukrainian (Latin)</description>
|
||||
<countryList>
|
||||
<iso3166Id>UA</iso3166Id>
|
||||
</countryList>
|
||||
<languageList>
|
||||
<iso639Id>ukr</iso639Id>
|
||||
</languageList>
|
||||
</configItem>
|
||||
<variantList/>
|
||||
</layout>
|
||||
```
|
||||
|
||||
2. Щоб не вказувати вручну в графічних налаштуваннях клавіатури:
|
||||
|
||||
``` bash
|
||||
gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'ua'), ('xkb', 'ul')]"
|
||||
```
|
||||
|
||||
3. Перезапускаю GDM:
|
||||
|
||||
``` bash
|
||||
sudo systemctl restart gdm.service
|
||||
```
|
||||
|
||||
Детальніше про абетку читайте на сайті проєкту:
|
||||
|
||||
=> https://www.ukr-latynka.org
|
||||
|
||||
Pobačymos' o/
|
||||
115
public/uk/yggdrasil-is-network-with-distributed-routing.gmi
Normal file
115
public/uk/yggdrasil-is-network-with-distributed-routing.gmi
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# Yggdrasil - мережа з децентралізованою маршрутизацією
|
||||
|
||||
Yggdrasil - експериментальний протокол для побудови само-організованої локальної мережі з шифруванням трафіку та підтримкою оверлейного підключення через Інтернет.
|
||||
|
||||
В своїй основі використовує приватний ключ для генерації постійних псевдо-адрес IPv6 в діапазоні 0200::/7.
|
||||
|
||||
Має різні сфери застосування, зокрема:
|
||||
|
||||
* автоматизована побудова локальних мереж
|
||||
* маскування IP (як транспорт I2P)
|
||||
* обхід блокувань та обмежень віртуальної адресації NAT
|
||||
|
||||
Останній варіант корисний для організації:
|
||||
|
||||
* відео-спостереження без наявності виділеної адреси
|
||||
* розгортання домашнього веб-серверу
|
||||
* віддаленого керування інфраструктурою підприємства тощо
|
||||
|
||||
Завдяки простоті встановлення та здатності до саморозгортання підключень, здобув широку популярність зокрема серед користувачів CJDNS.
|
||||
|
||||
## Встановлення
|
||||
|
||||
Yggdrasil написаний на Go, початковий код доступний в репозиторії на GitHub:
|
||||
|
||||
=> https://github.com/yggdrasil-network/yggdrasil-go
|
||||
|
||||
В системах Debian, бінарні збірки можуть бути встановлені з репозиторію командою:
|
||||
|
||||
```
|
||||
apt install yggdrasil
|
||||
```
|
||||
|
||||
Також доступні офіційні збірки для кожної версії:
|
||||
|
||||
```
|
||||
wget https://github.com/yggdrasil-network/yggdrasil-go/releases/download/v0.5.5/yggdrasil-0.5.5-amd64.deb
|
||||
dpkg -i yggdrasil-0.5.5-amd64.deb
|
||||
```
|
||||
|
||||
## Налаштування
|
||||
|
||||
У разі підключення до мережі Yggdrasil через Інтернет, необхідно додати до файлу конфігурації щонайменше один географічно близький публічний вузол:
|
||||
|
||||
=> https://publicpeers.neilalexander.dev
|
||||
|
||||
``` /etc/yggdrasil/yggdrasil.conf
|
||||
Peers: [
|
||||
tls://xx.xx.xx.xx:xxxx
|
||||
]
|
||||
```
|
||||
|
||||
Також, зручно вказати назву для інтерфейсу Yggdrasil - щоб потім не плутати його наприклад з VPN:
|
||||
|
||||
``` /etc/yggdrasil/yggdrasil.conf
|
||||
IfName: ygg0
|
||||
```
|
||||
|
||||
Після цього можна перезапустити сервіс, щоб застосувати зміни:
|
||||
|
||||
```
|
||||
systemctl restart yggdrasil
|
||||
```
|
||||
|
||||
Якщо все зроблено правильно, можна спробувати відкрити URL з каталогу сервісів:
|
||||
|
||||
=> https://yggdrasil-network.github.io/services.html
|
||||
|
||||
## Додатково
|
||||
|
||||
Перевірити наявність нового інтерфейсу можна командою:
|
||||
|
||||
```
|
||||
ifconfig
|
||||
```
|
||||
|
||||
Отримати власну адресу:
|
||||
|
||||
```
|
||||
yggdrasilctl getself
|
||||
```
|
||||
|
||||
Можна створювати маски підмереж для окремих сервісів - вебсайти, пошта, та інше:
|
||||
|
||||
```
|
||||
ifconfig lo inet6 add 3xx:xxxx:xxxx:xxxx::xx
|
||||
```
|
||||
|
||||
* де 3xx:xxxx:xxxx:xxxx - адреса з yggdrasilctl getself
|
||||
|
||||
Якщо підключення відбувається через власний публічний вузол, на ньому відкривається довільний порт.
|
||||
|
||||
У разі необхідності обмеження доступу для невідомих динамічних адрес, починаючи з версії 0.5.0 можна вказувати пароль в параметрі password:
|
||||
|
||||
* tls://[::]:12345?password=123456abcdef - сервер
|
||||
* tls://a.b.c.d:12345?password=123456abcdef - клієнт
|
||||
|
||||
Початківцям важливо встановити мережний екран, наприклад ufw для iptables, інакше такі служби як принтери або локальні веб-сервери для розробки (що стандартно прослуховують 0.0.0.0) можуть стати доступними для інших учасників локальної мережі.
|
||||
|
||||
З цієї точки зору, буде зручним організація окремого роутеру наприклад на базі OpenWRT.
|
||||
|
||||
Важливо розуміти, що Yggdrasil не має на меті захист від ідентифікації користувача в мережі, порівнюючи з анонімайзерами Tor чи I2P.
|
||||
|
||||
Іноді, може використовуватись як додатковий канал, але походження його пакетів може бути встановлено, якщо не використовуються такі засоби маскування трафіку як Shadowsocks та інші.
|
||||
|
||||
Yggdrasil - перш за все альтернативний маршрутизатор, який дозволяє автоматично створювати перманентні IPv6 адреси в середовищі IPv4 та є корисним інструментом для швидкого розгортання інфраструктури локальних дротових, лазерних та радіо мереж.
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://yggdrasil-network.github.io Офіційна сторінка
|
||||
=> https://github.com/yggdrasil-network Проєкт на GitHub
|
||||
=> https://yggdrasil-map.cwinfo.net Орієнтовна мапа вузлів
|
||||
|
||||
## Дивіться також
|
||||
|
||||
=> yggmail-messenger-with-email-protocol.gmi Yggmail - месенджер з поштовим інтерфейсом
|
||||
125
public/uk/yggmail-messenger-with-email-protocol.gmi
Normal file
125
public/uk/yggmail-messenger-with-email-protocol.gmi
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# Yggmail - месенджер з поштовим інтерфейсом
|
||||
|
||||
Yggmail - це дочірній проєкт від розробників Yggdrasil, написаний на Go та має відкритий код.
|
||||
|
||||
* Дозволяє в декілька простих кроків розгорнути сервер для підключення будь якого поштового клієнта, сумісного з протоколами IMAP / SMTP.
|
||||
* Працює без залежності від центрального серверу та виділеної адреси IP.
|
||||
* Для транспорту даних використовує вбудований вузол Yggdrasil, не потребує його окремого встановлення а також може ізольовано працювати поряд з ним.
|
||||
* Трафік, що передається - захищений і не потребує додаткового шифрування TLS.
|
||||
|
||||
З коробки, являє собою сервер, підключитись до якого можна через поштовий клієнт типу Thunderbird або DeltaChat.
|
||||
|
||||
Утім, від класичних поштових серверів відрізняється протоколом транспорту, тому не є сумісним зі звичайними скриньками і працює у власній мережі.
|
||||
|
||||
Yggmail варто сприймати саме як месенджер, оскільки він має тільки спільний поштовий інтерфейс IMAP / SMTP але іншу модель транспорту.
|
||||
|
||||
## Встановлення
|
||||
|
||||
Yggmail знаходиться на стадії розробки, тому для встановлення будемо збирати з початкового коду на останній версії Go:
|
||||
|
||||
=> install-latest-golang-version-on-debian-linux.gmi Встановлення останньої версії Go в Debian
|
||||
|
||||
Збірка Yggmail виконується наступною командою в теці і користувачем, від якого планується робота з поштою:
|
||||
|
||||
```
|
||||
cd ~
|
||||
go install github.com/neilalexander/yggmail/cmd/yggmail@latest
|
||||
```
|
||||
|
||||
Наступним кроком, ініціюємо новий профіль:
|
||||
|
||||
```
|
||||
yggmail -password
|
||||
```
|
||||
|
||||
Записуємо згенеровану адресу в полі "Mail address", на неї будуть отримуватись листи і вона вказується при підключенні поштового клієнта.
|
||||
|
||||
Профіль користувача буде збережено до файлу "~/yggmail.db" - його можна використовувати для резервного копіювання пошти.
|
||||
Щоб вказати альтернативний шлях, вказується атрибут "-database=/path/to/yggmail.db".
|
||||
|
||||
Довідка доступна командою:
|
||||
|
||||
```
|
||||
yggmail -help
|
||||
```
|
||||
|
||||
## Запуск
|
||||
|
||||
Запуск відбувається однойменною командою, утім декілька слів про режими підключень:
|
||||
|
||||
* Мультикаст
|
||||
* Підключення через певний публічний пір (https://publicpeers.neilalexander.dev)
|
||||
* Обидва варіанти
|
||||
|
||||
Відповідно:
|
||||
|
||||
```
|
||||
yggmail -multicast
|
||||
yggmail -peer=tls://...
|
||||
yggmail -multicast -peer=tls://...
|
||||
```
|
||||
|
||||
## Автозапуск
|
||||
|
||||
Yggmail отримує пошту тільки тоді, коли він працює та підключений до мережі.
|
||||
Якщо вузол отримувача недосяжний, лист переміщується в до черги очікування і буде надіслано як тільки зв'язок між вузлами відновиться.
|
||||
|
||||
Тому зручно створити системний сервіс з автозапуском:
|
||||
|
||||
``` /etc/systemd/system/yggmail.service
|
||||
[Unit]
|
||||
Description=Yggmail
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/home/user/go/bin/yggmail -peer=tcp://... 2>&1
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Активуємо сервіс:
|
||||
|
||||
```
|
||||
systemctl daemon-reload
|
||||
systemctl enable yggmail
|
||||
systemctl start yggmail
|
||||
```
|
||||
|
||||
Перевіряємо:
|
||||
|
||||
```
|
||||
netstat -tulpn | grep LISTEN
|
||||
```
|
||||
|
||||
## Підключення поштового клієнта
|
||||
|
||||
Yggmail прослуховує порти:
|
||||
|
||||
* 1025 - SMTP / TCP
|
||||
* 1143 - IMAP / TCP
|
||||
|
||||
В залежності від поштового клієнта, при створенні нового підключення скриньки, вказуються наступні дані:
|
||||
|
||||
* Логін - адреса, наприклад "123abc@yggmail"
|
||||
* Пароль - пароль, вказаний при виконанні команди "yggmail -password"
|
||||
* SMTP сервер - localhost
|
||||
* SMTP порт - 1025
|
||||
* IMAP сервер - localhost
|
||||
* IMAP порт - 1143
|
||||
|
||||
Якщо сервер працює на віддаленому VPS, на ньому потрібно відкрити відповідні порти, а "localhost" замінити на його IP.
|
||||
|
||||
Єдиною рекомендацією можна додати, що не варто відправляти пошту самому собі для тестування, наразі це не працює.
|
||||
Щоб перевірити роботу Yggmail - відправляйте пошту з іншого вузла або залиште вашу адресу в коментарях - можливо хтось вам відповість!
|
||||
|
||||
## Посилання
|
||||
|
||||
=> https://github.com/neilalexander/yggmail Проєкт на GitHub
|
||||
=> https://matrix.to/#/#yggmail:matrix.org Канал в Matrix
|
||||
|
||||
## Дивіться також
|
||||
|
||||
=> yggdrasil-is-network-with-distributed-routing.gmi Yggdrasil - мережа з децентралізованим роутингом
|
||||
Loading…
Add table
Add a link
Reference in a new issue