diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c18dd8d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+__pycache__/
diff --git a/static/css/style.css b/static/css/style.css
index 64634fe..a1bd1f9 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -664,6 +664,9 @@ img.card-icon { border-radius: 12px; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
font-family: var(--mono);
font-size: .76rem;
padding: 6px 12px;
@@ -673,6 +676,130 @@ img.card-icon { border-radius: 12px; }
color: #b6beff;
}
+.chip-icon { width: 14px; height: 14px; flex: none; object-fit: contain; }
+.chip-icon-svg { color: #b6beff; }
+
+/* ---------- карточка проектов с выдвижным списком ---------- */
+.card-flyout { overflow: visible; }
+
+.card-flyout:hover,
+.card-flyout:focus-within,
+.card-flyout.flyout-open { z-index: 30; }
+
+.flyout-hint {
+ margin-top: auto;
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
+ font-family: var(--mono);
+ font-size: .72rem;
+ letter-spacing: .05em;
+ color: var(--muted);
+}
+
+.flyout-hint::before {
+ content: "▸";
+ color: var(--accent);
+ transition: transform .25s ease;
+}
+
+.card-flyout:hover .flyout-hint::before,
+.card-flyout:focus-within .flyout-hint::before,
+.card-flyout.flyout-open .flyout-hint::before { transform: rotate(90deg); }
+
+.flyout {
+ position: absolute;
+ top: calc(100% + 12px);
+ left: 0;
+ right: 0;
+ z-index: 30;
+ opacity: 0;
+ visibility: hidden;
+ transform: translateY(-10px);
+ transition: opacity .25s ease, transform .25s ease, visibility .25s;
+ pointer-events: none;
+}
+
+.card-flyout:hover .flyout,
+.card-flyout:focus-within .flyout,
+.card-flyout.flyout-open .flyout {
+ opacity: 1;
+ visibility: visible;
+ transform: none;
+ pointer-events: auto;
+}
+
+.flyout-inner {
+ display: flex;
+ flex-direction: column;
+ gap: 18px;
+ padding: 22px;
+ border-radius: var(--radius);
+ background: rgba(10, 12, 20, .97);
+ border: 1px solid rgba(94, 234, 212, .3);
+ box-shadow: 0 24px 60px rgba(0, 0, 0, .6);
+ backdrop-filter: blur(14px);
+ -webkit-backdrop-filter: blur(14px);
+}
+
+.project + .project {
+ border-top: 1px dashed rgba(255, 255, 255, .1);
+ padding-top: 18px;
+}
+
+.project-title {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: baseline;
+ gap: 10px;
+ font-size: 1rem;
+ letter-spacing: -0.01em;
+ margin-bottom: 10px;
+}
+
+.project-year {
+ font-family: var(--mono);
+ font-size: .7rem;
+ color: var(--accent);
+ border: 1px solid rgba(94, 234, 212, .3);
+ border-radius: 999px;
+ padding: 2px 9px;
+}
+
+.project-stack {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+ margin-bottom: 12px;
+}
+
+.stack-tag {
+ font-family: var(--mono);
+ font-size: .68rem;
+ padding: 3px 9px;
+ border-radius: 999px;
+ background: rgba(124, 140, 255, .1);
+ border: 1px solid rgba(124, 140, 255, .25);
+ color: #b6beff;
+}
+
+.stack-tag.stack-role {
+ background: rgba(94, 234, 212, .08);
+ border-color: rgba(94, 234, 212, .3);
+ color: var(--accent);
+}
+
+.project-points { gap: 7px; font-size: .88rem; }
+
+.project-points code {
+ font-family: var(--mono);
+ font-size: .8rem;
+ padding: 2px 6px;
+ border-radius: 6px;
+ background: rgba(255, 255, 255, .06);
+ border: 1px solid rgba(255, 255, 255, .08);
+}
+
/* ---------- трекер Яндекс Музыки ---------- */
.ym-tracker {
max-width: 880px;
@@ -999,6 +1126,18 @@ a.ym-now-link:hover .ym-track-title { color: var(--accent); }
@media (max-width: 900px) {
.about { grid-template-columns: 1fr; }
.author-cards .card-wide { grid-column: auto; }
+
+ .flyout {
+ position: static;
+ display: none;
+ opacity: 1;
+ visibility: visible;
+ transform: none;
+ pointer-events: auto;
+ margin-top: 16px;
+ }
+
+ .card-flyout.flyout-open .flyout { display: block; }
}
@media (max-width: 760px) {
diff --git a/static/images/freshrss.svg b/static/images/freshrss.svg
new file mode 100644
index 0000000..257ae29
--- /dev/null
+++ b/static/images/freshrss.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/static/images/icons/ansible.svg b/static/images/icons/ansible.svg
new file mode 100644
index 0000000..2a121a2
--- /dev/null
+++ b/static/images/icons/ansible.svg
@@ -0,0 +1 @@
+Ansible
\ No newline at end of file
diff --git a/static/images/icons/argo.svg b/static/images/icons/argo.svg
new file mode 100644
index 0000000..34239b8
--- /dev/null
+++ b/static/images/icons/argo.svg
@@ -0,0 +1 @@
+Argo
\ No newline at end of file
diff --git a/static/images/icons/bash.svg b/static/images/icons/bash.svg
new file mode 100644
index 0000000..c32ba93
--- /dev/null
+++ b/static/images/icons/bash.svg
@@ -0,0 +1 @@
+GNU Bash
\ No newline at end of file
diff --git a/static/images/icons/docker.svg b/static/images/icons/docker.svg
new file mode 100644
index 0000000..e68a661
--- /dev/null
+++ b/static/images/icons/docker.svg
@@ -0,0 +1 @@
+Docker
\ No newline at end of file
diff --git a/static/images/icons/git.svg b/static/images/icons/git.svg
new file mode 100644
index 0000000..52ced6b
--- /dev/null
+++ b/static/images/icons/git.svg
@@ -0,0 +1 @@
+Git
\ No newline at end of file
diff --git a/static/images/icons/githubactions.svg b/static/images/icons/githubactions.svg
new file mode 100644
index 0000000..b3d91c1
--- /dev/null
+++ b/static/images/icons/githubactions.svg
@@ -0,0 +1 @@
+GitHub Actions
\ No newline at end of file
diff --git a/static/images/icons/kubernetes.svg b/static/images/icons/kubernetes.svg
new file mode 100644
index 0000000..b2dded3
--- /dev/null
+++ b/static/images/icons/kubernetes.svg
@@ -0,0 +1 @@
+Kubernetes
\ No newline at end of file
diff --git a/static/images/icons/linux.svg b/static/images/icons/linux.svg
new file mode 100644
index 0000000..58be2b6
--- /dev/null
+++ b/static/images/icons/linux.svg
@@ -0,0 +1 @@
+Linux
\ No newline at end of file
diff --git a/static/images/icons/python.svg b/static/images/icons/python.svg
new file mode 100644
index 0000000..e1b14ec
--- /dev/null
+++ b/static/images/icons/python.svg
@@ -0,0 +1 @@
+Python
\ No newline at end of file
diff --git a/static/images/icons/terraform.svg b/static/images/icons/terraform.svg
new file mode 100644
index 0000000..f4f3dbe
--- /dev/null
+++ b/static/images/icons/terraform.svg
@@ -0,0 +1 @@
+Terraform
\ No newline at end of file
diff --git a/static/images/icons/vim.svg b/static/images/icons/vim.svg
new file mode 100644
index 0000000..63d1512
--- /dev/null
+++ b/static/images/icons/vim.svg
@@ -0,0 +1 @@
+Vim
\ No newline at end of file
diff --git a/static/js/main.js b/static/js/main.js
index 376a8dc..eaeac91 100644
--- a/static/js/main.js
+++ b/static/js/main.js
@@ -79,6 +79,28 @@
counters.forEach((el) => (el.textContent = el.dataset.count));
}
+ /* ---------- выдвижной список проектов (тач/клик) ---------- */
+ const flyoutCards = document.querySelectorAll(".card-flyout");
+
+ flyoutCards.forEach((card) => {
+ card.addEventListener("click", (e) => {
+ if (e.target.closest("a")) return;
+ card.classList.toggle("flyout-open");
+ });
+ });
+
+ document.addEventListener("click", (e) => {
+ flyoutCards.forEach((card) => {
+ if (!card.contains(e.target)) card.classList.remove("flyout-open");
+ });
+ });
+
+ document.addEventListener("keydown", (e) => {
+ if (e.key === "Escape") {
+ flyoutCards.forEach((card) => card.classList.remove("flyout-open"));
+ }
+ });
+
/* ---------- печатающийся терминал ---------- */
const typed = document.getElementById("typed");
if (typed) {
@@ -91,6 +113,7 @@
{ t: "sharkey Up 14 months \n", d: 14 },
{ t: "copyparty Up 14 months \n", d: 14 },
{ t: "grafana Up 14 months \n", d: 14 },
+ { t: "freshrss Up 14 months \n", d: 14 },
{ t: "4get Up 14 months \n", d: 14 },
{ t: '$ uptime', d: 22 },
{ t: '\n all systems nominal ', d: 14 },
diff --git a/templates/author.html b/templates/author.html
index 6344832..d063dbe 100644
--- a/templates/author.html
+++ b/templates/author.html
@@ -159,37 +159,93 @@
Навыки
-
Linux
-
Docker / Compose
-
Ansible
-
Terraform
-
Git
-
GitHub Actions
-
Bash
-
Python
-
Kubernetes
-
Argo CD
-
Vim
-
сеть и безопасность
+
Linux
+
Docker / Compose
+
Ansible
+
Terraform
+
Git
+
GitHub Actions
+
Bash
+
Python
+
Kubernetes
+
Argo CD
+
Vim
+
сеть и безопасность
-
+
Проекты
- Terraform — инфраструктура как код:
- final_assessment
- Argo CD — GitOps-доставка в Kubernetes:
- lab4
- Ansible + Docker — конфигурация серверов и контейнеризация:
- MyResume
+ Отказоустойчивый веб-хостинг в VK Cloud
+ Мониторинг веб-инфраструктуры в VK Cloud
+ CI/CD и GitOps для Kubernetes
+ наведите курсор — расскажу подробнее
+
+
+
+
+
Отказоустойчивый веб-хостинг в VK Cloud 2026
+
+ Terraform
+ Docker
+ GitHub Actions
+ PostgreSQL
+ L7 Load Balancer
+ личный проект
+ GitHub
+
+
+ Спроектировал облачную инфраструктуру: VPC с public/private-подсетями, 2 web-VM в приватном контуре, bastion и управляемую PostgreSQL; веб-доступ организовал через L7 Load Balancer.
+ Описал инфраструктуру в Terraform, настроил S3 backend для state и security groups: SSH ограничен моим IP, PostgreSQL доступна только из приватной подсети.
+ Настроил запуск Flask/Gunicorn в Docker и CI/CD в GitHub Actions с этапами plan → apply → destroy; параметры облака передаются через GitHub Secrets.
+ Проверил отказоустойчивость остановкой контейнера на одном web-сервере и нагрузочным тестом ab -n 1000 -c 10: 999 успешных запросов из 1000, 76.54 req/s.
+
+
+
+
+
Мониторинг веб-инфраструктуры в VK Cloud 2026
+
+ Terraform
+ Prometheus
+ Grafana
+ Alertmanager
+ Node Exporter
+ L7 Load Balancer
+ личный проект
+
+
+ Развернул через Terraform 2 web-VM в приватной подсети, bastion/monitoring-VM и L7 Load Balancer с HTTP health-check для контроля доступности сервисов.
+ Автоматизировал установку и запуск Prometheus, Grafana, Alertmanager и Node Exporter через user_data и systemd; Prometheus собирает метрики с monitoring- и web-узлов каждые 15 секунд.
+ Добавил правило алерта InstanceDown с условием недоступности узла более 5 минут и настроил сетевые правила для мониторинговых endpoints.
+
+
+
+
+
CI/CD и GitOps для Kubernetes 2026
+
+ Terraform
+ GitHub Actions
+ Kubernetes
+ Argo CD
+ Kustomize
+ GitHub
+
+
+ Описал инфраструктуру VK Cloud и Kubernetes-ресурсы в Git: Deployment, Service, ConfigMap и Kustomize; приложение разворачивается в двух репликах.
+ Настроил GitHub Actions: validate и plan запускаются для изменений, артефакт tfplan передаётся в этап apply после успешного плана.
+ Интегрировал pipeline с GitOps-подходом: после Terraform Apply workflow получает IP балансировщика, обновляет ConfigMap и коммитит изменение обратно в Git для дальнейшей синхронизации в Kubernetes.
+
+
+
+
diff --git a/templates/main.html b/templates/main.html
index a49d855..b7ec0eb 100644
--- a/templates/main.html
+++ b/templates/main.html
@@ -3,7 +3,7 @@
-
+
srcoder.moe
@@ -53,7 +53,7 @@
-
0 сервисов
+
0 сервисов
0 часа аптайма / 7 дней
0 год запуска
@@ -63,7 +63,7 @@
// сервисы
Что развёрнуто на сервере
-
Шесть сервисов, закрывающих все базовые потребности — от кода до поиска.
+
Семь сервисов, закрывающих все базовые потребности — от кода до поиска.
@@ -125,6 +125,18 @@
мониторинг
+
+
+
+
FreshRSS
+
Самохостируемый RSS-агрегатор: собирает ленты блогов, новостных
+ сайтов и подкастов в одну спокойную читалку — без рекламы,
+ алгоритмов и слежки. Подписки и история чтения хранятся
+ на моём сервере и синхронизируются с любого устройства.
+
+ RSS / подписки
+
+