Compare commits
4 Commits
c3a025a83b
...
802715acb2
Author | SHA1 | Date | |
---|---|---|---|
802715acb2 | |||
dda2cd1009 | |||
694a426dc6 | |||
82469bbf98 |
@ -8,7 +8,7 @@ tags: [crystal, tutorial, development]
|
||||
## О статье
|
||||
|
||||
Это мой вольный перевод оригинальной
|
||||
[статьи](https://crystal-lang.org/reference/1.6/syntax_and_semantics/documenting_code.html)
|
||||
[статьи](https://crystal-lang.org/reference/1.8/syntax_and_semantics/documenting_code.html)
|
||||
из документации по **Crystal**.
|
||||
|
||||
> Если есть желание внести правки, пожалуйста пишите.
|
||||
|
@ -42,7 +42,7 @@ Windows Defender - спасибо, что ты есть!
|
||||
### imgur, imageshack, freepic
|
||||
Перекрасили логотипы, вставили флаги, etc.
|
||||
Те ещё помойки. Что-бы посмотреть изображение, нужно скачать тонны JS.
|
||||
В общём ещё один повод. Спасибо за сорудничество!
|
||||
В общем ещё один повод. Спасибо за сорудничество!
|
||||
|
||||
### Vim
|
||||
Перекрасил логотип
|
||||
@ -50,7 +50,7 @@ Windows Defender - спасибо, что ты есть!
|
||||
### Pastebin.com
|
||||
Перекрасили логотип
|
||||
|
||||
### Downlod Master
|
||||
### Download Master
|
||||
При заходе с Российского IP HTTP 403
|
||||
|
||||
### Bash.org.ru / Bash.im
|
||||
@ -63,9 +63,6 @@ Windows Defender - спасибо, что ты есть!
|
||||
### Rust
|
||||
И так не использовал, но на всякий случай, чтобы не [забыть](https://blog.rust-lang.org/2022/02/24/Rust-1.59.0.html).
|
||||
|
||||
### Qt
|
||||
Закрыли всё
|
||||
|
||||
### Grammarly
|
||||
> Grammarly stands with our friends, colleagues, and family in Ukraine, and with all people of Ukraine.
|
||||
|
||||
|
@ -181,5 +181,5 @@ setTimeout(() => location.replace(location.protocol + "//" + location.host + "/p
|
||||
|
||||
## Мой инстанс
|
||||
|
||||
Мой pasty доступен по адресу
|
||||
Мой pasty доступен по адресу `TODO: add links`
|
||||
|
||||
|
@ -24,7 +24,7 @@ Web–интерфейс для управления WireGuard сервером.
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "admin",
|
||||
"password": "admin"
|
||||
"username": "admin",
|
||||
"password": "admin"
|
||||
}
|
||||
```
|
||||
|
516
content/posts/2023/c/sockets.md
Normal file
516
content/posts/2023/c/sockets.md
Normal file
@ -0,0 +1,516 @@
|
||||
---
|
||||
title: "↔️ Программирование сокетов в Linux"
|
||||
date: 2023-06-18T17:52:44+03:00
|
||||
draft: true
|
||||
tags: [c, development, linux]
|
||||
---
|
||||
|
||||
> Автор: Александр Шаргин
|
||||
> Опубликовано: 16.05.2001
|
||||
> Исправлено: 04.02.2006
|
||||
> Версия текста: 1.1
|
||||
> Оригинальная страница: https://rsdn.org/article/unix/sockets.xml
|
||||
|
||||
## Введение
|
||||
|
||||
**Socket API** был впервые реализован в операционной системе Berkley UNIX.
|
||||
Сейчас этот программный интерфейс доступен практически в любой модификации Unix,
|
||||
в том числе в Linux. Хотя все реализации чем-то отличаются друг от друга,
|
||||
основной набор функций в них совпадает.
|
||||
Изначально сокеты использовались в программах на C/C++,
|
||||
но в настоящее время средства для работы с ними предоставляют многие языки (Perl, Java и др.).
|
||||
|
||||
Сокеты предоставляют весьма мощный и гибкий механизм межпроцессного взаимодействия (IPC).
|
||||
Они могут использоваться для организации взаимодействия программ на одном компьютере,
|
||||
по локальной сети или через Internet, что позволяет вам создавать распределённые приложения
|
||||
различной сложности. Кроме того, с их помощью можно организовать взаимодействие с программами,
|
||||
работающими под управлением других операционных систем.
|
||||
Например, под Windows существует интерфейс Window Sockets,
|
||||
спроектированный на основе socket API. Ниже мы увидим, насколько легко можно
|
||||
адаптировать существующую Unix-программу для работы под Windows.
|
||||
|
||||
Сокеты поддерживают многие стандартные сетевые протоколы
|
||||
(конкретный их список зависит от реализации)
|
||||
и предоставляют унифицированный интерфейс для работы с ними.
|
||||
Наиболее часто сокеты используются для работы в IP-сетях.
|
||||
В этом случае их можно использовать для взаимодействия приложений
|
||||
не только по специально разработанным, но и по стандартным протоколам -
|
||||
HTTP, FTP, Telnet и т. д. Например, вы можете написать собственный Web-браузер или Web-сервер,
|
||||
способный обслуживать одновременно множество клиентов.
|
||||
|
||||
Как видим, сокеты - весьма мощное и удобное средство для сетевого программирования.
|
||||
В этой статье я покажу, как ими пользоваться. Начав с понятия сокета
|
||||
и самых основных функций для работы с ним, мы постепенно перейдём
|
||||
к обсуждению более сложных тем. В частности, мы рассмотрим использование низкоуровневых сокетов,
|
||||
различные способы организации параллельного обслуживания клиентов,
|
||||
использование стандартных протоколов Internet и взаимодействие с программами,
|
||||
работающими под управлением операционной системы Microsoft Windows.
|
||||
|
||||
**ПРИМЕЧАНИЕ**
|
||||
|
||||
Большая часть материала, изложенного в статье, применимо ко всему семейству ОС Unix.
|
||||
Тем не менее, все приводимые далее факты и демонстрационные программы проверялись
|
||||
только под Linux, поэтому название этой ОС и вынесено в заголовок статьи.
|
||||
|
||||
## Основы socket API
|
||||
|
||||
### Понятие сокета
|
||||
Сокет (socket) - это конечная точка сетевых коммуникаций.
|
||||
Он является чем-то вроде "портала", через которое можно отправлять байты во внешний мир.
|
||||
Приложение просто пишет данные в сокет; их дальнейшая буферизация,
|
||||
отправка и транспортировка осуществляется используемым стеком протоколов и сетевой аппаратурой.
|
||||
Чтение данных из сокета происходит аналогичным образом.
|
||||
|
||||
В программе сокет идентифицируется дескриптором - это просто переменная типа `int`.
|
||||
Программа получает дескриптор от операционной системы при создании сокета,
|
||||
а затем передаёт его сервисам socket API для указания сокета,
|
||||
над которым необходимо выполнить то или иное действие.
|
||||
|
||||
### Атрибуты сокета
|
||||
С каждым сокет связываются три атрибута: `домен`, `тип` и `протокол`.
|
||||
Эти атрибуты задаются при создании сокета и остаются неизменными
|
||||
на протяжении всего времени его существования.
|
||||
Для создания сокета используется функция `socket`, имеющая следующий прототип.
|
||||
|
||||
```c
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
int socket(int domain, int type, int protocol);
|
||||
```
|
||||
|
||||
Домен определяет пространство адресов, в котором располагается сокет,
|
||||
и множество протоколов, которые используются для передачи данных.
|
||||
Чаще других используются домены Unix и Internet, задаваемые константами
|
||||
`AF_UNIX` и `AF_INET` соответственно
|
||||
(префикс AF означает "address family" - "семейство адресов").
|
||||
При задании `AF_UNIX` для передачи данных используется файловая система ввода/вывода Unix.
|
||||
В этом случае сокеты используются для межпроцессного взаимодействия
|
||||
на одном компьютере и не годятся для работы по сети.
|
||||
Константа `AF_INET` соответствует Internet-домену.
|
||||
Сокеты, размещённые в этом домене, могут использоваться для работы в любой IP-сети.
|
||||
Существуют и другие домены (`AF_IPX` для протоколов Novell,
|
||||
`AF_INET6` для новой модификации протокола IP - IPv6 и т. д.),
|
||||
но в этой статье мы не будем их рассматривать.
|
||||
|
||||
Тип сокета определяет способ передачи данных по сети. Чаще других применяются:
|
||||
|
||||
- `SOCK_STREAM`. Передача потока данных с предварительной установкой соединения.
|
||||
Обеспечивается надёжный канал передачи данных, при котором фрагменты отправленного блока
|
||||
не теряются, не переупорядочиваются и не дублируются.
|
||||
Поскольку этот тип сокетов является самым распространённым,
|
||||
до конца раздела мы будем говорить только о нём.
|
||||
Остальным типам будут посвящены отдельные разделы.
|
||||
- `SOCK_DGRAM`. Передача данных в виде отдельных сообщений (датаграмм).
|
||||
Предварительная установка соединения не требуется. Обмен данными происходит быстрее,
|
||||
но является ненадёжным: сообщения могут теряться в пути, дублироваться и переупорядочиваться.
|
||||
Допускается передача сообщения нескольким получателям (multicasting)
|
||||
и широковещательная передача (broadcasting).
|
||||
- `SOCK_RAW`. Этот тип присваивается низкоуровневым (т. н. "сырым") сокетам.
|
||||
Их отличие от обычных сокетов состоит в том, что с их помощью программа может
|
||||
взять на себя формирование некоторых заголовков, добавляемых к сообщению.
|
||||
|
||||
Обратите внимание, что не все домены допускают задание произвольного типа сокета.
|
||||
Например, совместно с доменом Unix используется только тип `SOCK_STREAM`.
|
||||
С другой стороны, для Internet-домена можно задавать любой из перечисленных типов.
|
||||
В этом случае для реализации `SOCK_STREAM` используется протокол TCP,
|
||||
для реализации `SOCK_DGRAM` - протокол UDP,
|
||||
а тип `SOCK_RAW` используется для низкоуровневой работы с протоколами IP, ICMP и т. д.
|
||||
|
||||
Наконец, последний атрибут определяет протокол, используемый для передачи данных.
|
||||
Как мы только что видели, часто протокол однозначно определяется по домену и типу сокета.
|
||||
В этом случае в качестве третьего параметра функции `socket` можно передать 0,
|
||||
что соответствует протоколу по умолчанию. Тем не менее, иногда
|
||||
(например, при работе с низкоуровневыми сокетами) требуется задать протокол явно.
|
||||
Числовые идентификаторы протоколов зависят от выбранного домена;
|
||||
их можно найти в документации.
|
||||
|
||||
### Адреса
|
||||
|
||||
Прежде чем передавать данные через сокет, его необходимо связать с адресом в выбранном домене
|
||||
(эту процедуру называют именованием сокета). Иногда связывание осуществляется неявно
|
||||
(внутри функций `connect` и `accept`), но выполнять его необходимо во всех случаях.
|
||||
Вид адреса зависит от выбранного вами домена.
|
||||
В Unix-домене это текстовая строка - имя файла, через который происходит обмен данными.
|
||||
В Internet-домене адрес задаётся комбинацией IP-адреса и 16-битного номера порта.
|
||||
IP-адрес определяет хост в сети, а порт - конкретный сокет на этом хосте.
|
||||
Протоколы TCP и UDP используют различные пространства портов.
|
||||
|
||||
Для явного связывания сокета с некоторым адресом используется функция `bind`.
|
||||
Её прототип имеет вид:
|
||||
|
||||
```c
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
int bind(int sockfd, struct sockaddr *addr, int addrlen);
|
||||
```
|
||||
|
||||
В качестве первого параметра передаётся дескриптор сокета,
|
||||
который мы хотим привязать к заданному адресу.
|
||||
Второй параметр, `addr`, содержит указатель на структуру с адресом,
|
||||
а третий - длину этой структуры. Посмотрим, что она собой представляет.
|
||||
|
||||
```c
|
||||
struct sockaddr {
|
||||
unsigned short sa_family; // Семейство адресов, AF_xxx
|
||||
char sa_data[14]; // 14 байтов для хранения адреса
|
||||
};
|
||||
```
|
||||
|
||||
Поле `sa_family` содержит идентификатор домена, тот же, что и первый параметр функции `socket`.
|
||||
В зависимости от значения этого поля по-разному интерпретируется содержимое массива `sa_data`.
|
||||
Разумеется, работать с этим массивом напрямую не очень удобно,
|
||||
поэтому вы можете использовать вместо `sockaddr` одну из альтернативных структур вида
|
||||
`sockaddr_XX` (`XX` - суффикс, обозначающий домен: `un` - Unix, `in` - Internet и т. д.).
|
||||
При передаче в функцию `bind` указатель на эту структуру приводится к указателю на `sockaddr`.
|
||||
Рассмотрим для примера структуру `sockaddr_in`.
|
||||
|
||||
```c
|
||||
struct sockaddr_in {
|
||||
short int sin_family; // Семейство адресов
|
||||
unsigned short int sin_port; // Номер порта
|
||||
struct in_addr sin_addr; // IP-адрес
|
||||
unsigned char sin_zero[8]; // "Дополнение" до размера структуры sockaddr
|
||||
};
|
||||
```
|
||||
|
||||
Здесь поле `sin_family` соответствует полю `sa_family` в `sockaddr`,
|
||||
в `sin_port` записывается номер порта, а в `sin_addr` - IP-адрес хоста.
|
||||
Поле `sin_addr` само является структурой, которая имеет вид:
|
||||
|
||||
```c
|
||||
struct in_addr {
|
||||
unsigned long s_addr;
|
||||
};
|
||||
```
|
||||
|
||||
Зачем понадобилось заключать всего одно поле в структуру?
|
||||
Дело в том, что раньше `in_addr` представляла собой объединение (union),
|
||||
содержащее гораздо большее число полей. Сейчас, когда в ней осталось всего одно поле,
|
||||
она продолжает использоваться для обратной совместимости.
|
||||
|
||||
И ещё одно важное замечание. Существует два порядка хранения байтов в слове и двойном слове.
|
||||
Один из них называется порядком хоста (host byte order),
|
||||
другой - сетевым порядком (network byte order) хранения байтов.
|
||||
При указании IP-адреса и номера порта необходимо преобразовать число
|
||||
из порядка хоста в сетевой. Для этого используются функции
|
||||
`htons` (Host TO Network Short) и `htonl` (Host TO Network Long).
|
||||
Обратное преобразование выполняют функции `ntohs` и `ntohl`.
|
||||
|
||||
**ПРИМЕЧАНИЕ**
|
||||
|
||||
На некоторых машинах (к PC это не относится) порядок хоста и сетевой порядок хранения байтов
|
||||
совпадают. Тем не менее, функции преобразования лучше применять и там,
|
||||
поскольку это улучшит переносимость программы.
|
||||
Это никак не скажется на производительности, так как препроцессор сам уберёт
|
||||
все "лишние" вызовы этих функций, оставив их только там,
|
||||
где преобразование действительно необходимо.
|
||||
|
||||
### Установка соединения (сервер)
|
||||
|
||||
Установка соединения на стороне сервера состоит из четырёх этапов,
|
||||
ни один из которых не может быть опущен. Сначала сокет создаётся и привязывается
|
||||
к локальному адресу. Если компьютер имеет несколько сетевых интерфейсов
|
||||
с различными IP-адресами, вы можете принимать соединения только с одного из них,
|
||||
передав его адрес функции `bind`. Если же вы готовы соединяться с клиентами
|
||||
через любой интерфейс, задайте в качестве адреса константу `INADDR_ANY`.
|
||||
Что касается номера порта, вы можете задать конкретный номер
|
||||
или 0 (в этом случае система сама выберет произвольный неиспользуемый
|
||||
в данный момент номер порта).
|
||||
|
||||
На следующем шаге создаётся очередь запросов на соединение.
|
||||
При этом сокет переводится в режим ожидания запросов со стороны клиентов.
|
||||
Всё это выполняет функция `listen`.
|
||||
|
||||
```c
|
||||
int listen(int sockfd, int backlog);
|
||||
```
|
||||
|
||||
Первый параметр - дескриптор сокета, а второй задаёт размер очереди запросов.
|
||||
Каждый раз, когда очередной клиент пытается соединиться с сервером,
|
||||
его запрос ставится в очередь, так как сервер может быть занят обработкой других запросов.
|
||||
Если очередь заполнена, все последующие запросы будут игнорироваться.
|
||||
Когда сервер готов обслужить очередной запрос, он использует функцию `accept`.
|
||||
|
||||
```c
|
||||
#include <sys/socket.h>
|
||||
|
||||
int accept(int sockfd, void *addr, int *addrlen);
|
||||
```
|
||||
|
||||
Функция `accept` создаёт для общения с клиентом новый сокет и возвращает его дескриптор.
|
||||
Параметр `sockfd` задаёт слушающий сокет. После вызова он остаётся
|
||||
в слушающем состоянии и может принимать другие соединения.
|
||||
В структуру, на которую ссылается `addr`, записывается адрес сокета клиента,
|
||||
который установил соединение с сервером. В переменную, адресуемую указателем `addrlen`,
|
||||
изначально записывается размер структуры;
|
||||
функция `accept` записывает туда длину, которая реально была использована.
|
||||
Если вас не интересует адрес клиента, вы можете просто передать `NULL`
|
||||
в качестве второго и третьего параметров.
|
||||
|
||||
Обратите внимание, что полученный от `accept` новый сокет связан с тем же самым адресом,
|
||||
что и слушающий сокет. Сначала это может показаться странным.
|
||||
Но дело в том, что адрес TCP-сокета не обязан быть уникальным в Internet-домене.
|
||||
Уникальными должны быть только соединения, для идентификации которых используются
|
||||
два адреса сокетов, между которыми происходит обмен данными.
|
||||
|
||||
### Установка соединения (клиент)
|
||||
|
||||
На стороне клиента для установления соединения используется функция `connect`,
|
||||
которая имеет следующий прототип.
|
||||
|
||||
```c
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
|
||||
```
|
||||
|
||||
Здесь `sockfd` - сокет, который будет использоваться для обмена данными с сервером,
|
||||
`serv_addr` содержит указатель на структуру с адресом сервера,
|
||||
а `addrlen` - длину этой структуры. Обычно сокет не требуется предварительно привязывать
|
||||
к локальному адресу, так как функция `connect` сделает это за вас,
|
||||
подобрав подходящий свободный порт. Вы можете принудительно назначить
|
||||
клиентскому сокету некоторый номер порта, используя `bind` перед вызовом `connect`.
|
||||
Делать это следует в случае, когда сервер соединяется с только с клиентами,
|
||||
использующими определённый порт (примерами таких серверов являются `rlogind` и `rshd`).
|
||||
В остальных случаях проще и надёжнее предоставить системе выбрать порт за вас.
|
||||
|
||||
### Обмен данными
|
||||
|
||||
После того как соединение установлено, можно начинать обмен данными.
|
||||
Для этого используются функции `send` и `recv`.
|
||||
В Unix для работы с сокетами можно использовать также файловые функции `read` и `write`,
|
||||
но они обладают меньшими возможностями, а кроме того не будут работать на других платформах
|
||||
(например, под Windows), поэтому я не рекомендую ими пользоваться.
|
||||
|
||||
Функция `send` используется для отправки данных и имеет следующий прототип.
|
||||
|
||||
```c
|
||||
int send(int sockfd, const void *msg, int len, int flags);
|
||||
```
|
||||
|
||||
Здесь `sockfd` - это, как всегда, дескриптор сокета, через который мы отправляем данные,
|
||||
`msg` - указатель на буфер с данными,
|
||||
`len` - длина буфера в байтах,
|
||||
а `flags` - набор битовых флагов, управляющих работой функции
|
||||
(если флаги не используются, передайте функции 0).
|
||||
Вот некоторые из них (полный список можно найти в документации):
|
||||
|
||||
- `MSG_OOB`. Предписывает отправить данные как срочные (out of band data, OOB).
|
||||
Концепция срочных данных позволяет иметь два параллельных канала данных в одном соединении.
|
||||
Иногда это бывает удобно. Например, Telnet использует срочные данные для передачи команд типа
|
||||
Ctrl+C. В настоящее время использовать их не рекомендуется из-за проблем с совместимостью
|
||||
(существует два разных стандарта их использования, описанные в RFC793 и RFC1122).
|
||||
Безопаснее просто создать для срочных данных отдельное соединение.
|
||||
- `MSG_DONTROUTE`. Запрещает маршрутизацию пакетов.
|
||||
Нижележащие транспортные слои могут проигнорировать этот флаг.
|
||||
|
||||
Функция `send` возвращает число байтов, которое на самом деле было отправлено
|
||||
(или -1 в случае ошибки). Это число может быть меньше указанного размера буфера.
|
||||
Если вы хотите отправить весь буфер целиком,
|
||||
вам придётся написать свою функцию и вызывать в ней `send`,
|
||||
пока все данные не будут отправлены. Она может выглядеть примерно так.
|
||||
|
||||
```c
|
||||
int sendall(int s, char *buf, int len, int flags)
|
||||
{
|
||||
int total = 0;
|
||||
int n;
|
||||
|
||||
while(total < len)
|
||||
{
|
||||
n = send(s, buf+total, len-total, flags);
|
||||
if(n == -1) { break; }
|
||||
total += n;
|
||||
}
|
||||
|
||||
return (n==-1 ? -1 : total);
|
||||
}
|
||||
```
|
||||
|
||||
Использование `sendall` ничем не отличается от использования `send`,
|
||||
но она отправляет весь буфер с данными целиком.
|
||||
|
||||
Для чтения данных из сокета используется функция `recv`.
|
||||
|
||||
```c
|
||||
int recv(int sockfd, void *buf, int len, int flags);
|
||||
```
|
||||
|
||||
В целом её использование аналогично `send`.
|
||||
Она точно так же принимает дескриптор сокета, указатель на буфер и набор флагов.
|
||||
Флаг `MSG_OOB` используется для приёма срочных данных,
|
||||
а `MSG_PEEK` позволяет "подсмотреть" данные, полученные от удалённого хоста,
|
||||
не удаляя их из системного буфера
|
||||
(это означает, что при следующем обращении к `recv` вы получите те же самые данные).
|
||||
Полный список флагов можно найти в документации.
|
||||
По аналогии с `send` функция `recv` возвращает количество прочитанных байтов,
|
||||
которое может быть меньше размера буфера.
|
||||
Вы без труда сможете написать собственную функцию `recvall`, заполняющую буфер целиком.
|
||||
Существует ещё один особый случай, при котором `recv` возвращает 0.
|
||||
Это означает, что соединение было разорвано.
|
||||
|
||||
### Закрытие сокета
|
||||
|
||||
Закончив обмен данными, закройте сокет с помощью функции `close`.
|
||||
Это приведёт к разрыву соединения.
|
||||
|
||||
```c
|
||||
#include <unistd.h>
|
||||
|
||||
int close(int fd);
|
||||
```
|
||||
|
||||
Вы также можете запретить передачу данных в каком-то одном направлении, используя `shutdown`.
|
||||
|
||||
```c
|
||||
int shutdown(int sockfd, int how);
|
||||
```
|
||||
|
||||
Параметр `how` может принимать одно из следующих значений:
|
||||
|
||||
- 0 - запретить чтение из сокета.
|
||||
- 1 - запретить запись в сокет.
|
||||
- 2 - запретить и то и другое.
|
||||
|
||||
Хотя после вызова `shutdown` с параметром `how`, равным 2,
|
||||
вы больше не сможете использовать сокет для обмена данными,
|
||||
вам всё равно потребуется вызвать `close`,
|
||||
чтобы освободить связанные с ним системные ресурсы.
|
||||
|
||||
### Обработка ошибок
|
||||
|
||||
До сих пор я ни слова не сказал об ошибках,
|
||||
которые могут происходить (и часто происходят) в процессе работы с сокетами.
|
||||
Так вот: если что-то пошло не так, все рассмотренные нами функции возвращают -1,
|
||||
записывая в глобальную переменную `errno` код ошибки.
|
||||
Соответственно, вы можете проанализировать значение этой переменной
|
||||
и предпринять действия по восстановлению нормальной работы программы,
|
||||
не прерывая её выполнения. А можете просто выдать диагностическое сообщение
|
||||
(для этого удобно использовать функцию `perror`),
|
||||
а затем завершить программу с помощью `exit`.
|
||||
Именно так я буду поступать в демонстрационных примерах.
|
||||
|
||||
### Отладка программ
|
||||
|
||||
Начинающие программисты часто спрашивают, как можно отлаживать сетевую программу,
|
||||
если под рукой нет сети. Оказывается, можно обойтись и без неё.
|
||||
Достаточно запустить клиента и сервера на одной машине,
|
||||
а затем использовать для соединения адрес интерфейса внутренней петли (loopback interface).
|
||||
В программе ему соответствует константа `INADDR_LOOPBACK`
|
||||
(не забудьте применять к ней функцию `htonl!`).
|
||||
Пакеты, направляемые по этому адресу, в сеть не попадают.
|
||||
Вместо этого они передаются стеку протоколов TCP/IP как только что принятые.
|
||||
Таким образом моделируется наличие виртуальной сети,
|
||||
в которой вы можете отлаживать ваши сетевые приложения.
|
||||
|
||||
Для простоты я буду использовать в демонстрационных примерах интерфейс внутренней петли.
|
||||
|
||||
### Эхо-клиент и эхо-сервер
|
||||
|
||||
Теперь, когда мы изучили основные функции для работы с сокетами,
|
||||
самое время посмотреть, как они используются на практике.
|
||||
Для этого я написал две небольшие демонстрационные программы.
|
||||
Эхо-клиент посылает сообщение **«Hello there!»** и выводит на экран ответ сервера.
|
||||
Его код приведён в **листинге 1**.
|
||||
Эхо-сервер читает всё, что передаёт ему клиент,
|
||||
а затем просто отправляет полученные данные обратно.
|
||||
Его код содержится в **листинге 2**.
|
||||
|
||||
**Листинг 1. Эхо-клиент.**
|
||||
|
||||
```c
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
char message[] = "Hello there!\n";
|
||||
char buf[sizeof(message)];
|
||||
|
||||
int main()
|
||||
{
|
||||
int sock;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if(sock < 0)
|
||||
{
|
||||
perror("socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(3425); // или любой другой порт...
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
perror("connect");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
send(sock, message, sizeof(message), 0);
|
||||
recv(sock, buf, sizeof(message), 0);
|
||||
|
||||
printf(buf);
|
||||
close(sock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
**Листинг 2. Эхо-сервер.**
|
||||
|
||||
```c
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int sock, listener;
|
||||
struct sockaddr_in addr;
|
||||
char buf[1024];
|
||||
int bytes_read;
|
||||
|
||||
listener = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if(listener < 0)
|
||||
{
|
||||
perror("socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(3425);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
perror("bind");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
listen(listener, 1);
|
||||
|
||||
while(1)
|
||||
{
|
||||
sock = accept(listener, NULL, NULL);
|
||||
if(sock < 0)
|
||||
{
|
||||
perror("accept");
|
||||
exit(3);
|
||||
}
|
||||
|
||||
while(1)
|
||||
{
|
||||
bytes_read = recv(sock, buf, 1024, 0);
|
||||
if(bytes_read <= 0) break;
|
||||
send(sock, buf, bytes_read, 0);
|
||||
}
|
||||
|
||||
close(sock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "🎒 Увеличиваю переносимый вес в S.T.A.L.K.E.R."
|
||||
title: "🎒 Увеличиваем переносимый вес в S.T.A.L.K.E.R."
|
||||
date: 2023-01-09T01:43:01+03:00
|
||||
draft: false
|
||||
tags: [games, tips]
|
||||
|
@ -17,13 +17,15 @@ tags: [http, tips]
|
||||
|
||||
Методы HTTP спроектированы для воздействия на ресурс стандартным способом:
|
||||
|
||||
{{< table "table table-sm table-striped table-hover" >}}
|
||||
| Метод HTTP | Действие | Пример |
|
||||
|------------|----------|--------|
|
||||
| GET | Получить информацию о ресурсе | example.com/api/orders (получить список заказов) |
|
||||
| GET | Получить информацию о ресурсе | example.com/api/orders/123 (получить заказ #123) |
|
||||
| POST | Создать новый ресурс | example.com/api/orders (создать новый заказ из данных переданных с запросом) |
|
||||
| PUT | Обновить ресурс | example.com/api/orders/123 (обновить заказ #123 данными переданными с запросом) |
|
||||
| DELETE| Удалить ресурс |example.com/api/orders/123 (удалить заказ #123) |
|
||||
|:----------:|:---------|:-------|
|
||||
| GET | Получить информацию о ресурсе | `example.com/api/orders` (получить список заказов) |
|
||||
| GET | Получить информацию о ресурсе | `example.com/api/orders/123` (получить заказ #123) |
|
||||
| POST | Создать новый ресурс | `example.com/api/orders` (создать новый заказ из данных переданных с запросом) |
|
||||
| PUT | Обновить ресурс | `example.com/api/orders/123` (обновить заказ #123 данными переданными с запросом) |
|
||||
| DELETE | Удалить ресурс | `example.com/api/orders/123` (удалить заказ #123) |
|
||||
{{</ table >}}
|
||||
|
||||
Дизайн REST не дает рекомендаций каким конкретно должен быть
|
||||
формат данных передаваемых с запросами.
|
||||
@ -60,13 +62,15 @@ http://[hostname]/todo/api/v1.0/
|
||||
|
||||
Для доступа к ресурсам будем использовать следующие методы HTTP:
|
||||
|
||||
{{< table "table table-sm table-striped table-hover" >}}
|
||||
| Метод HTTP | URI | Действие |
|
||||
|------------|-----|----------|
|
||||
| GET | http://[hostname]/todo/api/v1.0/tasks | Получить список задач |
|
||||
| GET | http://[hostname]/todo/api/v1.0/tasks/[task_id] | Получить задачу |
|
||||
| POST | http://[hostname]/todo/api/v1.0/tasks | Создать новую задачу |
|
||||
| PUT | http://[hostname]/todo/api/v1.0/tasks/[task_id] | Обновить существующую задачу |
|
||||
| DELETE | http://[hostname]/todo/api/v1.0/tasks/[task_id] | Удалить задачу |
|
||||
|:----------:|-----|----------|
|
||||
| GET | `http://[hostname]/todo/api/v1.0/tasks` | Получить список задач |
|
||||
| GET | `http://[hostname]/todo/api/v1.0/tasks/[task_id]` | Получить задачу |
|
||||
| POST | `http://[hostname]/todo/api/v1.0/tasks` | Создать новую задачу |
|
||||
| PUT | `http://[hostname]/todo/api/v1.0/tasks/[task_id]` | Обновить существующую задачу |
|
||||
| DELETE | `http://[hostname]/todo/api/v1.0/tasks/[task_id]` | Удалить задачу |
|
||||
{{</ table >}}
|
||||
|
||||
## Используемые ресурсы
|
||||
|
||||
|
134
content/posts/2023/privacy/gpg.md
Normal file
134
content/posts/2023/privacy/gpg.md
Normal file
@ -0,0 +1,134 @@
|
||||
---
|
||||
title: "🔐 GnuPG"
|
||||
date: 2023-04-05T12:05:35+03:00
|
||||
draft: false
|
||||
tags: [gpg, privacy, tips]
|
||||
---
|
||||
|
||||
## GnuPG
|
||||
|
||||
GnuPG — полная и свободная реализация [OpenPGP](https://openpgp.org/about/) стандарта,
|
||||
определенного в [RFC4880](https://tools.ietf.org/html/rfc4880) (также известного, как PGP).
|
||||
GnuPG позволяет вам шифровать и подписывать данные и сообщения.
|
||||
Он оснащен универсальной системой управления ключами,
|
||||
а также модулями доступа для всех типов открытых ключей.
|
||||
GnuPG, также известный как GPG,
|
||||
это инструмент командной строки с возможностью легкой интеграции с другими приложениями.
|
||||
Доступен богатый выбор пользовательских приложений и библиотек.
|
||||
Также 2 версия GnuPG поддерживает S/MIME и Secure Shell (ssh).
|
||||
|
||||
## Расположение каталогов
|
||||
|
||||
`$GNUPGHOME` используется GnuPG для определения каталога,
|
||||
в котором хранятся конфигурационные файлы.
|
||||
По умолчанию `$GNUPGHOME` не назначена и вместо этого используется `$HOME`;
|
||||
таким образом, вы найдете каталог `~/.gnupg` сразу после установки.
|
||||
|
||||
Чтобы изменить стандартное расположение, выполните `gpg --homedir путь/к/файлу`
|
||||
или установите переменную окружения `GNUPGHOME`.
|
||||
|
||||
## Файлы конфигурации
|
||||
|
||||
Файлы конфигурации по умолчанию `~/.gnupg/gpg.conf`.
|
||||
По умолчанию разрешения доступа каталога gnupg установлены в `700`,
|
||||
а файлов, которые он содержит - `600`.
|
||||
Только владелец каталога имеет разрешение на просмотр содержимого,
|
||||
радактирование и доступ к файлам. В целях безопасности,
|
||||
эти разрешения не должны быть изменены.
|
||||
В случае, если этот каталог или любые файлы внутри не следуют данной мере безопасности,
|
||||
вы получите предупреждение о наличии небезопасных файлов и разрешений домашнего каталога.
|
||||
|
||||
## Создание пары ключей
|
||||
|
||||
Чтобы создать пару ключей, необходимо ввести команду:
|
||||
```sh
|
||||
gpg --full-gen-key
|
||||
```
|
||||
|
||||
ℹ️ Используйте опцию `--expert`, чтобы выбрать другие шифры,
|
||||
такие как [ECC](https://en.wikipedia.org/wiki/Elliptic_curve_cryptography).
|
||||
|
||||
ℹ️ Более простая опция `--gen-key` использует параметры по умолчанию для шифра,
|
||||
размера и срока действия ключа и запрашивает только имя и адрес электронной почты.
|
||||
|
||||
## Экспорт открытого ключа
|
||||
|
||||
Основное назначение GnuPG — обеспечение конфиденциальности обмена сообщениями
|
||||
с помощью криптографии с открытым ключом.
|
||||
С его помощью каждый пользователь распространяет открытый ключ своей связки ключей,
|
||||
который может быть использован другими пользователями для шифрования сообщений пользователю.
|
||||
Закрытый ключ всегда должен оставаться в тайне, иначе конфиденциальность будет нарушена.
|
||||
|
||||
Таким образом, чтобы другие могли отправлять вам зашифрованные сообщения,
|
||||
им нужен ваш открытый ключ.
|
||||
|
||||
Чтобы сгенерировать ASCII-версию открытого ключа пользователя в файл `public.key`
|
||||
(например, для отправки по электронной почте):
|
||||
|
||||
```sh
|
||||
gpg --export --armor --output public.key user-id
|
||||
```
|
||||
|
||||
### Импорт открытого ключа
|
||||
|
||||
Чтобы зашифровать сообщения другим людям, а также проверить их подписи,
|
||||
вам нужен их открытый ключ. Чтобы импортировать открытый ключ из файла public.key в свой список открытых ключей, выполните команду:
|
||||
|
||||
```sh
|
||||
gpg --import public.key
|
||||
```
|
||||
|
||||
## Резервное копирование закрытого ключа
|
||||
|
||||
Чтобы создать резервную копию вашего закрытого ключа, выполните:
|
||||
|
||||
```sh
|
||||
gpg --export-secret-keys --armor --output privkey.asc user-id
|
||||
```
|
||||
|
||||
Обратите внимание, что вышеуказанная команда требует ввода пароля от ключа.
|
||||
В противном случае любой, кто получит доступ к экспортированному файлу,
|
||||
сможет шифровать и подписывать документы, как если бы он был вами,
|
||||
без необходимости знать пароль.
|
||||
|
||||
⚠️ **Важно:**
|
||||
Пароль — обычно самое слабое звено в защите закрытого ключа.
|
||||
Поместите закрытый ключ в безопасное место на другой системе или на другом устройстве,
|
||||
например, в заблокированный контейнер или на зашифрованный диск.
|
||||
Это единственное средство защиты, которое поможет
|
||||
вам восстановить контроль над списком ваших ключей в случае,
|
||||
например, поломки диска, кражи или ещё чего-нибудь похуже.
|
||||
Этот способ резервного копирования ключей имеет некоторые ограничения по безопасности.
|
||||
Более безопасный способ резервного копирования и импорта ключей с помощью gpg описан
|
||||
[здесь](https://web.archive.org/web/20210803213236/https://habd.as/post/moving-gpg-keys-privately/).
|
||||
|
||||
## Импорт закрытого ключа из резервной копии:
|
||||
|
||||
```sh
|
||||
gpg --import privkey.asc
|
||||
```
|
||||
|
||||
ℹ️ Совет: [Paperkey](https://www.jabberwocky.com/software/paperkey/) позволяет экспортировать ключ в виде простого текста
|
||||
или машиночитаемого штрих-кода, которые можно отпечатать на бумаге.
|
||||
|
||||
## Резервное копирование сертификата отзыва
|
||||
|
||||
Сертификаты отзыва автоматически генерируются для вновь создаваемых ключей.
|
||||
По умолчанию они находятся в `~/.gnupg/openpgp-revocs.d/`.
|
||||
Имя файла сертификата — это отпечаток ключа, который он отзывает.
|
||||
Сертификаты отзыва также можно сгенерированы вручную с помощью следующей команды:
|
||||
|
||||
```sh
|
||||
gpg --gen-revoke --armor --output revcert.asc user-id
|
||||
```
|
||||
|
||||
Этот сертификат используется, чтобы выполнить отзыв ключа в случае,
|
||||
если он оказался потерян или скомпрометирован.
|
||||
Резервная копия будет полезна, если у вас больше нет доступа к закрытому ключу,
|
||||
из-за чего вы не можете сгенерировать
|
||||
новый сертификат отзыва с помощью приведённой выше команды.
|
||||
Он достаточно короткий, чтобы его можно было распечатать и набрать от руки при необходимости.
|
||||
|
||||
⚠️ **Важно:** Любой человек, имеющий доступ к сертификату отзыва,
|
||||
может публично отозвать ключ, и это действие нельзя отменить.
|
||||
Защищайте свой сертификат отзыва так же, как вы защищаете свой закрытый ключ.
|
@ -48,3 +48,5 @@ Ctrl + b $
|
||||
```text
|
||||
Ctrl + b d
|
||||
```
|
||||
|
||||
`TODO: сделать из этого таблицу`
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "Диаграмма выводов Ardunino Nano"
|
||||
date: 2023-06-19T22:05:27+03:00
|
||||
draft: true
|
||||
title: "🔌 Диаграмма выводов Ardunino Nano"
|
||||
date: 2023-06-25T20:53:27+03:00
|
||||
draft: false
|
||||
tags: [arduino, tips, hardware]
|
||||
---
|
||||
|
||||
@ -20,7 +20,8 @@ tags: [arduino, tips, hardware]
|
||||
|
||||
## Цифровые пины
|
||||
|
||||
| Pin Name | Description | Secondary Function | Description |
|
||||
{{< table "table table-sm table-striped table-hover" >}}
|
||||
| Имя ПИНА | Описание | Вторичная функция | Описание |
|
||||
|----------|----------------|--------------------|------------------------------|
|
||||
| D0 | Digital Pin 0 | RX | Receive pin for Serial UART |
|
||||
| D1 | Digital Pin 1 | TX | Transmit pin for Serial UART |
|
||||
@ -36,10 +37,12 @@ tags: [arduino, tips, hardware]
|
||||
| D11 | Digital Pin 11 | MOSI | SPI Master Out-Slave In |
|
||||
| D12 | Digital Pin 12 | MISO | SPI Master In-Slave Out |
|
||||
| D13 | Digital Pin 13 | SCK | SPI Clock |
|
||||
{{</ table >}}
|
||||
|
||||
## Аналоговые пины
|
||||
|
||||
| Pin Name | Description | Secondary Function | Description |
|
||||
{{< table "table table-sm table-striped table-hover" >}}
|
||||
| Имя ПИНА | Описание | Вторичная функция | Описание |
|
||||
|----------|--------------|--------------------|-------------|
|
||||
| A0 | Analog Pin 0 | | |
|
||||
| A1 | Analog Pin 1 | | |
|
||||
@ -49,10 +52,12 @@ tags: [arduino, tips, hardware]
|
||||
| A5 | Analog Pin 5 | SCLI2C | Clock |
|
||||
| A6 | Analog Pin 6 | SCL | |
|
||||
| A7 | Analog Pin 7 | SCL | |
|
||||
{{</ table >}}
|
||||
|
||||
## Пины питания
|
||||
|
||||
| Pin Name | Description |
|
||||
{{< table "table table-sm table-striped table-hover" >}}
|
||||
| Имя пина | Описание |
|
||||
|----------|----------------------------------------------------------------------------------------------------|
|
||||
| 5V | 5V (Regulated) Source |
|
||||
| 3.3V | 3.3V Source |
|
||||
@ -61,3 +66,4 @@ tags: [arduino, tips, hardware]
|
||||
| Vin | DC Jack Input Voltage |
|
||||
| IOREF | I/O Reference Voltage. This pin is connected to 5V for the UNO |
|
||||
| AREF | ADC Reference Voltage. Insert other voltage (0-5V only) to use as reference for analog conversions |
|
||||
{{</ table >}}
|
||||
|
@ -5,7 +5,7 @@ draft: true
|
||||
tags: [music]
|
||||
---
|
||||
|
||||
Моё сердечко пренадлежит:
|
||||
Моё сердечко пренадлежит:
|
||||
Adept
|
||||
Metallica
|
||||
Ill Nino
|
||||
|
@ -51,6 +51,7 @@ DFPlayer Mini — это миниатюрный MP3 проигрыватель
|
||||
|
||||
![](/content/images/smd/dfplayer_mini_pinout_back-scaled.jpg)
|
||||
|
||||
{{< table "table table-sm table-striped table-hover" >}}
|
||||
| Пин | Описание | Примечание |
|
||||
|--------|--------------------|---------------------------------------------------------------------------------------|
|
||||
| VCC | Входное напряжение | DC3.2~5.0В; DC4.2В |
|
||||
@ -69,6 +70,7 @@ DFPlayer Mini — это миниатюрный MP3 проигрыватель
|
||||
| USB+ | USB+ DP | Порт USB |
|
||||
| USB- | USB- DM | Порт USB |
|
||||
| BUSY | Статус | Low — включено, High — выключено |
|
||||
{{</ table >}}
|
||||
|
||||
## Рабочие режимы
|
||||
|
||||
|
1
themes/papercut/README.md
Normal file
1
themes/papercut/README.md
Normal file
@ -0,0 +1 @@
|
||||
Drop `bootstrap.min.css` from Bootstrap **5.3.0** in `static/css` folder.
|
83
themes/papercut/layouts/_index/projects.html
Normal file
83
themes/papercut/layouts/_index/projects.html
Normal file
@ -0,0 +1,83 @@
|
||||
<section class="bg-white border rounded shadow-sm p-3 mb-3">
|
||||
<h2 class="text-center mb-3">🗂️ Project list</h2>
|
||||
|
||||
<div class="row row-cols-1 row-cols-md-2 g-3">
|
||||
<!-- minecraft launcher -->
|
||||
<div class="col">
|
||||
<div class="bg-body-tertiary border rounded p-3">
|
||||
<p class="fw-bold mb-2">⛏️
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://me.a2s.su/minecraft-launcher/" target="_blank">
|
||||
Minecraft JE Batch Launcher</a>
|
||||
</p>
|
||||
<p class="mb-0">Simple script for launch Minecraft without official launcher. Any OS</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ё с точками -->
|
||||
<div class="col">
|
||||
<div class="bg-body-tertiary border rounded p-3">
|
||||
<p class="fw-bold mb-2">🔡
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://xn--61a.a2s.su" target="_blank">
|
||||
е с точками</a>
|
||||
</p>
|
||||
<p class="mb-0">Сайт для тех, что ценит русский язык и красивое написание текста</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ytcg -->
|
||||
<div class="col">
|
||||
<div class="bg-body-tertiary border rounded p-3">
|
||||
<p class="fw-bold mb-2">🎞️
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://me.a2s.su/ytcg/" target="_blank">
|
||||
Get YouTube video cover</a>
|
||||
</p>
|
||||
<p class="mb-0">Service for download video cover from YouTube</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<!-- chola -->
|
||||
<div class="bg-body-tertiary border rounded p-3">
|
||||
<p class="fw-bold mb-2">⌨️
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://me.a2s.su/chola/" target="_blank">
|
||||
CHange Of LAyout</a>
|
||||
</p>
|
||||
<p class="mb-0">Меняем написанный текст буржуйской раскладки на нашинскую</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- pmng -->
|
||||
<div class="col">
|
||||
<div class="bg-body-tertiary border rounded p-3">
|
||||
<p class="fw-bold mb-2">🔑
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://git.a2s.su/iiiypuk/pmng" target="_blank">
|
||||
pmng</a>
|
||||
</p>
|
||||
<p class="mb-0">The very simple password manager for humans</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- assm -->
|
||||
<div class="col">
|
||||
<div class="bg-body-tertiary border rounded p-3">
|
||||
<p class="fw-bold mb-2">🍎
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://git.a2s.su/emilecok/assm" target="_blank">
|
||||
assm</a>
|
||||
</p>
|
||||
<p class="mb-0">iOS splash screen generator</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- vk0nline -->
|
||||
<div class="col">
|
||||
<div class="bg-body-tertiary border rounded p-3">
|
||||
<p class="fw-bold mb-2">🟢
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://git.a2s.su/iiiypuk/vk0nline" target="_blank">
|
||||
vk0nline</a>
|
||||
</p>
|
||||
<p class="mb-0">systemd service and utility for set online status on vk.com</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
126
themes/papercut/layouts/_index/projects2.html
Normal file
126
themes/papercut/layouts/_index/projects2.html
Normal file
@ -0,0 +1,126 @@
|
||||
<section class="bg-white border rounded shadow-sm p-2 mb-3">
|
||||
<h2 class="text-center mt-2 mb-3">💼 Other projects</h2>
|
||||
|
||||
<div class="list-group">
|
||||
<div class="list-group-item list-group-item-action border-0 rounded-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/mirror"
|
||||
target="_blank">git.a2s.su</a> —
|
||||
Git mirrors of some repos
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-group-item list-group-item-action border-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/battlepillars-bot"
|
||||
target="_blank">battlepillars-bot</a> —
|
||||
Farming apples bot for
|
||||
<a class="border-bottom border-primary text-decoration-none py-1" href="https://store.steampowered.com/app/280930/Battlepillars_Gold_Edition/">Battlepillars</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-group-item list-group-item-action border-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/delver"
|
||||
target="_blank">delver-pack-ru-ru</a> —
|
||||
Репозиторий перевода Delver
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-group-item list-group-item-action border-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/git-ahead-check"
|
||||
target="_blank">git-ahead-check</a> —
|
||||
A tool for checking changes that have not been sent in Git repos
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-group-item list-group-item-action border-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/eir"
|
||||
target="_blank">eir</a> —
|
||||
Easy Internet Radio (Visual Basic 6)
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-group-item list-group-item-action border-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/aimp-theme-pscc"
|
||||
target="_blank">aimp-theme-pscc</a> —
|
||||
Photoshop CC 2014 Visual Style for AIMP 4
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-group-item list-group-item-action border-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/proot-alpine"
|
||||
target="_blank">proot-alpine</a> —
|
||||
Script for run Alpine Linux from user directory
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-group-item list-group-item-action border-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/stanis-tits-scrap"
|
||||
target="_blank">stanis-tits-scrap</a> —
|
||||
Скрипт для скачивания голых и не только девушек с блога
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="http://blog.stanis.ru/" target="_blank">stanis.ru</a>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-group-item list-group-item-action border-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/lpp-linux-builder"
|
||||
target="_blank">lpp-linux-builder</a> —
|
||||
Script for build
|
||||
<a class="border-bottom border-primary text-decoration-none py-1" href="http://rinnegatamante.github.io/lpp-vita/" target="_blank">Lua Player Plus</a>
|
||||
apps for PSVITA (PSP2)
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-group-item list-group-item-action border-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/lastfm-backup"
|
||||
target="_blank">lastfm-backup</a> —
|
||||
Script for download music scrobbles from Last.fm
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-group-item list-group-item-action border-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/olr_steam"
|
||||
target="_blank">olr_steam</a> —
|
||||
Скрипт для запуска Oblivion Lost Remake в Steam
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-group-item list-group-item-action border-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/ogsr_steam"
|
||||
target="_blank">ogsr_steam</a> —
|
||||
Скрипт для запуска OGSR Mod в Steam
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-group-item list-group-item-action border-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/minecraft-launcher-libs"
|
||||
target="_blank">minecraft-launcher-libs</a> —
|
||||
Library parser from
|
||||
<code class="font-monospace rounded p-1 bg-dark text-white">version.json</code>
|
||||
on Minecraft JE
|
||||
</p>
|
||||
</div>
|
||||
<div class="list-group-item list-group-item-action border-0 rounded-0 my-1">
|
||||
<p class="mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/cuboid"
|
||||
target="_blank">cuboid</a> —
|
||||
Stuff JoyeTech Cuboid TC 150W
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
9
themes/papercut/layouts/_index/reach_me.html
Normal file
9
themes/papercut/layouts/_index/reach_me.html
Normal file
@ -0,0 +1,9 @@
|
||||
<section class="bg-white border rounded shadow-sm p-3 mb-3">
|
||||
<h2 class="text-center mb-3">📫 How to reach me</h2>
|
||||
<div class="lh-lg text-center">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 mx-1" target="_blank" href="mailto:iiiypuk@fastmail.fm">Mail</a>
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 mx-1" target="_blank" href="http://a2s.su:8000/">Micro blog</a>
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 mx-1" target="_blank" href="irc://iiiypuk.me:6667/admin">IRC</a>
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 mx-1" target="_blank" href="gemini://a2s.su">Gemini</a>
|
||||
</div>
|
||||
</section>
|
42
themes/papercut/layouts/_index/what_i_use.html
Normal file
42
themes/papercut/layouts/_index/what_i_use.html
Normal file
@ -0,0 +1,42 @@
|
||||
<section class="bg-white border rounded shadow-sm p-3 mb-3">
|
||||
<h2 class="text-center">🔌 What I use</h2>
|
||||
|
||||
<p class="text-center lh-lg">
|
||||
<span class="fw-bold">📕 Languages & frameworks:</span>
|
||||
|
||||
<span class="border-bottom p-1">Pure C</span>,
|
||||
<span class="border-bottom p-1">Crystal</span>,
|
||||
<span class="border-bottom p-1">JavaScript</span>,
|
||||
<span class="border-bottom p-1">Python</span>,
|
||||
<span class="border-bottom p-1">PHP</span>,
|
||||
<span class="border-bottom p-1 text-nowrap">Bootstrap 5</span>
|
||||
</p>
|
||||
|
||||
<p class="text-center lh-lg">
|
||||
<span class="fw-bold">🧰 Tools:</span>
|
||||
|
||||
<span class="border-bottom p-1">Porteus</span>,
|
||||
<span class="border-bottom p-1 text-nowrap">Alpine Linux</span>,
|
||||
<span class="border-bottom p-1">OpenBSD</span>,
|
||||
<span class="border-bottom p-1 text-nowrap">Arch Linux</span>,
|
||||
<span class="border-bottom p-1 text-nowrap">Windows 11</span>,
|
||||
<span class="border-bottom p-1 text-nowrap">Sublime Text 4</span>,
|
||||
<span class="border-bottom p-1 text-nowrap">Windows Terminal</span>,
|
||||
<span class="border-bottom p-1">CorelDRAW</span>,
|
||||
<span class="border-bottom p-1">GameMaker</span>,
|
||||
<span class="border-bottom p-1">EditorConfig</span>,
|
||||
<span class="border-bottom p-1">Gitea</span>,
|
||||
<span class="border-bottom p-1">Git</span>,
|
||||
<span class="border-bottom p-1">Tmux</span>
|
||||
</p>
|
||||
|
||||
<p class="text-center lh-lg">
|
||||
<span class="fw-bold">📢 Services:</span>
|
||||
|
||||
<span class="border-bottom p-1">FastMail</span>,
|
||||
<span class="border-bottom p-1">Last.fm</span>,
|
||||
<span class="border-bottom p-1">MyAnimeList</span>,
|
||||
<span class="border-bottom p-1">Postcrossing</span>,
|
||||
<span class="border-bottom p-1">Pinterest</span>
|
||||
</p>
|
||||
</section>
|
@ -3,303 +3,17 @@
|
||||
<main>
|
||||
<section>
|
||||
<p class="bg-white border rounded shadow-sm p-3 text-center">
|
||||
Hey, my name is Alexander Popov.<br>
|
||||
I am a chef of anything IT :)
|
||||
🙃<br>
|
||||
Hey, my name is Alexander Popov.
|
||||
I am a chef of anything IT.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="bg-white border rounded shadow-sm p-3 mb-3">
|
||||
<h2 class="text-center">🔌 What I use</h2>
|
||||
{{ template "_index/projects.html" . }}
|
||||
{{ template "_index/projects2.html" . }}
|
||||
{{ template "_index/what_i_use.html" . }}
|
||||
{{ template "_index/reach_me.html" . }}
|
||||
|
||||
<p class="text-center lh-lg">
|
||||
<span class="fw-bold">📕 Languages & frameworks:</span>
|
||||
|
||||
<span class="border-bottom p-1">Pure C</span>,
|
||||
<span class="border-bottom p-1">Crystal</span>,
|
||||
<span class="border-bottom p-1">JavaScript</span>,
|
||||
<span class="border-bottom p-1">Python</span>,
|
||||
<span class="border-bottom p-1">PHP</span>,
|
||||
<span class="border-bottom p-1 text-nowrap">Bootstrap 5</span>
|
||||
</p>
|
||||
|
||||
<p class="text-center lh-lg">
|
||||
<span class="fw-bold">🧰 Tools:</span>
|
||||
|
||||
<span class="border-bottom p-1">Porteus</span>,
|
||||
<span class="border-bottom p-1 text-nowrap">Alpine Linux</span>,
|
||||
<span class="border-bottom p-1">OpenBSD</span>,
|
||||
<span class="border-bottom p-1 text-nowrap">Arch Linux</span>,
|
||||
<span class="border-bottom p-1 text-nowrap">Windows 11</span>,
|
||||
<span class="border-bottom p-1 text-nowrap">Sublime Text 4</span>,
|
||||
<span class="border-bottom p-1 text-nowrap">Windows Terminal</span>,
|
||||
<span class="border-bottom p-1">CorelDRAW</span>,
|
||||
<span class="border-bottom p-1">GameMaker</span>,
|
||||
<span class="border-bottom p-1">EditorConfig</span>,
|
||||
<span class="border-bottom p-1">Gitea</span>,
|
||||
<span class="border-bottom p-1">Git</span>,
|
||||
<span class="border-bottom p-1">Tmux</span>
|
||||
</p>
|
||||
|
||||
<p class="text-center lh-lg">
|
||||
<span class="fw-bold">📢 Services:</span>
|
||||
|
||||
<span class="border-bottom p-1">FastMail</span>,
|
||||
<span class="border-bottom p-1">Last.fm</span>,
|
||||
<span class="border-bottom p-1">Pinterest</span>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="bg-white border rounded shadow-sm p-3 mb-3">
|
||||
<h2 class="text-center mb-3">🗂️ Project list</h2>
|
||||
|
||||
<div class="row row-cols-1 row-cols-md-2 g-3">
|
||||
<!-- minecraft launcher -->
|
||||
<div class="col">
|
||||
<div class="border rounded p-3">
|
||||
<p class="fw-bold mb-2">⛏️
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://me.a2s.su/minecraft-launcher/" target="_blank">
|
||||
Minecraft JE Batch Launcher</a>
|
||||
</p>
|
||||
<p class="mb-0">Simple script for launch Minecraft without official launcher. Any OS</p>
|
||||
<p class="mb-0">
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">Game</span>
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">Shell</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ё с точками -->
|
||||
<div class="col">
|
||||
<div class="border rounded p-3">
|
||||
<p class="fw-bold mb-2">🔡
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://xn--61a.a2s.su" target="_blank">
|
||||
е с точками</a>
|
||||
</p>
|
||||
<p class="mb-0">Сайт для тех, что ценит русский язык и красивое написание текста</p>
|
||||
<p class="mb-0">
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">Grammar</span>
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">JavaScript</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ytcg -->
|
||||
<div class="col">
|
||||
<div class="border rounded p-3">
|
||||
<p class="fw-bold mb-2">🎞️
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://me.a2s.su/ytcg/" target="_blank">
|
||||
Get YouTube video cover</a>
|
||||
</p>
|
||||
<p class="mb-0">Service for download video cover from YouTube</p>
|
||||
<p class="mb-0">
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">YouTube</span>
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">JavaScript</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<!-- chola -->
|
||||
<div class="border rounded p-3">
|
||||
<p class="fw-bold mb-2">⌨️
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://me.a2s.su/chola/" target="_blank">
|
||||
CHange Of LAyout</a>
|
||||
</p>
|
||||
<p class="mb-0">Меняем написанный текст буржуйской раскладки на нашинскую</p>
|
||||
<p class="mb-0">
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">Grammar</span>
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">JavaScript</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- pmng -->
|
||||
<div class="col">
|
||||
<div class="border rounded p-3">
|
||||
<p class="fw-bold mb-2">🔑
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://git.a2s.su/iiiypuk/pmng" target="_blank">
|
||||
pmng</a>
|
||||
</p>
|
||||
<p class="mb-0">The very simple password manager for humans</p>
|
||||
<p class="mb-0">
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">CLI</span>
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">Tool</span>
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">Crystal</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- assm -->
|
||||
<div class="col">
|
||||
<div class="border rounded p-3">
|
||||
<p class="fw-bold mb-2">🍎
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://git.a2s.su/emilecok/assm" target="_blank">
|
||||
assm</a>
|
||||
</p>
|
||||
<p class="mb-0">iOS splash screen generator</p>
|
||||
<p class="mb-0">
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">CLI</span>
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">Tool</span>
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">Python</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- vk0nline -->
|
||||
<div class="col">
|
||||
<div class="border rounded p-3">
|
||||
<p class="fw-bold mb-2">🟢
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="https://git.a2s.su/iiiypuk/vk0nline" target="_blank">
|
||||
vk0nline</a>
|
||||
</p>
|
||||
<p class="mb-0">systemd service and utility for set online status on vk.com</p>
|
||||
<p class="mb-0">
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">Social</span>
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">Crystal</span>
|
||||
<span class="badge bg-secondary text-nowrap fw-normal">systemd</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="bg-white border rounded shadow-sm p-3 mb-3">
|
||||
<h2 class="text-center mb-3">💼 Other projects</h2>
|
||||
|
||||
<div class="row row-cols-1 g-3">
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/mirror"
|
||||
target="_blank">git.a2s.su</a> —
|
||||
Git mirrors of some repos
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/battlepillars-bot"
|
||||
target="_blank">battlepillars-bot</a> —
|
||||
Farming apples bot for
|
||||
<a class="border-bottom border-primary text-decoration-none py-1" href="https://store.steampowered.com/app/280930/Battlepillars_Gold_Edition/">Battlepillars</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/delver"
|
||||
target="_blank">delver-pack-ru-ru</a> —
|
||||
Репозиторий перевода Delver
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/git-ahead-check"
|
||||
target="_blank">git-ahead-check</a> —
|
||||
A tool for checking changes that have not been sent in Git repos
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/eir"
|
||||
target="_blank">eir</a> —
|
||||
Easy Internet Radio (Visual Basic 6)
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/aimp-theme-pscc"
|
||||
target="_blank">aimp-theme-pscc</a> —
|
||||
Photoshop CC 2014 Visual Style for AIMP 4
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/proot-alpine"
|
||||
target="_blank">proot-alpine</a> —
|
||||
Script for run Alpine Linux from user directory
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/stanis-tits-scrap"
|
||||
target="_blank">stanis-tits-scrap</a> —
|
||||
Скрипт для скачивания голых и не только девушек с блога
|
||||
<a class="border-bottom border-primary text-decoration-none py-1"
|
||||
href="http://blog.stanis.ru/" target="_blank">stanis.ru</a>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/lpp-linux-builder"
|
||||
target="_blank">lpp-linux-builder</a> —
|
||||
Script for build
|
||||
<a class="border-bottom border-primary text-decoration-none py-1" href="http://rinnegatamante.github.io/lpp-vita/" target="_blank">Lua Player Plus</a>
|
||||
apps for PSVITA (PSP2)
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/lastfm-backup"
|
||||
target="_blank">lastfm-backup</a> —
|
||||
Script for download music scrobbles from Last.fm
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/olr_steam"
|
||||
target="_blank">olr_steam</a> —
|
||||
Скрипт для запуска Oblivion Lost Remake в Steam
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/ogsr_steam"
|
||||
target="_blank">ogsr_steam</a> —
|
||||
Скрипт для запуска OGSR Mod в Steam
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/minecraft-launcher-libs"
|
||||
target="_blank">minecraft-launcher-libs</a> —
|
||||
Library parser from
|
||||
<code class="font-monospace rounded p-1 bg-dark text-white">version.json</code>
|
||||
on Minecraft JE
|
||||
</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="border rounded p-2 mb-0">
|
||||
<a class="border-bottom border-primary text-decoration-none py-1 fw-bold"
|
||||
href="https://git.a2s.su/iiiypuk/cuboid"
|
||||
target="_blank">cuboid</a> —
|
||||
Stuff JoyeTech Cuboid TC 150W
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="bg-white border rounded shadow-sm p-3 mb-3">
|
||||
<h2 class="text-center mb-3">📫 How to reach me</h2>
|
||||
<div class="lh-lg text-center">
|
||||
<a class="border-bottom border-primary text-decoration-none p-1" target="_blank" href="mailto:iiiypuk@fastmail.fm">EMail</a>
|
||||
<a class="border-bottom border-primary text-decoration-none p-1" target="_blank" href="http://a2s.su:8000/">Micro blog</a>
|
||||
<a class="border-bottom border-primary text-decoration-none p-1" target="_blank" href="irc://iiiypuk.me:6667/admin">IRC</a>
|
||||
<a class="border-bottom border-primary text-decoration-none p-1" target="_blank" href="gemini://a2s.su">Gemini</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
6
themes/papercut/layouts/shortcodes/table.html
Normal file
6
themes/papercut/layouts/shortcodes/table.html
Normal file
@ -0,0 +1,6 @@
|
||||
{{ $htmlTable := .Inner | markdownify }}
|
||||
{{ $class := .Get 0 }}
|
||||
{{ $old := "<table>" }}
|
||||
{{ $new := printf "<table class=\"%s\">" $class }}
|
||||
{{ $htmlTable := replace $htmlTable $old $new }}
|
||||
{{ $htmlTable | safeHTML }}
|
Loading…
Reference in New Issue
Block a user