Commit Graph

11 Commits

Author SHA1 Message Date
vasyansk 27d70a987e fix(store): scope SetDomainStatus by project (IDOR); scheduler reuses DeriveStatus
handleCheck's error branch wrote last_check_status via an id-only UPDATE, so
an authenticated caller's own valid project id paired with a foreign domain
id in the URL could flip a stranger's domain to "error" even though Check
itself is project-scoped and would 404/error out first. Add project_id to
the WHERE clause (queries/domains.sql + generated db/domains.sql.go), thread
projectID through Store/TenantStore/SchedStore SetDomainStatus, and pass pid
from context at both call sites in handleCheck plus the scheduler.

Also collapse checkDomain's inline status derivation in scheduler.go into a
call to service.DeriveStatus, the same helper handleCheck already uses, so
there's a single source of truth for "drift vs in_sync" instead of two
copies that could drift apart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
2026-07-05 14:40:13 +07:00
vasyansk 1b367c4bda fix(api): manual check persists last_check_status (was stale unknown)
Manual domain checks (Recheck button / diff page load) never wrote
domains.last_check_status - only the scheduler did, leaving a
newly-templated domain stuck at "unknown" until the next scheduled run.

Extract status derivation into internal/service (single source of truth):
StatusUnknown/InSync/Drift/Error constants and DeriveStatus(diff.Changeset).
The scheduler now aliases these constants instead of duplicating them.
handleCheck persists the derived status (or StatusError on failure) via
TenantStore.SetDomainStatus after every manual check - status/history only,
no notification, which remains the scheduler's job.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
2026-07-05 14:22:02 +07:00
vasyansk 9ccb304d2e feat(api): read zone records without template + snapshot-to-template
LoadDomain requires a template, so a zone without one could never be
viewed or snapshotted. Adds a template-free path: store.LoadZone /
service.ZoneRef / DomainService.ZoneRecords read a zone's live records
straight from the provider (no diff, no template). GET
/domains/{did}/records exposes read-only viewing; POST
/domains/{did}/template-from-zone snapshots only managed record types
(NS/SOA excluded) into a new template and auto-attaches it to the domain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
2026-07-05 12:00:27 +07:00
vasyansk 7d4bf153d7 feat(api): CRUD расписания/каналов + тест-отправка + история проверок
Task 5 Фазы 3: GET/PUT /schedule (дефолт при отсутствии строки, валидация
interval>=60), POST/GET/DELETE /channels (секрет шифруется Cipher, никогда
не возвращается в ответах), POST /channels/{cid}/test через узкий
TestSender-интерфейс (200/502 без утечки секрета), GET /domains/{did}/history
(сначала GetDomain для project-scoping, затем ListCheckRuns — иначе IDOR
через check_runs, который сам по себе не scoped по project).

Добавлены store.GetDomain (обёртка над существующим sqlc-запросом) и
store.ListCheckRuns (новый запрос + sqlc regen) для поддержки истории.
2026-07-04 13:24:50 +07:00
vasyansk 4533b0ca25 feat(api): RequireAuth+RequireProjectAccess middleware, IDOR-scope check/apply по projectID 2026-07-03 20:47:40 +07:00
vasyansk 35ffe73ae3 fix(auth): wiring Auth/Sessions, нормализация email, GetUserByID для /me, 409 на дубль, timing-guard логина 2026-07-03 20:29:05 +07:00
vasyansk aa0ef1c6a9 feat(api): auth-хендлеры register/login/logout/me + session cookie 2026-07-03 20:11:00 +07:00
vasyansk ddab6e2162 fix(store,api): идемпотентный import (UNIQUE+ON CONFLICT) + PATCH привязки шаблона к домену 2026-07-03 15:24:08 +07:00
vasyansk 2aca92d070 fix(api): tenant-проверка account/template в CreateDomain (HIGH), атомарный import через транзакцию (MEDIUM) 2026-07-03 15:08:16 +07:00
vasyansk ae6a4d7f4c feat(api): CRUD accounts/templates/domains + import зон (полный цикл), secret не в ответах
Task 9 Фазы 1B: узкий интерфейс TenantStore (внутри store.Account/Template/Domain,
без db.* в api) реализован тонкими обёртками в internal/store/tenant.go; API.Store/
Cipher/Reg добавлены к существующему Svc. Роуты POST/GET/DELETE для accounts/
templates/domains + POST /accounts/{aid}/import (ListZones -> CreateDomain на зону).
accountResponse не содержит секрет ни в каком виде.
2026-07-03 14:53:29 +07:00
vasyansk fdf90a7c23 feat(api): chi-роутер, check/apply хендлеры, changeset DTO 2026-07-03 14:28:06 +07:00