Skip to content

P1 Foundation + IPAM — Phase 1 Complete (v0.1.0)#22

Merged
fray-cloud merged 64 commits intomainfrom
dev
Mar 22, 2026
Merged

P1 Foundation + IPAM — Phase 1 Complete (v0.1.0)#22
fray-cloud merged 64 commits intomainfrom
dev

Conversation

@fray-cloud
Copy link
Copy Markdown
Owner

Summary

Phase 1 마일스톤 전체 구현. 17개 이슈(#2~#18) 모두 CLOSED.

Backend Services (Python 3.13, FastAPI, DDD+CQRS+ES)

  • IPAM Service — 11개 엔티티, REST API + GraphQL, Bulk CRUD, Full-text Search, Saved Filters, Import/Export, Jinja2 Templates
  • Auth Service — JWT(RS256), RBAC, 사용자/역할/그룹/API토큰
  • Tenant Service — 멀티테넌트 DB 격리, Setup Wizard API
  • Event Service — Event Store, ChangeLog, Journal Entries
  • Webhook Service — 이벤트 매칭, HTTP 발송, HMAC 서명, 재시도

Frontend (Next.js 16 Monorepo)

  • apps/client — IPAM CRUD, 대시보드, 글로벌 검색(Cmd+K), 변경이력, 저널
  • apps/admin — 스켈레톤(P2)
  • packages/shared — API 클라이언트, 인증, 타입, hooks
  • Setup Wizard, shadcn/ui, TanStack Query/Table

Infrastructure

  • Docker Compose 통합 (10개 컨테이너 + 4개 init)
  • Nginx API Gateway (JWT auth_request, rate limiting)
  • make dev-init 자동화

Testing

  • 681+ 단위 테스트, Docker 통합 테스트 (testcontainers)

Merge 후 작업

  • v0.1.0 태그 + GitHub Release 생성 예정

P2 이슈

🤖 Generated with Claude Code

fray-cloud and others added 30 commits March 18, 2026 23:23
- Set up uv workspace with shared library and 5 service packages (ipam, auth, tenant, event, webhook)
- Each service follows DDD layer structure (domain, application, infrastructure, interface)
- Initialize Next.js 16 frontend with TypeScript, Tailwind CSS, App Router
- Add ruff for Python linting/formatting with pre-commit hooks
- Add GitHub Actions CI for Python lint/test and frontend lint
- Add nginx config, .gitignore, version files (.python-version, .node-version)

Resolves #2

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Base Dockerfile with multi-stage dev/prod targets
- Per-service Dockerfiles (prod) and Dockerfile.dev (dev with hot reload)
- Infrastructure compose: PostgreSQL (5 DBs), Kafka KRaft, Redis, Nginx
- Per-service compose files with complete dev/prod separation
- Root compose with include (dev includes dev only, prod includes prod only)
- Makefile for command standardization (extended set)
- Environment variable management (.env.example, .env.dev)
- Nginx config with per-service upstream routing and WebSocket support

Resolves #3

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Entity base class (Pydantic, id/created_at/updated_at)
- ValueObject base class (frozen Pydantic for immutability)
- Repository abstract base class (async find_by_id/save/delete)
- DomainService marker base class
- Domain exception hierarchy: DomainError, EntityNotFoundError,
  BusinessRuleViolationError, AuthorizationError, ConflictError,
  ValidationError, InfrastructureError

Part of #4 (section 3.4)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Command/Query base classes (frozen Pydantic models)
- CommandHandler/QueryHandler abstract base classes (async, generic return)
- CommandBus/QueryBus with explicit type mapping registration
- Dispatch raises ValueError for unregistered command/query types

Part of #4 (section 3.1)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- DomainEvent base class (frozen Pydantic, auto event_type via __init_subclass__)
- AggregateRoot with convention-based _apply_{EventName} dispatch
- EventStore ABC with optimistic concurrency (expected_version)
- PostgresEventStore implementation (SQLAlchemy async, JSONB payload)
- StoredEvent/StoredSnapshot SQLAlchemy models (separate DeclarativeBase)
- SnapshotStrategy (configurable every N events)
- Snapshot support (to_snapshot/from_snapshot on AggregateRoot)

Part of #4 (section 3.2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- EventSerializer with registry-based DomainEvent deserialization
- KafkaEventProducer (aggregate_id as key for partition ordering)
- KafkaEventConsumer with manual commit and Dead Letter Queue support
- Context manager support for producer lifecycle

Part of #4 (section 3.3)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CustomFieldDefinition with FieldType enum and validation
- CustomFieldValidator for type checking, choices, and regex validation
- Tag model (name, slug, color)
- SQLAlchemy models: CustomFieldDefinitionModel, TagModel, TagAssignmentModel
- CustomFieldMixin (JSONB column + GIN index) for entities
- Filtering utilities for custom fields and tags
- Generic FK pattern (content_type + object_id) for cross-service tags

Part of #4 (section 3.5)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- TenantMiddleware (X-Tenant-ID header extraction)
- CorrelationIdMiddleware (X-Correlation-ID generation/propagation)
- Pagination: OffsetParams/Page + CursorParams/Page with base64 cursor encoding
- Filtering: FilterOperator enum + apply_filters for SQLAlchemy queries
- Sorting: SortParam + apply_sorting for SQLAlchemy queries
- RFC 7807 Problem Details error responses with DomainError → HTTP status mapping
- OpenAPI schema customization utility

Part of #4 (section 3.6)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Tenant entity with create/suspend/mark_deleted/update_settings
- TenantStatus (active/suspended/deleted), TenantSettings, TenantDbConfig
- Domain events: TenantCreated, TenantSuspended, TenantDeleted
- TenantRepository interface with find_by_slug and find_all

Part of #5 (section 4.1)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CreateTenantCommand/Handler with DB provisioning and Kafka events
- SuspendTenantCommand/Handler, DeleteTenantCommand/Handler
- UpdateTenantSettingsCommand/Handler
- GetTenantQuery/Handler, ListTenantsQuery/Handler
- TenantDTO for read model (hides db_config credentials)

Part of #5 (section 4.2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- PostgresTenantRepository (SQLAlchemy async, merge for upsert)
- TenantModel (tenants table with JSONB settings/db_config)
- TenantDbProvisioner (CREATE DATABASE + programmatic Alembic)
- TenantDbManager (dynamic engine cache per tenant)
- Database helper (async engine + session factory)
- Settings (pydantic-settings)
- Alembic setup for tenant service DB (001_create_tenants_table)
- Alembic scaffold for per-tenant DBs (empty, ready for service migrations)
- Updated pyproject.toml with asyncpg, alembic, pydantic-settings deps

Part of #5 (section 4.3)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- FastAPI app with lifespan (DB, Kafka producer, provisioner, DB manager)
- REST endpoints: POST/GET/PATCH/DELETE /tenants, POST /tenants/{id}/suspend
- Per-request CommandBus/QueryBus wiring via FastAPI dependencies
- Request/response schemas with slug validation (^[a-z0-9-]+$)
- CorrelationIdMiddleware and DomainError exception handler

Resolves #5

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auth Service for the CMDB platform implementing local authentication,
authorization, and API token management as defined in issue #6.

Domain: User, Role, Group, APIToken aggregates with Permission VO
Application: CQRS with 12 commands and 7 queries
Infrastructure: bcrypt hashing, RS256 JWT with JWKS endpoint,
  Redis token blacklist, login rate limiting, PostgreSQL repositories
Interface: 17 REST endpoints across 5 routers (auth, users, roles,
  api-tokens, permissions)

Deferred: SSO (OIDC/SAML), MFA (TOTP), email verification

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… rate limiting

Nginx API Gateway for the CMDB platform as defined in issue #7.

- URL versioning: /api/v1/ prefix for all service endpoints
- JWT validation via auth_request subrequest to Auth Service /auth/validate
- Public endpoints (login, register, refresh, JWKS) bypass auth
- Rate limiting: IP-based 30r/s with burst=50 (limit_req_zone)
- CORS: map-based origin matching (localhost dev + *.cmdb.io prod)
- SSL/TLS: self-signed cert support (443 + 80→443 redirect)
- JSON access logging with correlation_id
- Auth Service additions: GET /auth/validate, GET /health endpoints

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nge Log

Central Event Service for the CMDB platform as defined in issue #8.

- Kafka Consumer with regex pattern subscription (*.events)
- Generic event storage: raw JSON payload without deserialization
- stored_events table: aggregate_id, event_type, version, payload (JSONB)
- change_logs table: aggregate_id, action, user_id, tenant_id metadata
- REST API: event stream replay, changelog by aggregate/type/tenant
- Auto-extraction of aggregate_type and action from event_type strings
- DLQ support for failed message processing
- Alembic migration for stored_events + change_logs tables

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…cing pattern

IPAM Core Domain for the CMDB platform as defined in issue #9.

Core Aggregates (AggregateRoot pattern with event sourcing):
- Prefix: IPv4/IPv6 subnet management with hierarchical structure
- IPAddress: Individual IP address management with VRF scoping
- VRF: Virtual Routing and Forwarding domains with Route Distinguisher
- VLAN: IEEE 802.1Q VLAN management with VLANId(1-4094) validation

Value Objects: PrefixNetwork (CIDR validation, contains()),
  IPAddressValue, VLANId, RouteDistinguisher, Status enums

Domain Services: PrefixUtilization, AvailablePrefix, IPAvailability

Fix: shared DomainEvent.__init_subclass__ → __pydantic_init_subclass__
  to preserve subclass fields during model rebuild

338 unit tests (pytest) covering all aggregates, VOs, and services

Deferred: IPRange, RIR/ASN, FHRP Group, Service (후속 이터레이션)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d refactoring

- Extract TenantDbManager to shared/db for cross-service reuse
- Add external session support to PostgresEventStore for transactional consistency
- Add custom_fields, tags to all IPAM aggregates and events
- Add vlan_id to Prefix aggregate for VLAN-to-Prefix linking
- Create IPRange, RIR, ASN, FHRPGroup aggregates with event sourcing
- Add new value objects: IPRangeStatus, ASNumber, FHRPProtocol, FHRPAuthType
- Add repository interfaces for new aggregates
- Add 118 new domain tests (456 total, all passing)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add 28 command classes for 8 aggregates (Prefix, IPAddress, VRF, VLAN, IPRange, RIR, ASN, FHRPGroup)
- Add 28 command handlers using EventStore + Read Model pattern
- Add Read Model repository interfaces for each aggregate
- Implement IP duplicate check (VRF scope) in CreateIPAddressHandler
- All handlers: EventStore.append → ReadModel upsert → Kafka publish

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add 20 query classes for 8 aggregates (Get/List + Prefix specials)
- Add 20 query handlers reading from Read Model repositories
- Add domain service query handlers (utilization, available prefixes/IPs)
- Add 8 DTOs with custom_fields, tags, timestamps

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add 8 BulkCreate commands and handlers (per-aggregate)
- Add SQLAlchemy Read Model tables (8 tables) with Alembic migration
- Add PostgreSQL Read Model repository implementations
- Add Settings, Database infrastructure
- Add FastAPI routers for all 8 aggregates with CRUD + Bulk + special endpoints
- Add request/response schemas
- Add main.py with lifespan (EventStore, Kafka, middleware)
- Prefix-specific: children, utilization, available-prefixes, available-ips

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…hot on append

- Add PostgresEventStore.load_aggregate() that loads snapshot first, then replays only newer events
- Add optional aggregate param to append() for automatic snapshot saving via SnapshotStrategy
- Refactor all IPAM Update/ChangeStatus/Delete command handlers to use load_aggregate() + aggregate= pattern

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ection

- IPAMEventProjector handles all 28 event types with direct SQL updates (no aggregate reconstruction)
- Created → INSERT/upsert, Updated → UPDATE changed columns, Deleted → is_deleted=true, StatusChanged → UPDATE status
- KafkaEventConsumer runs as background task in FastAPI lifespan (group_id: ipam-projector)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- RedisCache with get_json/set_json/invalidate_prefix_utilization (TTL 300s)
- GetPrefixUtilizationHandler checks cache before computing, stores result after
- Event projector invalidates cache on prefix Created/Updated/StatusChanged/Deleted events
- Redis connected/closed in FastAPI lifespan, passed to router via app.state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CrossServiceConsumer listens to dcim.events and tenancy.events topics
- Handlers log received events (no-op until Phase 2)
- No lifespan integration — code-only preparation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- RouteTarget: name (RD format), tenant_id, description, custom_fields, tags
- VLANGroup: name, slug, min_vid/max_vid range validation (1-4094)
- Service: name, protocol (tcp/udp/sctp), ports validation (1-65535), ip_addresses
- Add ServiceProtocol StrEnum value object
- Add 9 domain events (Created/Updated/Deleted × 3)
- Add comprehensive tests for all 3 aggregates (52 new tests)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add import_targets/export_targets (list[UUID]) to VRF domain model
- Extend VRFCreated/VRFUpdated events with target fields
- Update create(), update(), snapshot, and event handlers
- Add 8 tests for route target functionality

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- IPRangeUtilizationService calculates utilization as used_ips / total_range
- GetIPRangeUtilizationQuery + handler with read model integration
- Add find_ips_in_range() to IPAddressReadModelRepository
- 6 tests for utilization calculation (IPv4, IPv6, edge cases)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…VLANGroup, Service

- CQRS commands, handlers, queries, DTOs for 3 new aggregates (12 command handlers, 6 query handlers)
- Read model tables + Postgres repositories for RouteTarget, VLANGroup, Service
- Event projector handlers for 9 new event types
- VRF infrastructure updated for import_targets/export_targets (models, repository, projector, DTO, commands)
- Alembic migration 002: new tables + VRF columns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fray-cloud and others added 25 commits March 22, 2026 03:39
…afka consumer

- WebhookDeliveryService: httpx POST with HMAC-SHA256 signing (X-Webhook-Signature)
- RetryManager: Redis sorted set queue with exponential backoff (10s→1h, max 5 retries)
- WebhookDispatcher: orchestrates delivery → logging → retry scheduling
- WebhookConsumerWorker: Kafka *.events pattern, tenant-scoped matching, Redis cache
- RetryWorker: 5s polling for due retries, re-dispatches via dispatcher

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 7 REST endpoints: CRUD + delivery logs + test webhook (ping payload)
- Schemas with secret excluded from WebhookResponse
- Lifespan manages: Database, DeliveryService, RetryManager, WebhookCache, Consumer, RetryWorker
- Health check at GET /health
- OpenAPI with tags and description

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Domain tests: Webhook matching (exact, wildcard, inactive, empty), activation, creation (11 tests)
- Domain tests: WebhookEventLog creation and defaults (2 tests)
- Infrastructure tests: Delivery service with httpx mock (success, failure, timeout, HMAC signature) (4 tests)
- Infrastructure tests: RetryManager with fakeredis (schedule, retrieve, max retry, pending count) (4 tests)
- Infrastructure tests: Tenant+event matching logic (5 tests)
- Add pytest-httpx, fakeredis to dev dependencies

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement comprehensive search and filtering for IPAM service:

- Add ILIKE filter operator and UserMiddleware to shared library
- Introduce BaseListQuery with common filter fields (description, tags,
  custom fields, date range, sorting) for all 11 entity types
- Extend ReadModelRepository.find_all with sort_params, tag_slugs,
  custom_field_filters parameters
- Add Saved Filters CRUD (model, repository, handlers, router, API)
  with per-user/entity-type defaults and X-User-ID header auth
- Add PostgreSQL full-text search with tsvector generated columns
  and GIN indexes across all 11 read models
- Add global search API (GET /search) with UNION ALL across entities,
  ts_rank relevance ordering, and entity_type filtering
- Add Alembic migration for search_vector columns and saved_filters table

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add journal entry feature to Event Service for user-created notes:

- JournalEntryModel with object_type, object_id, entry_type
  (info/success/warning/danger), comment, user_id, tenant_id
- JournalRepository with flexible filtering (object_type, object_id,
  tenant_id, user_id, entry_type) and pagination
- POST /journal-entries — create with user_id from X-User-ID header
- GET /journal-entries — list with optional filters
- DELETE /journal-entries/{id} — author-only delete (403 if mismatch)
- UserMiddleware added to Event Service
- Alembic migration 002 for journal_entries table
- Unit tests for schemas, model, and entry_type validation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add comprehensive data import/export functionality:

- CSV Import: parse CSV → BulkCreate command integration for all
  11 entity types, with partial success (errors per row)
- CSV/JSON/YAML Export: read model query → format conversion,
  export-specific limit (max 10000), StreamingResponse download
- Jinja2 Export Templates: SandboxedEnvironment for security,
  ExportTemplateModel with output_format metadata, CRUD API,
  POST /export/{entity_type}/render for custom rendering
- Dependencies: jinja2, pyyaml, python-multipart added
- Alembic migration 004 for export_templates table
- 20 unit tests for CSV parsing, format serialization, entity types

Bulk Edit API was already implemented (PATCH /*/bulk endpoints).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restructure frontend/ as pnpm workspace monorepo:
- apps/client — Main IPAM client app (Next.js 16, port 3000)
- apps/admin — Admin panel skeleton (Next.js, port 3001)
- packages/shared — Shared library:
  - types/ — 11 IPAM entity types, auth types, common types
  - lib/api.ts — fetch wrapper with JWT auto-attach and 401 refresh
  - lib/api-client.ts — typed API functions for all entities
  - lib/auth.ts — JWT token management (localStorage)
  - hooks/use-auth.tsx — AuthProvider + useAuth hook

Dependencies: @tanstack/react-query, @tanstack/react-table,
next-themes, lucide-react added to client app.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- shadcn/ui initialized with Button, Input, Card, Table, Dialog,
  Select, Badge, Tabs, Sheet, DropdownMenu, Separator, Skeleton,
  Label, Textarea components
- Providers: QueryClientProvider, ThemeProvider (dark mode), AuthProvider
- Auth route group: login page (email/password), signup page
- Dashboard route group: sidebar navigation (IPAM entities),
  header (search placeholder, dark mode toggle, user menu)
- Auth guard: client-side redirect for unauthenticated users
- Dashboard placeholder with stat cards

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Full CRUD (list/detail/create) for 4 core entities:
- Prefix: network, status badge, VRF, role, inline edit, delete
- IP Address: address, dns_name, status, VRF
- VRF: name, RD, import/export targets
- VLAN: VID, name, group, status, role

List-only pages for 7 remaining entities:
- IP Ranges, ASNs, RIRs, FHRP Groups, Route Targets, VLAN Groups, Services

Reusable components:
- DataTable: @tanstack/react-table with pagination, skeleton loading
- StatusBadge: color-coded status indicators

All pages use TanStack Query for data fetching/mutations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Global Search: Cmd+K command palette with debounced API search,
  results grouped by entity type, click-to-navigate
- Changelog Timeline: vertical timeline with action badges
  (created/updated/deleted), integrated into Prefix detail tabs
- Journal Entries: per-object notes with entry_type badges
  (info/success/warning/danger), add/delete with mutations
- IPAM Dashboard: live summary cards (Prefix/IP/VRF/VLAN counts),
  recent changes section, loading skeletons

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mock-based tests (run without Docker):
- IPAM E2E: Prefix CQRS flow (Create→Event→ReadModel→Query),
  BulkCreate, Update, Delete with InMemory mocks
- Event serialization roundtrip for all 37 IPAM event types
- Auth E2E: registration, login, JWT lifecycle, role permissions
  with real JWTService/BcryptPasswordService
- Webhook E2E: event matching (7 cases), delivery with HMAC
  signature verification via httpx_mock

Docker-based tests (@pytest.mark.integration, testcontainers):
- IPAM DB: real PostgreSQL CRUD, filtering (status, ILIKE)
- Tenant isolation: tenant A/B data separation
- Auth DB: user persist/retrieve/delete
- Kafka flow: produce/consume with testcontainers Kafka

Infrastructure:
- testcontainers[postgresql,kafka,redis] added to dev deps
- pytest markers: "integration" for Docker tests
- asyncio_mode="auto" for IPAM and Auth services

Test counts: IPAM 622, Auth 9, Webhook 36, Event 9 (non-integration)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix IPAM pyproject.toml: add missing runtime deps (sqlalchemy,
  asyncpg, alembic, pydantic-settings, aiokafka, redis)
- Fix frontend Dockerfile.dev for monorepo structure
- Add frontend/docker-compose.dev.yml at workspace root
- Fix nginx depends_on to wait for all services before starting
- Switch Kafka image from bitnami/kafka to apache/kafka:3.9.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Merge all docker-compose include files into single root file
  (Docker Compose v5 cross-file depends_on not supported)
- Switch Kafka from bitnami/kafka to apache/kafka:3.9.0
- Fix all DATABASE_URL to use postgresql+asyncpg://
- Add RSA key file mount for Auth service (rsa_private_key_path)
- Add DNS resolver to nginx for dynamic upstream resolution
- Fix IPAM pyproject.toml missing runtime deps
- Fix frontend Dockerfile.dev for monorepo structure
- Add nginx resolver directive for Docker DNS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Infrastructure:
- Add DATABASE_URL env override to all 4 alembic env.py files
- Add 4 init containers (profiles: ["init"]) for DB migration
- Add `make dev-init` target
- Auth config: support RSA key file paths (rsa_private/public_key_path)
- Nginx: add public /api/v1/setup/ route

Tenant Service:
- Add GET /setup/status — returns {initialized: bool}
- Add POST /setup/create-tenant — only works when no tenants exist

Frontend:
- Add Setup Wizard page (/setup) with 2-step flow:
  Step 1: Create organization (tenant)
  Step 2: Create admin account
- AuthGuard checks setup status before auth check
- Add @cmdb/shared/lib/setup.ts API functions

Flow: make dev-build → make dev-up → make dev-init → browser /setup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use `image: cmdb-*:latest` instead of `build:` for init containers
  (reuse pre-built service images that include alembic files)
- Change `make dev-init` from --abort-on-container-exit to sequential
  `docker compose run --rm` for each init container
- Add alembic.ini, alembic/, src/ to all service Dockerfile.dev files
- Makefile dev-init: depends on dev-build + dev-up

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- setup.ts: use NEXT_PUBLIC_TENANT_API_URL (default localhost:8003)
- api.ts: use NEXT_PUBLIC_API_URL (default localhost:8001 for IPAM)
- api.ts: add authApi with NEXT_PUBLIC_AUTH_API_URL (localhost:8002)
- auth.ts: use authApi for login/signup/me endpoints
- api.ts: request() accepts baseUrl parameter for multi-service support

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix CORS: add CORSMiddleware to all 4 backend services (tenant, auth, ipam, event)
- Fix auth: getCurrentUser uses JWT decode instead of /auth/me (endpoint doesn't exist)
- Fix login: auto-attach tenant_id from localStorage
- Fix setup wizard: use direct fetch for register (bypass api wrapper)
- Fix error display: handle array detail from FastAPI 422 errors
- Fix Makefile: remove broken prod compose reference from clean target
- Fix Tenant Dockerfile.dev: include alembic_tenant_db directory

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Always show Organization dropdown on login page
- Auto-select if tenant_id exists in localStorage
- Fetch tenant list from Tenant API on page load
- Pass tenant_id to login request

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove direct service port calls (localhost:8001/8002/8003)
- Single API_BASE_URL: http://localhost (Nginx port 80)
- All paths use /api/v1/ prefix matching Nginx routes
- Add HTTP server block to nginx.conf (dev, no SSL required)
- Remove authApi/separate base URLs from shared lib
- Update login, setup, auth, api-client to use gateway paths

API routing: Frontend → Nginx:80 → backend services

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Frontend served through Nginx gateway, so API calls use relative
paths (/api/v1/*) instead of absolute URLs. This eliminates CORS
issues entirely since all requests are same-origin.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nginx:
- Remove CORS preflight regex that caught all /api/* (same-origin)
- IPAM: /api/v1/ipam/ → /api/v1/ catch-all proxy to ipam/api/v1/
- Add public /api/v1/tenant/tenants for login tenant selector

Frontend:
- Fix dashboard: changelogApi.list() instead of invalid _recent
- Add changelogApi.list() to api-client

IPAM:
- Increase DB pool_size from 5 to 20, max_overflow 30
- Add pool_pre_ping and pool_recycle to prevent connection exhaustion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Share same DB session between CommandBus and QueryBus in prefix
  router to ensure flush() data is visible for post-create query
- Add session.commit() after command dispatch in create/update/delete
- Fix schema import name conflict (BulkUpdatePrefixItem alias)
- Increase DB pool_size to 20 with pool_pre_ping and pool_recycle

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Session management (all 11 routers):
- Share DB session between CommandBus and QueryBus
- Add session.commit() after command dispatch

Nullable command fields:
- Allow None for custom_fields, tags, import/export_targets,
  ports, ip_addresses in all Create commands
- Handlers use `or {}` / `or []` to default None to empty

All 11 entities tested via gateway: ✅

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
P1 마일스톤 전체 구현 (Issue #2~#18 CLOSED)

Backend: IPAM(11 entities), Auth(JWT/RBAC), Tenant, Event, Webhook
Frontend: Next.js monorepo (client/admin/shared), IPAM CRUD, Dashboard
Infra: Docker Compose, Nginx Gateway, Init containers, Setup Wizard
Tests: 681+ unit tests, testcontainers integration tests
Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, we are unable to review this pull request

The GitHub API does not allow us to fetch diffs exceeding 300 files, and this pull request has 427

fray-cloud and others added 3 commits March 23, 2026 02:25
- Add eslint.config.mjs to admin app
- Fix Python import sorting across all services

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-fix import sorting across all services.
Resolves CI python-lint failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CI workflow:
- python-test: run per-service with --package and -m "not integration"
- frontend-build: replace placeholder with actual build step

Frontend lint fixes:
- Remove unused Badge/useState imports
- Fix setState inside useEffect in global-search.tsx

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@fray-cloud fray-cloud merged commit 94b404a into main Mar 22, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant