feat(#88): implement QUERY message type for federation
Implement complete QUERY message protocol for federated queries between Mosaic Stack instances, building on existing connection infrastructure. Database Changes: - Add FederationMessageType enum (QUERY, COMMAND, EVENT) - Add FederationMessageStatus enum (PENDING, DELIVERED, FAILED, TIMEOUT) - Add FederationMessage model for tracking all federation messages - Add workspace and connection relations Types & DTOs: - QueryMessage: Signed query request payload - QueryResponse: Signed query response payload - QueryMessageDetails: API response type - SendQueryDto: Client request DTO - IncomingQueryDto: Validated incoming query DTO QueryService: - sendQuery: Send signed query to remote instance via ACTIVE connection - handleIncomingQuery: Process and validate incoming queries - processQueryResponse: Handle and verify query responses - getQueryMessages: List workspace queries with optional status filter - getQueryMessage: Get single query message details - Message deduplication via unique messageId - Signature verification using SignatureService - Timestamp validation (5-minute window) QueryController: - POST /api/v1/federation/query: Send query (authenticated) - POST /api/v1/federation/incoming/query: Receive query (public, signature-verified) - GET /api/v1/federation/queries: List queries (authenticated) - GET /api/v1/federation/queries/🆔 Get query details (authenticated) Security: - All messages signed with instance private key - All responses verified with remote public key - Timestamp validation prevents replay attacks - Connection status validation (must be ACTIVE) - Workspace isolation enforced via RLS Testing: - 15 QueryService tests (100% coverage) - 9 QueryController tests (100% coverage) - All tests passing with proper mocking - TypeScript strict mode compliance Refs #88 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -173,6 +173,19 @@ enum FederationConnectionStatus {
|
||||
DISCONNECTED
|
||||
}
|
||||
|
||||
enum FederationMessageType {
|
||||
QUERY
|
||||
COMMAND
|
||||
EVENT
|
||||
}
|
||||
|
||||
enum FederationMessageStatus {
|
||||
PENDING
|
||||
DELIVERED
|
||||
FAILED
|
||||
TIMEOUT
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// MODELS
|
||||
// ============================================
|
||||
@@ -257,6 +270,7 @@ model Workspace {
|
||||
qualityGates QualityGate[]
|
||||
runnerJobs RunnerJob[]
|
||||
federationConnections FederationConnection[]
|
||||
federationMessages FederationMessage[]
|
||||
|
||||
@@index([ownerId])
|
||||
@@map("workspaces")
|
||||
@@ -1273,7 +1287,8 @@ model FederationConnection {
|
||||
disconnectedAt DateTime? @map("disconnected_at") @db.Timestamptz
|
||||
|
||||
// Relations
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
messages FederationMessage[]
|
||||
|
||||
@@unique([workspaceId, remoteInstanceId])
|
||||
@@index([workspaceId])
|
||||
@@ -1301,3 +1316,40 @@ model FederatedIdentity {
|
||||
@@index([oidcSubject])
|
||||
@@map("federated_identities")
|
||||
}
|
||||
|
||||
model FederationMessage {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
workspaceId String @map("workspace_id") @db.Uuid
|
||||
connectionId String @map("connection_id") @db.Uuid
|
||||
|
||||
// Message metadata
|
||||
messageType FederationMessageType @map("message_type")
|
||||
messageId String @unique @map("message_id") // UUID for deduplication
|
||||
correlationId String? @map("correlation_id") // For request/response tracking
|
||||
|
||||
// Message content
|
||||
query String? @db.Text
|
||||
response Json? @default("{}")
|
||||
|
||||
// Status tracking
|
||||
status FederationMessageStatus @default(PENDING)
|
||||
error String? @db.Text
|
||||
|
||||
// Security
|
||||
signature String @db.Text
|
||||
|
||||
// Timestamps
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
||||
deliveredAt DateTime? @map("delivered_at") @db.Timestamptz
|
||||
|
||||
// Relations
|
||||
connection FederationConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([workspaceId])
|
||||
@@index([connectionId])
|
||||
@@index([messageId])
|
||||
@@index([correlationId])
|
||||
@@map("federation_messages")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user