CRM для фриланса, которую не стыдно открыть при заказчике: сделки, счета, клиенты, задачи и аналитика — в одном месте.
Тёмная «чернильная» база + один фиолетовый акцент (отлив воронова пера), типографика Manrope + Inter с кириллицей, светлая и тёмная темы из oklch-токенов. Сдержанность уровня Linear — ноль каруселей и градиентных баннеров.
| Модуль | Коротко |
|---|---|
| 📊 Дашборд | воронка продаж, выручка по месяцам, задачи на сегодня и просрочки |
| 🎯 Воронка | Kanban с drag-and-drop; перенос в «Выиграна»/«Проиграна» закрывает сделку |
| 🤝 Сделки | карточки: задачи, активности, теги (через запятую), CSV-экспорт, фильтры |
| 🧾 Счета | позиции работ, автонумерация INV-0001…, статусы, просрочки, печать/PDF |
| 👥 Клиенты | контакты и компании, CSV-импорт и экспорт |
| ✅ Задачи | список + Kanban-доска по статусам, приоритеты, дедлайны |
| 📅 Календарь | задачи и дедлайны проектов по месяцам |
| 📁 Проекты | привязка к сделке 1:1 (уровень БД), статусы |
| 💬 Активности | звонки, письма, встречи, заметки — по сделкам, контактам, компаниям |
| ⚙️ Настройки | этапы воронки: названия, цвета, порядок drag-and-drop |
| 🔎 ⌘K | командная палитра: навигация + глобальный поиск по сделкам, контактам, счетам |
| 🌓 Темы | светлая и тёмная из oklch-токенов, переключатель в сайдбаре |
| 👤 Роли | ADMIN / MANAGER / VIEWER — зритель только читает (проверено матрицей тестов) |
git clone https://github.com/ssrjkk/corvid_crm.git
cd corvid_crm
npm install
# .env — пример:
# DATABASE_URL="file:./dev.db"
# BETTER_AUTH_SECRET="сгенерируй: npx auth secret"
# BETTER_AUTH_URL="http://localhost:3000"
npx prisma migrate dev
npm run dev # → http://localhost:3000 (в другом терминале:)
npm run db:seed-demo # демо-аккаунты ([email protected] / [email protected], оба Test12345!) + демо-данные: этапы воронки, сделка, счёт INV-0001 (идемпотентно, трогает только пустые таблицы)Первый зарегистрированный через /sign-up пользователь получает роль ADMIN.
Для e2e нужны запущенный npm run start и демо-аккаунты: npm run db:seed-demo
сеет ОБА аккаунта идемпотентно (админ [email protected] и зритель [email protected]) — оба Test12345!.
npm test # Vitest: 129 тестов — вся чистая логика + ВСЕ 11 tRPC-роутеров на изолированной test.db, матрица ролей, русские тосты, money-арифметика, гонка номеров счетов, сводка дашборда, ротация бэкапов, мультивалютные сводки
npm run lint # ESLint: 0 ошибок, 0 варнингов
npm run build # Next.js 16 production-сборка: prerender ловит то, что dev прощает
npm run e2e # Playwright (20): setup-логины (админ + VIEWER), лендинг, OG-мета и robots.txt, 307-защита, неверный пароль→тост, security-заголовки и CSP, счета, печать, read-only матрица VIEWER
npx playwright test --project=a11y # axe-core a11y-аудит (5): WCAG 2 A/AA, serious/critical = 0 на лендинге, входе, 404, дашборде, счетах- CI гоняет ВСЁ: drift guard (
npm run db:check— схема обязана соответствовать миграциям, иначе красный) → test → lint → build ДО e2e (webServer в e2e стартует собранный.next) → полный e2e (Playwright сам поднимает сервер через webServer,CI=true); новый пуш отменяет устаревший прогон ветки. - Один логин на роль в e2e (setup-проекты + shared storage): дефолтный rate-limit better-auth — max 3 запроса к
/sign-in*за 10 секунд. - CSV-инъекции нейтрализуются (
'-префикс, CWE-1236), импорт ограничен 5 МБ. - Без
BETTER_AUTH_SECRETproduction-сборка падает сразу (fail-closed), а не раздаёт forgeable-сессии. - Удаление любых сущностей — только ADMIN (проверено тестами на всех роутерах); security-заголовки (X-Frame-Options, nosniff, Referrer-Policy, Permissions-Policy) проверяются e2e-тестом.
SQLite живёт в WAL-режиме (journal_mode=WAL — быстрые записи, читатели не блокируют писателя).
Следствие: наивное копирование dev.db при работающем приложении теряет последние транзакции —
они лежат в dev.db-wal, а не в основном файле.
npm run db:backup # снапшот в backups/corvid-YYYYMMDD-HHmmss.dbСкрипт делает безопасно:
PRAGMA wal_checkpoint(TRUNCATE)— сливает wal в основной файл и обрезает его;- копирует файл в
backups/; - доказывает целостность копии (
PRAGMA quick_checkна копии; битая копия удаляется, скрипт падает); - ротация: хранит
BACKUP_KEEP(по умолчанию 10) свежих бэкапов, старые удаляет.
Восстановление: остановить сервер → заменить dev.db файлом из backups/ → запустить.
backups/ в git не попадает.
Next.js 16 (App Router, Cache Components, Partial Prerender) · React 19 · TypeScript 5 · Tailwind CSS v4 (oklch-токены) · tRPC v11 + TanStack Query · Prisma 7 + SQLite · Better Auth (email/пароль + роли) · recharts · dnd-kit · next-themes · Vitest + Playwright.
Дизайн-система проекта живёт в .agents/skills/corvid-design/SKILL.md — токены, типографика, грабли Next 16 и чек-листы подхватываются агентами автоматически.
- Роли: ADMIN / MANAGER / VIEWER; мутации — только MANAGER+ADMIN, удаление любой сущности — только ADMIN (серверные
adminProcedure+ UI-гейтыusePermissions, проверены тестами на всех 11 роутерах и e2e под ролью VIEWER). - Заголовки (проверяются e2e-тестом):
Content-Security-Policy(script-src self, SRI-хеши на чанках — experimentalsri),X-Frame-Options: DENY,X-Content-Type-Options: nosniff,Referrer-Policy: strict-origin-when-cross-origin,Permissions-Policy(camera/microphone/geolocation выключены). - Секрет: без
BETTER_AUTH_SECRETв production процесс падает при старте (fail-closed), поддельные сессии невозможны. - CSV: инъекции формул (
= + - @ Tab CR, CWE-1236) нейтрализуются префиксом'; импорт ограничен 5 МБ. - Русский интерфейс ошибок: локаль zod (русская), читаемое сообщение вместо JSON от tRPC, маппинг кодов better-auth — сырые английские тексты до пользователя не доходят (проверено e2e).
- Аудит зависимостей: продакшен-дерево
dependenciesбез известных уязвимостей (см. SKILL.md про dev-цепочки).
Подходит для Vercel/собственного сервера. Для продакшена с доступом с нескольких устройств переведи БД на PostgreSQL (prisma/schema.prisma → provider = "postgresql", миграции те же). Локальный SQLite удобен для личного использования на одном ПК.
Обязательные переменные окружения: DATABASE_URL, BETTER_AUTH_SECRET (≥32 символов, npx auth secret), BETTER_AUTH_URL — точный адрес, по которому открывается приложение: better-auth отбивает запросы с чужим Origin (403 INVALID_ORIGIN).
Порядок выкладки: npm run db:backup → npx prisma migrate deploy → npm run build → старт сервера. Миграции применяются до старта, бэкап — до миграций (sqlite-файл возвращается на место при откате). Схема без миграции не пройдёт и CI: npm run db:check (exit 2 = дрейф).
MIT © 2026 Sergey Sitnikov (ssrjkk)