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 / подписки +
+