Compare commits
26 Commits
c722255161
...
gitea-draf
| Author | SHA1 | Date | |
|---|---|---|---|
| 08a4112420 | |||
| a420980165 | |||
| c4ce5155fe | |||
| e34afe876c | |||
| b24d95ac83 | |||
| 0891a4dcc1 | |||
| c8d7f83fcc | |||
| abd5f2c97a | |||
| 868dec5ab3 | |||
| 4ef1bedad7 | |||
| 0f7e022a5f | |||
| 25cc09b930 | |||
| 7f0d996c00 | |||
| 69653d9883 | |||
| 49fc40b1f7 | |||
| 534f950565 | |||
| 7f7bdfa749 | |||
| 5174e06ce5 | |||
| 74ebd01b5f | |||
| 3d954256a8 | |||
| 46d036d2c1 | |||
| 607ee6297c | |||
| fa1d8dad61 | |||
| cdad11f2d1 | |||
| 631c25885c | |||
| be7c730423 |
@@ -11,7 +11,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: Clone repository
|
||||||
run: |
|
run: |
|
||||||
git clone --branch main https://git.tishenko.dev/tish/knowledge-base.git
|
git clone --depth 1 --branch main https://git.tishenko.dev/tish/knowledge-base.git
|
||||||
|
|
||||||
- name: Build site
|
- name: Build site
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
143
docs/VS-Code/attachments/latex.json
Normal file
143
docs/VS-Code/attachments/latex.json
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
{
|
||||||
|
// Place your snippets for latex here. Each snippet is defined under a snippet name and has a prefix, body and
|
||||||
|
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
||||||
|
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
|
||||||
|
// same ids are connected.
|
||||||
|
// Example:
|
||||||
|
// "Print to console": {
|
||||||
|
// "prefix": "log",
|
||||||
|
// "body": [
|
||||||
|
// "console.log('$1');",
|
||||||
|
// "$2"
|
||||||
|
// ],
|
||||||
|
// "description": "Log output to console"
|
||||||
|
// }
|
||||||
|
"Inclue image": {
|
||||||
|
"prefix": [
|
||||||
|
"\\fig",
|
||||||
|
"\\img"
|
||||||
|
],
|
||||||
|
"body": [
|
||||||
|
"\\begin{figure}[h!]",
|
||||||
|
" \\centering",
|
||||||
|
" \\includegraphics[width=0.${1:5}\\linewidth]{img/$2.png}",
|
||||||
|
" \\caption{$3.}",
|
||||||
|
" \\label{fig:$2}",
|
||||||
|
"\\end{figure}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Include listing": {
|
||||||
|
"prefix": [
|
||||||
|
"\\lst",
|
||||||
|
"\\listing"
|
||||||
|
],
|
||||||
|
"body": [
|
||||||
|
"\\begin{lstlisting}[caption={$1.}, label={lst:$2}]",
|
||||||
|
"$3",
|
||||||
|
"\\end{lstlisting}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Texttt": {
|
||||||
|
"prefix": "\\tt",
|
||||||
|
"body": [
|
||||||
|
"\\texttt{$1}",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Textit": {
|
||||||
|
"prefix": "\\it",
|
||||||
|
"body": [
|
||||||
|
"\\textit{$1}",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Textbf": {
|
||||||
|
"prefix": "\\bf",
|
||||||
|
"body": [
|
||||||
|
"\\textbf{$1}",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Picture reference": {
|
||||||
|
"prefix": [
|
||||||
|
"рис",
|
||||||
|
"Рис"
|
||||||
|
],
|
||||||
|
"body": [
|
||||||
|
"Рис.~\\ref{fig:$1}",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Special multiline cell for tables": {
|
||||||
|
"prefix": "\\specialcell",
|
||||||
|
"body": "\\newcommand{\\specialcell}[2][l]{\\begin{tabular}[#1]{@{}l@{}}#2\\end{tabular}}"
|
||||||
|
},
|
||||||
|
"TableX": {
|
||||||
|
// c - это просто колонка с тектом по центру, X - максимальная по ширине
|
||||||
|
"prefix": "\\tablex",
|
||||||
|
"body": [
|
||||||
|
"${1:\\usepackage{tabularx\\}}",
|
||||||
|
"\\begin{table}[h!]",
|
||||||
|
" \\centering",
|
||||||
|
" \\caption{$2.}",
|
||||||
|
" \\footnotesize",
|
||||||
|
" \\begin{tabularx}{\\textwidth}{${3:|X|X|X|}}",
|
||||||
|
" \\hline",
|
||||||
|
" \\textbf{${4:Заголовок 1}} & \\textbf{${5:Заголовок 2}} & \\textbf{${6:Заголовок 3}} \\\\\\",
|
||||||
|
" \\hline",
|
||||||
|
" Один & Два & Три \\\\\\",
|
||||||
|
" \\hline",
|
||||||
|
" \\end{tabularx}",
|
||||||
|
"\\end{table}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Table": {
|
||||||
|
"prefix": "\\table",
|
||||||
|
"body": [
|
||||||
|
"\\begin{table}[h!]",
|
||||||
|
" \\centering",
|
||||||
|
" \\caption{$1.}",
|
||||||
|
" \\footnotesize",
|
||||||
|
" \\begin{tabular}{${2:|c|c|c|}}",
|
||||||
|
" \\hline",
|
||||||
|
" \\textbf{${3:Заголовок 1}} & \\textbf{${4:Заголовок 2}} & \\textbf{${5:Заголовок 3}} \\\\\\",
|
||||||
|
" \\hline",
|
||||||
|
" Один & Два & Три \\\\\\",
|
||||||
|
" \\hline",
|
||||||
|
" \\end{tabular}",
|
||||||
|
"\\end{table}"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"Literature list": {
|
||||||
|
"prefix": ["\\literature", "\\bibliography"],
|
||||||
|
"body": [
|
||||||
|
"\\newpage",
|
||||||
|
"\\section*{Список литературы}",
|
||||||
|
"\\addcontentsline{toc}{section}{Список литературы}",
|
||||||
|
"",
|
||||||
|
"\\vspace{-1.5cm}",
|
||||||
|
"\\begin{thebibliography}{0}",
|
||||||
|
" \\bibitem{novikov}",
|
||||||
|
" Новиков, Ф. А. <<Дискретная математика для программистов>>. — 3-е изд. — Санкт-Петербург: Питер, 2009. — 383 с.",
|
||||||
|
" \\bibitem{vostrov}",
|
||||||
|
" Востров А. В, <<Лекции по теории графов>> URL: \\url{https://tema.spbstu.ru/tgraph_lect/}, Дата обращения: 17.09.2024",
|
||||||
|
"\\end{thebibliography}",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"change number": {
|
||||||
|
"prefix": ["\\counter", "\\addtocounter"],
|
||||||
|
"body": [
|
||||||
|
"\\addtocounter{${1:table}}{-1}",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include pdf": {
|
||||||
|
"prefix": ["\\includepdf", "\\pdf"],
|
||||||
|
"body": [
|
||||||
|
"${1:\\usepackage{pdfpages\\}}",
|
||||||
|
"${2:\\usepackage{tikz\\}}",
|
||||||
|
"\\addtocounter{figure}{1}",
|
||||||
|
"\\includepdf[pages={1}, fitpaper, pagecommand={",
|
||||||
|
"\\thispagestyle{empty}",
|
||||||
|
" \\begin{tikzpicture}[remember picture, overlay]",
|
||||||
|
" \\node at (current page.south) [anchor=north, yshift=35pt] {\\large{${3:Рис 3. }}};",
|
||||||
|
" \\end{tikzpicture}",
|
||||||
|
"}]{${4:pdf/$5.pdf}}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 365 KiB After Width: | Height: | Size: 376 KiB |
@@ -11,11 +11,13 @@
|
|||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
|
|
||||||
|
`ctrl + shift + p` - главное сочетание клавиш, которое стоит запомнить.
|
||||||
|
|
||||||
Удалить множественные курсоры можно с помощью `Esc`.
|
Удалить множественные курсоры можно с помощью `Esc`.
|
||||||
|
|
||||||
Если с помощью `ctrl + d` выделили лишнее, то сочетанием `ctrl + u` можно убрать последнее выделение.
|
Если с помощью `ctrl + d` выделили лишнее, то сочетанием `ctrl + u` можно убрать последнее выделение.
|
||||||
|
|
||||||
`ctrl + alt + ←/→` - перенести файл в левую/правую группу вкладок.
|
`ctrl + alt + ←/→` - перенести файл в левую/правую группу вкладок.
|
||||||
|
|
||||||
|
`alt + z` - крайне полезно при работе с Latex, Markdown и другими "широкими" файлами.
|
||||||
|
|
||||||
|
|||||||
39
docs/VS-Code/latex.md
Normal file
39
docs/VS-Code/latex.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# LaTeX в VS Code
|
||||||
|
|
||||||
|
## Подготовка
|
||||||
|
|
||||||
|
Вся работа с LaTeX в VS Code завязана на расширении [LaTeX Workshop](https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop). Вот [документация](https://github.com/James-Yu/LaTeX-Workshop/wiki/Install) по установке. Вкратце:
|
||||||
|
|
||||||
|
1. Устанавливаем [Perl](https://strawberryperl.com/)
|
||||||
|
2. Устанавливаем [MikTeX](https://miktex.org/)
|
||||||
|
3. Устанавливаем [LaTeX Workshop](https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop)
|
||||||
|
|
||||||
|
После установки можно открыть любой `.tex` файл и попробовать скомпилировать его (`ctrl + alt + b`), а затем открыть предпросмотр (`ctrl + alt + v`). При сохранении файл будет автоматически компилироваться, а предпросмотр обновляться.
|
||||||
|
|
||||||
|
## Сниппеты
|
||||||
|
|
||||||
|
### Создание
|
||||||
|
В LaTeX очень много повторяющихся конструкций, грех не использовать сниппеты. Нажимаем `ctrl + shift + p` (или `f1`), ищем пункт `Snippets: Configure Snippets`, затем ищем `latex`. Автоматически будет создан файл `latex.json`, в который можно добавлять сниппеты.
|
||||||
|
|
||||||
|
### Использование
|
||||||
|
|
||||||
|
Примеры [сниппетов](attachments/latex.json), которые я использую.
|
||||||
|
|
||||||
|
- `\img` - для вставки картинок.
|
||||||
|
- `\lst` или `\listing` - для вставки листингов.
|
||||||
|
- `\tablex` и `\table` - для вставки таблиц.
|
||||||
|
- `\pdf` - для вставки PDF файлов.
|
||||||
|
|
||||||
|
## Примечание
|
||||||
|
|
||||||
|
- Работать с текстом в VS Code намного удобнее, в первую очередь за счёт [горячих клавиш](hotkeys.md). В особенности полезно сочетание `alt + z`, с помощью которого включается автоматический перенос строк.
|
||||||
|
- При написании отчётов, которые обычно обрастают множеством правок, полезно использовать `Git`. `MikTex` создаёт много временных файлов, которые в `Git` добавлять не нужно, поэтому ниже представлена заготовка для `.gitignore`.
|
||||||
|
```sh title=".gitignore"
|
||||||
|
# Игнорировать всё,
|
||||||
|
**/*
|
||||||
|
# кроме
|
||||||
|
!.gitignore
|
||||||
|
!report.tex
|
||||||
|
!img
|
||||||
|
!img/*
|
||||||
|
```
|
||||||
28
docs/assets/logo.svg
Normal file
28
docs/assets/logo.svg
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 175.000000 198.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
|
||||||
|
<g transform="translate(0.000000,198.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#ffffff" stroke="none">
|
||||||
|
<path d="M202 1689 l-92 -231 250 -620 c137 -342 262 -639 277 -661 31 -45 98
|
||||||
|
-92 153 -107 47 -13 155 -13 205 1 58 16 123 65 155 116 32 49 100 214 100
|
||||||
|
240 0 28 -153 434 -160 426 -4 -4 -42 -102 -85 -218 -42 -115 -86 -219 -96
|
||||||
|
-230 -19 -19 -19 -19 -38 0 -10 11 -40 78 -66 150 -26 72 -89 243 -140 380
|
||||||
|
-51 138 -152 412 -225 610 -73 198 -135 363 -139 367 -3 4 -48 -97 -99 -223z"/>
|
||||||
|
<path d="M783 1906 c-60 -19 -107 -51 -139 -93 -27 -36 -114 -232 -114 -259 0
|
||||||
|
-12 59 -178 130 -366 12 -32 25 -58 29 -58 4 0 43 96 86 213 43 116 85 220 95
|
||||||
|
230 16 17 17 17 37 0 14 -12 111 -262 289 -743 147 -399 271 -736 276 -750 9
|
||||||
|
-22 22 5 104 209 l93 234 -229 573 c-126 315 -241 598 -255 629 -35 79 -75
|
||||||
|
126 -137 160 -45 24 -66 29 -138 32 -52 2 -101 -2 -127 -11z"/>
|
||||||
|
<path d="M1412 1737 c-34 -94 -62 -178 -62 -185 0 -20 173 -443 179 -437 2 3
|
||||||
|
35 80 73 171 l67 165 -91 230 c-51 126 -94 229 -97 229 -3 0 -34 -78 -69 -173z"/>
|
||||||
|
<path d="M834 1116 c-35 -16 -72 -58 -80 -93 -17 -67 30 -145 99 -164 83 -22
|
||||||
|
159 29 172 116 15 101 -97 184 -191 141z"/>
|
||||||
|
<path d="M242 850 c-5 -14 -36 -90 -68 -169 -31 -79 -57 -150 -57 -156 0 -17
|
||||||
|
176 -455 183 -455 7 0 130 333 130 353 0 15 -151 402 -170 437 -7 12 -10 10
|
||||||
|
-18 -10z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -1,8 +1,5 @@
|
|||||||
# Алиасы команд в Git
|
# Алиасы команд в Git
|
||||||
|
|
||||||
## Полезные ссылки
|
|
||||||
- [Раздел](https://githowto.com/ru/aliases) в GitHowTo
|
|
||||||
|
|
||||||
## Мои алиасы
|
## Мои алиасы
|
||||||
Просто выполнить в терминале:
|
Просто выполнить в терминале:
|
||||||
```sh
|
```sh
|
||||||
@@ -36,4 +33,7 @@ git config --global --get-regexp alias
|
|||||||
[Вопрос](https://stackoverflow.com/a/48110875/17341937) на StatckOverflow
|
[Вопрос](https://stackoverflow.com/a/48110875/17341937) на StatckOverflow
|
||||||
```sh
|
```sh
|
||||||
git config --global --unset-all alias.your-alias
|
git config --global --unset-all alias.your-alias
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Полезные ссылки
|
||||||
|
- [Раздел в GitHowTo](https://githowto.com/ru/aliases)
|
||||||
|
|||||||
84
docs/git/git-filter-repo.md
Normal file
84
docs/git/git-filter-repo.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# git-filter-repo
|
||||||
|
|
||||||
|
С помощью [git-filter-repo](https://github.com/newren/git-filter-repo/) можно переписать историю репозитория.
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
Глобально или в `virtualenv`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip install git-filter-repo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Замена метаданных коммитов
|
||||||
|
|
||||||
|
`git-filter-repo` позволяет создать скрипт на `Python`, который будет вызван для всех коммитов репозитория. Доступ к данным коммита, с возможностю их редактирования, предоставляется через объект `commit`. Таким образом можно описать любую логику изменения метаданных коммитов на обычном `Python`.
|
||||||
|
|
||||||
|
Ниже представлен пример изменения метаданных коммита по его хэшу. Узнать хэш коммита можно с помощью, например, `git log`.
|
||||||
|
|
||||||
|
=== "Command"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git filter-repo --force --commit-callback "callback.py"
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "callback.py"
|
||||||
|
|
||||||
|
```python
|
||||||
|
"""Файл создан для использования с git-filter-repo.
|
||||||
|
|
||||||
|
Пример команды:
|
||||||
|
git filter-repo --force --commit-callback "callback.py"
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
|
|
||||||
|
def git_timestamp(date_str: str, tz_offset: int = 3) -> bytes:
|
||||||
|
"""
|
||||||
|
Преобразует строку с датой в формат временной метки для git-filter-repo.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
date_str (str): Дата в формате "DD.MM.YYYY HH:MM:SS".
|
||||||
|
tz_offset (int, optional): Сдвиг временной зоны относительно UTC.
|
||||||
|
По умолчанию 3 (московское время).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bytes: Байтовая строка с временной меткой и сдвигом временной зоны
|
||||||
|
в формате `b"{timestamp} {timezone_offset}"`.
|
||||||
|
"""
|
||||||
|
tz = timezone(timedelta(hours=tz_offset))
|
||||||
|
dt = datetime.strptime(date_str, "%d.%m.%Y %H:%M:%S").replace(tzinfo=tz)
|
||||||
|
|
||||||
|
timestamp = int(dt.timestamp())
|
||||||
|
timezone_offset = f"{tz_offset:+03d}00"
|
||||||
|
|
||||||
|
return f"{timestamp} {timezone_offset}".encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
# Обязательно нужно указывать полный хэш коммита
|
||||||
|
if commit.original_id == b"72f6afa37aac4cdbf8d37f22470b77c221a8fce0":
|
||||||
|
# Сообщение коммита
|
||||||
|
commit.message = "Новое сообщение коммита!".encode("utf-8")
|
||||||
|
|
||||||
|
new_name = "Иван Иванов".encode("utf-8")
|
||||||
|
new_email = "ivan-ivanov@mail.com".encode("utf-8")
|
||||||
|
new_date = git_timestamp("01.01.2000 10:00:00")
|
||||||
|
|
||||||
|
# Автор изменений
|
||||||
|
commit.author_name = new_name
|
||||||
|
commit.author_email = new_email
|
||||||
|
|
||||||
|
# Автор коммита
|
||||||
|
commit.committer_name = new_name
|
||||||
|
commit.committer_email = new_email
|
||||||
|
|
||||||
|
# Дата изменений и дата коммита
|
||||||
|
commit.author_date = new_date
|
||||||
|
commit.committer_date = new_date
|
||||||
|
```
|
||||||
|
|
||||||
|
## Полезные ссылки
|
||||||
|
|
||||||
|
- [git-filter-repo](https://github.com/newren/git-filter-repo/?tab=readme-ov-file#simple-example-with-comparisons) on GitHub
|
||||||
|
- [usage examples](https://www.mankier.com/1/git-filter-repo)
|
||||||
134
docs/git/undo.md
Normal file
134
docs/git/undo.md
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
# Отмена действий в Git
|
||||||
|
|
||||||
|
## Отмена изменений до `git add`
|
||||||
|
|
||||||
|
Вернуть состояние файла к последнему коммиту.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git restore <path-to-dir-or-file>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Отмена изменений после `git add` до `git commit`
|
||||||
|
|
||||||
|
Если нежелательные изменения уже проиндексированы, т. е. выполнена команда `git add`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Отменяет git add
|
||||||
|
git restore --staged <path-to-dir-or-file>
|
||||||
|
# Возвращает к исходному состоянию
|
||||||
|
git restore <path-to-dir-or-file>
|
||||||
|
```
|
||||||
|
|
||||||
|
Можно и одной командой.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git reset --hard <path-to-dir-or-file>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Отмена коммитов до `git push`
|
||||||
|
|
||||||
|
### Изменение последнего коммита
|
||||||
|
|
||||||
|
Часто нужно что-то добавить/исправить/удалить в последнем коммите, в том числе сообщение коммита.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Добавляем/исправляем/удаляем
|
||||||
|
# Индексируем изменения (git add)
|
||||||
|
git commit -m "New message" --amend
|
||||||
|
```
|
||||||
|
|
||||||
|
Можно без `-m` и сообщения, если не хотим его менять, тогда надо будет просто закрыть открывшийся редактор, либо добавить флаг `--no-edit`, чтобы `Git` даже и не предлагал изменять сообщение.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Добавляем/исправляем/удаляем
|
||||||
|
# Индексируем изменения (git add)
|
||||||
|
git commit --amend --no-edit
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning "Коммит не должен быть запушен!"
|
||||||
|
|
||||||
|
Команда `git commit --amend` изменяет историю репозитория, не нужно использовать её, если последний коммит уже оказался на сервере (была выполнена команда `git push`).
|
||||||
|
|
||||||
|
### Удаление последних коммитов
|
||||||
|
|
||||||
|
Можно полностью удалить коммит из истории репозитория с помощью `git reset`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Можно добавить --hard, чтобы откатить изменения и вернуть рабочий каталог
|
||||||
|
# к состоянию, в котором он был на предыдущем коммите
|
||||||
|
git reset HEAD~1
|
||||||
|
```
|
||||||
|
|
||||||
|
После такого удаления можно делать `git push`, не опасаясь, что кто-нибудь ещё сможет получить доступ к данным коммита, его следы могут остаться лишь в локальной копии репозитория.
|
||||||
|
|
||||||
|
!!! warning "Коммит не должен быть запушен!"
|
||||||
|
|
||||||
|
Команда `git reset` изменяет историю репозитория, не нужно использовать её, если последний коммит уже оказался на сервере (была выполнена команда `git push`).
|
||||||
|
|
||||||
|
??? info "Подробнее про `git reset`"
|
||||||
|
|
||||||
|
Команда `git reset <commit>` просто перемещает текущую ветку и `HEAD` на указанный коммит. Соответственно, вместо `HEAD~1` (указатель на предыдущий коммит) можно указать любой другой коммит в текущей ветке, например, `HEAD~3` - позволит отменить последние 3 коммита. Разумеется можно указать хэш коммита, к которому нужно откатиться.
|
||||||
|
|
||||||
|
Параметр `--hard` говорит о том, что нужно сбросить и индекс и рабочий каталог до состояния указанного коммита. Есть и другие варианты: `--soft` - оставить изменения в индексе и рабочем каталоге, `--mixed` (значение по умолчанию) - сбросить индекс, но не трогать рабочий каталог.
|
||||||
|
|
||||||
|
??? info "Как восстановить коммиты, удалённые с помощью `git reset`"
|
||||||
|
|
||||||
|
`Git` ведёт локальный журнал всех операций, так что хэш коммита ещё можно восстановить.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git reflog
|
||||||
|
```
|
||||||
|
|
||||||
|
Зная хэш коммита, можно вернуть состояние ветки к нему с помощью того же `git reset`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git reset <commit-hash>
|
||||||
|
```
|
||||||
|
|
||||||
|
Или, например, вынести его в отдельную ветку.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git checkout <commit-hash>
|
||||||
|
git checkout -b branch-with-restored-commit
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Отмена коммитов после `git push`
|
||||||
|
|
||||||
|
### Отмена изменений последнего коммита
|
||||||
|
|
||||||
|
Если коммит уже оказался в удалённом репозитории, то самый простой вариант - просто сделать новый коммит, который будет отменять изменения предыдущего.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git revert HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
Можно указать диапазон коммитов, которые нужно отменить, или перечислить их хэши.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Создаст коммит с отменой изменений последних трёх коммитов
|
||||||
|
git revert HEAD~3..HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
После `git revert` можно спокойно делать `git push`, не опасаясь, что возникнут какие-либо конфликты с удалённым репозиторием.
|
||||||
|
|
||||||
|
!!! warning "Изменения сохраняются в репозитории"
|
||||||
|
|
||||||
|
Никакие данные ни из локального, ни из удалённого репозитория не удаляются, все коммиты остаются в истории той же самой ветки. `git revert` не подходит, если нужно полностью удалить нежелательные изменения (например, конфиденциальные данные) из локального и удалённого репозитория.
|
||||||
|
|
||||||
|
### Удаление последних коммитов из удалённого репозитория
|
||||||
|
|
||||||
|
Переписывать историю удалённого репозитория можно если над репозиторием работает один человек, либо, если на сервер была отправлена конфиденциальная информация, которую непременно нужно удалить, в других случаях лучше использовать `git revert`.
|
||||||
|
|
||||||
|
Для отмены коммитов можно использовать методы, описанные в предыдущем разделе, а затем перезаписать историю удалённого репозитория.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git push --force
|
||||||
|
```
|
||||||
|
|
||||||
|
## Полезные ссылки
|
||||||
|
- GitHowTo [12](https://githowto.com/ru/undoing_local_changes), [13](https://githowto.com/ru/undoing_staged_changes), [14](https://githowto.com/ru/undoing_committed_changes), [15](https://githowto.com/ru/removing_commits_from_a_branch).
|
||||||
|
- [Отмена нескольких коммитов](https://stackoverflow.com/questions/1463340/how-can-i-revert-multiple-git-commits#answer-1470452).
|
||||||
|
- [Удаление запушенного коммита](https://stackoverflow.com/questions/3293531/how-to-permanently-remove-few-commits-from-remote-branch#answer-41726152).
|
||||||
86
docs/misc/gitea.md
Normal file
86
docs/misc/gitea.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# Кастомизация Gitea
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo su - git
|
||||||
|
cd /var/lib/gitea/custom
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p /var/lib/gitea/custom/public/css
|
||||||
|
mkdir -p /var/lib/gitea/custom/templates/custom
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nano /var/lib/gitea/custom/templates/custom
|
||||||
|
```
|
||||||
|
|
||||||
|
```html
|
||||||
|
<link rel="stylesheet" href="/custom/css/custom.css">
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo -u git mkdir css
|
||||||
|
sudo -u git nano css/custom.css
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo systemctl restart gitea
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/gitea/app.ini
|
||||||
|
```
|
||||||
|
https://docs.gitea.com/administration/customizing-gitea
|
||||||
|
https://docs.gitea.com/administration/config-cheat-sheet
|
||||||
|
|
||||||
|
```ini title="app.ini"
|
||||||
|
[server]
|
||||||
|
LANDING_PAGE = explore
|
||||||
|
|
||||||
|
[other]
|
||||||
|
SHOW_FOOTER_VERSION = false
|
||||||
|
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false
|
||||||
|
SHOW_FOOTER_POWERED_BY = false
|
||||||
|
ENABLE_FEED = false
|
||||||
|
|
||||||
|
[i18n]
|
||||||
|
LANGS = en-US,ru-RU
|
||||||
|
NAMES = English,Русский
|
||||||
|
|
||||||
|
[repository]
|
||||||
|
DISABLE_STARS = true
|
||||||
|
|
||||||
|
[ui.meta]
|
||||||
|
AUTHOR = Artem Tishenko: Personal Git Repository Hub
|
||||||
|
DESCRIPTION = A personal hub for managing Git repositories by Artem Tishenko.
|
||||||
|
KEYWORDS = Artem Tishenko, Artyom Tishchenko, Git, self-hosted, personal projects, repositories, Gitea
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p /var/lib/gitea/custom/templates/base
|
||||||
|
cd /var/lib/gitea/custom/templates/base
|
||||||
|
# перейти туда
|
||||||
|
wget https://raw.githubusercontent.com/go-gitea/gitea/refs/tags/v1.22.3/templates/base/footer_content.tmpl
|
||||||
|
|
||||||
|
# remove help - https://docs.gitea.com
|
||||||
|
# remove explore - explore.repos
|
||||||
|
# remove sign_in (just visit /user/login)
|
||||||
|
wget https://github.com/go-gitea/gitea/raw/refs/tags/v1.22.3/templates/base/head_navbar.tmpl
|
||||||
|
|
||||||
|
|
||||||
|
mkdir -p /var/lib/gitea/custom/templates/repo
|
||||||
|
cd /var/lib/gitea/custom/templates/repo
|
||||||
|
# remove packages
|
||||||
|
# remove wiki
|
||||||
|
# remove repo.activity
|
||||||
|
# remove repo.issues
|
||||||
|
# remove repo.pulls
|
||||||
|
# remove watch, fork
|
||||||
|
wget https://raw.githubusercontent.com/go-gitea/gitea/refs/tags/v1.22.3/templates/repo/header.tmpl
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# <a class="item" href="https://kb.tishenko.dev/" target="_blank">Knowledge base</a>
|
||||||
|
/var/lib/gitea/custom/templates/custom/extra_links.tmpl
|
||||||
|
```
|
||||||
248
docs/python/django-deployment.md
Normal file
248
docs/python/django-deployment.md
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
Предполагается, что на сервере установлена `Ubuntu`.
|
||||||
|
|
||||||
|
## Подготовка
|
||||||
|
|
||||||
|
В этом разделе общая последовательность действий для запуска `Django` приложения на сервере. Многое в нём зависит от конкретного проекта, поэтому команды и их последовательность может быть совсем другой. Главное, что в результате папка с приложением должна оказаться на сервере, а также должно быть создано виртуальное окружение `Python` (не обязательно через `venv`) со всеми зависимостями проекта.
|
||||||
|
|
||||||
|
### Установка Python
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Для начала обновим установленные пакеты
|
||||||
|
sudo apt update
|
||||||
|
sudo apt upgrade
|
||||||
|
|
||||||
|
# Не забудьте указать нужную версию python
|
||||||
|
sudo apt install python3.10 python3.10-venv -y
|
||||||
|
```
|
||||||
|
|
||||||
|
Python может сходу не установиться и из-за следующей ошибки.
|
||||||
|
```
|
||||||
|
E: Unable to locate package python3.10
|
||||||
|
E: Couldn't find any package by glob 'python3.10'
|
||||||
|
E: Unable to locate package python3.10-venv
|
||||||
|
E: Couldn't find any package by glob 'python3.10-venv'
|
||||||
|
```
|
||||||
|
|
||||||
|
Нужно просто добавить репозиторий со списками пакетов Python.
|
||||||
|
```sh
|
||||||
|
sudo add-apt-repository ppa:deadsnakes/ppa
|
||||||
|
sudo apt update
|
||||||
|
# и снова пробуем установить python
|
||||||
|
```
|
||||||
|
|
||||||
|
### Клонируем проект
|
||||||
|
|
||||||
|
На сервере создаём SSH ключ и копируем его в раздел `Deploy keys` (в случае `GitHub`) в настройках репозитория. Если проект публичный, то ключ создавать не обязательно.
|
||||||
|
```sh
|
||||||
|
ssh-keygen
|
||||||
|
cat ~/.ssh/id_rsa.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
А затем клонируем репозиторий.
|
||||||
|
```sh
|
||||||
|
git clone <repo-url.git>
|
||||||
|
```
|
||||||
|
|
||||||
|
Если вдруг `Git` не установлен.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt install git
|
||||||
|
```
|
||||||
|
|
||||||
|
### Готовим Django к запуску
|
||||||
|
|
||||||
|
Предварительно нужно добавить домен или IP-адрес сервера в `ALLOWED_HOSTS` в `settings.py`. Также стоит посмотреть на [deployment checklist](https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/), который предоставляет документация `Django`.
|
||||||
|
|
||||||
|
Настраиваем виртуальное окружение.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3.10 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Создаём миграции и суперпользователя. Команды отличаются в зависимости от проекта.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python manage.py makemigrations <app_name>
|
||||||
|
python manage.py migrate
|
||||||
|
python manage.py createsuperuser
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Gunicorn
|
||||||
|
|
||||||
|
### Установка
|
||||||
|
```sh { .annotate }
|
||||||
|
# В виртуальном окружении проекта выполнить
|
||||||
|
pip install gunicorn
|
||||||
|
|
||||||
|
# Можно перейти по адресу сервера в браузере на порт 8000 и убедиться,
|
||||||
|
# что всё работает (ну почти, статики тут не будет)
|
||||||
|
# Не на всех VDS может быть открыт 8000 порт, в таком случае просто
|
||||||
|
# смотрим на отсутствие ошибок
|
||||||
|
gunicorn --bind 0.0.0.0:8000 django-app-name.wsgi # (1)!
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Не забудьте заменить `django-app-name.wsgi` на реальный путь к `wsgi.py` файлу проекта.
|
||||||
|
|
||||||
|
### UNIX-сокет
|
||||||
|
Создаём UNIX-сокет в `systemd` для локального обмена данными между `Gunicorn`, в котором развёрнут `Django`, и `nginx`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/systemd/system/gunicorn.socket
|
||||||
|
```
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=gunicorn socket
|
||||||
|
|
||||||
|
[Socket]
|
||||||
|
ListenStream=/run/gunicorn.sock
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=sockets.target
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip "UNIX-сокет можно назвать как угодно"
|
||||||
|
|
||||||
|
Это особенно полезно, когда на одной машине с помощью `Gunicorn` нужно развёрнуть сразу несколько приложений. Кстати, имя сокета (`gunicorn.sock`) не обязательно должно совпадать с названием файла конфигурации сокета для `systemd` (`gunicorn.socket`). При этом в командах по типу `systemctl status` нужно будет использовать имя конфигурационного файла.
|
||||||
|
|
||||||
|
### Сервис в systemd
|
||||||
|
|
||||||
|
Создаём сервис в `systemd`, чтобы `Gunicorn` мог работать как фоновый процесс и запускался вместе с системой.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/systemd/system/gunicorn.service
|
||||||
|
```
|
||||||
|
|
||||||
|
В секции `Service` нужно указать актуального пользователя и путь до проекта, а также путь до `wsgi.py` внутри проекта.
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=gunicorn daemon
|
||||||
|
Requires=gunicorn.socket
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=user
|
||||||
|
Group=user
|
||||||
|
WorkingDirectory=/home/user/project-folder
|
||||||
|
ExecStart=/home/user/project-folder/venv/bin/gunicorn \
|
||||||
|
--access-logfile - \
|
||||||
|
--workers 1 \
|
||||||
|
--bind unix:/run/gunicorn.sock \
|
||||||
|
django-app-name.wsgi:application
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь можно запустить сокет и добавить его в автозапуск. `systemd` автоматически запустит сервис `Gunicorn`, когда на сокет придёт первый запрос.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Предварительно перезагружаем systemd
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
|
||||||
|
sudo systemctl start gunicorn.socket
|
||||||
|
sudo systemctl enable gunicorn.socket
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Nginx
|
||||||
|
|
||||||
|
### Установка
|
||||||
|
|
||||||
|
После установки `nginx` должен запуститься автоматически, чтобы проверить можно перейти по адресу сервера в браузере.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt install nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Собираем статику
|
||||||
|
|
||||||
|
Собираем статику (стили, скрипты, картинки) и переносим в `/var/www/` - именно этот каталог обычно используется для её хранения. Если поместить статику в другое место, то могут возникнуть проблемы с доступами. `nginx` просто не сможет работать с нашими файлами, если у него не будет прав на чтение файлов статики и прав на исполнение всех директорий в путях к этим файлам. В конфиге `/etc/nginx/nginx.conf` можно узнать от имени какого пользователя `nginx` обрабатывает запросы.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Команда собирает всю статику Django в папку, которая
|
||||||
|
# указана в settings.py (см. STATIC_ROOT)
|
||||||
|
python manage.py collectstatic
|
||||||
|
|
||||||
|
# Переносим статику из staticfiles (см. STATIC_ROOT) в /var/www/
|
||||||
|
# Вместо static может потребоваться указать другую папку (см. STATIC_URL)
|
||||||
|
sudo mkdir /var/www/django-project
|
||||||
|
sudo cp -r staticfiles /var/www/django-project/static
|
||||||
|
```
|
||||||
|
|
||||||
|
### Конфиг nginx
|
||||||
|
|
||||||
|
Теперь можно настроить `nginx`.
|
||||||
|
```bash
|
||||||
|
# Вместо my-site можно указать название проекта
|
||||||
|
sudo nano /etc/nginx/sites-available/my-site
|
||||||
|
```
|
||||||
|
|
||||||
|
Минимальный конфиг.
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name your_domain_or_ip;
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
location /static/ {
|
||||||
|
root /var/www/django-project;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
include proxy_params;
|
||||||
|
proxy_pass http://unix:/run/gunicorn.sock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Добавляем конфиг в активные конфиги, с которыми сейчас работает nginx.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ln -s /etc/nginx/sites-available/my-site /etc/nginx/sites-enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверка корректности конфигов.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nginx -t
|
||||||
|
```
|
||||||
|
|
||||||
|
Если всё в порядке, перезапускаем `nginx`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo systemctl restart nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Если что-то идёт не так...
|
||||||
|
|
||||||
|
### Логи NGINX
|
||||||
|
```bash
|
||||||
|
# Информация обо всех запросах
|
||||||
|
sudo tail -f /var/log/nginx/access.log
|
||||||
|
|
||||||
|
# Информация обо всех ошибках и предупреждениях
|
||||||
|
sudo tail -f /var/log/nginx/error.log
|
||||||
|
|
||||||
|
# Проверка корректности конфигов
|
||||||
|
sudo nginx -t
|
||||||
|
|
||||||
|
# Очистка логов без необходимости перезапуска NGINX
|
||||||
|
sudo truncate -s 0 /var/log/nginx/access.log
|
||||||
|
sudo truncate -s 0 /var/log/nginx/error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Логи Gunicorn
|
||||||
|
```bash
|
||||||
|
sudo journalctl -u gunicorn.service -f
|
||||||
|
sudo journalctl -u gunicorn.socket -f
|
||||||
|
```
|
||||||
|
|
||||||
|
## Полезные ссылки
|
||||||
|
- [How to deploy Django](https://docs.djangoproject.com/en/5.1/howto/deployment/)
|
||||||
|
- [Deployment checklist](https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/)
|
||||||
261
docs/servers/index.md
Normal file
261
docs/servers/index.md
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
# Начальная настройка сервера
|
||||||
|
|
||||||
|
## Первое подключение
|
||||||
|
Подключаемся к серверу к `root` по паролю, который должен выдаваться вместе с VDS.
|
||||||
|
```sh
|
||||||
|
ssh root@<IPv4>
|
||||||
|
```
|
||||||
|
|
||||||
|
Уже в терминале сервера выполняем.
|
||||||
|
```sh { .code-wrap }
|
||||||
|
# Создаём пользователя и наделяем правом использовать `sudo`.
|
||||||
|
adduser <user>
|
||||||
|
adduser <username> sudo
|
||||||
|
|
||||||
|
# Переключаемся на нового пользователя
|
||||||
|
su - <user>
|
||||||
|
|
||||||
|
# Все файлы, созданные пользователем, по умолчанию будут иметь права 600, а директории - 700.
|
||||||
|
echo 'umask 0077' >> .bashrc
|
||||||
|
source ~/.bashrc
|
||||||
|
|
||||||
|
# Добавляем свой публичный SSH ключ (cat ~/.ssh/id_rsa.pub), чтобы подключаться к пользователю по SSH напрямую.
|
||||||
|
mkdir .ssh
|
||||||
|
echo "<your-id-rsa.pub>" >> .ssh/authorized_keys
|
||||||
|
# Актуально, только если настройку umask не добавлять в .bashrc
|
||||||
|
# chmod 700 ~/.ssh
|
||||||
|
# chmod 600 ~/.ssh/authorized_keys
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь можно попробовать подключиться к серверу по SSH-ключу.
|
||||||
|
```sh
|
||||||
|
ssh <user>@<IPv4>
|
||||||
|
```
|
||||||
|
|
||||||
|
Желательно обновить все пакеты и перезагрузить сервер.
|
||||||
|
```sh
|
||||||
|
sudo apt update
|
||||||
|
sudo apt upgrade
|
||||||
|
sudo reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
Можно придумать серверу имя, оно будет отображаться в терминале после `<user>@`.
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/hostname
|
||||||
|
sudo nano /etc/hosts
|
||||||
|
sudo systemctl restart systemd-hostnamed
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Настройка конфига SSH
|
||||||
|
Открываем конфиг SSH.
|
||||||
|
```sh
|
||||||
|
sudo nano /etc/ssh/sshd_config
|
||||||
|
```
|
||||||
|
|
||||||
|
- `Port <ssh-port>` - можно поменять со стандартного 22 на какой-нибудь другой. Лучше больше 10000, чтобы уменьшить вероятность конфликтов с другим ПО.
|
||||||
|
- `PermitRootLogin no` - запрещаем авторизацию по SSH под `root`.
|
||||||
|
- `PasswordAuthentication no` - запрещаем авторизацию по SSH по паролю.
|
||||||
|
|
||||||
|
После внесения изменений в конфиг, необходимо перезагрузить `sshd`.
|
||||||
|
```sh
|
||||||
|
# На некоторых системах ssh вместо sshd
|
||||||
|
sudo systemctl reload sshd
|
||||||
|
```
|
||||||
|
|
||||||
|
??? question "А что будет, если потерять SSH-ключ?"
|
||||||
|
|
||||||
|
Хостинг предоставляет доступ к `VNC` или другие методы подключения к серверу, которые не требуют подключения по SSH. Однако в таком случае будет необходим доступ к личному кабинету хостинга.
|
||||||
|
|
||||||
|
|
||||||
|
На своей машине добавляем сервер в конфиг SSH.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# На Windows надо будет нажать на Tab, чтобы раскрыть `~`.
|
||||||
|
# code - VS Code
|
||||||
|
code ~/.ssh/config
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
Host <host>
|
||||||
|
HostName <IPv4>
|
||||||
|
User <user>
|
||||||
|
Port <ssh-port>
|
||||||
|
```
|
||||||
|
|
||||||
|
Можно проверить, что подключение проходит без ошибок.
|
||||||
|
```sh
|
||||||
|
# Если конфиг настроен
|
||||||
|
ssh <host>
|
||||||
|
```
|
||||||
|
```sh
|
||||||
|
# Без конфига
|
||||||
|
ssh <user>@<IPv4> -p <ssh-port>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Настройка фаерволла c UFW
|
||||||
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Установка UFW
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install ufw -y
|
||||||
|
|
||||||
|
# Открываем порт, используемый для SSH (по умолчанию 22)
|
||||||
|
sudo ufw allow <ssh-port>/tcp
|
||||||
|
|
||||||
|
# Закрываем все входные
|
||||||
|
sudo ufw default deny incoming
|
||||||
|
sudo ufw default allow outgoing
|
||||||
|
|
||||||
|
# Включаем фаерволл
|
||||||
|
sudo ufw enable
|
||||||
|
|
||||||
|
# Показать состояние ufw и активные правила
|
||||||
|
sudo ufw status verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
??? tip "Дополнительные команды `ufw`"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Отключить фаерволл
|
||||||
|
sudo ufw disable
|
||||||
|
|
||||||
|
# Удалить правило (будут применены настройки по умолчанию)
|
||||||
|
sudo ufw delete allow <port>/<protocol> # удалить разрешение
|
||||||
|
sudo ufw delete deny <port>/<protocol> # удалить запрет
|
||||||
|
|
||||||
|
# Сброс всех правил
|
||||||
|
sudo ufw reset
|
||||||
|
|
||||||
|
# Вывести логи ufw
|
||||||
|
sudo tail -f -n 100 /var/log/ufw.log
|
||||||
|
|
||||||
|
# Изменить уровень логирования
|
||||||
|
sudo ufw logging <low/medium/high>
|
||||||
|
|
||||||
|
# Разрешить доступ ко всем портам с определённого IP-адреса
|
||||||
|
sudo ufw allow from <IPv4>
|
||||||
|
|
||||||
|
# Разрешить доступ к порту с определённого IP-адреса
|
||||||
|
sudo ufw allow from <IPv4> to any port <port>
|
||||||
|
```
|
||||||
|
|
||||||
|
Некоторые приложения, например `OpenSSH` или `Nginx`, добавляют пресеты с правилами для `ufw`, которые точно так же можно разрешать и запрещать.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Вывести список пресетов
|
||||||
|
sudo ufw app list
|
||||||
|
|
||||||
|
# Открыть все соединения, которые нужны Nginx
|
||||||
|
sudo ufw allow "Nginx Full"
|
||||||
|
|
||||||
|
# Удалить правило для пресета
|
||||||
|
sudo ufw delete allow "Nginx Full"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Настройка Fail2Ban
|
||||||
|
|
||||||
|
[Fail2Ban](https://github.com/fail2ban/fail2ban) - базовая защита сервера от brute-force аттак.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install fail2ban -y
|
||||||
|
sudo systemctl enable fail2ban
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь нужно [правильно настроить Fail2Ban](https://github.com/fail2ban/fail2ban/wiki/Proper-fail2ban-configuration).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Создаём файл с пользовательскими настройками
|
||||||
|
sudo nano /etc/fail2ban/jail.local
|
||||||
|
```
|
||||||
|
|
||||||
|
Настройка защиты SSH сервера.
|
||||||
|
```ini
|
||||||
|
[sshd]
|
||||||
|
# Единственный обязательный параметр
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
# Можно не указывать, если используется стандартный порт
|
||||||
|
port = <ssh-port>
|
||||||
|
|
||||||
|
# Пример настроек. Эти параметры можно не указывать, тогда будут использованы
|
||||||
|
# значения по умолчанию.
|
||||||
|
# Если в течении 24 часов
|
||||||
|
findtime = 86400
|
||||||
|
# произведено 3 неудачных попытки логина,
|
||||||
|
maxretry = 3
|
||||||
|
# то банить IP навсегда.
|
||||||
|
bantime = -1
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip "Более строгий конфиг для `fail2ban`"
|
||||||
|
|
||||||
|
Если на сервере настроена авторизация по ключу, можно смело использовать такой конфиг.
|
||||||
|
|
||||||
|
При авторизации по ключу, бан возможен только в случае указания неправильного имени пользователя, что исключено при использовании корректно настроенного SSH-конфига. В случае случайного бана всегда можно зайти на сервер через личный кабинет хостинга.
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[sshd]
|
||||||
|
enabled = true
|
||||||
|
port = <ssh-port>
|
||||||
|
# Если произведена хотя бы одна неудачная попытка логина,
|
||||||
|
maxretry = 1
|
||||||
|
# то банить IP навсегда.
|
||||||
|
bantime = -1
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Применяем настройки
|
||||||
|
sudo fail2ban-client reload
|
||||||
|
```
|
||||||
|
|
||||||
|
После установки и первоначальной настройки `fail2ban` лучше перезагрузить сервер, иначе `fail2ban` может не заработать.
|
||||||
|
```sh
|
||||||
|
sudo reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
??? tip "Дополнительные команды `fail2ban`"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Вывести список активных jail's
|
||||||
|
sudo fail2ban-client status
|
||||||
|
|
||||||
|
# Вывести информацию по конкретному jail, в т. ч. список заблокированных IP
|
||||||
|
sudo fail2ban-client status <jail-name>
|
||||||
|
|
||||||
|
# Разблокировать IP
|
||||||
|
sudo fail2ban-client set <jail-name> unbanip <IP>
|
||||||
|
|
||||||
|
# Вывести логи fail2ban
|
||||||
|
sudo tail -f -n 100 /var/log/fail2ban.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Дополнительно
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Вывести записи о неудачных попытках входа в систему
|
||||||
|
sudo lastb | head -n 20
|
||||||
|
|
||||||
|
# Очистить записи о неудачных попытках входа в систему
|
||||||
|
sudo truncate -s 0 /var/log/btmp
|
||||||
|
|
||||||
|
# Показывает, кто в системе прямо сейчас
|
||||||
|
sudo w
|
||||||
|
|
||||||
|
# Логи попыток входа
|
||||||
|
sudo grep "Accepted password" /var/log/auth.log | tail -n 20
|
||||||
|
sudo grep "Failed password" /var/log/auth.log | tail -n 20
|
||||||
|
sudo grep "Invalid user" /var/log/auth.log | tail -n 20
|
||||||
|
|
||||||
|
# Очистить логи с попытками входа
|
||||||
|
sudo truncate -s 0 /var/log/auth.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Полезные ссылки
|
||||||
|
- [Initial Server Setup with Ubuntu 20.04](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04)
|
||||||
|
- [UFW Essentials: Common Firewall Rules and Commands](https://www.digitalocean.com/community/tutorials/ufw-essentials-common-firewall-rules-and-commands)
|
||||||
|
- [VPS cheatsheet](https://habr.com/ru/articles/756804/)
|
||||||
|
- [Fail2Ban](https://github.com/fail2ban/fail2ban)
|
||||||
50
docs/servers/nginx.md
Normal file
50
docs/servers/nginx.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
|
||||||
|
## SSL-сертификат с certbot
|
||||||
|
|
||||||
|
!!! tip "Актуальная версия Python"
|
||||||
|
|
||||||
|
Обычно системый `Python` достаточно старый. Для установки `certbot` может потребоваться более новая версия. Минимальные требования можно узнать на [pypi](https://pypi.org/project/certbot/).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt update
|
||||||
|
sudo add-apt-repository ppa:deadsnakes/ppa
|
||||||
|
|
||||||
|
# Вместо 3.1x нужно указать актуальную версию Python
|
||||||
|
sudo apt install python3.1x python3.1x-venv
|
||||||
|
```
|
||||||
|
|
||||||
|
Теперь вместо `python3` можно использовать `python3.1x`.
|
||||||
|
|
||||||
|
SSL-сертификат получается и устанавливается с помощью [certbot](https://github.com/certbot/certbot). На их сайте есть подробная пошаговая [инструкция](https://certbot.eff.org/instructions?ws=nginx&os=pip) о том, как правильно его установить, получить сертификаты и включить их автообновление.
|
||||||
|
|
||||||
|
??? note "Установка вкратце"
|
||||||
|
|
||||||
|
```sh { .code-wrap }
|
||||||
|
# Установили certbot в venv
|
||||||
|
# Вместо 3.1x нужно указать актуальную версию Python
|
||||||
|
sudo python3.x -m venv /opt/certbot/
|
||||||
|
sudo /opt/certbot/bin/pip install --upgrade pip
|
||||||
|
sudo /opt/certbot/bin/pip install certbot certbot-nginx
|
||||||
|
|
||||||
|
# Добавили в PATH
|
||||||
|
sudo ln -s /opt/certbot/bin/certbot /usr/bin/certbot
|
||||||
|
|
||||||
|
# Автообновление сертификатов
|
||||||
|
echo "0 0,12 * * * root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)' && sudo certbot renew -q" | sudo tee -a /etc/crontab > /dev/null
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Несколько полезных команд.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Получить сертификат для определённого домена. Предварительно нужно
|
||||||
|
# настроить конфиг nginx для этого домена.
|
||||||
|
sudo certbot --nginx -d example.com -d www.example.com
|
||||||
|
|
||||||
|
# Список сертификатов со сроками их жизни
|
||||||
|
sudo certbot certificates
|
||||||
|
|
||||||
|
# Удалить сертификат (команда предложит выбор)
|
||||||
|
sudo certbot delete
|
||||||
|
```
|
||||||
@@ -4,4 +4,18 @@
|
|||||||
|
|
||||||
.md-footer {
|
.md-footer {
|
||||||
margin-top: 50px;
|
margin-top: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Достаточно добавить { .code-wrap } к блоку кода, чтобы включить автоперенос строк.
|
||||||
|
attr_list, очевидно, должен быть подключен в markdown_extensions.
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
|
||||||
|
```sh { .code-wrap }
|
||||||
|
*very long line of code here*
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
.code-wrap code {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ edit_uri: edit/main/docs/
|
|||||||
theme:
|
theme:
|
||||||
name: material
|
name: material
|
||||||
language: ru
|
language: ru
|
||||||
|
logo: assets/logo.svg
|
||||||
favicon: assets/favicon.ico
|
favicon: assets/favicon.ico
|
||||||
|
|
||||||
icon:
|
icon:
|
||||||
repo: fontawesome/brands/github
|
repo: fontawesome/brands/github
|
||||||
|
|
||||||
features:
|
features:
|
||||||
|
- navigation.indexes
|
||||||
- navigation.instant
|
- navigation.instant
|
||||||
- navigation.tabs
|
- navigation.tabs
|
||||||
- content.code.copy
|
- content.code.copy
|
||||||
@@ -43,6 +45,11 @@ extra:
|
|||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
- attr_list
|
- attr_list
|
||||||
- admonition
|
- admonition
|
||||||
|
- pymdownx.tabbed:
|
||||||
|
alternate_style: true
|
||||||
|
slugify: !!python/object/apply:pymdownx.slugs.slugify
|
||||||
|
kwds:
|
||||||
|
case: lower
|
||||||
- pymdownx.highlight:
|
- pymdownx.highlight:
|
||||||
anchor_linenums: true
|
anchor_linenums: true
|
||||||
line_spans: __span
|
line_spans: __span
|
||||||
|
|||||||
Reference in New Issue
Block a user