[{"data":1,"prerenderedAt":668},["ShallowReactive",2],{"api:\u002Fapi\u002Fconfig:ru":3,"api:\u002Fapi\u002Fabout:ru":8,"api:\u002Fapi\u002Fstack:ru":50,"api:\u002Fapi\u002Fprojects:ru":235},{"email":4,"lang":5,"name":6,"role":7},"hello@ewsirdev.example","ru","Ewsirdev","Веб-разработчик: сайты, лендинги и сервисы на Go",{"honest":9,"how":15,"intro":31,"lang":5,"principles":34},[10,11,12,13,14],"Почти все проекты учебно-демонстрационные; один сайт — реализованный клиентский, имя клиента не называется. Отзывы и цифры по заказам здесь не заявляются.","Race-детектор (go test -race) на моей рабочей машине не запускается из-за отсутствия C-компилятора; он выполняется в CI.","Свой слой WebSocket не проходил Autobahn Test Suite: корректность проверена собственными тестами. В продакшене разумнее брать проверенную библиотеку.","Демо-данные в проектах (история мониторинга, ссылки, задачи) сгенерированы и помечены в интерфейсах как демо.","Контакты: только почта. Телефон и соцсети намеренно не указаны.",[16,19,22,25,28],{"title":17,"text":18},"Разговор","Выясняем задачу, аудиторию и ограничения. Если сайт не нужен, скажу об этом сразу.",{"title":20,"text":21},"Договорённости","Состав работ, сроки и что считается готовым. Письменно, без сюрпризов.",{"title":23,"text":24},"Сборка","Короткие итерации: промежуточный результат можно открыть и потрогать.",{"title":26,"text":27},"Проверка","Тесты, мобильные экраны, доступность, нагрузка на форму и API.",{"title":29,"text":30},"Запуск","Docker-образ или статика, инструкция по запуску, передача кода.",[32,33],"Я веб-разработчик: делаю лендинги и небольшие сервисы на Go. Почти все проекты на этом сайте учебно-демонстрационные: бренды вымышленные, данные демо, код приватный и открывается по запросу. Исключение — один реализованный клиентский сайт (клиента не называю).","Здесь нет отзывов и цифр о заказах: показываю только то, что можно проверить, то есть работающий код, тесты и README с ограничениями.",[35,38,41,44,47],{"title":36,"text":37},"Просто и проверяемо","Стандартная библиотека Go, понятная структура пакетов, тесты на то, что действительно важно: конкурентный захват задач, двойная бронь, протокол WebSocket.",{"title":39,"text":40},"Честные ограничения","В каждом README есть раздел «Ограничения»: один токен вместо ролей, один процесс, at-least-once вместо exactly-once. Лучше сказать заранее, чем объяснять потом.",{"title":42,"text":43},"Доступность по умолчанию","Контраст по WCAG AA, видимый фокус, работа с клавиатуры, уважение к prefers-reduced-motion, mobile-first вёрстка.",{"title":45,"text":46},"Без секретов в коде","Настройки идут через переменные окружения, токены генерируются при старте, если не заданы.",{"title":48,"text":49},"Один бинарник","Фронтенд встроен в Go-сервис через embed, запуск не требует установки зависимостей; для SQLite используется драйвер без CGO.",{"groups":51,"lang":5,"not":100,"stats":103},[52,67,76,85],{"name":53,"items":54},"Язык и сервер",[55,58,61,64],{"name":56,"note":57},"Go","стандартная библиотека: net\u002Fhttp, embed, log\u002Fslog, context",{"name":59,"note":60},"REST \u002F JSON","API под Bearer-токеном или сессией, ошибки в формате {\"error\": ...}",{"name":62,"note":63},"SSE","живые дашборды в мониторе и очереди задач",{"name":65,"note":66},"WebSocket","собственная реализация RFC 6455 в realtime-rooms",{"name":68,"items":69},"Данные",[70,73],{"name":71,"note":72},"SQLite (modernc.org\u002Fsqlite)","чистый Go, без CGO, режим WAL",{"name":74,"note":75},"SQL (no ORM)","атомарные UPDATE … RETURNING, частичные UNIQUE-индексы, транзакции",{"name":77,"items":78},"Фронтенд",[79,82],{"name":80,"note":81},"HTML, CSS, vanilla JS","без фреймворков в учебных проектах, встроены в бинарник",{"name":83,"note":84},"Canvas","общая доска в realtime-rooms",{"name":86,"items":87},"Качество и поставка",[88,91,94,97],{"name":89,"note":90},"go vet, gofmt, go test","проверяются в CI; go test -race запускается в CI",{"name":92,"note":93},"GitHub Actions","CI в репозиториях проектов",{"name":95,"note":96},"Docker","multi-stage образы проектов",{"name":98,"note":99},"Prometheus","метрики очереди задач на \u002Fmetrics в текстовом формате",[101,102],"Нет опыта, который здесь не показан: фронтенд-фреймворки, ORM, Kubernetes и облачные платформы на этом сайте не заявляются.","Нет распределённых систем: все сервисы рассчитаны на один процесс и один файл SQLite.",{"generated_at":104,"tests_method":105,"note":106,"totals":107,"projects":117},"2026-10-22","go test -list","Lines are non-blank lines. Endpoints are HandleFunc\u002FHandle route patterns (without \u002Fdemo\u002F*). Tests are top-level Test* functions.",{"projects":108,"go_projects":109,"go_lines":110,"go_test_lines":111,"tests":112,"endpoints":113,"html":114,"css":115,"js":116},17,13,26503,12027,418,230,1973,3525,4815,[118,128,137,146,155,162,171,179,185,189,193,197,201,207,215,222,228],{"slug":119,"kind":120,"go_lines":121,"go_test_lines":122,"tests":123,"endpoints":124,"html":125,"css":126,"js":127},"api-gateway","go",2334,1179,48,11,115,139,163,{"slug":129,"kind":120,"go_lines":130,"go_test_lines":131,"tests":132,"endpoints":133,"html":134,"css":135,"js":136},"auth-service",2826,1393,50,35,23,166,500,{"slug":138,"kind":120,"go_lines":139,"go_test_lines":140,"tests":141,"endpoints":142,"html":143,"css":144,"js":145},"booking-slots",891,338,12,18,146,123,309,{"slug":147,"kind":120,"go_lines":148,"go_test_lines":149,"tests":150,"endpoints":151,"html":152,"css":153,"js":154},"finance-tracker",3741,1371,30,45,24,1148,72,{"slug":156,"kind":120,"go_lines":157,"go_test_lines":158,"tests":159,"endpoints":160,"html":161,"css":161,"js":161},"gitpulse",2133,735,32,3,0,{"slug":163,"kind":120,"go_lines":164,"go_test_lines":165,"tests":166,"endpoints":167,"html":168,"css":169,"js":170},"job-queue",2161,833,25,19,138,188,331,{"slug":172,"kind":120,"go_lines":173,"go_test_lines":174,"tests":175,"endpoints":142,"html":176,"css":177,"js":178},"kanban-tasks",871,272,7,102,156,328,{"slug":180,"kind":181,"go_lines":161,"go_test_lines":161,"tests":161,"endpoints":161,"html":182,"css":183,"js":184},"landing-3d","static",201,162,377,{"slug":186,"kind":181,"go_lines":161,"go_test_lines":161,"tests":161,"endpoints":161,"html":143,"css":187,"js":188},"landing-event",122,59,{"slug":190,"kind":181,"go_lines":161,"go_test_lines":161,"tests":161,"endpoints":161,"html":191,"css":192,"js":188},"landing-saas",172,136,{"slug":194,"kind":181,"go_lines":161,"go_test_lines":161,"tests":161,"endpoints":161,"html":195,"css":196,"js":188},"landing-studio",187,109,{"slug":198,"kind":120,"go_lines":139,"go_test_lines":178,"tests":141,"endpoints":124,"html":199,"css":183,"js":200},"link-shortener",119,317,{"slug":202,"kind":120,"go_lines":203,"go_test_lines":173,"tests":159,"endpoints":167,"html":204,"css":205,"js":206},"notes-search",2027,174,192,517,{"slug":208,"kind":120,"go_lines":209,"go_test_lines":210,"tests":211,"endpoints":212,"html":213,"css":205,"js":214},"pixel-wall",2715,1910,77,16,106,625,{"slug":216,"kind":120,"go_lines":217,"go_test_lines":218,"tests":219,"endpoints":109,"html":220,"css":204,"js":221},"raft-kv",2646,895,27,91,213,{"slug":223,"kind":120,"go_lines":224,"go_test_lines":225,"tests":123,"endpoints":175,"html":226,"css":227,"js":206},"realtime-rooms",1822,1313,103,184,{"slug":229,"kind":120,"go_lines":230,"go_test_lines":231,"tests":142,"endpoints":232,"html":233,"css":191,"js":234},"uptime-monitor",1445,589,15,126,369,[236,258,274,289,309,325,340,356,372,388,411,432,455,481,505,529,548,570,594,619,634,650],{"slug":172,"title":237,"type":238,"stack":239,"description":241,"highlights":242,"run":247,"hue":248,"og":249,"shots":250},"Янтарь: канбан-доска","backend",[56,240,95],"SQLite","Многопользовательская канбан-доска: регистрация, доски, колонки, карточки с перетаскиванием. Go + SQLite без CGO, фронт встроен в бинарник.",[243,244,245,246],"PBKDF2 и сессии в HttpOnly-cookie","Изоляция данных пользователей","Тесты API","Docker multi-stage","cd kanban-tasks && go run .",344,"\u002Fog\u002Fkanban-tasks.jpg",{"desktop":251,"desktop_sm":252,"mobile":253,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fkanban-tasks-desktop.webp","\u002Fshots\u002Fkanban-tasks-desktop-sm.webp","\u002Fshots\u002Fkanban-tasks-mobile.webp",1280,800,390,844,{"slug":198,"title":259,"type":238,"stack":260,"description":261,"highlights":262,"run":267,"hue":268,"og":269,"shots":270},"Короче: сократитель ссылок",[56,240,95],"Сокращение ссылок с алиасами и сроком жизни, статистика кликов по дням, браузерам и устройствам, дашборд под токеном. IP не хранятся.",[263,264,265,266],"Редирект и учёт кликов","Лимит запросов","Приватная статистика (хэш с солью)","Тесты и Docker","cd link-shortener && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fserver",111,"\u002Fog\u002Flink-shortener.jpg",{"desktop":271,"desktop_sm":272,"mobile":273,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Flink-shortener-desktop.webp","\u002Fshots\u002Flink-shortener-desktop-sm.webp","\u002Fshots\u002Flink-shortener-mobile.webp",{"slug":138,"title":275,"type":238,"stack":276,"description":277,"highlights":278,"run":282,"hue":283,"og":284,"shots":285},"Запись на приём",[56,240,95],"Онлайн-запись на услуги: свободные слоты с учётом длительности и рабочих часов, защита от двойной записи, админ-панель. Вымышленная студия, демо.",[279,280,281,266],"Расчёт свободных слотов","Без двойных записей","Админка для услуг и расписания","cd booking-slots && go run .",65,"\u002Fog\u002Fbooking-slots.jpg",{"desktop":286,"desktop_sm":287,"mobile":288,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fbooking-slots-desktop.webp","\u002Fshots\u002Fbooking-slots-desktop-sm.webp","\u002Fshots\u002Fbooking-slots-mobile.webp",{"slug":194,"title":290,"type":291,"stack":292,"description":296,"highlights":297,"run":302,"hue":303,"og":304,"shots":305},"Полутон: студия света","landing",[293,294,295],"HTML","CSS","JS","Лендинг вымышленной студии интерьерного света: тёплая палитра, засечный шрифт, SVG-иллюстрация, FAQ и демо-форма. Адаптивный, без внешних картинок.",[298,299,300,301],"Адаптив 390-1280","SVG вместо картинок","Доступные FAQ и форма","Честная демо-форма","cd landing-studio && python -m http.server 8091",212,"\u002Fog\u002Flanding-studio.jpg",{"desktop":306,"desktop_sm":307,"mobile":308,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Flanding-studio-desktop.webp","\u002Fshots\u002Flanding-studio-desktop-sm.webp","\u002Fshots\u002Flanding-studio-mobile.webp",{"slug":190,"title":310,"type":291,"stack":311,"description":312,"highlights":313,"run":318,"hue":319,"og":320,"shots":321},"Рубильник: трекер подписок",[293,294,295],"Лендинг вымышленного SaaS-продукта: тёмный градиентный hero, макет интерфейса на CSS, тарифы, FAQ, демо-форма. Все цены и названия придуманы.",[314,315,316,317],"Макет интерфейса на CSS","Тарифная сетка","Адаптив","Демо-форма без отправки данных","cd landing-saas && python -m http.server 8092",182,"\u002Fog\u002Flanding-saas.jpg",{"desktop":322,"desktop_sm":323,"mobile":324,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Flanding-saas-desktop.webp","\u002Fshots\u002Flanding-saas-desktop-sm.webp","\u002Fshots\u002Flanding-saas-mobile.webp",{"slug":186,"title":326,"type":291,"stack":327,"description":328,"highlights":329,"run":333,"hue":334,"og":335,"shots":336},"Малый масштаб: конференция",[293,294,295],"Лендинг вымышленной однодневной конференции: плакатная типографика, расписание, спикеры-заглушки, условия, демо-форма записи.",[330,331,316,332],"Расписание дня","Плакатная типографика","Без выдуманных имён спикеров","cd landing-event && python -m http.server 8093",124,"\u002Fog\u002Flanding-event.jpg",{"desktop":337,"desktop_sm":338,"mobile":339,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Flanding-event-desktop.webp","\u002Fshots\u002Flanding-event-desktop-sm.webp","\u002Fshots\u002Flanding-event-mobile.webp",{"slug":229,"title":341,"type":238,"stack":342,"description":344,"highlights":345,"run":349,"hue":350,"og":351,"shots":352},"Пульс: монитор доступности",[56,240,62,343,95],"REST API","Монитор доступности: HTTP и TCP проверки по расписанию, аптайм, p50\u002Fp95, детектор инцидентов, живой дашборд на SSE и публичная статус-страница. Go + SQLite без CGO.",[346,347,348,266],"Горутина-воркер на каждую проверку","Инциденты после серии падений","Дашборд на SSE и публичный \u002Fstatus","cd uptime-monitor && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fserver",101,"\u002Fog\u002Fuptime-monitor.jpg",{"desktop":353,"desktop_sm":354,"mobile":355,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fuptime-monitor-desktop.webp","\u002Fshots\u002Fuptime-monitor-desktop-sm.webp","\u002Fshots\u002Fuptime-monitor-mobile.webp",{"slug":163,"title":357,"type":238,"stack":358,"description":359,"highlights":360,"run":365,"hue":366,"og":367,"shots":368},"Конвейер: очередь задач",[56,240,343,62,98,95],"Очередь фоновых задач: приоритеты, отложенные и cron-задачи, ретраи с backoff и jitter, dead-letter, идемпотентные ключи, lease с fencing, метрики Prometheus.",[361,362,363,364],"At-least-once с lease и fencing","Ретраи с backoff и jitter + dead-letter","Собственный cron-парсер","Метрики Prometheus и graceful shutdown","cd job-queue && set API_TOKEN=demo&& set SEED_DEMO=1&& go run .\u002Fcmd\u002Fserver",249,"\u002Fog\u002Fjob-queue.jpg",{"desktop":369,"desktop_sm":370,"mobile":371,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fjob-queue-desktop.webp","\u002Fshots\u002Fjob-queue-desktop-sm.webp","\u002Fshots\u002Fjob-queue-mobile.webp",{"slug":223,"title":373,"type":238,"stack":374,"description":375,"highlights":376,"run":381,"hue":382,"og":383,"shots":384},"Комнаты: чат и доска в реальном времени",[56,65,240,83,95],"Комнаты в реальном времени: чат с историей в SQLite, список «кто онлайн», индикатор «печатает», общая доска со штрихами и курсорами, анонимные ники. WebSocket (RFC 6455) написан на stdlib.",[377,378,379,380],"Свой слой WebSocket (RFC 6455) на stdlib","Отключение медленных клиентов и лимиты частоты","Graceful shutdown с кодом 1001","Нагрузочный тест: 360 соединений в 6 комнатах","cd realtime-rooms && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fserver",46,"\u002Fog\u002Frealtime-rooms.jpg",{"desktop":385,"desktop_sm":386,"mobile":387,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Frealtime-rooms-desktop.webp","\u002Fshots\u002Frealtime-rooms-desktop-sm.webp","\u002Fshots\u002Frealtime-rooms-mobile.webp",{"slug":119,"title":389,"type":238,"stack":390,"description":398,"highlights":399,"run":404,"hue":405,"og":406,"shots":407},"Шлюз: API-gateway",[56,391,392,393,394,62,98,395,396,397,95,92],"stdlib only","Reverse proxy","Circuit breaker","Token bucket","slog","Vanilla JS","embed","API-шлюз на чистой stdlib: маршруты по хосту\u002Fпрефиксу из JSON-конфига с горячей перезагрузкой без обрыва соединений, балансировка round-robin и least-conn, health-checks, circuit breaker на узел, ретраи идемпотентных запросов, rate limit, хэшированные API-ключи, метрики Prometheus, живой дашборд на SSE.",[400,401,402,403],"Ноль зависимостей: только stdlib","Горячая перезагрузка конфига без обрыва соединений","Circuit breaker на узел и ретраи идемпотентных запросов","Нагрузочный тест: 4000 запросов, 40 клиентов (демо-замер)","cd api-gateway && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fgateway",200,"\u002Fog\u002Fapi-gateway.jpg",{"desktop":408,"desktop_sm":409,"mobile":410,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fapi-gateway-desktop.webp","\u002Fshots\u002Fapi-gateway-desktop-sm.webp","\u002Fshots\u002Fapi-gateway-mobile.webp",{"slug":202,"title":412,"type":238,"stack":413,"description":419,"highlights":420,"run":425,"hue":426,"og":427,"shots":428},"Картотека: заметки с поиском",[56,240,414,415,416,343,417,418,396,397,95,92],"FTS5","bm25","modernc.org\u002Fsqlite (без CGO)","Markdown (свой безопасный рендер)","SVG force-layout","База Markdown-заметок на Go и SQLite: вики-ссылки [[заметка]] с обратными ссылками и графом связей, полнотекстовый поиск FTS5 (bm25, подсветка, упрощённая русская морфология), история версий с собственным diff, автосохранение с разрешением конфликтов, экспорт и импорт .md.",[421,422,423,424],"Поиск FTS5 с bm25 и подсветкой","Вики-ссылки, backlinks и граф связей","История версий с собственным diff","Свой безопасный Markdown-рендер с тестом на XSS","cd notes-search && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fserver",160,"\u002Fog\u002Fnotes-search.jpg",{"desktop":429,"desktop_sm":430,"mobile":431,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fnotes-search-desktop.webp","\u002Fshots\u002Fnotes-search-desktop-sm.webp","\u002Fshots\u002Fnotes-search-mobile.webp",{"slug":180,"title":433,"type":291,"stack":434,"description":441,"highlights":442,"run":448,"hue":449,"og":450,"shots":451},"ОКСО Т1: 3D-лендинг",[435,436,396,437,438,439,440,92],"HTML5","CSS3","WebGL2","GLSL ES 3.00","Raymarching\u002FSDF","Scroll choreography","Лендинг вымышленного датчика воздуха (демо): объект рисуется одним фрагментным шейдером на чистом WebGL2 без библиотек, четыре SDF-формы перетекают по скроллу, есть SVG-фолбэк, reduced-motion и пауза вне экрана.",[443,444,445,446,447],"Один фрагментный шейдер","WebGL2 без библиотек","Четыре SDF-формы с морфингом по скроллу","SVG-фолбэк и prefers-reduced-motion","Пауза отрисовки вне экрана","cd landing-3d && python -m http.server 8094",90,"\u002Fog\u002Flanding-3d.jpg",{"desktop":452,"desktop_sm":453,"mobile":454,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Flanding-3d-desktop.webp","\u002Fshots\u002Flanding-3d-desktop-sm.webp","\u002Fshots\u002Flanding-3d-mobile.webp",{"slug":216,"title":456,"type":238,"stack":457,"description":466,"highlights":467,"run":474,"hue":475,"og":476,"shots":477},"Плот: KV на Raft",[56,458,459,460,391,461,62,462,463,464,465,396,95,92],"Raft","Distributed systems","Consensus","HTTP\u002FJSON","WAL + fsync","Snapshots","Linearizability","Deterministic simulation testing","Учебная реализация Raft с нуля на чистой stdlib: выборы лидера, репликация лога, коммит по кворуму, лог и term\u002Fvote на диске с fsync, снапшоты с компакцией, линеаризуемые чтения (read-index), редирект фолловера на лидера. Тестируется детерминированным симулятором сети и времени с проверкой линеаризуемости; дашборд умеет убивать лидера и рвать сеть (демо).",[468,469,470,471,472,473],"Raft с нуля на stdlib","без raft-библиотек","Детерминированный симулятор сети и времени + проверка линеаризуемости","Лог и term\u002Fvote с fsync","снапшоты и компакция","Дашборд: убить лидера и разорвать сеть (демо)","cd raft-kv && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fraft-kv",290,"\u002Fog\u002Fraft-kv.jpg",{"desktop":478,"desktop_sm":479,"mobile":480,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fraft-kv-desktop.webp","\u002Fshots\u002Fraft-kv-desktop-sm.webp","\u002Fshots\u002Fraft-kv-mobile.webp",{"slug":129,"title":482,"type":238,"stack":483,"description":492,"highlights":493,"run":498,"hue":499,"og":500,"shots":501},"Ключница: сервис аутентификации",[56,484,240,416,485,486,487,488,489,490,491,396,397,95,92],"stdlib crypto","JWT EdDSA\u002FEd25519","JWKS","PBKDF2","TOTP","RBAC","REST","OpenAPI 3","Сервис аутентификации и авторизации на Go: EdDSA-JWT с ротацией ключей и JWKS, refresh-токены с ротацией и обнаружением кражи, TOTP-2FA (RFC 6238) с резервными кодами, RBAC, блокировка при переборе, сброс пароля по одноразовому токену, журнал аудита, список устройств, openapi.yaml. Не прошёл независимый аудит, не production-IdP.",[494,495,496,497],"JWT EdDSA и ротация ключей с JWKS","Refresh-токены с ротацией и reuse-detection","TOTP по RFC 6238 с проверкой на векторах RFC","Журнал аудита только на дозапись (триггеры БД)","cd auth-service && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fserver",20,"\u002Fog\u002Fauth-service.jpg",{"desktop":502,"desktop_sm":503,"mobile":504,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fauth-service-desktop.webp","\u002Fshots\u002Fauth-service-desktop-sm.webp","\u002Fshots\u002Fauth-service-mobile.webp",{"slug":147,"title":506,"type":238,"stack":507,"description":516,"highlights":517,"run":522,"hue":523,"og":524,"shots":525},"Книга учёта: личные финансы",[56,240,416,343,508,509,510,511,512,513,514,95,92,487,515],"Nuxt 4","Vue 3","TypeScript","Pinia","SVG-графики","vitest","ESLint","CSV import\u002Fexport","Учёт личных финансов: счета в разных валютах с ручными курсами, доходы, расходы и переводы, бюджеты, платежи по cron-подобному расписанию, импорт CSV с превью, отчёты. Деньги только в целых минорных единицах, данные пользователей изолированы, графики нарисованы на SVG без библиотек. Go + SQLite и фронтенд на Nuxt 4.",[518,519,520,521],"Деньги только целыми минорными единицами","Мультивалютность с ручными курсами","Импорт CSV с превью и пробным прогоном","Свои SVG-графики без библиотек","cd finance-tracker && set SEED_DEMO=1&& go run .",140,"\u002Fog\u002Ffinance-tracker.jpg",{"desktop":526,"desktop_sm":527,"mobile":528,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Ffinance-tracker-desktop.webp","\u002Fshots\u002Ffinance-tracker-desktop-sm.webp","\u002Fshots\u002Ffinance-tracker-mobile.webp",{"slug":208,"title":530,"type":238,"stack":531,"description":535,"highlights":536,"run":541,"hue":542,"og":543,"shots":544},"Пиксельная стена: общий холст",[56,391,65,532,396,397,533,534,95,92],"Canvas 2D","Binary protocol","Snapshot + journal","Совместный холст 256×256 в духе r\u002Fplace: 16 цветов, кулдаун на сессию и IP, свой WebSocket-слой на stdlib (RFC 6455) с бинарным протоколом (4 бита на пиксель), снимок и дельты, снапшот плюс журнал с восстановлением, таймлапс, зум и пан, мини-карта, экспорт PNG, админ-инструменты. Пет-проект.",[537,538,539,540],"Свой WebSocket (RFC 6455) на stdlib","Бинарный протокол: 4 бита на пиксель","Снапшот + журнал с восстановлением после падения","Таймлапс по журналу изменений","cd pixel-wall && set SEED_DEMO=1&& set ADMIN_TOKEN=secret&& go run .\u002Fcmd\u002Fserver",320,"\u002Fog\u002Fpixel-wall.jpg",{"desktop":545,"desktop_sm":546,"mobile":547,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fpixel-wall-desktop.webp","\u002Fshots\u002Fpixel-wall-desktop-sm.webp","\u002Fshots\u002Fpixel-wall-mobile.webp",{"slug":156,"title":549,"type":550,"stack":551,"description":558,"highlights":559,"run":564,"hue":175,"og":565,"shots":566},"GitPulse: аналитика git-репозиториев","cli",[56,391,552,553,554,555,556,557,92,95],"CLI","git","html\u002Ftemplate","SVG","ANSI","os\u002Fexec","CLI на Go для анализа git-репозиториев без внешних зависимостей (нужен только git): stats с цветами и спарклайнами в терминале, report — самодостаточный HTML-отчёт с тепловой картой активности, churn и горячими файлами, вкладом авторов, bus factor по каталогам, ростом кода по языкам и периодами тишины (светлая и тёмная темы), diff-hotspots между ревизиями и локальный serve на 127.0.0.1. История читается потоком через git log -z --numstat -M. Пет-проект.",[560,561,562,563],"Только stdlib Go, внешний бинарник один: git","Поток git log -z --numstat -M с учётом .mailmap и переименований","Самодостаточный HTML-отчёт: тепловая карта, churn, bus factor, светлая и тёмная темы","Синтетическая история из 100 000 коммитов анализируется около 5 секунд","cd gitpulse && go run .\u002Fcmd\u002Fgitpulse stats .","\u002Fog\u002Fgitpulse.jpg",{"desktop":567,"desktop_sm":568,"mobile":569,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fgitpulse-desktop.webp","\u002Fshots\u002Fgitpulse-desktop-sm.webp","\u002Fshots\u002Fgitpulse-mobile.webp",{"slug":571,"title":572,"type":573,"stack":574,"description":579,"highlights":580,"run":587,"hue":588,"og":589,"shots":590},"legal-multipage-site","Многостраничный сайт юридической компании","site",[508,509,510,575,576,577,578,95,92],"Bootstrap 5","Zod","Vitest","Playwright","Реализованный сайт юридической и экспертной компании на Nuxt 4: 65 страниц с серверным рендерингом, JSON-LD разметка и 301-редиректы со старого сайта. Заявки уходят через серверный эндпоинт с проверкой Origin, лимитом размера тела и ограничением частоты запросов.",[581,582,583,584,585,586],"65 маршрутов с SSR, sitemap.xml и robots.txt; индексация включается явным флагом окружения","Форма заявок: серверный эндпоинт, проверка Origin, лимит тела 10 КБ, 3 запроса за 10 минут с одного IP, honeypot, опциональная капча","JSON-LD: LegalService, WebSite, BreadcrumbList","Одношаговые 301-редиректы со старых URL и со слешей на конце, заголовки безопасности (CSP, HSTS)","CI\u002FCD: lint, typecheck, Vitest, сборка, Playwright; затем Docker-образ и выкладка на сервер","210 модульных тестов в 13 файлах и 4 набора e2e-тестов","npm install && npm run build && node .output\u002Fserver\u002Findex.mjs",250,"\u002Fog\u002Flegal-multipage-site.jpg",{"desktop":591,"desktop_sm":592,"mobile":593,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Flegal-multipage-site-desktop.webp","\u002Fshots\u002Flegal-multipage-site-desktop-sm.webp","\u002Fshots\u002Flegal-multipage-site-mobile.webp",{"slug":595,"title":596,"type":597,"stack":598,"description":606,"highlights":607,"run":612,"hue":613,"og":614,"shots":615},"bad-club-booking-app","Bad Club: бронирование мест в игровом коворкинге","app",[509,599,511,600,601,602,603,604,605],"Vite","Vue Router","GSAP","Express","node:sqlite","JWT (httpOnly cookie)","Resend","Демо-приложение на Vue 3 и Express с SQLite: интерактивная карта зала с бронированием мест, личный кабинет и админ-панель с редактором расстановки. Вход без паролей, по одноразовому коду на e-mail.",[608,609,610,611],"Карта зала с зонами (обычные, тихая, VIP, два монитора), фильтром по типу и статусом мест","Админ-панель: обзор, брони, столы, компьютеры, пользователи и перетаскиваемая расстановка зала","Вход по OTP с ограничением попыток и частоты запросов, роль admin задаётся через переменную окружения","Данные для демо создаются сид-скриптом при первом запуске","npm install && npm run dev",150,"\u002Fog\u002Fbad-club-booking-app.jpg",{"desktop":616,"desktop_sm":617,"mobile":618,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fbad-club-booking-app-desktop.webp","\u002Fshots\u002Fbad-club-booking-app-desktop-sm.webp","\u002Fshots\u002Fbad-club-booking-app-mobile.webp",{"slug":620,"title":621,"type":291,"stack":622,"description":623,"highlights":624,"run":612,"hue":628,"og":629,"shots":630},"dovod-concept","Довод: концепт лендинга экспертной организации",[508,509,510,294,92],"Концепт лендинга на Nuxt 4 для вымышленной экспертной организации: направления услуг, форма консультации с маской телефона, карусель отзывов и страница политики. Компания, отзывы, цифры и контакты придуманы; форма ничего не отправляет.",[625,626,627],"Форма с маской телефона, валидацией и подсказками по ошибкам; в демо показывает «данные не отправляются»","Бесконечная карусель отзывов со свайпом и автопрокруткой","Мобильная панель действий, адаптивная вёрстка, скелетон поверх серверного HTML",215,"\u002Fog\u002Fdovod-concept.jpg",{"desktop":631,"desktop_sm":632,"mobile":633,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fdovod-concept-desktop.webp","\u002Fshots\u002Fdovod-concept-desktop-sm.webp","\u002Fshots\u002Fdovod-concept-mobile.webp",{"slug":635,"title":636,"type":573,"stack":637,"description":640,"highlights":641,"run":612,"hue":159,"og":645,"shots":646},"niche-landings","Каталог демо-лендингов под 10 ниш",[509,638,639,92],"Vue Router 4","Vite 8","Vue-каталог из десяти самостоятельных лендингов под разные ниши малого бизнеса: у каждого своя палитра, шрифты и структура секций. Каждый лендинг загружается отдельным чанком; весь контент демонстрационный.",[642,643,644],"10 ниш: медицина, мебель, ремонт, онлайн-школа, глэмпинг, юристы, автосервис, салон красоты, кафе, фитнес","Код-сплиттинг по маршрутам, общий набор SVG-иконок","Плашка «Демо-контент» на каталоге и каждом лендинге","\u002Fog\u002Fniche-landings.jpg",{"desktop":647,"desktop_sm":648,"mobile":649,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fniche-landings-desktop.webp","\u002Fshots\u002Fniche-landings-desktop-sm.webp","\u002Fshots\u002Fniche-landings-mobile.webp",{"slug":651,"title":652,"type":291,"stack":653,"description":656,"highlights":657,"run":661,"hue":662,"og":663,"shots":664},"dental-concept","Кварц: концепт лендинга стоматологии",[293,294,654,532,655,92],"JavaScript","htmlhint","Концепт одностраничного лендинга вымышленной стоматологии в стиле Liquid Chrome Dark: стеклянные карточки, хромированные заголовки и 3D-блоб в виде зуба на canvas. Один HTML-файл без сборки и зависимостей.",[658,659,660],"Анимированный блоб на canvas с учётом prefers-reduced-motion и видимости на экране","Форма записи с маской телефона и доступными сообщениями об ошибках","CI: htmlhint и проверка локальных ссылок и якорей","python -m http.server 8080",190,"\u002Fog\u002Fdental-concept.jpg",{"desktop":665,"desktop_sm":666,"mobile":667,"dw":254,"dh":255,"mw":256,"mh":257},"\u002Fshots\u002Fdental-concept-desktop.webp","\u002Fshots\u002Fdental-concept-desktop-sm.webp","\u002Fshots\u002Fdental-concept-mobile.webp",1792697892476]