[{"data":1,"prerenderedAt":767},["ShallowReactive",2],{"api:\u002Fapi\u002Fconfig:ru":3,"api:\u002Fapi\u002Fprofile:":8,"api:\u002Fapi\u002Fcategories:ru":19,"api:\u002Fapi\u002Fprojects:ru":171,"api:\u002Fapi\u002Fabout:ru":725},{"email":4,"lang":5,"name":6,"role":7},"djrciv@gmail.com","ru","Ewsirdev","Веб-разработчик: сайты, лендинги и сервисы на Go",{"name":6,"handle":6,"contacts":9,"note":18},{"email":10,"github":12,"telegram":15},{"url":11,"label":4},"mailto:djrciv@gmail.com",{"url":13,"label":14},"https:\u002F\u002Fgithub.com\u002Fewsir247","github.com\u002Fewsir247",{"url":16,"label":17},"https:\u002F\u002Ft.me\u002FEwsirdev","@Ewsirdev","Contacts: Telegram, GitHub, email. This file is the single source of contacts for the site, the contact block, JSON-LD sameAs, security.txt and llms.txt.",[20,49,80,108,142],{"slug":21,"title":22,"blurb":23,"variant":24,"palette":25,"layout":26,"count":27,"preview":28,"stack":44},"landings","Лендинги","Одна страница, одна задача. Вёрстка без фреймворков, где это возможно, и один WebGL-эксперимент.","ink","cold","shots",6,[29,34,39],{"slug":30,"title":31,"shot":32,"hue":33},"landing-studio","Полутон: студия света","\u002Fshots\u002Flanding-studio-desktop-sm.webp",212,{"slug":35,"title":36,"shot":37,"hue":38},"landing-saas","Рубильник: трекер подписок","\u002Fshots\u002Flanding-saas-desktop-sm.webp",182,{"slug":40,"title":41,"shot":42,"hue":43},"landing-event","Малый масштаб: конференция","\u002Fshots\u002Flanding-event-desktop-sm.webp",124,[45,46,47,48],"CSS","HTML","GitHub Actions","JS",{"slug":50,"title":51,"blurb":52,"variant":53,"palette":54,"layout":55,"count":56,"langs":57,"preview":61,"stack":77},"sites","Многостраничные сайты","Сайты со своей структурой, маршрутами и формами: от клиентского проекта до набора нишевых страниц.","honey","mono","frames",4,[58],{"lang":59,"count":60},"TypeScript",1,[62,67,72],{"slug":63,"title":64,"shot":65,"hue":66},"legal-multipage-site","Многостраничный сайт юридической компании","\u002Fshots\u002Flegal-multipage-site-desktop-sm.webp",250,{"slug":68,"title":69,"shot":70,"hue":71},"niche-landings","Каталог демо-лендингов под 10 ниш","\u002Fshots\u002Fniche-landings-desktop-sm.webp",32,{"slug":73,"title":74,"shot":75,"hue":76},"docs-engine","Сверло: генератор документации на Node.js","\u002Fshots\u002Fdocs-engine-desktop-sm.webp",25,[47,59,78,79],"Vue 3","Astro",{"slug":81,"title":82,"blurb":83,"variant":84,"palette":85,"layout":86,"count":87,"langs":88,"preview":90,"stack":105},"apps","Веб-приложения","Интерфейс плюс сервер: авторизация, данные, письма. Приложения, в которые входят.","beads","warm","phone",3,[89],{"lang":59,"count":60},[91,96,101],{"slug":92,"title":93,"shot":94,"hue":95},"bad-club-booking-app","Bad Club: бронирование мест в игровом коворкинге","\u002Fshots\u002Fbad-club-booking-app-desktop-sm.webp",150,{"slug":97,"title":98,"shot":99,"hue":100},"typing-lab","typing-lab: тренажёр слепой печати","\u002Fshots\u002Ftyping-lab-desktop-sm.webp",45,{"slug":102,"title":103,"shot":104,"hue":100},"budget-split","budget-split: «Поровну», учёт общих расходов","\u002Fshots\u002Fbudget-split-desktop-sm.webp",[59,106,78,107],"Vite","axe",{"slug":109,"title":110,"blurb":111,"variant":112,"palette":113,"layout":114,"count":115,"langs":116,"preview":123,"stack":139},"backend","Бэкенды","Сервисы с API, базой и тестами: в основном на Go, есть и на Python и TypeScript. Фильтр по языку внутри раздела.","ferro","acid","list",14,[117,120,122],{"lang":118,"count":119},"Go",12,{"lang":121,"count":60},"Python",{"lang":59,"count":60},[124,129,134],{"slug":125,"title":126,"shot":127,"hue":128},"kanban-tasks","Янтарь: канбан-доска","\u002Fshots\u002Fkanban-tasks-desktop-sm.webp",344,{"slug":130,"title":131,"shot":132,"hue":133},"link-shortener","Короче: сократитель ссылок","\u002Fshots\u002Flink-shortener-desktop-sm.webp",111,{"slug":135,"title":136,"shot":137,"hue":138},"booking-slots","Запись на приём","\u002Fshots\u002Fbooking-slots-desktop-sm.webp",65,[140,118,141,47],"Docker","SQLite",{"slug":143,"title":144,"blurb":145,"variant":146,"palette":25,"layout":147,"count":87,"langs":148,"preview":152,"stack":168},"cli","CLI-утилиты","Программы для терминала: один бинарник, понятные флаги, код выхода, который можно проверить в скрипте.","film","terminal",[149,151],{"lang":118,"count":150},2,{"lang":121,"count":60},[153,158,163],{"slug":154,"title":155,"shot":156,"hue":157},"gitpulse","GitPulse: аналитика git-репозиториев","\u002Fshots\u002Fgitpulse-desktop-sm.webp",7,{"slug":159,"title":160,"shot":161,"hue":162},"logslice","logslice: анализ логов без зависимостей","\u002Fshots\u002Flogslice-desktop-sm.webp",160,{"slug":164,"title":165,"shot":166,"hue":167},"envguard","envguard: линтер и сейф .env-файлов","\u002Fshots\u002Fenvguard-desktop-sm.webp",35,[169,140,47,170],"CLI","stdlib only",[172,190,203,215,230,244,257,276,294,314,337,359,384,411,437,460,480,498,518,538,554,568,587,603,620,639,659,678,694,708],{"slug":125,"title":126,"type":109,"category":109,"lang":118,"kind":173,"stack":174,"description":175,"highlights":176,"run":181,"hue":128,"og":182,"shots":183},"api",[118,141,140],"Многопользовательская канбан-доска: регистрация, доски, колонки, карточки с перетаскиванием. Go + SQLite без CGO, фронт встроен в бинарник.",[177,178,179,180],"PBKDF2 и сессии в HttpOnly-cookie","Изоляция данных пользователей","Тесты API","Docker multi-stage","cd kanban-tasks && go run .","\u002Fog\u002Fkanban-tasks.jpg",{"desktop":184,"desktop_sm":127,"mobile":185,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fkanban-tasks-desktop.webp","\u002Fshots\u002Fkanban-tasks-mobile.webp",1280,800,390,844,{"slug":130,"title":131,"type":109,"category":109,"lang":118,"kind":173,"stack":191,"description":192,"highlights":193,"run":198,"hue":133,"og":199,"shots":200},[118,141,140],"Сокращение ссылок с алиасами и сроком жизни, статистика кликов по дням, браузерам и устройствам, дашборд под токеном. IP не хранятся.",[194,195,196,197],"Редирект и учёт кликов","Лимит запросов","Приватная статистика (хэш с солью)","Тесты и Docker","cd link-shortener && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fserver","\u002Fog\u002Flink-shortener.jpg",{"desktop":201,"desktop_sm":132,"mobile":202,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Flink-shortener-desktop.webp","\u002Fshots\u002Flink-shortener-mobile.webp",{"slug":135,"title":136,"type":109,"category":109,"lang":118,"kind":173,"stack":204,"description":205,"highlights":206,"run":210,"hue":138,"og":211,"shots":212},[118,141,140],"Онлайн-запись на услуги: свободные слоты с учётом длительности и рабочих часов, защита от двойной записи, админ-панель. Вымышленная студия, демо.",[207,208,209,197],"Расчёт свободных слотов","Без двойных записей","Админка для услуг и расписания","cd booking-slots && go run .","\u002Fog\u002Fbooking-slots.jpg",{"desktop":213,"desktop_sm":137,"mobile":214,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fbooking-slots-desktop.webp","\u002Fshots\u002Fbooking-slots-mobile.webp",{"slug":30,"title":31,"type":216,"category":21,"live_url":217,"stack":218,"description":219,"highlights":220,"run":225,"hue":33,"og":226,"shots":227},"landing","https:\u002F\u002Fewsirdev-studio.pages.dev",[46,45,48],"Лендинг вымышленной студии интерьерного света: тёплая палитра, засечный шрифт, SVG-иллюстрация, FAQ и демо-форма. Адаптивный, без внешних картинок.",[221,222,223,224],"Адаптив 390-1280","SVG вместо картинок","Доступные FAQ и форма","Честная демо-форма","cd landing-studio && python -m http.server 8091","\u002Fog\u002Flanding-studio.jpg",{"desktop":228,"desktop_sm":32,"mobile":229,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Flanding-studio-desktop.webp","\u002Fshots\u002Flanding-studio-mobile.webp",{"slug":35,"title":36,"type":216,"category":21,"live_url":231,"stack":232,"description":233,"highlights":234,"run":239,"hue":38,"og":240,"shots":241},"https:\u002F\u002Fewsirdev-saas.pages.dev",[46,45,48],"Лендинг вымышленного SaaS-продукта: тёмный градиентный hero, макет интерфейса на CSS, тарифы, FAQ, демо-форма. Все цены и названия придуманы.",[235,236,237,238],"Макет интерфейса на CSS","Тарифная сетка","Адаптив","Демо-форма без отправки данных","cd landing-saas && python -m http.server 8092","\u002Fog\u002Flanding-saas.jpg",{"desktop":242,"desktop_sm":37,"mobile":243,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Flanding-saas-desktop.webp","\u002Fshots\u002Flanding-saas-mobile.webp",{"slug":40,"title":41,"type":216,"category":21,"live_url":245,"stack":246,"description":247,"highlights":248,"run":252,"hue":43,"og":253,"shots":254},"https:\u002F\u002Fewsirdev-event.pages.dev",[46,45,48],"Лендинг вымышленной однодневной конференции: плакатная типографика, расписание, спикеры-заглушки, условия, демо-форма записи.",[249,250,237,251],"Расписание дня","Плакатная типографика","Без выдуманных имён спикеров","cd landing-event && python -m http.server 8093","\u002Fog\u002Flanding-event.jpg",{"desktop":255,"desktop_sm":42,"mobile":256,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Flanding-event-desktop.webp","\u002Fshots\u002Flanding-event-mobile.webp",{"slug":258,"title":259,"type":109,"category":109,"lang":118,"kind":260,"stack":261,"description":264,"highlights":265,"run":269,"hue":270,"og":271,"shots":272},"uptime-monitor","Пульс: монитор доступности","infra",[118,141,262,263,140],"SSE","REST API","Монитор доступности: HTTP и TCP проверки по расписанию, аптайм, p50\u002Fp95, детектор инцидентов, живой дашборд на SSE и публичная статус-страница. Go + SQLite без CGO.",[266,267,268,197],"Горутина-воркер на каждую проверку","Инциденты после серии падений","Дашборд на SSE и публичный \u002Fstatus","cd uptime-monitor && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fserver",101,"\u002Fog\u002Fuptime-monitor.jpg",{"desktop":273,"desktop_sm":274,"mobile":275,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fuptime-monitor-desktop.webp","\u002Fshots\u002Fuptime-monitor-desktop-sm.webp","\u002Fshots\u002Fuptime-monitor-mobile.webp",{"slug":277,"title":278,"type":109,"category":109,"lang":118,"kind":260,"stack":279,"description":281,"highlights":282,"run":287,"hue":288,"og":289,"shots":290},"job-queue","Конвейер: очередь задач",[118,141,263,262,280,140],"Prometheus","Очередь фоновых задач: приоритеты, отложенные и cron-задачи, ретраи с backoff и jitter, dead-letter, идемпотентные ключи, lease с fencing, метрики Prometheus.",[283,284,285,286],"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":291,"desktop_sm":292,"mobile":293,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fjob-queue-desktop.webp","\u002Fshots\u002Fjob-queue-desktop-sm.webp","\u002Fshots\u002Fjob-queue-mobile.webp",{"slug":295,"title":296,"type":109,"category":109,"lang":118,"kind":297,"stack":298,"description":301,"highlights":302,"run":307,"hue":308,"og":309,"shots":310},"realtime-rooms","Комнаты: чат и доска в реальном времени","realtime",[118,299,141,300,140],"WebSocket","Canvas","Комнаты в реальном времени: чат с историей в SQLite, список «кто онлайн», индикатор «печатает», общая доска со штрихами и курсорами, анонимные ники. WebSocket (RFC 6455) написан на stdlib.",[303,304,305,306],"Свой слой 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":311,"desktop_sm":312,"mobile":313,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Frealtime-rooms-desktop.webp","\u002Fshots\u002Frealtime-rooms-desktop-sm.webp","\u002Fshots\u002Frealtime-rooms-mobile.webp",{"slug":315,"title":316,"type":109,"category":109,"lang":118,"kind":260,"stack":317,"description":324,"highlights":325,"run":330,"hue":331,"og":332,"shots":333},"api-gateway","Шлюз: API-gateway",[118,170,318,319,320,262,280,321,322,323,140,47],"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.",[326,327,328,329],"Ноль зависимостей: только stdlib","Горячая перезагрузка конфига без обрыва соединений","Circuit breaker на узел и ретраи идемпотентных запросов","Нагрузочный тест: 4000 запросов, 40 клиентов (демо-замер)","cd api-gateway && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fgateway",200,"\u002Fog\u002Fapi-gateway.jpg",{"desktop":334,"desktop_sm":335,"mobile":336,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fapi-gateway-desktop.webp","\u002Fshots\u002Fapi-gateway-desktop-sm.webp","\u002Fshots\u002Fapi-gateway-mobile.webp",{"slug":338,"title":339,"type":109,"category":109,"lang":118,"kind":340,"stack":341,"description":347,"highlights":348,"run":353,"hue":162,"og":354,"shots":355},"notes-search","Картотека: заметки с поиском","data",[118,141,342,343,344,263,345,346,322,323,140,47],"FTS5","bm25","modernc.org\u002Fsqlite (без CGO)","Markdown (свой безопасный рендер)","SVG force-layout","База Markdown-заметок на Go и SQLite: вики-ссылки [[заметка]] с обратными ссылками и графом связей, полнотекстовый поиск FTS5 (bm25, подсветка, упрощённая русская морфология), история версий с собственным diff, автосохранение с разрешением конфликтов, экспорт и импорт .md.",[349,350,351,352],"Поиск FTS5 с bm25 и подсветкой","Вики-ссылки, backlinks и граф связей","История версий с собственным diff","Свой безопасный Markdown-рендер с тестом на XSS","cd notes-search && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fserver","\u002Fog\u002Fnotes-search.jpg",{"desktop":356,"desktop_sm":357,"mobile":358,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fnotes-search-desktop.webp","\u002Fshots\u002Fnotes-search-desktop-sm.webp","\u002Fshots\u002Fnotes-search-mobile.webp",{"slug":360,"title":361,"type":216,"category":21,"live_url":362,"stack":363,"description":370,"highlights":371,"run":377,"hue":378,"og":379,"shots":380},"landing-3d","ОКСО Т1: 3D-лендинг","https:\u002F\u002Fewsirdev-3d.pages.dev",[364,365,322,366,367,368,369,47],"HTML5","CSS3","WebGL2","GLSL ES 3.00","Raymarching\u002FSDF","Scroll choreography","Лендинг вымышленного датчика воздуха (демо): объект рисуется одним фрагментным шейдером на чистом WebGL2 без библиотек, четыре SDF-формы перетекают по скроллу, есть SVG-фолбэк, reduced-motion и пауза вне экрана.",[372,373,374,375,376],"Один фрагментный шейдер","WebGL2 без библиотек","Четыре SDF-формы с морфингом по скроллу","SVG-фолбэк и prefers-reduced-motion","Пауза отрисовки вне экрана","cd landing-3d && python -m http.server 8094",90,"\u002Fog\u002Flanding-3d.jpg",{"desktop":381,"desktop_sm":382,"mobile":383,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Flanding-3d-desktop.webp","\u002Fshots\u002Flanding-3d-desktop-sm.webp","\u002Fshots\u002Flanding-3d-mobile.webp",{"slug":385,"title":386,"type":109,"category":109,"lang":118,"kind":340,"stack":387,"description":396,"highlights":397,"run":404,"hue":405,"og":406,"shots":407},"raft-kv","Плот: KV на Raft",[118,388,389,390,170,391,262,392,393,394,395,322,140,47],"Raft","Distributed systems","Consensus","HTTP\u002FJSON","WAL + fsync","Snapshots","Linearizability","Deterministic simulation testing","Учебная реализация Raft с нуля на чистой stdlib: выборы лидера, репликация лога, коммит по кворуму, лог и term\u002Fvote на диске с fsync, снапшоты с компакцией, линеаризуемые чтения (read-index), редирект фолловера на лидера. Тестируется детерминированным симулятором сети и времени с проверкой линеаризуемости; дашборд умеет убивать лидера и рвать сеть (демо).",[398,399,400,401,402,403],"Raft с нуля на stdlib","без raft-библиотек","Детерминированный симулятор сети и времени + проверка линеаризуемости","Лог и term\u002Fvote с fsync","снапшоты и компакция","Дашборд: убить лидера и разорвать сеть (демо)","cd raft-kv && set SEED_DEMO=1&& go run .\u002Fcmd\u002Fraft-kv",290,"\u002Fog\u002Fraft-kv.jpg",{"desktop":408,"desktop_sm":409,"mobile":410,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fraft-kv-desktop.webp","\u002Fshots\u002Fraft-kv-desktop-sm.webp","\u002Fshots\u002Fraft-kv-mobile.webp",{"slug":412,"title":413,"type":109,"category":109,"lang":118,"kind":414,"stack":415,"description":424,"highlights":425,"run":430,"hue":431,"og":432,"shots":433},"auth-service","Ключница: сервис аутентификации","auth",[118,416,141,344,417,418,419,420,421,422,423,322,323,140,47],"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.",[426,427,428,429],"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":434,"desktop_sm":435,"mobile":436,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fauth-service-desktop.webp","\u002Fshots\u002Fauth-service-desktop-sm.webp","\u002Fshots\u002Fauth-service-mobile.webp",{"slug":438,"title":439,"type":109,"category":109,"lang":118,"kind":173,"stack":440,"description":447,"highlights":448,"run":453,"hue":454,"og":455,"shots":456},"finance-tracker","Книга учёта: личные финансы",[118,141,344,263,441,78,59,442,443,444,445,140,47,419,446],"Nuxt 4","Pinia","SVG-графики","vitest","ESLint","CSV import\u002Fexport","Учёт личных финансов: счета в разных валютах с ручными курсами, доходы, расходы и переводы, бюджеты, платежи по cron-подобному расписанию, импорт CSV с превью, отчёты. Деньги только в целых минорных единицах, данные пользователей изолированы, графики нарисованы на SVG без библиотек. Go + SQLite и фронтенд на Nuxt 4.",[449,450,451,452],"Деньги только целыми минорными единицами","Мультивалютность с ручными курсами","Импорт CSV с превью и пробным прогоном","Свои SVG-графики без библиотек","cd finance-tracker && set SEED_DEMO=1&& go run .",140,"\u002Fog\u002Ffinance-tracker.jpg",{"desktop":457,"desktop_sm":458,"mobile":459,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Ffinance-tracker-desktop.webp","\u002Fshots\u002Ffinance-tracker-desktop-sm.webp","\u002Fshots\u002Ffinance-tracker-mobile.webp",{"slug":461,"title":462,"type":109,"category":109,"lang":118,"kind":297,"stack":463,"description":467,"highlights":468,"run":473,"hue":474,"og":475,"shots":476},"pixel-wall","Пиксельная стена: общий холст",[118,170,299,464,322,323,465,466,140,47],"Canvas 2D","Binary protocol","Snapshot + journal","Совместный холст 256×256 в духе r\u002Fplace: 16 цветов, кулдаун на сессию и IP, свой WebSocket-слой на stdlib (RFC 6455) с бинарным протоколом (4 бита на пиксель), снимок и дельты, снапшот плюс журнал с восстановлением, таймлапс, зум и пан, мини-карта, экспорт PNG, админ-инструменты. Пет-проект.",[469,470,471,472],"Свой 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":477,"desktop_sm":478,"mobile":479,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fpixel-wall-desktop.webp","\u002Fshots\u002Fpixel-wall-desktop-sm.webp","\u002Fshots\u002Fpixel-wall-mobile.webp",{"slug":154,"title":155,"type":143,"category":143,"lang":118,"stack":481,"description":487,"highlights":488,"run":493,"hue":157,"og":494,"shots":495},[118,170,169,482,483,484,485,486,47,140],"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. Пет-проект.",[489,490,491,492],"Только 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":496,"desktop_sm":156,"mobile":497,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fgitpulse-desktop.webp","\u002Fshots\u002Fgitpulse-mobile.webp",{"slug":63,"title":64,"type":499,"category":50,"stack":500,"description":505,"highlights":506,"run":513,"hue":66,"og":514,"shots":515},"site",[441,78,59,501,502,503,504,140,47],"Bootstrap 5","Zod","Vitest","Playwright","Реализованный сайт юридической и экспертной компании на Nuxt 4: 65 страниц с серверным рендерингом, JSON-LD разметка и 301-редиректы со старого сайта. Заявки уходят через серверный эндпоинт с проверкой Origin, лимитом размера тела и ограничением частоты запросов.",[507,508,509,510,511,512],"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","\u002Fog\u002Flegal-multipage-site.jpg",{"desktop":516,"desktop_sm":65,"mobile":517,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Flegal-multipage-site-desktop.webp","\u002Fshots\u002Flegal-multipage-site-mobile.webp",{"slug":92,"title":93,"type":519,"category":81,"stack":520,"description":527,"highlights":528,"run":533,"hue":95,"og":534,"shots":535},"app",[78,106,442,521,522,523,524,525,526],"Vue Router","GSAP","Express","node:sqlite","JWT (httpOnly cookie)","Resend","Демо-приложение на Vue 3 и Express с SQLite: интерактивная карта зала с бронированием мест, личный кабинет и админ-панель с редактором расстановки. Вход без паролей, по одноразовому коду на e-mail.",[529,530,531,532],"Карта зала с зонами (обычные, тихая, VIP, два монитора), фильтром по типу и статусом мест","Админ-панель: обзор, брони, столы, компьютеры, пользователи и перетаскиваемая расстановка зала","Вход по OTP с ограничением попыток и частоты запросов, роль admin задаётся через переменную окружения","Данные для демо создаются сид-скриптом при первом запуске","npm install && npm run dev","\u002Fog\u002Fbad-club-booking-app.jpg",{"desktop":536,"desktop_sm":94,"mobile":537,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fbad-club-booking-app-desktop.webp","\u002Fshots\u002Fbad-club-booking-app-mobile.webp",{"slug":539,"title":540,"type":216,"category":21,"live_url":541,"stack":542,"description":543,"highlights":544,"run":533,"hue":548,"og":549,"shots":550},"dovod-concept","Довод: концепт лендинга экспертной организации","https:\u002F\u002Fewsirdev-dovod.pages.dev",[441,78,59,45,47],"Концепт лендинга на Nuxt 4 для вымышленной экспертной организации: направления услуг, форма консультации с маской телефона, карусель отзывов и страница политики. Компания, отзывы, цифры и контакты придуманы; форма ничего не отправляет.",[545,546,547],"Форма с маской телефона, валидацией и подсказками по ошибкам; в демо показывает «данные не отправляются»","Бесконечная карусель отзывов со свайпом и автопрокруткой","Мобильная панель действий, адаптивная вёрстка, скелетон поверх серверного HTML",215,"\u002Fog\u002Fdovod-concept.jpg",{"desktop":551,"desktop_sm":552,"mobile":553,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fdovod-concept-desktop.webp","\u002Fshots\u002Fdovod-concept-desktop-sm.webp","\u002Fshots\u002Fdovod-concept-mobile.webp",{"slug":68,"title":69,"type":499,"category":50,"live_url":555,"stack":556,"description":559,"highlights":560,"run":533,"hue":71,"og":564,"shots":565},"https:\u002F\u002Fewsirdev-niche.pages.dev",[78,557,558,47],"Vue Router 4","Vite 8","Vue-каталог из десяти самостоятельных лендингов под разные ниши малого бизнеса: у каждого своя палитра, шрифты и структура секций. Каждый лендинг загружается отдельным чанком; весь контент демонстрационный.",[561,562,563],"10 ниш: медицина, мебель, ремонт, онлайн-школа, глэмпинг, юристы, автосервис, салон красоты, кафе, фитнес","Код-сплиттинг по маршрутам, общий набор SVG-иконок","Плашка «Демо-контент» на каталоге и каждом лендинге","\u002Fog\u002Fniche-landings.jpg",{"desktop":566,"desktop_sm":70,"mobile":567,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fniche-landings-desktop.webp","\u002Fshots\u002Fniche-landings-mobile.webp",{"slug":569,"title":570,"type":216,"category":21,"live_url":571,"stack":572,"description":575,"highlights":576,"run":580,"hue":581,"og":582,"shots":583},"dental-concept","Кварц: концепт лендинга стоматологии","https:\u002F\u002Fewsirdev-dental.pages.dev",[46,45,573,464,574,47],"JavaScript","htmlhint","Концепт одностраничного лендинга вымышленной стоматологии в стиле Liquid Chrome Dark: стеклянные карточки, хромированные заголовки и 3D-блоб в виде зуба на canvas. Один HTML-файл без сборки и зависимостей.",[577,578,579],"Анимированный блоб на canvas с учётом prefers-reduced-motion и видимости на экране","Форма записи с маской телефона и доступными сообщениями об ошибках","CI: htmlhint и проверка локальных ссылок и якорей","python -m http.server 8080",190,"\u002Fog\u002Fdental-concept.jpg",{"desktop":584,"desktop_sm":585,"mobile":586,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fdental-concept-desktop.webp","\u002Fshots\u002Fdental-concept-desktop-sm.webp","\u002Fshots\u002Fdental-concept-mobile.webp",{"slug":159,"title":160,"type":143,"category":143,"lang":121,"stack":588,"description":592,"highlights":593,"run":598,"hue":162,"og":599,"shots":600},[121,170,169,484,589,590,591,47,140],"pytest","ruff","mypy","CLI на Python 3.11+ только на стандартной библиотеке: потоково разбирает логи nginx\u002Fapache (combined), JSON-lines, logfmt и syslog (gz, bz2, xz тоже), считает статистику и топы, кластеризует похожие ошибки, ищет скачки нагрузки по z-score и EWMA и собирает один HTML-отчёт с SVG-графиками и тепловой картой. Пет-проект.",[594,595,596,597],"Только стандартная библиотека, pip install ничего не тянет","Кластеры ошибок по шаблону и аномалии двумя методами (z-score и EWMA)","Один самодостаточный HTML-отчёт: SVG, тепловая карта, светлая и тёмная темы","53 теста, ruff и mypy --strict; 1 млн строк combined за 8,2 с (один прогон, Windows, CPython 3.12.10)","logslice report access.log -o report.html","\u002Fog\u002Flogslice.jpg",{"desktop":601,"desktop_sm":161,"mobile":602,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Flogslice-desktop.webp","\u002Fshots\u002Flogslice-mobile.webp",{"slug":164,"title":165,"type":143,"category":143,"lang":118,"stack":604,"description":609,"highlights":610,"run":615,"hue":167,"og":616,"shots":617},[118,170,169,605,419,606,607,608,47,140],"AES-256-GCM","fuzzing","govulncheck","gitleaks","CLI на Go без внешних модулей: check сверяет .env со схемой, scan ищет секреты в дереве и git-истории, diff и doctor проверяют окружения и git-гигиену, seal и unseal шифруют .env в .env.sealed (AES-256-GCM, PBKDF2 600 тыс. итераций), run запускает команду с чистым окружением, report и serve строят отчёт. Значение секрета не попадает ни в вывод, ни в лог, ни в HTML. Пет-проект.",[611,612,613,614],"Значения секретов не печатаются нигде: только тип находки, файл, строка и маска из 4 символов","seal и unseal: AES-256-GCM, PBKDF2 600 тыс. итераций, подмена любого бита файла отвергается","Fuzz-тесты: 4 цели по 20 секунд без падений; govulncheck и gitleaks без находок","Сборки под windows, linux и darwin, только стандартная библиотека","go run .\u002Fcmd\u002Fenvguard check","\u002Fog\u002Fenvguard.jpg",{"desktop":618,"desktop_sm":166,"mobile":619,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fenvguard-desktop.webp","\u002Fshots\u002Fenvguard-mobile.webp",{"slug":621,"title":622,"type":109,"category":109,"lang":121,"stack":623,"description":626,"highlights":627,"run":632,"hue":633,"og":634,"shots":635},"catalog-api","catalog-api: API каталога с поиском",[121,624,141,342,589,590,591,625,140,47],"FastAPI","bandit","API каталога товаров на Python 3.12 и FastAPI: SQLite с полнотекстовым поиском FTS5 (ранжирование bm25), фасеты по категории, цене, наличию, тегам и характеристикам, курсорная пагинация, ETag, импорт и экспорт CSV, история цен, API-ключи с ролями и аудит-лог. Витрина и админка без фреймворка отдаются самим приложением. Пет-проект.",[628,629,630,631],"Поиск FTS5 с bm25, фасеты и курсорная пагинация","API-ключи с ролями admin, editor и reader; в базе только хэши ключей","511 тестов, покрытие 96%; ruff, mypy --strict, bandit, pip-audit и gitleaks без замечаний","Нагрузка: 339 запросов\u002Fс, p95 50 мс при 8 соединениях (Windows, один процесс)","SEED_DEMO=1 python -m app   # http:\u002F\u002F127.0.0.1:8110",265,"\u002Fog\u002Fcatalog-api.jpg",{"desktop":636,"desktop_sm":637,"mobile":638,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fcatalog-api-desktop.webp","\u002Fshots\u002Fcatalog-api-desktop-sm.webp","\u002Fshots\u002Fcatalog-api-mobile.webp",{"slug":640,"title":641,"type":109,"category":109,"lang":59,"live_url":642,"stack":643,"description":648,"highlights":649,"run":533,"hue":548,"og":654,"shots":655},"paste-vault","Конверт: записки с шифрованием в браузере","https:\u002F\u002Fewsirdev-paste.ewsirdev.workers.dev",[59,644,645,646,106,444,647,47],"Cloudflare Workers","D1","Web Crypto","fast-check","Сервис одноразовых записок на TypeScript, Cloudflare Workers и D1: текст шифруется в браузере (AES-256-GCM), ключ лежит только во фрагменте URL и на сервер не попадает. У записки есть срок жизни, пароль и сгорание после первого прочтения. Пет-проект.",[650,651,652,653],"Шифрование в браузере: AES-256-GCM, ключ только во фрагменте URL","Сгорание после прочтения атомарно: ровно один успех из 40 параллельных чтений","52 теста в рантайме workerd, включая property-тесты и фаззинг схемы","Живая версия на Cloudflare Workers + D1","\u002Fog\u002Fpaste-vault.jpg",{"desktop":656,"desktop_sm":657,"mobile":658,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fpaste-vault-desktop.webp","\u002Fshots\u002Fpaste-vault-desktop-sm.webp","\u002Fshots\u002Fpaste-vault-mobile.webp",{"slug":73,"title":74,"type":499,"category":50,"live_url":660,"stack":661,"description":667,"highlights":668,"run":673,"hue":76,"og":674,"shots":675},"https:\u002F\u002Fewsirdev-docs.pages.dev",[662,663,664,665,666,47],"Node.js","JavaScript (ESM)","zero dependencies","Markdown","Cloudflare Pages","Генератор статических сайтов документации, написанный с нуля на Node.js без зависимостей: собственный Markdown-парсер, подсветка кода и офлайн-поиск. Демо-сайт про вымышленную библиотеку «Сверло» собран из 43 страниц на двух языках (RU и EN) и двух версиях (v1 и v2). Пет-проект.",[669,670,671,672],"Свой Markdown-парсер, подсветка кода и клиентский поиск без библиотек","43 страницы, RU и EN, версии v1 и v2, hreflang, sitemap и RSS","131 тест; в CI проверка битых ссылок и якорей","Сайт читается без JavaScript, поиск работает офлайн","npm test && npm run build","\u002Fog\u002Fdocs-engine.jpg",{"desktop":676,"desktop_sm":75,"mobile":677,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fdocs-engine-desktop.webp","\u002Fshots\u002Fdocs-engine-mobile.webp",{"slug":97,"title":98,"type":519,"category":81,"live_url":679,"stack":680,"description":684,"highlights":685,"run":533,"hue":100,"og":690,"shots":691},"https:\u002F\u002Fewsirdev-typing.pages.dev",[441,78,59,681,682,683,484,444,107,666],"IndexedDB","WebAudio","PWA","Тренажёр слепой печати, который целиком работает в браузере и не обращается к серверу: Nuxt 4, история в IndexedDB, звук клавиш на WebAudio, офлайн через PWA. Режимы времени, слов, цитат, своего текста, кода и «слабых мест», шесть раскладок, метрики WPM, точность и консистентность, графики на чистом SVG. Пет-проект.",[686,687,688,689],"Без сервера и аккаунтов: история в IndexedDB, экспорт и импорт JSON","Шесть раскладок, режимы вплоть до адаптивных упражнений из ваших ошибок","59 unit-тестов и 65 e2e-проверок, axe без нарушений, CLS 0,000","PWA: работает офлайн после первого открытия","\u002Fog\u002Ftyping-lab.jpg",{"desktop":692,"desktop_sm":99,"mobile":693,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Ftyping-lab-desktop.webp","\u002Fshots\u002Ftyping-lab-mobile.webp",{"slug":102,"title":103,"type":519,"category":81,"lang":59,"live_url":695,"stack":696,"description":698,"highlights":699,"run":533,"hue":100,"og":704,"shots":705},"https:\u002F\u002Fewsirdev-budget.ewsirdev.workers.dev",[59,106,644,645,697,444,647,107,47],"WebGL","Веб-приложение учёта общих расходов группы без регистрации: комната по секретной ссылке, деление поровну, по долям, суммам и процентам, расчёт «кто кому» не более n−1 переводов. Пет-проект.",[700,701,702,703],"Без аккаунтов: комната по секретной ссылке в 128 бит, отдельные ссылки «просмотр» и «редактирование»","Четыре способа деления и мультивалюта с ручным курсом; расчёт «кто кому» не более n−1 переводов","Оптимистичная блокировка: из 4 одновременных правок одного расхода проходит одна","60 тестов (vitest в workerd, property-тесты fast-check), CI зелёный, npm audit 0, axe 0 нарушений","\u002Fog\u002Fbudget-split.jpg",{"desktop":706,"desktop_sm":104,"mobile":707,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fbudget-split-desktop.webp","\u002Fshots\u002Fbudget-split-mobile.webp",{"slug":709,"title":710,"type":499,"category":50,"lang":59,"live_url":711,"stack":712,"description":714,"highlights":715,"run":533,"hue":100,"og":720,"shots":721},"kladka-site","kladka-site: «Кладка», демо-сайт студии каркасного строительства","https:\u002F\u002Fewsirdev-kladka.pages.dev",[79,59,713,484,697,107,574,47],"Content collections","Демо-сайт вымышленной студии каркасного строительства: 5 проектов с чертежами, услуги, цены с калькулятором-ориентиром, журнал. Компания, цены и сроки выдуманы, на каждой странице есть пометка.",[716,717,718,719],"24 страницы (19 RU, 4 EN, 404) на Astro 7 со статической сборкой и content collections","Все 20 чертежей рисует код: SVG по данным проекта; 24 картинки OG рендерятся скриптом","Строгий CSP без inline, robots, sitemap с hreflang, llms.txt, security.txt, JSON-LD Organization\u002FService\u002FArticle\u002FFAQPage","756 ссылок и якорей без ошибок, htmlhint 0, axe 0 по всем страницам на 390 и 1280, npm audit 0","\u002Fog\u002Fkladka-site.jpg",{"desktop":722,"desktop_sm":723,"mobile":724,"dw":186,"dh":187,"mw":188,"mh":189},"\u002Fshots\u002Fkladka-site-desktop.webp","\u002Fshots\u002Fkladka-site-desktop-sm.webp","\u002Fshots\u002Fkladka-site-mobile.webp",{"honest":726,"how":732,"intro":748,"lang":5,"principles":751},[727,728,729,730,731],"Почти все проекты учебно-демонстрационные; один сайт — реализованный клиентский, имя клиента не называется. Отзывы и цифры по заказам здесь не заявляются.","Race-детектор (go test -race) на моей рабочей машине не запускается из-за отсутствия C-компилятора; он выполняется в CI.","Свой слой WebSocket не проходил Autobahn Test Suite: корректность проверена собственными тестами. В продакшене разумнее брать проверенную библиотеку.","Демо-данные в проектах (история мониторинга, ссылки, задачи) сгенерированы и помечены в интерфейсах как демо.","Контакты: Telegram, ссылка на GitHub и почта djrciv@gmail.com.",[733,736,739,742,745],{"title":734,"text":735},"Разговор","Выясняем задачу, аудиторию и ограничения. Если сайт не нужен, скажу об этом сразу.",{"title":737,"text":738},"Договорённости","Состав работ, сроки и что считается готовым. Письменно, без сюрпризов.",{"title":740,"text":741},"Сборка","Короткие итерации: промежуточный результат можно открыть и потрогать.",{"title":743,"text":744},"Проверка","Тесты, мобильные экраны, доступность, нагрузка на форму и API.",{"title":746,"text":747},"Запуск","Docker-образ или статика, инструкция по запуску, передача кода.",[749,750],"Я веб-разработчик: делаю лендинги, сайты, веб-приложения и бэкенды, чаще всего на Go. В каталоге 30 проектов: 6 лендингов, 4 многостраничных сайта, 3 веб-приложения, 14 бэкендов и 3 CLI-утилиты; 12 из них работают вживую на Cloudflare. Почти все проекты учебно-демонстрационные: бренды вымышленные, данные демо, код приватный и открывается по запросу. Исключение — один реализованный клиентский сайт (клиента не называю).","Здесь нет отзывов и цифр о заказах: показываю только то, что можно проверить, то есть работающий код, тесты и README с ограничениями. Безопасность проверяю отдельно: подход и таблица закрытых классов уязвимостей на странице «Инженерия».",[752,755,758,761,764],{"title":753,"text":754},"Просто и проверяемо","Стандартная библиотека Go, понятная структура пакетов, тесты на то, что действительно важно: конкурентный захват задач, двойная бронь, протокол WebSocket.",{"title":756,"text":757},"Честные ограничения","В каждом README есть раздел «Ограничения»: один токен вместо ролей, один процесс, at-least-once вместо exactly-once. Лучше сказать заранее, чем объяснять потом.",{"title":759,"text":760},"Доступность по умолчанию","Контраст по WCAG AA, видимый фокус, работа с клавиатуры, уважение к prefers-reduced-motion, mobile-first вёрстка.",{"title":762,"text":763},"Без секретов в коде","Настройки идут через переменные окружения, токены генерируются при старте, если не заданы.",{"title":765,"text":766},"Один бинарник","Фронтенд встроен в Go-сервис через embed, запуск не требует установки зависимостей; для SQLite используется драйвер без CGO.",1793288284287]