Аким на 5 часов
Веб-симулятор распределения городского бюджета: пользователь выбирает 5 мер по районам и получает объяснимый индекс качества жизни с лагами…
О проекте
Команда: nerds
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Рабочий MVP
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Веб-симулятор распределения городского бюджета: пользователь выбирает 5 мер по районам и получает объяснимый индекс качества жизни с лагами, синергиями и ограничениями совместимости.
Как устроено
Встроенный набор данных: 5 районов, 10 показателей, 14 мер, 70 строк эффектов. Клиентский JS-движок считает новые показатели как clip(исходный + Σ эффект×(8−лаг)/8 + синергии), индекс района взвешенной суммой, итоговый Score = 0.7×средний индекс по населению + 0.3×худший район − число критических показателей; проверяет бюджет и несовместимые пары мер. Next.js + PostgreSQL отвечают только за регистрацию и сессии, сценарии хранятся в localStorage; AI не реализован.
Ключевые решения
- deterministic scoring formula
- lag coefficients
- synergy/incompatibility rules
- population-weighted index
Особенности
Прозрачная формула Score с учётом худшего района и штрафом за критические показатели; README открыто перечисляет, что не реализовано (серверный Score, обязательный охват 5 направлений, генеративный AI). Из минусов: в репозитории лежат ZIP-архивы исходников.
Стек
Next.js · React · Node.js · PostgreSQL · pg · JavaScript · Netlify OpenNext · Docker
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-7e132a35-nerds.
README
README.md«Аким на 5 часов» — симулятор городских решений
Веб-приложение для учебного распределения бюджета между городскими инициативами. Пользователь регистрируется, выбирает пять мероприятий, задаёт районы и получает объяснимый индекс качества жизни с изменениями показателей, лагами, синергиями и ограничениями совместимости.
Этот README относится именно к архиву akim-server-deploy-netlify.zip. Здесь есть серверная авторизация, но расчёт сценариев выполняется в браузере. Генеративный AI и серверное хранение сценариев в этой версии не реализованы. Возможности из других архивов проекта сюда автоматически не перенесены.
Возможности и границы реализации
| Возможность | Текущее состояние |
|---|---|
| Регистрация, вход, выход, сессии | Next.js API + PostgreSQL |
| Стартовый бюджет | 100 условных единиц во встроенном наборе |
| Данные | 5 районов, 10 показателей, 14 мер, 70 строк эффектов |
| Итоговый план | Ровно 5 мер, каждую можно выбрать только один раз; не более 2 мер одного направления |
| Контроль бюджета и совместимости | В интерфейсе и клиентском движке |
| Расчёт индекса и разбор результата | Детерминированная формула, без AI |
| Сохранение сценариев | localStorage, отдельно для аккаунта и браузера; последние 10 версий |
| Перенос сценария | Экспорт / импорт JSON; импорт данных JSON, CSV, XLSX |
| Общий утверждённый набор и серверный Score | Не реализованы |
| Обязательный охват всех пяти направлений | Не проверяется: допустим план с 4 направлениями |
| Администрирование импортируемых данных | Надпись «Режим организатора» не ограничивает импорт; участник тоже может менять локальный набор |
Проект полезен как объяснимый учебный симулятор. Для защищённого соревнования и полного соответствия исходному заданию нужны общий серверный набор, проверка/сохранение результатов на сервере и настоящий AI-анализ. Предлагаемый порядок развития — в конце README.
Технологии и архитектура
- Next.js 16.3.6 / React: страница входа и личный кабинет.
- Node.js /
pg/ PostgreSQL: авторизация и хранение сессий. - HTML/CSS/JavaScript: защищённый симулятор, открываемый в iframe.
- Netlify OpenNext: целевая платформа выполнения серверных маршрутов.
Зафиксированные pnpm-lock.yaml версии: React и React DOM 19.3.0, pg 8.23.0. В package.json для этих библиотек заданы диапазоны; воспроизводимая установка требует --frozen-lockfile.
flowchart LR
U[Браузер] --> N[Next.js: вход и API]
N --> P[(PostgreSQL: пользователи и сессии)]
N --> S[Защищённый HTML симулятора]
S --> M[Клиентская модель: бюджет и Score]
M --> L[(localStorage: сценарии)]
Индекс не поступает на сервер для верификации. Резервная копия PostgreSQL не содержит пользовательские сценарии.
Быстрый локальный запуск
Нужны Node.js 24, pnpm 11.19.0 и доступная PostgreSQL. engines допускает Node 22.12+, но эта поставка проверена на Node 24.19.0. Docker-конфигурация использует PostgreSQL 16; нативная проверка выполнена на PostgreSQL 17.10.
Все команды выполняйте из папки с package.json:
npm install --global [email protected]
pnpm install --frozen-lockfile
Создайте отдельную пустую базу PostgreSQL и пользователя с правами создавать таблицы в ней. Скопируйте .env.example в .env.local и заполните:
APP_ORIGIN=http://localhost:3000
ALLOW_HTTP_LOCALHOST=true
DATABASE_URL=postgresql://USER:[email protected]:5432/akim
ALLOW_REGISTRATION=true
SESSION_HOURS=12
PG_POOL_MAX=1
USER, PASSWORD и akim замените своими значениями. Зарезервированные символы пароля в URL нужно кодировать. Реальные настройки не добавляйте в Git.
pnpm db:init
pnpm test:model
pnpm build
pnpm start
Откройте http://localhost:3000, зарегистрируйтесь и войдите. Предустановленного аккаунта нет. Пароль пользователя — 12–128 символов; логин — 3–40 латинских букв, цифр, точек, дефисов или подчёркиваний. Для разработки вместо сборки и запуска можно выполнить pnpm dev.
Важно открывать именно origin из APP_ORIGIN: localhost и 127.0.0.1 считаются разными адресами. При смене порта поменяйте также APP_ORIGIN.
Развёртывание на Netlify
Нужна внешняя PostgreSQL: Netlify не запускает базу из compose.yaml. Проект должен собираться из распакованных исходников в GitHub, а не из лежащего в репозитории ZIP. Netlify Drop со статическими файлами не заменяет серверные API.
- Создайте PostgreSQL и выполните
db/001_auth.sqlпользователем, имеющим право создавать таблицы. На Netlify миграция при сборке автоматически не запускается. - Выдайте серверной роли приложения права
SELECT, INSERT, UPDATE, DELETEнаusers,sessions,auth_limits, а такжеCONNECTна базу иUSAGEна схему. Для создания таблиц используйте отдельный административный доступ. - Подключите репозиторий к Netlify и выберите ветку с этой версией.
- Укажите Base directory: папку, где лежат
package.jsonиnetlify.toml. Если они в корне репозитория, оставьте поле пустым. Если проект вakim-server-deploy-netlify/, укажите эту папку. - Build command:
pnpm build; Publish directory:.next. Вnetlify.tomlзаданы Node 24 иPNPM_FLAGS=--shamefully-hoist; pnpm зафиксирован вpackage.json. - Добавьте переменные из таблицы ниже в Netlify UI, с доступом для Functions, и выполните Deploy.
- Проверьте
/api/health, затем регистрацию, вход, загрузку/simulator, оценку сценария и выход.
| Переменная | Значение / назначение |
|---|---|
DATABASE_URL | Секретная строка подключения к внешней PostgreSQL с SSL по требованиям провайдера |
APP_ORIGIN | Точный адрес, например https://YOUR-SITE.netlify.app, без / в конце |
ALLOW_REGISTRATION | true для самостоятельной регистрации; false для выдачи аккаунтов организатором |
SESSION_HOURS | 12; допустимо 1–168 часов |
PG_POOL_MAX | 1 для serverless; код допускает 1–10 |
ALLOW_HTTP_LOCALHOST | Для публичного HTTPS-сайта не требуется; оставьте false |
Для Supabase рекомендуемый вариант для serverless — Transaction pooler, порт 6543. Скопируйте адрес из панели Connect; для пользовательской роли логин имеет вид ROLE.PROJECT_REF. В приложении нет именованных prepared statements. Не используйте localhost или Docker-имя db в Netlify. Для миграций, бэкапов и тестов с search_path используйте прямое подключение либо подходящее session-подключение, а не transaction pooler.
После смены домена обновите APP_ORIGIN и разверните приложение повторно. Deploy Preview с другим доменом также требует своего APP_ORIGIN и, желательно, отдельной тестовой базы. Секреты не должны иметь префикс NEXT_PUBLIC_.
Полная инструкция: NETLIFY-QUICKSTART.md. Конфигурация сверена с Netlify Next.js, требованиями pnpm и Supabase connections. Реальный deploy в аккаунт Netlify в ходе проверки не выполнялся.
Воспроизводимый демонстрационный сценарий
Начните с встроенного набора, без импорта. Стартовый Score: 52,55768 (на экране 52,56).
В матрице нажмите ячейку нужного района, затем «Включить»:
| Код | Мера | Район | Стоимость |
|---|---|---|---|
| M7 | Школа + детсад | Нура | 24 |
| M8 | Центр семейного здоровья / поликлиника | Нура | 20 |
| M10 | Освещение и камеры | Нура | 12 |
| M12 | Единая цифровая платформа обращений | Весь город | 14 |
| M5 | Перевод частного сектора на чистое топливо | Сарыарка | 25 |
| Итого | 95 |
Нажмите «Оценить сценарий». Ожидаемые значения с округлением:
- Score: 56,54; остаток бюджета: 5.
- Средний индекс: 58,08; худший район: 52,96.
- Критических показателей: 0 вместо 2.
- Синергия M10 + M12 в Нуре: B1 +2.
Это пример текущих правил: он охватывает четыре направления, без транспорта. Не выдавайте этот тест за доказательство обязательного охвата всех пяти направлений.
Откройте результаты по районам, скачайте JSON и перезагрузите страницу: сценарий должен восстановиться в том же браузере. На другом компьютере для переноса понадобится экспортированный файл.
Методика расчёта
Десять показателей T1…C2 находятся в диапазоне 0–100; больше — лучше. Доли населения районов суммируются до 1. Данные в simulator/model.js — учебные допущения, не подтверждённая статистика города; название набора «HackAlem» в коде само по себе не подтверждает происхождение данных.
Горизонт — 8 кварталов; для меры с лагом L коэффициент эффекта r=(8−L)/8.
Новый показатель = clip(исходный + Σ(эффект × r) + Σсинергий, 0, 100)
Индекс района D = Σ(показатель × вес)
Средний индекс Davg = Σ(D × доля населения)
Score = clip(0.7 × Davg + 0.3 × min(D) − Ncrit, 0, 100)
Ncrit — число пар «район × показатель» со значением строго ниже 40. Ограничение шкалы применяется после суммирования эффектов. Веса T1…C2: 0.10, 0.10, 0.09, 0.11, 0.11, 0.11, 0.09, 0.09, 0.10, 0.10. Синергии фиксированы и не умножаются на лаг. Округление — только при отображении.
Синергии: M1+M2 → T1+2; M10+M12 → B1+2; M5+M6 → E2+2 в каждом районе совместного действия. Несовместимости: M1+M3 везде; M4+M7 в одном районе; M5+M13 в одном районе. Городская мера оплачивается один раз. Нельзя повторить одно мероприятие в другом районе, чтобы считать его отдельным решением.
Показатель можно называть учебным Astana Quality of Life Score; текущий интерфейс использует подпись «Итоговый балл города». Это авторская формула, не официальный индекс. Реальные сроки, эксплуатационные затраты и статистическая неопределённость полноценно не моделируются.
Тесты и проверка
pnpm test:model # модель; база не нужна
pnpm test # 12 проверок авторизации на PostgreSQL
pnpm test:snapshot # восстановление синтетических данных в отдельной схеме
pnpm test:http # требуется запущенный сервер и ALLOW_REGISTRATION=true
pnpm build # production-сборка
Для тестов используйте отдельную тестовую PostgreSQL в .env.local. Тестовой роли нужны права создавать схемы; ограниченная production-роль для этого не подходит. test и test:snapshot создают и удаляют только свои временные схемы; HTTP-тест создаёт и удаляет временного пользователя. Восстановление тестируется на синтетических данных и не требует приватного файла бэкапа.
Результаты проверки именно этого архива: VERIFICATION.md. Локальная сборка, модель, PostgreSQL-тесты, HTTP и пользовательский сценарий проверены. Docker, Windows-скрипты, внешняя Supabase и настоящий Netlify runtime отдельно не запускались.
Авторизация, API и данные
| Метод | Маршрут | Назначение |
|---|---|---|
| GET | /api/auth/config | Доступность регистрации |
| POST | /api/auth/register | {login,password,name}; создаётся только participant |
| POST | /api/auth/login | Вход и установка cookie |
| POST | /api/auth/logout | Удаление сессии |
| GET | /api/me | Текущий пользователь; без сессии 401 |
| GET | /api/health | Проверка подключения и чтения трёх таблиц авторизации |
| GET | /simulator | HTML симулятора после проверки сессии |
| GET | /simulator/app.js, /simulator/model.js | Защищённые ресурсы |
POST авторизации проверяет Origin. Пароли хешируются scrypt. Сервер хранит SHA-256 хеш токена сессии; cookie имеет HttpOnly, SameSite=Strict и Secure при HTTPS. Есть ограничения частоты регистрации и попыток входа. SQL-запросы параметризованы. Регистрация не позволяет повысить роль через JSON.
Таблицы: users, sessions, auth_limits. Таблиц для наборов и сценариев нет. Роль admin можно создать через pnpm user:create, задав NEW_USER_LOGIN, NEW_USER_PASSWORD, NEW_USER_NAME, NEW_USER_ROLE=admin; это не добавляет отсутствующую серверную защиту импорта.
Docker, сервер и резервные копии
Локальный Docker-вариант:
node scripts/review.mjs 3000
Скрипт создаёт .review.env со случайными паролями и запускает Compose. Требуется установленный Docker с Linux-контейнерами. PostgreSQL хранится в именованном томе; останавливайте docker compose --env-file .review.env down без -v, если нужно сохранить данные.
compose.server.yaml и Caddy предназначены для отдельного VPS, не для Netlify. deploy.sh требует заранее подготовленный .server.env с DOMAIN, POSTGRES_PASSWORD, APP_DB_PASSWORD, ALLOW_REGISTRATION; файл не включён в поставку. Для Netlify эти скрипты не запускайте.
scripts/export-snapshot.mjs экспортирует авторизацию в backup/database.json; restore-snapshot.mjs отказывается перезаписывать непустую базу. Резервные копии содержат пользовательские записи и данные сессий — храните их приватно, вне Git. Симуляционные планы нужно экспортировать отдельно через интерфейс.
Структура
app/ вход, кабинет, API, выдача симулятора
lib/ PostgreSQL, авторизация, HTTP-защита
simulator/ интерфейс, расчёт и встроенный набор
db/ схема авторизации и Docker-инициализация
scripts/ миграции, пользователи, запуск и бэкапы
tests/ модель, авторизация, HTTP, восстановление
netlify.toml настройки сборки Netlify
next.config.mjs заголовки и включение файлов симулятора
Dockerfile альтернативная контейнерная сборка
Типичные ошибки
- 503 на health / сервис входа недоступен: проверить подключение, SSL, три таблицы и права серверной роли.
- 403 при входе:
APP_ORIGINне совпадает с адресом браузера; проверьте HTTPS, порт и отсутствие завершающего/. - 404 / не загружается симулятор: нужен полный Next.js deploy; проверьте Base directory и inclusion файлов
simulatorв функции. - 429: достигнут лимит попыток; подождите 15 минут.
- План исчез на другом устройстве: он хранится в localStorage; используйте экспорт JSON.
- Ошибка импорта: набор должен содержать шесть листов, включая «Ограничения»; доли населения должны давать 1. XLSX требует доступа к CDN.
Приоритеты дальнейшей работы
- Подключить настоящий серверный AI-разбор, сохранив детерминированный расчёт балла.
- Закрепить общий набор и бюджет на сервере; проверять права организатора.
- Сохранять и пересчитывать сценарии в PostgreSQL, не доверять клиентскому Score.
- Согласовать правило пяти направлений с организаторами. Сейчас «5 решений» не означает «5 направлений».
- Подтвердить полный путь на опубликованном Netlify-сайте, затем добавить сравнение команд и проверяемые рекомендации.
Документ Рекомендации-по-проекту-Аким.md в архиве — ранее составленный план развития, а не список уже реализованных функций. Актуальное состояние этой поставки описано выше.
Авторы коммитов
История коммитов
- 2026-09-23 17:46Add files via uploadsnr-png
- 2026-09-23 17:14Add files via uploadamirlyand
- 2026-09-23 17:10Add verified Netlify project with README and verification reportJulia Kozlova
- 2026-09-23 16:56Add Netlify-ready city simulatorsnr-png
- 2026-09-23 16:28Add files via uploadamirlyand
- 2026-09-23 16:27Add updated auth simulator source archiveJulia Kozlova
- 2026-09-23 15:47Combine city simulator with existing hackathon resourcesJulia Kozlova
- 2026-09-23 15:34Add files via uploadsnr-png
Все коммиты (19)
- 2026-09-23 15:30Add files via uploadamirlyand
- 2026-09-23 15:18Add files via uploadlisstikks
- 2026-09-23 15:16Add files via uploadsnr-png
- 2026-09-23 15:13Add files via uploadamirlyand
- 2026-09-23 14:58Build city management simulator with budget validation and AI analysisJulia Kozlova
- 2026-09-23 14:34first commitJulia Kozlova
- 2026-09-23 14:32Add files via uploadamirlyand
- 2026-09-23 14:29Merge pull request #1 from BAITC-Hacks/codex/latest-city-planner-mockupamirlyand
- 2026-09-23 14:28Add files via uploadlisstikks
- 2026-09-23 14:27Add files via uploadsnr-png
- 2026-09-23 14:15Add latest Russian city planner mockup with data importamirlyand
Структура репозитория (66 файлов)
.dockerignore .env.example .env.production.example .gitignore Dockerfile NETLIFY-QUICKSTART.md README.md START-HERE.md akim-auth-source-updated.zip akim-auth-source.zip akim-mockup.html akim-server-deploy-netlify.zip app/api/auth/config/route.js app/api/auth/login/route.js app/api/auth/logout/route.js app/api/auth/register/route.js app/api/health/route.js app/api/me/route.js app/globals.css app/layout.js app/page.js app/simulator/[asset]/route.js app/simulator/route.js backup-server.sh compose.server.yaml compose.yaml db/001_auth.sql db/docker-init.sh deploy.sh deploy/Caddyfile lib/auth.mjs lib/db.mjs lib/http.mjs lib/service.mjs mockup/README.md mockup/akim-auth-source.zip mockup/akim-city-planner.fragment.html mockup/index.html netlify.toml next.config.mjs package.json pnpm-lock.yaml pnpm-workspace.yaml scripts/bootstrap-postgres.mjs scripts/create-user.mjs scripts/download-postgres.mjs scripts/export-snapshot.mjs scripts/migrate.mjs scripts/prepare-local-config.mjs scripts/restore-snapshot.mjs scripts/review.mjs scripts/setup-postgres.ps1 scripts/snapshot.mjs scripts/start-local.ps1 simulator/app.js simulator/index.html simulator/model.js tests/auth.test.mjs tests/http.test.mjs tests/model.test.cjs tests/snapshot.test.mjs ЗАПУСК-НА-СЕРВЕРЕ.md Рекомендации-по-проекту-Аким.md СОСТАВ-ПАКЕТА.md Формулы симулятора Аким на 5 часов.md Формулы_симулятора_Аким_на_5_часов.md
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.