Commit Graph
33 Commits
Author SHA1 Message Date
vasyanskandClaude Opus 5 98be357da8 fix(web): handle empty-body 2xx responses; fix(store): sync DeleteDomainsNotInZones query with generated columns
req() in the frontend api client called res.json() on every non-204 2xx
response, which throws SyntaxError on the empty body returned by
POST .../customs (201) and similar endpoints. Read the response body once
as text and parse it only when non-empty, so any bodyless 2xx resolves
instead of rejecting. Added web/src/api/client.test.ts coverage that
exercises the real req() against a stubbed fetch for both the 201
(addCustom) and 204 (removeCustom) empty-body cases.

Also brought internal/store/queries/domains.sql's DeleteDomainsNotInZones
query back in sync with the hand-maintained internal/store/db/domains.sql.go
(RETURNING * -> explicit column list) since sqlc isn't installed in this
environment and the two files are kept aligned by hand.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Yr8frsaxBgab1Aa7yfPuU
2026-08-19 19:04:13 +07:00
vasyansk 58a57ff4d0 docs: custom records and import-as-sync invariants
Document the two Task 1-9 features in CLAUDE.md invariants and README
features. Also closes two review gaps: a Go test proving importResponse
marshals created/removed as [] (never null) on a no-op import, and a
frontend test-race fix in DomainsPage.test.tsx where the import button
was clicked before its disabled state cleared.
2026-08-19 18:45:52 +07:00
vasyansk d6d91f8826 feat(web): show created and removed counts after zone import 2026-08-19 18:37:56 +07:00
vasyansk e00648baf7 feat(web): customs section with mark and unmark actions 2026-08-19 18:22:32 +07:00
vasyansk 404f869d42 feat(web): api client and hooks for custom record marks 2026-08-19 18:16:42 +07:00
vasyanskandClaude Opus 5 dcaf3e65f5 feat(web): clone existing templates
Add a "Clone" action to the templates table: it fills the create form
with a copy of the selected template's name and records instead of
creating the copy right away, so the operator can adjust the name or
records before saving. No new API — the existing POST /templates is
reused.

Cloning resets editingId so the submit goes to createTemplate even when
edit mode was active, and the copy name is picked to be free among the
listed templates ("X (копия)", then "X (копия 2)"). Template names have
no unique constraint in the DB, so this is a UX hint, not a guarantee.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Yr8frsaxBgab1Aa7yfPuU
2026-08-19 17:17:34 +07:00
vasyanskandClaude Opus 4.8 e283e5f22a fix: document apply ordering invariant; visible indeterminate checkbox + test
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
2026-07-05 15:26:46 +07:00
vasyanskandClaude Opus 4.8 2f1f5311ad feat(web): per-record apply checkboxes with select-all; prune opt-in
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 15:20:09 +07:00
vasyanskandClaude Opus 4.8 784e7bd822 fix(web): wrap long record values in diff and zone view (no horizontal overflow)
RecordRow now splits into a top line (badge/name/read-only, unaffected
by value length) and a plain block-level values line below it, so a
~400-char unbreakable DKIM key wraps via break-all instead of stretching
the flex row and forcing page-wide horizontal scroll. Zone records table
gets break-all on the values cell too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
2026-07-05 14:27:33 +07:00
vasyanskandClaude Opus 4.8 655ae8ccf8 feat(web): hint about {{domain_name}} placeholder in template editor
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 13:48:24 +07:00
vasyanskandClaude Opus 4.8 bc2e77ad4e fix: empty changeset must serialize as [] not null (white-screen after snapshot)
toChangesetResponse initialises updates/prunes/readOnly so a zone matching
its template exactly (e.g. right after 'create template from zone') marshals
arrays, not null. DiffView/DomainDiffPage also normalise null defensively.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
2026-07-05 13:10:08 +07:00
vasyanskandClaude Opus 4.8 9f0938daea fix: reject snapshot when template already attached (409); handle domains-load error; drop orphaned useDeleteDomain
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 12:54:52 +07:00
vasyanskandClaude Opus 4.8 137113cbe6 fix(web): gate zone-records fetch to no-template case; wait for domains load before branching
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 12:28:07 +07:00
vasyanskandClaude Opus 4.8 c2832348f8 feat(web): view zone without template, snapshot button, no-template status, drop delete
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
2026-07-05 12:19:50 +07:00
vasyanskandClaude Opus 4.8 e8e7371f09 fix: drain Identity error body (keep-alive); reject whitespace-only credential fields in form
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 20:36:50 +07:00
vasyanskandClaude Opus 4.8 be408a216c feat(web): Selectel service-user account form (IAM credentials)
Replace the single API-key field with 4 IAM service-user fields
(username, password, account_id, project_name) matching the new
backend contract; map 400 "invalid provider credentials" to a
user-facing message.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
2026-07-04 20:23:34 +07:00
vasyanskandClaude Opus 4.8 7256adf637 fix(web): scope Suspense to page body; guard formatConfig against null config
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 16:12:21 +07:00
vasyanskandClaude Opus 4.8 8c35aed8f2 perf(web): route-level code-splitting; harden channel config rendering
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
2026-07-04 16:04:17 +07:00
vasyansk 34422420ca feat(web): расписание, каналы уведомлений, история проверок, drift-badge 2026-07-04 14:40:29 +07:00
vasyanskandClaude Opus 4.8 45259b9720 feat(web,api): клиент/хуки расписания/каналов/истории + lastCheckStatus в domainResponse
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
2026-07-04 14:24:02 +07:00
vasyansk 901eb51e2a fix(auth): серверная проверка длины пароля, loading-guard и различение ошибок на auth-страницах 2026-07-03 21:33:03 +07:00
vasyansk 5a4d560e70 feat(web): Login/Register страницы, protected routes, logout
- ProtectedRoute: loading -> спиннер, !user -> /login, иначе children
- LoginPage/RegisterPage: field+react-hook-form/zod, ошибка через role=alert,
  редирект на /domains при успехе/уже авторизован
- main.tsx: AuthProvider + QueryCache/MutationCache onError -> notifyUnauthorized
  на UnauthorizedError (сброс сессии из кода вне React-дерева)
- AuthContext: logout и notifyUnauthorized чистят react-query кэш (qc.clear())
- Layout: email пользователя + кнопка Выйти
- App: /login и /register публичные (авторизованный -> /domains), остальное
  под ProtectedRoute

Починка page-тестов (Accounts/Domains/Templates/DomainDiff/App): AuthProvider
+ мок api.auth.me, спай-ассерты обновлены под projectId-первым-аргументом
сигнатур api.* (T5).
2026-07-03 21:21:29 +07:00
vasyanskandClaude Opus 4.8 222d6c0453 fix(web): null-guard в мутациях (no active project), AuthContext различает 401 и ошибки сервера
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
2026-07-03 21:07:48 +07:00
vasyansk b5d9e8f7ab feat(web): AuthContext + клиент под cookie-сессии, projectId из контекста 2026-07-03 21:00:18 +07:00
vasyansk 388bf4aeb6 fix(web): валидация записей шаблона — пустые values не уходят в API, ошибки видимы 2026-07-03 18:10:10 +07:00
vasyansk 99e09d35fb feat(web): TemplatesPage + RecordEditor — CRUD шаблонов с редактором записей 2026-07-03 18:02:08 +07:00
vasyansk 4e91211a89 feat(web): AccountsPage — CRUD учёток, secret-форма, инструкция Selectel 2026-07-03 17:52:59 +07:00
vasyansk ef003230fa fix(web): DomainsPage показывает ошибки привязки шаблона и удаления 2026-07-03 17:49:48 +07:00
vasyansk 0ce15d30a8 feat(web): DomainsPage — список, импорт зон, привязка шаблона 2026-07-03 17:43:49 +07:00
vasyanskandClaude Opus 4.8 1412da9a31 feat(web): DiffView + DomainDiffPage с prune-guard по умолчанию выключенным
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
2026-07-03 17:32:20 +07:00
vasyanskandClaude Opus 4.8 267ffc4ed9 fix(web): Domain.templateId опционален (Go omitempty), String() на error
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 17:19:51 +07:00
vasyansk 6f82036e38 feat(web): типизированный API-клиент, типы DTO, TanStack Query хуки 2026-07-03 17:14:11 +07:00
vasyansk 41242973e1 feat(web): scaffold Vite+React+TS, Tailwind v4, shadcn, router, тёмная console-тема 2026-07-03 17:06:23 +07:00