fix(static): Rebranding
This commit is contained in:
1 parent
214cb72e1e
commit
680ed76150
6 files changed
+376
-1839
No files matched your search
@@ -0,0 +1,111 @@
|
||||
:root {
|
||||
--bg: #0e1116;
|
||||
--card: #161b22;
|
||||
--line: #262d36;
|
||||
--line-strong: #3a4553;
|
||||
--ink: #e6edf3;
|
||||
--muted: #8b98a5;
|
||||
--accent: #3fb950;
|
||||
--mono: 'JetBrains Mono', monospace;
|
||||
}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
body { font-family: 'Inter', system-ui, sans-serif; background: var(--bg); color: var(--ink); line-height: 1.6; font-size: 16px; }
|
||||
.container { max-width: 1080px; margin: 0 auto; padding: 0 24px; }
|
||||
code { font-family: var(--mono); font-size: .9em; background: var(--card); padding: 1px 5px; }
|
||||
|
||||
header { border-bottom: 1px solid var(--line-strong); position: sticky; top: 0; background: var(--bg); z-index: 10; }
|
||||
.nav { display: flex; align-items: center; justify-content: space-between; height: 58px; }
|
||||
.logo { font-family: var(--mono); font-weight: 500; font-size: 15px; color: var(--ink); text-decoration: none; }
|
||||
.logo b { color: var(--accent); }
|
||||
.nav-links { display: flex; gap: 4px; list-style: none; }
|
||||
.nav-links a { color: var(--muted); text-decoration: none; font-size: 13.5px; font-family: var(--mono); padding: 6px 12px; border: 1px solid transparent; }
|
||||
.nav-links a:hover { color: var(--ink); border-color: var(--line); }
|
||||
.nav-links a.nav-cta { background: var(--accent); color: #000; border-color: var(--accent); font-weight: 500; }
|
||||
.nav-links a.nav-cta:hover { color: #000; border-color: var(--accent); opacity: .9; }
|
||||
|
||||
.hero { padding: 48px 0 36px; display: flex; justify-content: space-between; align-items: flex-start; gap: 40px; flex-wrap: wrap; border-bottom: 1px solid var(--line); }
|
||||
.hero-id { display: flex; align-items: center; gap: 28px; }
|
||||
.avatar { width: 108px; height: 108px; object-fit: cover; flex: none; border: 1px solid var(--line-strong); }
|
||||
.hero h1 { font-size: 36px; font-weight: 600; letter-spacing: -0.02em; }
|
||||
.hero .role { font-family: var(--mono); font-size: 13px; color: var(--accent); margin-top: 4px; }
|
||||
.hero-contacts { display: flex; flex-direction: column; gap: 14px; min-width: 280px; }
|
||||
|
||||
section { padding: 44px 0; border-bottom: 1px solid var(--line); }
|
||||
.sec-head { display: flex; align-items: baseline; gap: 14px; margin-bottom: 24px; }
|
||||
.sec-head .num { font-family: var(--mono); font-size: 13px; color: var(--accent); }
|
||||
h2 { font-size: 22px; font-weight: 600; letter-spacing: -0.02em; }
|
||||
.sec-head .rule { flex: 1; height: 1px; background: var(--line); }
|
||||
|
||||
.buttons { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||
.contact-btn { display: inline-flex; align-items: center; gap: 8px; background: var(--card); border: 1px solid var(--line); padding: 9px 15px; color: var(--ink); text-decoration: none; font-size: 14px; transition: border-color .15s; }
|
||||
.contact-btn:hover { border-color: var(--accent); }
|
||||
.contact-btn img { width: 16px; height: 16px; }
|
||||
.group-label { font-family: var(--mono); font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }
|
||||
.hero-contacts .group-label { margin-bottom: 8px; }
|
||||
|
||||
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1px; background: var(--line); border: 1px solid var(--line); }
|
||||
.cell { background: var(--card); padding: 24px; }
|
||||
.cell h3 { font-family: var(--mono); text-transform: uppercase; letter-spacing: .04em; font-size: 12.5px; font-weight: 500; color: var(--accent); margin-bottom: 10px; }
|
||||
.cell p, .cell li { font-size: 15px; color: var(--muted); }
|
||||
.cell ul { padding-left: 18px; }
|
||||
.cell a { color: var(--accent); }
|
||||
.wide { grid-column: 1 / -1; }
|
||||
|
||||
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||
.chip { display: inline-flex; align-items: center; gap: 6px; background: var(--bg); border: 1px solid var(--line); padding: 5px 12px; font-size: 13.5px; }
|
||||
.chip img { width: 14px; height: 14px; }
|
||||
|
||||
details { border-top: 1px solid var(--line); padding: 14px 0; }
|
||||
details:first-of-type { border-top: none; }
|
||||
summary { cursor: pointer; font-size: 15px; font-weight: 600; list-style: none; display: flex; justify-content: space-between; gap: 12px; }
|
||||
summary::-webkit-details-marker { display: none; }
|
||||
summary .year { color: var(--accent); font-family: var(--mono); font-size: 12.5px; font-weight: 400; }
|
||||
details .body { padding-top: 10px; color: var(--muted); font-size: 14.5px; }
|
||||
.stack { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
|
||||
.stack-tag { font-family: var(--mono); background: var(--bg); border: 1px solid var(--line); padding: 2px 8px; font-size: 12px; }
|
||||
|
||||
.activity-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--line); border: 1px solid var(--line); }
|
||||
.panel { background: var(--card); padding: 24px; }
|
||||
.panel-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 14px; }
|
||||
.panel-head h2 { margin: 0; font-size: 18px; }
|
||||
.panel-head .meta { display: flex; align-items: center; gap: 10px; }
|
||||
.meta { font-size: 12px; color: var(--muted); font-family: var(--mono); }
|
||||
.refresh { background: none; border: 1px solid var(--line); color: var(--muted); font-family: var(--mono); font-size: 13px; line-height: 1; padding: 4px 7px; cursor: pointer; transition: color .15s, border-color .15s; }
|
||||
.refresh:hover { color: var(--accent); border-color: var(--accent); }
|
||||
.sub { font-family: var(--mono); font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--accent); margin: 16px 0 8px; }
|
||||
.sub:first-of-type { margin-top: 0; }
|
||||
.list { list-style: none; }
|
||||
.list li { padding: 9px 0; border-bottom: 1px solid var(--line); font-size: 14.5px; color: var(--muted); }
|
||||
.list li:last-child { border-bottom: none; }
|
||||
.ym-track { display: flex; }
|
||||
.ym-link { display: flex; align-items: center; gap: 12px; width: 100%; color: inherit; text-decoration: none; }
|
||||
.ym-cover { width: 42px; height: 42px; object-fit: cover; flex: none; background: var(--bg); }
|
||||
.ym-cover-empty { display: flex; align-items: center; justify-content: center; font-size: 20px; }
|
||||
.ym-info { display: flex; flex-direction: column; min-width: 0; flex: 1; }
|
||||
.ym-track-title { color: var(--ink); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.ym-artists, .ym-context { font-size: 13px; }
|
||||
.ym-day { font-size: 13px; flex: none; }
|
||||
.ym-track.ym-playing .ym-track-title,
|
||||
.ym-track.ym-playing .ym-artists,
|
||||
.ym-day.ym-now,
|
||||
.ym-now-link .ym-track-title,
|
||||
.ym-now-link .ym-artists { color: var(--accent); }
|
||||
.ym-now-link { display: flex; align-items: center; gap: 14px; text-decoration: none; color: inherit; margin-bottom: 14px; }
|
||||
.ym-now-cover { width: 60px; height: 60px; object-fit: cover; background: var(--bg); }
|
||||
.ym-now-info { display: flex; flex-direction: column; }
|
||||
.ym-error { color: #f85149; }
|
||||
|
||||
.friend-cta { text-align: center; padding: 30px 0 10px; color: var(--muted); font-size: 14px; font-family: var(--mono); }
|
||||
.friend-cta a { color: var(--accent); font-size: 16px; font-weight: 500; text-decoration: none; }
|
||||
|
||||
footer { border-top: 1px solid var(--line-strong); }
|
||||
.footer-inner { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 22px 0; font-size: 14px; color: var(--muted); font-family: var(--mono); }
|
||||
.footer-inner a { color: var(--muted); text-decoration: none; }
|
||||
.footer-inner a:hover { color: var(--accent); }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.hero { flex-direction: column; align-items: flex-start; }
|
||||
.hero-id { flex-direction: column; align-items: flex-start; gap: 16px; }
|
||||
.activity-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
:root {
|
||||
--bg: #0e1116;
|
||||
--card: #161b22;
|
||||
--line: #262d36;
|
||||
--line-strong: #3a4553;
|
||||
--ink: #e6edf3;
|
||||
--muted: #8b98a5;
|
||||
--accent: #3fb950;
|
||||
--mono: 'JetBrains Mono', monospace;
|
||||
}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
body { font-family: 'Inter', system-ui, sans-serif; background: var(--bg); color: var(--ink); line-height: 1.6; font-size: 16px; }
|
||||
.container { max-width: 1080px; margin: 0 auto; padding: 0 24px; }
|
||||
|
||||
header { border-bottom: 1px solid var(--line-strong); background: var(--bg); position: sticky; top: 0; z-index: 10; }
|
||||
.nav { display: flex; align-items: center; justify-content: space-between; height: 58px; }
|
||||
.logo { font-family: var(--mono); font-weight: 500; font-size: 15px; color: var(--ink); text-decoration: none; }
|
||||
.logo b { color: var(--accent); }
|
||||
.nav-links { display: flex; gap: 4px; list-style: none; }
|
||||
.nav-links a {
|
||||
color: var(--muted); text-decoration: none; font-size: 13.5px;
|
||||
font-family: var(--mono); padding: 6px 12px; border: 1px solid transparent;
|
||||
}
|
||||
.nav-links a:hover { color: var(--ink); border-color: var(--line); }
|
||||
.nav-links a.nav-cta { background: var(--accent); color: #000; border-color: var(--accent); font-weight: 500; }
|
||||
.nav-links a.nav-cta:hover { color: #000; border-color: var(--accent); opacity: .9; }
|
||||
|
||||
.hero { padding: 80px 0 56px; border-bottom: 1px solid var(--line); }
|
||||
.hero .idx { font-family: var(--mono); font-size: 13px; color: var(--accent); margin-bottom: 20px; }
|
||||
.hero h1 { font-size: 46px; font-weight: 600; letter-spacing: -0.03em; line-height: 1.1; max-width: 760px; }
|
||||
.hero p { margin-top: 20px; font-size: 18px; color: var(--muted); max-width: 620px; }
|
||||
|
||||
section { padding: 56px 0; }
|
||||
.sec-head { display: flex; align-items: baseline; gap: 14px; margin-bottom: 28px; }
|
||||
.sec-head .num { font-family: var(--mono); font-size: 13px; color: var(--accent); }
|
||||
h2 { font-size: 24px; font-weight: 600; letter-spacing: -0.02em; }
|
||||
.sec-head .rule { flex: 1; height: 1px; background: var(--line); }
|
||||
|
||||
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 1px; background: var(--line); border: 1px solid var(--line); }
|
||||
.card { background: var(--card); padding: 26px; display: flex; flex-direction: column; }
|
||||
.card-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
|
||||
.card-top img { width: 26px; height: 26px; object-fit: contain; }
|
||||
.card-top .num { font-family: var(--mono); font-size: 12px; color: var(--muted); }
|
||||
.card h3 { font-size: 17px; font-weight: 600; margin-bottom: 8px; }
|
||||
.card p { font-size: 14.5px; color: var(--muted); flex: 1; }
|
||||
.card a { color: var(--accent); }
|
||||
.cat { margin-top: 16px; font-family: var(--mono); font-size: 11.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }
|
||||
|
||||
.about-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--line); border: 1px solid var(--line); }
|
||||
.about-grid > div { background: var(--card); padding: 32px; }
|
||||
.about-grid p { color: var(--muted); margin-bottom: 16px; }
|
||||
.about-grid a { color: var(--accent); }
|
||||
.specs { list-style: none; }
|
||||
.specs li { display: flex; justify-content: space-between; gap: 14px; padding: 11px 0; border-bottom: 1px solid var(--line); font-size: 15px; }
|
||||
.specs li:first-child { padding-top: 0; }
|
||||
.specs li:last-child { border-bottom: none; }
|
||||
.specs li span:first-child { color: var(--muted); font-family: var(--mono); font-size: 13.5px; }
|
||||
.specs li span:last-child { font-weight: 500; text-align: right; }
|
||||
|
||||
footer { border-top: 1px solid var(--line-strong); }
|
||||
.footer-inner { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 24px 0; font-size: 14px; color: var(--muted); font-family: var(--mono); }
|
||||
.footer-inner a { color: var(--muted); text-decoration: none; }
|
||||
.footer-inner a:hover { color: var(--accent); }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.hero h1 { font-size: 32px; }
|
||||
.about-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,176 +0,0 @@
|
||||
(() => {
|
||||
"use strict";
|
||||
|
||||
/* ---------- мобильное меню ---------- */
|
||||
const toggle = document.getElementById("navToggle");
|
||||
const links = document.getElementById("navLinks");
|
||||
|
||||
if (toggle && links) {
|
||||
toggle.addEventListener("click", () => {
|
||||
const open = links.classList.toggle("open");
|
||||
toggle.classList.toggle("open", open);
|
||||
toggle.setAttribute("aria-expanded", String(open));
|
||||
});
|
||||
|
||||
links.querySelectorAll("a").forEach((a) =>
|
||||
a.addEventListener("click", () => {
|
||||
links.classList.remove("open");
|
||||
toggle.classList.remove("open");
|
||||
toggle.setAttribute("aria-expanded", "false");
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/* ---------- тень хедера при скролле ---------- */
|
||||
const header = document.querySelector(".site-header");
|
||||
const onScroll = () => header && header.classList.toggle("scrolled", window.scrollY > 8);
|
||||
onScroll();
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
|
||||
/* ---------- появление блоков ---------- */
|
||||
const reveal = document.querySelectorAll("[data-reveal]");
|
||||
if ("IntersectionObserver" in window) {
|
||||
const io = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry, i) => {
|
||||
if (entry.isIntersecting) {
|
||||
const el = entry.target;
|
||||
setTimeout(() => el.classList.add("visible"), i * 70);
|
||||
io.unobserve(el);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.12, rootMargin: "0px 0px -40px 0px" }
|
||||
);
|
||||
reveal.forEach((el) => io.observe(el));
|
||||
} else {
|
||||
reveal.forEach((el) => el.classList.add("visible"));
|
||||
}
|
||||
|
||||
/* ---------- счётчики ---------- */
|
||||
const counters = document.querySelectorAll("[data-count]");
|
||||
const animateCount = (el) => {
|
||||
const target = parseInt(el.dataset.count, 10);
|
||||
const duration = 1400;
|
||||
const start = performance.now();
|
||||
const step = (now) => {
|
||||
const p = Math.min((now - start) / duration, 1);
|
||||
const eased = 1 - Math.pow(1 - p, 3);
|
||||
el.textContent = Math.round(target * eased).toString();
|
||||
if (p < 1) requestAnimationFrame(step);
|
||||
};
|
||||
requestAnimationFrame(step);
|
||||
};
|
||||
|
||||
if ("IntersectionObserver" in window) {
|
||||
const co = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
animateCount(entry.target);
|
||||
co.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.5 }
|
||||
);
|
||||
counters.forEach((el) => co.observe(el));
|
||||
} else {
|
||||
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) {
|
||||
const lines = [
|
||||
{ t: '<span class="prompt">$</span> docker ps --format "table {{.Names}}\\t{{.Status}}"', d: 22 },
|
||||
{ t: "\n", d: 0 },
|
||||
{ t: '<span class="dim">NAMES STATUS</span>\n', d: 14 },
|
||||
{ t: "gitea <span class=\"ok\">Up 14 months</span>\n", d: 14 },
|
||||
{ t: "matrix <span class=\"ok\">Up 14 months</span>\n", d: 14 },
|
||||
{ t: "sharkey <span class=\"ok\">Up 14 months</span>\n", d: 14 },
|
||||
{ t: "copyparty <span class=\"ok\">Up 14 months</span>\n", d: 14 },
|
||||
{ t: "grafana <span class=\"ok\">Up 14 months</span>\n", d: 14 },
|
||||
{ t: "freshrss <span class=\"ok\">Up 14 months</span>\n", d: 14 },
|
||||
{ t: "4get <span class=\"ok\">Up 14 months</span>\n", d: 14 },
|
||||
{ t: '<span class="prompt">$</span> uptime', d: 22 },
|
||||
{ t: '\n <span class="ok">all systems nominal</span>', d: 14 },
|
||||
{ t: '<span class="prompt">$</span> ', d: 22 },
|
||||
];
|
||||
|
||||
// токенизируем: HTML-теги — атомарные, текст — посимвольно
|
||||
const tokenize = (str) => str.match(/<[^>]+>|./gs) || [];
|
||||
|
||||
let lineIndex = 0;
|
||||
let tokens = [];
|
||||
let pos = 0;
|
||||
let html = "";
|
||||
|
||||
const render = () => {
|
||||
typed.innerHTML = html + '<span class="cursor"></span>';
|
||||
};
|
||||
|
||||
const typeNext = () => {
|
||||
if (lineIndex >= lines.length) {
|
||||
render();
|
||||
return;
|
||||
}
|
||||
|
||||
if (pos === 0) tokens = tokenize(lines[lineIndex].t);
|
||||
|
||||
if (pos < tokens.length) {
|
||||
html += tokens[pos++];
|
||||
render();
|
||||
const step = tokens[pos - 1].startsWith("<")
|
||||
? 0
|
||||
: lines[lineIndex].d + Math.random() * 35;
|
||||
setTimeout(typeNext, step);
|
||||
} else {
|
||||
lineIndex++;
|
||||
pos = 0;
|
||||
setTimeout(typeNext, 190);
|
||||
}
|
||||
};
|
||||
|
||||
const startTyping = () => setTimeout(typeNext, 500);
|
||||
|
||||
if ("IntersectionObserver" in window) {
|
||||
const to = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
startTyping();
|
||||
to.disconnect();
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.3 }
|
||||
);
|
||||
to.observe(typed);
|
||||
} else {
|
||||
startTyping();
|
||||
}
|
||||
}
|
||||
})();
|
||||
+122
-289
@@ -4,335 +4,168 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Black Raven — SRE/DevOps инженер. Контакты, аккаунты и немного о себе.">
|
||||
<title>Black Raven</title>
|
||||
<title>Black Raven — srcoder.moe</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/author.css') }}">
|
||||
<link rel="icon" href="{{ url_for('static', filename='images/gitAva.jpeg') }}" type="image/jpeg">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="bg-glow" aria-hidden="true"></div>
|
||||
<div class="grid-overlay" aria-hidden="true"></div>
|
||||
|
||||
<header class="site-header" id="top">
|
||||
<nav class="nav">
|
||||
<a class="logo" href="{{ url_for('index') }}">
|
||||
<span class="logo-mark"></></span>
|
||||
<span class="logo-text">srcoder<span class="accent">.moe</span></span>
|
||||
</a>
|
||||
|
||||
<button class="nav-toggle" id="navToggle" aria-label="Меню" aria-expanded="false">
|
||||
<span></span><span></span><span></span>
|
||||
</button>
|
||||
|
||||
<ul class="nav-links" id="navLinks">
|
||||
<li><a href="{{ url_for('index') }}">Главная</a></li>
|
||||
<li><a href="{{ url_for('index') }}#services">Сервисы</a></li>
|
||||
<li><a href="{{ url_for('author') }}" class="nav-cta" aria-current="page">Об авторе</a></li>
|
||||
<header>
|
||||
<div class="container nav">
|
||||
<a class="logo" href="{{ url_for('index') }}">srcoder<b>.moe</b></a>
|
||||
<ul class="nav-links">
|
||||
<li><a class="nav-cta" href="{{ url_for('index') }}">/home</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="author-hero">
|
||||
<img class="author-avatar"
|
||||
src="{{ url_for('static', filename='images/gitAva.jpeg') }}"
|
||||
alt="Аватар Black Raven" width="190" height="190" data-reveal>
|
||||
<main class="container">
|
||||
<section class="hero" style="border-top:none">
|
||||
<div class="hero-id">
|
||||
<img class="avatar" src="{{ url_for('static', filename='images/gitAva.jpeg') }}" alt="Аватар Black Raven">
|
||||
<div>
|
||||
<h1>Black Raven</h1>
|
||||
<div class="role">SRE / DevOps инженер</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="author-name" data-reveal>Black Raven</h1>
|
||||
<p class="author-role" data-reveal>SRE / DevOps инженер</p>
|
||||
|
||||
<p class="author-bio" data-reveal>
|
||||
Привет! Собираю инфраструктуру как код, автоматизирую рутину
|
||||
и держу собственный сервер — именно на нём живёт всё,
|
||||
что вы видите на главной странице. Здесь коротко о том,
|
||||
кто я и где меня можно найти.
|
||||
</p>
|
||||
|
||||
<div class="contact-groups" data-reveal>
|
||||
<div class="contact-group">
|
||||
<h3>Контактная информация</h3>
|
||||
<div class="contact-buttons">
|
||||
<a class="contact-btn" href="https://t.me/simpleRussianProger" target="_blank" rel="noopener">
|
||||
<img src="{{ url_for('static', filename='images/telegram.svg') }}" alt="" width="18" height="18">
|
||||
Telegram
|
||||
</a>
|
||||
|
||||
<a class="contact-btn" href="https://matrix.to/#/@raven:srcoder.moe" target="_blank" rel="noopener">
|
||||
<img src="{{ url_for('static', filename='images/matrix.svg') }}" alt="" width="18" height="18">
|
||||
Matrix
|
||||
</a>
|
||||
|
||||
<a class="contact-btn" href="https://discord.com/users/767707485255106590" target="_blank" rel="noopener">
|
||||
<img src="{{ url_for('static', filename='images/discord.svg') }}" alt="" width="18" height="18">
|
||||
Discord
|
||||
</a>
|
||||
<div class="hero-contacts">
|
||||
<div>
|
||||
<div class="group-label">Связь</div>
|
||||
<div class="buttons">
|
||||
<a class="contact-btn" href="https://t.me/simpleRussianProger" target="_blank" rel="noopener"><img src="{{ url_for('static', filename='images/telegram.svg') }}" alt="">telegram</a>
|
||||
<a class="contact-btn" href="https://matrix.to/#/@raven:srcoder.moe" target="_blank" rel="noopener"><img src="{{ url_for('static', filename='images/matrix.svg') }}" alt="">matrix</a>
|
||||
<a class="contact-btn" href="https://discord.com/users/767707485255106590" target="_blank" rel="noopener"><img src="{{ url_for('static', filename='images/discord.svg') }}" alt="">discord</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contact-group">
|
||||
<h3>Development</h3>
|
||||
<div class="contact-buttons">
|
||||
<a class="contact-btn" href="https://github.com/BlackCorbeau" target="_blank" rel="noopener">
|
||||
<img src="{{ url_for('static', filename='images/github.svg') }}" alt="" width="18" height="18">
|
||||
GitHub
|
||||
</a>
|
||||
|
||||
<a class="contact-btn" href="https://git.srcoder.moe/Black_Raven" target="_blank" rel="noopener">
|
||||
<img src="{{ url_for('static', filename='images/gitea.svg') }}" alt="" width="18" height="18">
|
||||
Gitea
|
||||
</a>
|
||||
<div>
|
||||
<div class="group-label">Разработка</div>
|
||||
<div class="buttons">
|
||||
<a class="contact-btn" href="https://github.com/BlackCorbeau" target="_blank" rel="noopener"><img src="{{ url_for('static', filename='images/github.svg') }}" alt="">github</a>
|
||||
<a class="contact-btn" href="https://git.srcoder.moe/Black_Raven" target="_blank" rel="noopener"><img src="{{ url_for('static', filename='images/gitea.svg') }}" alt="">gitea</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contact-group">
|
||||
<h3>Games</h3>
|
||||
<div class="contact-buttons">
|
||||
<a class="contact-btn" href="https://steamcommunity.com/profiles/76561199137306791" target="_blank" rel="noopener">
|
||||
<img src="{{ url_for('static', filename='images/steam.svg') }}" alt="" width="18" height="18">
|
||||
Steam
|
||||
</a>
|
||||
<div>
|
||||
<div class="group-label">Игры</div>
|
||||
<div class="buttons">
|
||||
<a class="contact-btn" href="https://steamcommunity.com/profiles/76561199137306791" target="_blank" rel="noopener"><img src="{{ url_for('static', filename='images/steam.svg') }}" alt="">steam</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section" id="details">
|
||||
<div class="cards author-cards">
|
||||
<article class="card" data-reveal>
|
||||
<div class="card-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path></svg>
|
||||
<section>
|
||||
<div class="sec-head"><span class="num">01</span><h2>Немного фактов</h2><span class="rule"></span></div>
|
||||
<div class="grid">
|
||||
<div class="cell">
|
||||
<h3>Образование</h3>
|
||||
<p><strong>2023–2027, ННГУ им. Лобачевского</strong>, Нижний Новгород — программная инженерия.</p>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<h3>Достижения</h3>
|
||||
<ul>
|
||||
<li><strong>2025</strong> — хакатон от ИТ-холдинга Т1, 3-е место (<a href="https://disk.rustprogersteam.ru/shr/kvpwutpbt7tt/%D0%A21_%D0%9A%D0%BE%D0%B4_%D0%9C%D0%B0%D1%80%D1%82_Certificate_2025_04_13_17_33_29_145Z_1_compressed.pdf" target="_blank" rel="noopener">сертификат</a>)</li>
|
||||
<li><strong>2026</strong> — ИТ-марафон «Кейс Лаб», «Облачные сервисы» (<a href="https://disk.rustprogersteam.ru/shr/890gdowwch0y/%D0%A0%D0%B5%D0%BC%D0%B8%D0%B7%D0%BE%D0%B2%20%D0%9A%D0%B8%D1%80%D0%B8%D0%BB%D0%BB%20%D0%9B%D1%8C%D0%B2%D0%BE%D0%B2%D0%B8%D1%87.pdf" target="_blank" rel="noopener">сертификат</a>)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<h3>Языки</h3>
|
||||
<ul>
|
||||
<li>Английский — B2</li>
|
||||
<li>Французский — A1?</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="cell wide">
|
||||
<h3>Навыки</h3>
|
||||
<div class="chips">
|
||||
<span class="chip"><img src="{{ url_for('static', filename='images/icons/linux.svg') }}" alt="">Linux</span>
|
||||
<span class="chip"><img src="{{ url_for('static', filename='images/icons/docker.svg') }}" alt="">Docker / Compose</span>
|
||||
<span class="chip"><img src="{{ url_for('static', filename='images/icons/ansible.svg') }}" alt="">Ansible</span>
|
||||
<span class="chip"><img src="{{ url_for('static', filename='images/icons/terraform.svg') }}" alt="">Terraform</span>
|
||||
<span class="chip"><img src="{{ url_for('static', filename='images/icons/git.svg') }}" alt="">Git</span>
|
||||
<span class="chip"><img src="{{ url_for('static', filename='images/icons/githubactions.svg') }}" alt="">GitHub Actions</span>
|
||||
<span class="chip"><img src="{{ url_for('static', filename='images/icons/bash.svg') }}" alt="">Bash</span>
|
||||
<span class="chip"><img src="{{ url_for('static', filename='images/icons/python.svg') }}" alt="">Python</span>
|
||||
<span class="chip"><img src="{{ url_for('static', filename='images/icons/kubernetes.svg') }}" alt="">Kubernetes</span>
|
||||
<span class="chip"><img src="{{ url_for('static', filename='images/icons/argo.svg') }}" alt="">Argo CD</span>
|
||||
<span class="chip"><img src="{{ url_for('static', filename='images/icons/vim.svg') }}" alt="">Vim</span>
|
||||
<span class="chip">сеть и безопасность</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h3>Образование</h3>
|
||||
<p><strong>2023–2027, ННГУ им. Лобачевского</strong>, Нижний Новгород —
|
||||
программная инженерия.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card" data-reveal>
|
||||
<div class="card-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="8" r="7"></circle><polyline points="8.21 13.89 7 23 12 20 17 23 15.79 13.88"></polyline></svg>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h3>Достижения</h3>
|
||||
<ul class="list">
|
||||
<li><strong>2025</strong> — хакатон от ИТ-холдинга Т1, 3-е место
|
||||
<a class="inline-link" href="https://disk.rustprogersteam.ru/shr/kvpwutpbt7tt/%D0%A21_%D0%9A%D0%BE%D0%B4_%D0%9C%D0%B0%D1%80%D1%82_Certificate_2025_04_13_17_33_29_145Z_1_compressed.pdf" target="_blank" rel="noopener">сертификат</a></li>
|
||||
<li><strong>2026</strong> — ИТ-марафон «Кейс Лаб», направление
|
||||
«Облачные сервисы»
|
||||
<a class="inline-link" href="https://disk.rustprogersteam.ru/shr/890gdowwch0y/%D0%A0%D0%B5%D0%BC%D0%B8%D0%B7%D0%BE%D0%B2%20%D0%9A%D0%B8%D1%80%D0%B8%D0%BB%D0%BB%20%D0%9B%D1%8C%D0%B2%D0%BE%D0%B2%D0%B8%D1%87.pdf" target="_blank" rel="noopener">сертификат</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card" data-reveal>
|
||||
<div class="card-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h3>Языки</h3>
|
||||
<ul class="list">
|
||||
<li>Английский — B2</li>
|
||||
<li>Французский — A1?</li>
|
||||
</ul>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card" data-reveal>
|
||||
<div class="card-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h3>Обо мне</h3>
|
||||
<p>Ответственный и пунктуальный, люблю своё дело,
|
||||
умею работать в команде и открыт к обмену опытом.</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card card-wide" data-reveal>
|
||||
<div class="card-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect><rect x="9" y="9" width="6" height="6"></rect><line x1="9" y1="1" x2="9" y2="4"></line><line x1="15" y1="1" x2="15" y2="4"></line><line x1="9" y1="20" x2="9" y2="23"></line><line x1="15" y1="20" x2="15" y2="23"></line><line x1="20" y1="9" x2="23" y2="9"></line><line x1="20" y1="14" x2="23" y2="14"></line><line x1="1" y1="9" x2="4" y2="9"></line><line x1="1" y1="14" x2="4" y2="14"></line></svg>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h3>Навыки</h3>
|
||||
<div class="chips">
|
||||
<span class="chip"><img class="chip-icon" src="{{ url_for('static', filename='images/icons/linux.svg') }}" alt="" width="14" height="14">Linux</span>
|
||||
<span class="chip"><img class="chip-icon" src="{{ url_for('static', filename='images/icons/docker.svg') }}" alt="" width="14" height="14">Docker / Compose</span>
|
||||
<span class="chip"><img class="chip-icon" src="{{ url_for('static', filename='images/icons/ansible.svg') }}" alt="" width="14" height="14">Ansible</span>
|
||||
<span class="chip"><img class="chip-icon" src="{{ url_for('static', filename='images/icons/terraform.svg') }}" alt="" width="14" height="14">Terraform</span>
|
||||
<span class="chip"><img class="chip-icon" src="{{ url_for('static', filename='images/icons/git.svg') }}" alt="" width="14" height="14">Git</span>
|
||||
<span class="chip"><img class="chip-icon" src="{{ url_for('static', filename='images/icons/githubactions.svg') }}" alt="" width="14" height="14">GitHub Actions</span>
|
||||
<span class="chip"><img class="chip-icon" src="{{ url_for('static', filename='images/icons/bash.svg') }}" alt="" width="14" height="14">Bash</span>
|
||||
<span class="chip"><img class="chip-icon" src="{{ url_for('static', filename='images/icons/python.svg') }}" alt="" width="14" height="14">Python</span>
|
||||
<span class="chip"><img class="chip-icon" src="{{ url_for('static', filename='images/icons/kubernetes.svg') }}" alt="" width="14" height="14">Kubernetes</span>
|
||||
<span class="chip"><img class="chip-icon" src="{{ url_for('static', filename='images/icons/argo.svg') }}" alt="" width="14" height="14">Argo CD</span>
|
||||
<span class="chip"><img class="chip-icon" src="{{ url_for('static', filename='images/icons/vim.svg') }}" alt="" width="14" height="14">Vim</span>
|
||||
<span class="chip"><svg class="chip-icon chip-icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>сеть и безопасность</span>
|
||||
</div>
|
||||
<div class="cell wide">
|
||||
<h3>Проекты</h3>
|
||||
<details open>
|
||||
<summary>Отказоустойчивый веб-хостинг в VK Cloud <span class="year">2026</span></summary>
|
||||
<div class="body">
|
||||
<div class="stack">
|
||||
<span class="stack-tag">Terraform</span><span class="stack-tag">Docker</span><span class="stack-tag">GitHub Actions</span><span class="stack-tag">PostgreSQL</span><span class="stack-tag">L7 LB</span>
|
||||
</div>
|
||||
Спроектировал облачную инфраструктуру: VPC с public/private-подсетями, 2 web-VM в приватном контуре, bastion и управляемую PostgreSQL; доступ через L7 Load Balancer. Terraform со S3 backend, CI/CD в GitHub Actions, проверка отказоустойчивости тестом <code>ab -n 1000 -c 10</code>.
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card card-wide card-flyout" data-reveal tabindex="0">
|
||||
<div class="card-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h3>Проекты</h3>
|
||||
<ul class="list">
|
||||
<li>Отказоустойчивый веб-хостинг в VK Cloud</li>
|
||||
<li>Мониторинг веб-инфраструктуры в VK Cloud</li>
|
||||
<li>CI/CD и GitOps для Kubernetes</li>
|
||||
</ul>
|
||||
</div>
|
||||
<span class="flyout-hint">наведите курсор — расскажу подробнее</span>
|
||||
|
||||
<div class="flyout">
|
||||
<div class="flyout-inner">
|
||||
<div class="project">
|
||||
<h4 class="project-title">Отказоустойчивый веб-хостинг в VK Cloud <span class="project-year">2026</span></h4>
|
||||
<div class="project-stack">
|
||||
<span class="stack-tag">Terraform</span>
|
||||
<span class="stack-tag">Docker</span>
|
||||
<span class="stack-tag">GitHub Actions</span>
|
||||
<span class="stack-tag">PostgreSQL</span>
|
||||
<span class="stack-tag">L7 Load Balancer</span>
|
||||
<span class="stack-tag stack-role">личный проект</span>
|
||||
<span class="stack-tag">GitHub</span>
|
||||
</div>
|
||||
<ul class="list project-points">
|
||||
<li>Спроектировал облачную инфраструктуру: VPC с public/private-подсетями, 2 web-VM в приватном контуре, bastion и управляемую PostgreSQL; веб-доступ организовал через L7 Load Balancer.</li>
|
||||
<li>Описал инфраструктуру в Terraform, настроил S3 backend для state и security groups: SSH ограничен моим IP, PostgreSQL доступна только из приватной подсети.</li>
|
||||
<li>Настроил запуск Flask/Gunicorn в Docker и CI/CD в GitHub Actions с этапами plan → apply → destroy; параметры облака передаются через GitHub Secrets.</li>
|
||||
<li>Проверил отказоустойчивость остановкой контейнера на одном web-сервере и нагрузочным тестом <code>ab -n 1000 -c 10</code>: 999 успешных запросов из 1000, 76.54 req/s.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="project">
|
||||
<h4 class="project-title">Мониторинг веб-инфраструктуры в VK Cloud <span class="project-year">2026</span></h4>
|
||||
<div class="project-stack">
|
||||
<span class="stack-tag">Terraform</span>
|
||||
<span class="stack-tag">Prometheus</span>
|
||||
<span class="stack-tag">Grafana</span>
|
||||
<span class="stack-tag">Alertmanager</span>
|
||||
<span class="stack-tag">Node Exporter</span>
|
||||
<span class="stack-tag">L7 Load Balancer</span>
|
||||
<span class="stack-tag stack-role">личный проект</span>
|
||||
</div>
|
||||
<ul class="list project-points">
|
||||
<li>Развернул через Terraform 2 web-VM в приватной подсети, bastion/monitoring-VM и L7 Load Balancer с HTTP health-check для контроля доступности сервисов.</li>
|
||||
<li>Автоматизировал установку и запуск Prometheus, Grafana, Alertmanager и Node Exporter через user_data и systemd; Prometheus собирает метрики с monitoring- и web-узлов каждые 15 секунд.</li>
|
||||
<li>Добавил правило алерта InstanceDown с условием недоступности узла более 5 минут и настроил сетевые правила для мониторинговых endpoints.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="project">
|
||||
<h4 class="project-title">CI/CD и GitOps для Kubernetes <span class="project-year">2026</span></h4>
|
||||
<div class="project-stack">
|
||||
<span class="stack-tag">Terraform</span>
|
||||
<span class="stack-tag">GitHub Actions</span>
|
||||
<span class="stack-tag">Kubernetes</span>
|
||||
<span class="stack-tag">Argo CD</span>
|
||||
<span class="stack-tag">Kustomize</span>
|
||||
<span class="stack-tag">GitHub</span>
|
||||
</div>
|
||||
<ul class="list project-points">
|
||||
<li>Описал инфраструктуру VK Cloud и Kubernetes-ресурсы в Git: Deployment, Service, ConfigMap и Kustomize; приложение разворачивается в двух репликах.</li>
|
||||
<li>Настроил GitHub Actions: validate и plan запускаются для изменений, артефакт tfplan передаётся в этап apply после успешного плана.</li>
|
||||
<li>Интегрировал pipeline с GitOps-подходом: после Terraform Apply workflow получает IP балансировщика, обновляет ConfigMap и коммитит изменение обратно в Git для дальнейшей синхронизации в Kubernetes.</li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Мониторинг веб-инфраструктуры в VK Cloud <span class="year">2026</span></summary>
|
||||
<div class="body">
|
||||
<div class="stack">
|
||||
<span class="stack-tag">Terraform</span><span class="stack-tag">Prometheus</span><span class="stack-tag">Grafana</span><span class="stack-tag">Alertmanager</span><span class="stack-tag">Node Exporter</span>
|
||||
</div>
|
||||
Развернул 2 web-VM в приватной подсети, bastion/monitoring-VM и L7 Load Balancer с HTTP health-check. Установку сервисов автоматизировал через user_data и systemd; добавил алерт InstanceDown на недоступность узла более 5 минут.
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</details>
|
||||
<details>
|
||||
<summary>CI/CD и GitOps для Kubernetes <span class="year">2026</span></summary>
|
||||
<div class="body">
|
||||
<div class="stack">
|
||||
<span class="stack-tag">Terraform</span><span class="stack-tag">GitHub Actions</span><span class="stack-tag">Kubernetes</span><span class="stack-tag">Argo CD</span><span class="stack-tag">Kustomize</span>
|
||||
</div>
|
||||
Описал инфраструктуру и Kubernetes-ресурсы в Git (Deployment, Service, ConfigMap, Kustomize). Настроил validate/plan/apply в GitHub Actions и GitOps-синхронизацию через Argo CD.
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section" id="listening">
|
||||
<section>
|
||||
<div class="sec-head"><span class="num">02</span><h2>Чем я занят</h2><span class="rule"></span></div>
|
||||
<div class="activity-grid">
|
||||
<div class="activity-col" data-reveal>
|
||||
<div class="ym-tracker">
|
||||
<div class="ym-head">
|
||||
<div class="ym-title">
|
||||
<h2>Что я слушаю</h2>
|
||||
</div>
|
||||
<div class="ym-meta">
|
||||
<span id="ymUpdated">подключение…</span>
|
||||
<button class="ym-refresh" id="ymRefresh" type="button" aria-label="Обновить">↻</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ym-now" id="ymNow">
|
||||
<span class="ym-now-label"><span class="pulse"></span>играет сейчас</span>
|
||||
<div class="ym-now-body" id="ymNowBody">проверяю…</div>
|
||||
</div>
|
||||
|
||||
<ol class="ym-list" id="ymHistory">
|
||||
<li class="ym-status" id="ymStatus">Загрузка истории…</li>
|
||||
</ol>
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Что я слушаю</h2>
|
||||
<span class="meta"><span id="ymUpdated">подключение…</span><button class="refresh" id="ymRefresh" type="button" aria-label="Обновить">↻</button></span>
|
||||
</div>
|
||||
<div id="ymNow"><div id="ymNowBody"></div></div>
|
||||
<ol class="list" id="ymHistory"><li id="ymStatus">Загрузка истории…</li></ol>
|
||||
</div>
|
||||
|
||||
<div class="activity-col" data-reveal>
|
||||
<div class="steam-panel">
|
||||
<div class="ym-head">
|
||||
<div class="ym-title">
|
||||
<h2>Во что я играю</h2>
|
||||
</div>
|
||||
<div class="ym-meta">
|
||||
<span id="steamUpdated">подключение…</span>
|
||||
<button class="ym-refresh" id="steamRefresh" type="button" aria-label="Обновить">↻</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="st-sub"><h3>Недавно Играл</h3></div>
|
||||
<ol class="ym-list" id="steamRecent">
|
||||
<li class="ym-status">Загрузка…</li>
|
||||
</ol>
|
||||
|
||||
<div class="st-sub"><h3>Больше всего времени</h3></div>
|
||||
<ol class="ym-list" id="steamTop">
|
||||
<li class="ym-status">Загрузка…</li>
|
||||
</ol>
|
||||
|
||||
<p class="st-foot">
|
||||
<a class="inline-link" href="https://steamcommunity.com/profiles/76561199137306791" target="_blank" rel="noopener">профиль Steam</a>
|
||||
<span id="steamCount"></span>
|
||||
</p>
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Во что я играю</h2>
|
||||
<span class="meta"><span id="steamUpdated">подключение…</span><button class="refresh" id="steamRefresh" type="button" aria-label="Обновить">↻</button></span>
|
||||
</div>
|
||||
<div class="sub">Недавно играл</div>
|
||||
<ol class="list" id="steamRecent"><li>Загрузка…</li></ol>
|
||||
<div class="sub">Больше всего времени</div>
|
||||
<ol class="list" id="steamTop"><li>Загрузка…</li></ol>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="friend-cta" data-reveal>
|
||||
<p class="friend-note">Также рекомендую ознакомиться</p>
|
||||
<a class="friend-btn" href="https://risdeveau.lair.moe" target="_blank" rel="noopener">
|
||||
risdeveau.lair.moe
|
||||
</a>
|
||||
<div class="friend-cta">
|
||||
<p># также рекомендую ознакомиться</p>
|
||||
<a href="https://risdeveau.lair.moe" target="_blank" rel="noopener">risdeveau.lair.moe</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="footer-inner">
|
||||
<div class="footer-col footer-left">
|
||||
<span class="footer-badge"><span class="pulse"></span>Сервер существует с 2025 года</span>
|
||||
</div>
|
||||
|
||||
<div class="footer-col footer-center">
|
||||
<a href="https://git.srcoder.moe/Black_Raven/srcoder.moe" rel="noopener">
|
||||
Исходный код сайта
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="footer-col footer-right">
|
||||
<a href="mailto:w0ron.black@yandex.ru">w0ron.black@yandex.ru</a>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="container footer-inner">
|
||||
<span>// с 2025 года</span>
|
||||
<a href="https://git.srcoder.moe/Black_Raven/srcoder.moe" rel="noopener">исходный код</a>
|
||||
<a href="mailto:w0ron.black@yandex.ru">w0ron.black@yandex.ru</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/ym.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/steam.js') }}"></script>
|
||||
</body>
|
||||
|
||||
+74
-165
@@ -7,200 +7,109 @@
|
||||
<title>srcoder.moe</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🛰️</text></svg>">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="bg-glow" aria-hidden="true"></div>
|
||||
<div class="grid-overlay" aria-hidden="true"></div>
|
||||
|
||||
<header class="site-header" id="top">
|
||||
<nav class="nav">
|
||||
<a class="logo" href="#top">
|
||||
<span class="logo-mark"></></span>
|
||||
<span class="logo-text">srcoder<span class="accent">.moe</span></span>
|
||||
</a>
|
||||
|
||||
<button class="nav-toggle" id="navToggle" aria-label="Меню" aria-expanded="false">
|
||||
<span></span><span></span><span></span>
|
||||
</button>
|
||||
|
||||
<ul class="nav-links" id="navLinks">
|
||||
<li><a href="{{ url_for('author') }}" class="nav-cta">Об авторе</a></li>
|
||||
<header>
|
||||
<div class="container nav">
|
||||
<a class="logo" href="{{ url_for('index') }}">srcoder<b>.moe</b></a>
|
||||
<ul class="nav-links">
|
||||
<li><a class="nav-cta" href="{{ url_for('author') }}">/author</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="hero">
|
||||
<h1 class="hero-title" data-reveal>
|
||||
Привет, это <span class="gradient-text">srcoder.moe</span>
|
||||
</h1>
|
||||
|
||||
<main class="container">
|
||||
<section class="hero" style="border-top:none">
|
||||
<div class="idx">01 — личный сервер</div>
|
||||
<h1>Привет, это srcoder.moe</h1>
|
||||
</section>
|
||||
|
||||
<section class="section" id="services">
|
||||
<div class="section-head" data-reveal>
|
||||
<span class="section-tag">// сервисы</span>
|
||||
<h2 class="section-title">Что развёрнуто на сервере</h2>
|
||||
<p class="section-desc">Семь сервисов, закрывающих все базовые потребности — от кода до поиска.</p>
|
||||
<section id="services">
|
||||
<div class="sec-head">
|
||||
<span class="num">02</span>
|
||||
<h2>Что развёрнуто на сервере</h2>
|
||||
<span class="rule"></span>
|
||||
</div>
|
||||
|
||||
<div class="cards">
|
||||
<article class="card" data-reveal>
|
||||
<img class="card-icon" src="{{ url_for('static', filename='images/gitea.svg')}}" alt="" loading="lazy">
|
||||
<div class="card-body">
|
||||
<h3>Gitea</h3>
|
||||
<p>Лёгкая самохостируемая платформа для хранения git-репозиториев:
|
||||
Issues, Pull Requests, вики, CI и права доступа — весь процесс
|
||||
разработки живёт на моём домене, без зависимости от внешних хостингов.</p>
|
||||
</div>
|
||||
<span class="card-badge">git / разработка</span>
|
||||
<article class="card">
|
||||
<div class="card-top"><img src="{{ url_for('static', filename='images/gitea.svg') }}" alt=""><span class="num">01</span></div>
|
||||
<h3>Gitea</h3>
|
||||
<p>Лёгкая самохостируемая платформа для хранения git-репозиториев: issues, PR, вики, CI и права доступа — весь процесс разработки живёт на моём домене.</p>
|
||||
<div class="cat">git / разработка</div>
|
||||
</article>
|
||||
|
||||
<article class="card" data-reveal>
|
||||
<img class="card-icon" src="{{ url_for('static', filename='images/matrix.svg')}}" alt="" loading="lazy">
|
||||
<div class="card-body">
|
||||
<h3>Matrix</h3>
|
||||
<p>Открытый децентрализованный протокол обмена сообщениями в реальном времени —
|
||||
по сути независимый мессенджер с сквозным шифрованием, комнатами и звонками.
|
||||
Свои данные сервер хранит сам, а общение идёт напрямую между участниками.</p>
|
||||
</div>
|
||||
<span class="card-badge">мессенджер</span>
|
||||
<article class="card">
|
||||
<div class="card-top"><img src="{{ url_for('static', filename='images/matrix.svg') }}" alt=""><span class="num">02</span></div>
|
||||
<h3>Matrix</h3>
|
||||
<p>Децентрализованный протокол обмена сообщениями: сквозное шифрование, комнаты и звонки. Данные хранит сервер, общение идёт напрямую.</p>
|
||||
<div class="cat">мессенджер</div>
|
||||
</article>
|
||||
|
||||
<article class="card" data-reveal>
|
||||
<img class="card-icon" src="{{ url_for('static', filename='images/sharkey.png')}}" alt="" loading="lazy">
|
||||
<div class="card-body">
|
||||
<h3>Sharkey</h3>
|
||||
<p>Современный форк федерированной соцсети Misskey: микроблоги, реакции,
|
||||
сторис и ленты. Через протокол ActivityPub аккаунты общаются с другими
|
||||
инстансами Fediverse — то есть это моя собственная соцсеть с мостом
|
||||
в Mastodon и соседние сервера.</p>
|
||||
</div>
|
||||
<span class="card-badge">соцсеть</span>
|
||||
<article class="card">
|
||||
<div class="card-top"><img src="{{ url_for('static', filename='images/sharkey.png') }}" alt=""><span class="num">03</span></div>
|
||||
<h3>Sharkey</h3>
|
||||
<p>Форк федерированной соцсети Misskey: микроблоги, реакции, сторис и ленты с мостом в Mastodon и соседние серверы Fediverse.</p>
|
||||
<div class="cat">соцсеть</div>
|
||||
</article>
|
||||
|
||||
<article class="card" data-reveal>
|
||||
<img class="card-icon" src="{{ url_for('static', filename='images/copyparty.svg')}}" alt="" loading="lazy">
|
||||
<div class="card-body">
|
||||
<h3>Copyparty</h3>
|
||||
<p>Самописный файловый сервер с веб-интерфейсом: загрузка, шаринг,
|
||||
инстант-загрузчики, интегрированный файловый браузер и даже
|
||||
возможность стримить звук. Простое и быстрое хранилище
|
||||
для любых файлов — от бэкапов до публичных дистрибутивов.</p>
|
||||
</div>
|
||||
<span class="card-badge">файлы</span>
|
||||
<article class="card">
|
||||
<div class="card-top"><img src="{{ url_for('static', filename='images/copyparty.svg') }}" alt=""><span class="num">04</span></div>
|
||||
<h3>Copyparty</h3>
|
||||
<p>Файловый сервер с веб-интерфейсом: загрузка, шаринг, инстант-загрузчики и стриминг звука. Быстрое хранилище от бэкапов до дистрибутивов.</p>
|
||||
<div class="cat">файлы</div>
|
||||
</article>
|
||||
|
||||
<article class="card" data-reveal>
|
||||
<img class="card-icon" src="{{ url_for('static', filename='images/grafana.svg')}}" alt="" loading="lazy">
|
||||
<div class="card-body">
|
||||
<h3>Grafana</h3>
|
||||
<p>Система мониторинга и визуализации: собирает метрики сервера —
|
||||
нагрузку CPU, память, диск, трафик и здоровье сервисов —
|
||||
и рисует их живыми дашбордами. Благодаря ей я вижу, что происходит
|
||||
с машиной, ещё до того, как что-то сломается.</p>
|
||||
</div>
|
||||
<span class="card-badge">мониторинг</span>
|
||||
<article class="card">
|
||||
<div class="card-top"><img src="{{ url_for('static', filename='images/grafana.svg') }}" alt=""><span class="num">05</span></div>
|
||||
<h3>Grafana</h3>
|
||||
<p>Мониторинг и визуализация: собирает нагрузку CPU, память, диск, трафик и здоровье сервисов, рисует их живыми дашбордами.</p>
|
||||
<div class="cat">мониторинг</div>
|
||||
</article>
|
||||
|
||||
<article class="card" data-reveal>
|
||||
<img class="card-icon" src="{{ url_for('static', filename='images/freshrss.svg')}}" alt="" loading="lazy">
|
||||
<div class="card-body">
|
||||
<h3>FreshRSS</h3>
|
||||
<p>Самохостируемый RSS-агрегатор: собирает ленты блогов, новостных
|
||||
сайтов и подкастов в одну спокойную читалку — без рекламы,
|
||||
алгоритмов и слежки. Подписки и история чтения хранятся
|
||||
на моём сервере и синхронизируются с любого устройства.</p>
|
||||
</div>
|
||||
<span class="card-badge">RSS / подписки</span>
|
||||
<article class="card">
|
||||
<div class="card-top"><img src="{{ url_for('static', filename='images/freshrss.svg') }}" alt=""><span class="num">06</span></div>
|
||||
<h3>FreshRSS</h3>
|
||||
<p>RSS-агрегатор без рекламы и алгоритмов: блоги, новости и подкасты в одной спокойной читалке, синхронизируемой с любого устройства.</p>
|
||||
<div class="cat">RSS / подписки</div>
|
||||
</article>
|
||||
|
||||
<article class="card card-featured" data-reveal>
|
||||
<img class="card-icon" src="{{ url_for('static', filename='images/4get.png')}}" alt="" loading="lazy">
|
||||
<div class="card-body">
|
||||
<h3>4get</h3>
|
||||
<p>Самописная мета-поисковая система: параллельно опрашивает несколько
|
||||
поисковиков и выдаёт объединённую выдачу без рекламы и слежки.
|
||||
Расположена по адресу <a class="inline-link" href="https://search.srcoder.moe" rel="noopener">search.srcoder.moe</a>.</p>
|
||||
</div>
|
||||
<span class="card-badge">поиск</span>
|
||||
<article class="card">
|
||||
<div class="card-top"><img src="{{ url_for('static', filename='images/4get.png') }}" alt=""><span class="num">07</span></div>
|
||||
<h3>4get</h3>
|
||||
<p>Мета-поисковая система: параллельно опрашивает несколько поисковиков и выдаёт объединённую выдачу без рекламы и слежки. <a href="https://search.srcoder.moe" rel="noopener">search.srcoder.moe</a></p>
|
||||
<div class="cat">поиск</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section" id="about">
|
||||
<div class="about" data-reveal>
|
||||
<div class="about-text">
|
||||
<span class="section-tag">// о сервере</span>
|
||||
|
||||
<p>
|
||||
Хостер моего сервера — <a class="inline-link" href="https://play2go.cloud" rel="noopener">Play2Go</a>:
|
||||
он предоставляет хорошие тарифы на виртуальные серверы по доступной цене
|
||||
и современное оборудование. Из минусов — иногда случаются проблемы с сетью,
|
||||
но в целом для моих задач подходит хорошо.
|
||||
</p>
|
||||
|
||||
<h3 class="about-sub">Характеристики сервера</h3>
|
||||
<dl class="spec-list">
|
||||
<div class="spec-row">
|
||||
<dt>Процессор</dt>
|
||||
<dd>2 vCPU AMD Ryzen 9 5950X</dd>
|
||||
</div>
|
||||
<div class="spec-row">
|
||||
<dt>Оперативная память</dt>
|
||||
<dd>4 GB DDR4</dd>
|
||||
</div>
|
||||
<div class="spec-row">
|
||||
<dt>Хранилище</dt>
|
||||
<dd>120 GB NVMe SSD</dd>
|
||||
</div>
|
||||
<div class="spec-row">
|
||||
<dt>Скорость сети</dt>
|
||||
<dd>До 1 Gbit/s</dd>
|
||||
</div>
|
||||
<div class="spec-row">
|
||||
<dt>Защита от DDoS</dt>
|
||||
<dd>Мощная защита от атак L3–L4</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<a href="{{ url_for('author') }}" class="btn btn-primary about-btn">Страница об авторе →</a>
|
||||
<section id="about">
|
||||
<div class="sec-head">
|
||||
<span class="num">03</span>
|
||||
<h2>О сервере</h2>
|
||||
<span class="rule"></span>
|
||||
</div>
|
||||
<div class="about-grid">
|
||||
<div>
|
||||
<p>Хостер моего сервера — <a href="https://play2go.cloud" rel="noopener">Play2Go</a>: хорошие тарифы на виртуальные серверы по доступной цене и современное оборудование. Из минусов — иногда случаются проблемы с сетью, но в целом для моих задач подходит хорошо.</p>
|
||||
</div>
|
||||
<div class="about-terminal" aria-hidden="true">
|
||||
<div class="terminal-bar">
|
||||
<span class="dot red"></span><span class="dot yellow"></span><span class="dot green"></span>
|
||||
<span class="terminal-title">srcoder@srv</span>
|
||||
</div>
|
||||
<pre class="terminal-body"><code id="typed"></code></pre>
|
||||
<div>
|
||||
<ul class="specs">
|
||||
<li><span>cpu</span><span>2 vCPU AMD Ryzen 9 5950X</span></li>
|
||||
<li><span>ram</span><span>4 GB DDR4</span></li>
|
||||
<li><span>disk</span><span>120 GB NVMe SSD</span></li>
|
||||
<li><span>net</span><span>До 1 Gbit/s</span></li>
|
||||
<li><span>ddos</span><span>Защита L3–L4</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="footer-inner">
|
||||
<div class="footer-col footer-left">
|
||||
<span class="footer-badge"><span class="pulse"></span>Сервер существует с 2025 года</span>
|
||||
</div>
|
||||
|
||||
<div class="footer-col footer-center">
|
||||
<a href="https://git.srcoder.moe/Black_Raven/srcoder.moe" rel="noopener">
|
||||
Исходный код сайта
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="footer-col footer-right">
|
||||
<a href="mailto:w0ron.black@yandex.ru">w0ron.black@yandex.ru</a>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="container footer-inner">
|
||||
<span>// с 2025 года</span>
|
||||
<a href="https://git.srcoder.moe/Black_Raven/srcoder.moe" rel="noopener">исходный код</a>
|
||||
<a href="mailto:w0ron.black@yandex.ru">w0ron.black@yandex.ru</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user