feat: expand companion platform, device controls, and memoir workflows
ci / verify (push) Canceled after 0s

Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
This commit is contained in:
Codex Local
2026-10-08 10:06:44 +08:00
parent 98fb2965d0
commit bf1233d5df
861 changed files with 94614 additions and 3209 deletions
+36 -1
View File
@@ -8,12 +8,14 @@ import (
"data-terminal-cloud/internal/alerts"
"data-terminal-cloud/internal/billing"
"data-terminal-cloud/internal/buildinfo"
"data-terminal-cloud/internal/callmemory"
"data-terminal-cloud/internal/contentmodel"
"data-terminal-cloud/internal/device"
"data-terminal-cloud/internal/diagnostics"
"data-terminal-cloud/internal/health"
"data-terminal-cloud/internal/identity"
"data-terminal-cloud/internal/media"
"data-terminal-cloud/internal/memoir"
"data-terminal-cloud/internal/memorytool"
"data-terminal-cloud/internal/onboarding"
"data-terminal-cloud/internal/opsaccess"
@@ -25,6 +27,8 @@ import (
)
type RouterOptions struct {
MemoirService *memoir.Service
CallMemoryService *callmemory.Service
OnboardingService *onboarding.Service
ServiceName string
Logger *slog.Logger
@@ -75,6 +79,10 @@ func NewRouter(options RouterOptions) http.Handler {
mux := http.NewServeMux()
if options.ServiceName == "api" && options.OnboardingService != nil {
options.OnboardingService.RegisterRoutes(mux)
if options.CallMemoryService != nil {
options.CallMemoryService.RegisterDeviceRoutes(mux, options.OnboardingService.AuthenticateCallRequest)
options.CallMemoryService.RegisterDirectRoutes(mux, options.OnboardingService.AuthenticateCallRequest)
}
}
mux.Handle("GET /metrics", MetricsHandler())
mux.HandleFunc("GET /health/live", func(writer http.ResponseWriter, request *http.Request) {
@@ -94,20 +102,32 @@ func NewRouter(options RouterOptions) http.Handler {
mux.HandleFunc("GET /api/v1/version", func(writer http.ResponseWriter, request *http.Request) {
WriteJSON(writer, request, http.StatusOK, buildinfo.Current())
})
if options.ServiceName == "api" && options.SaaSService != nil && options.SaaSService.Fleet != nil {
mux.Handle("GET /f/{token}", FleetFirmwareDownloadHandler(options.SaaSService.Fleet))
}
if options.ServiceName == "api" && options.MemoryToolService != nil && options.MemoryToolSigner != nil {
mux.Handle("POST /internal/v1/tools/memory-search", MemoryToolSearchHandler(options.MemoryToolService, options.MemoryToolSigner, options.RateLimiter, logger))
}
if options.ServiceName == "api" && options.TokenVerifier != nil && options.IdentityRepository != nil {
handler := UserRateLimit(logger, options.RateLimiter, CurrentUserHandler(options.IdentityRepository))
mux.Handle("GET /api/v1/me", onboardingCustomerAuth(options.OnboardingService, BearerAuth(options.TokenVerifier, handler), handler))
profileHandler := UserRateLimit(logger, options.RateLimiter, UpdateCustomerProfileHandler(options.IdentityRepository))
mux.Handle("PATCH /api/v1/me/profile", onboardingCustomerAuth(options.OnboardingService, BearerAuth(options.TokenVerifier, profileHandler), profileHandler))
}
if options.ServiceName == "api" && options.TokenVerifier != nil && options.SaaSService != nil {
authenticated := func(handler http.Handler) http.Handler {
limited := UserRateLimit(logger, options.RateLimiter, handler)
return onboardingCustomerAuth(options.OnboardingService, BearerAuth(options.TokenVerifier, limited), limited)
}
if options.CallMemoryService != nil {
options.CallMemoryService.RegisterMemoryRoutes(mux, authenticated)
}
if options.MemoirService != nil {
options.MemoirService.RegisterRoutes(mux, authenticated)
}
if options.WebEventService != nil {
mux.Handle("GET /api/v1/events", authenticated(StreamWebEventsHandler(options.WebEventService)))
mux.Handle("GET /api/v1/events/history", authenticated(WebEventHistoryHandler(options.WebEventService)))
}
if options.SharingService != nil {
mux.Handle("GET /api/v1/sharing-grants", authenticated(ListSharingGrantsHandler(options.SharingService)))
@@ -203,6 +223,7 @@ func NewRouter(options RouterOptions) http.Handler {
mux.Handle("GET /api/v1/households/{id}/subjects", authenticated(ListSubjectsHandler(options.SaaSService)))
mux.Handle("POST /api/v1/households/{id}/subjects", authenticated(CreateSubjectHandler(options.SaaSService)))
mux.Handle("GET /api/v1/subjects/{id}", authenticated(GetSubjectHandler(options.SaaSService)))
mux.Handle("POST /api/v1/subjects/{id}/voice-agent", authenticated(EnsureSubjectVoiceAgentHandler(options.SaaSService)))
mux.Handle("PATCH /api/v1/subjects/{id}", authenticated(PatchSubjectHandler(options.SaaSService)))
mux.Handle("GET /api/v1/subjects/{id}/conversation-readiness", authenticated(GetConversationReadinessHandler(options.SaaSService)))
mux.Handle("GET /api/v1/subjects/{id}/facts", authenticated(ListSubjectFactsHandler(options.SaaSService)))
@@ -217,6 +238,14 @@ func NewRouter(options RouterOptions) http.Handler {
mux.Handle("GET /api/v1/devices/{id}/binding-impact", authenticated(GetDeviceBindingImpactHandler(options.SaaSService)))
mux.Handle("POST /api/v1/devices/{id}/binding-change", authenticated(ChangeDeviceBindingHandler(options.SaaSService)))
mux.Handle("GET /api/v1/devices/{id}/presence", authenticated(GetDevicePresenceHandler(options.SaaSService)))
mux.Handle("GET /api/v1/devices/{id}/status", authenticated(FleetDeviceHandler(options.SaaSService, false)))
mux.Handle("GET /api/v1/devices/{id}/firmware", authenticated(FleetDeviceHandler(options.SaaSService, false)))
mux.Handle("POST /api/v1/devices/{id}/ota", authenticated(FleetDeviceHandler(options.SaaSService, true)))
mux.Handle("GET /api/v1/ops/device-firmware", authenticated(FleetFirmwareHandler(options.SaaSService, "list")))
mux.Handle("POST /api/v1/ops/device-firmware", authenticated(FleetFirmwareHandler(options.SaaSService, "prepare")))
mux.Handle("GET /api/v1/ops/device-firmware/models", authenticated(FleetFirmwareHandler(options.SaaSService, "models")))
mux.Handle("POST /api/v1/ops/device-firmware/{id}/finalize", authenticated(FleetFirmwareHandler(options.SaaSService, "finalize")))
mux.Handle("GET /api/v1/ops/device-firmware/{id}/download", authenticated(FleetFirmwareHandler(options.SaaSService, "download")))
mux.Handle("GET /api/v1/devices/{id}/twin", authenticated(GetDeviceTwinHandler(options.SaaSService)))
mux.Handle("PATCH /api/v1/devices/{id}/twin", authenticated(PatchDeviceTwinHandler(options.SaaSService)))
mux.Handle("GET /api/v1/devices/{id}/commands", authenticated(ListDeviceCommandsHandler(options.SaaSService)))
@@ -253,6 +282,7 @@ func NewRouter(options RouterOptions) http.Handler {
mux.Handle("GET /api/v1/ops/firmware-rollouts/{id}", authenticated(GetOpsFirmwareRolloutHandler(options.SaaSService)))
mux.Handle("POST /api/v1/ops/firmware-rollouts/{id}/pause", authenticated(PauseOpsFirmwareRolloutHandler(options.SaaSService)))
mux.Handle("GET /api/v1/subjects/{id}/conversations", authenticated(ListSubjectConversationsHandler(options.SaaSService)))
mux.Handle("GET /api/v1/subjects/{id}/home-overview", authenticated(SubjectHomeOverviewHandler(options.SaaSService)))
mux.Handle("GET /api/v1/conversations/{id}", authenticated(GetConversationHandler(options.SaaSService)))
mux.Handle("GET /api/v1/conversations/{id}/usage", authenticated(GetConversationUsageHandler(options.SaaSService)))
if options.MediaService != nil {
@@ -275,6 +305,7 @@ func NewRouter(options RouterOptions) http.Handler {
mux.Handle("GET /api/v1/memories/{id}", authenticated(GetMemoryHandler(options.SaaSService)))
mux.Handle("PATCH /api/v1/memories/{id}", authenticated(PatchMemoryHandler(options.SaaSService)))
mux.Handle("DELETE /api/v1/memories/{id}", authenticated(DeleteMemoryHandler(options.SaaSService)))
mux.Handle("GET /api/v1/households/{id}/privacy-requests", authenticated(ListHouseholdPrivacyRequestsHandler(options.SaaSService)))
mux.Handle("GET /api/v1/subjects/{id}/privacy-requests", authenticated(ListPrivacyRequestsHandler(options.SaaSService)))
mux.Handle("POST /api/v1/subjects/{id}/privacy-requests", authenticated(CreatePrivacyRequestHandler(options.SaaSService)))
mux.Handle("GET /api/v1/privacy-requests/{id}", authenticated(GetPrivacyRequestHandler(options.SaaSService)))
@@ -284,7 +315,8 @@ func NewRouter(options RouterOptions) http.Handler {
}
if options.ServiceName == "api" && options.TokenVerifier != nil && options.AlertService != nil {
authenticated := func(handler http.Handler) http.Handler {
return BearerAuth(options.TokenVerifier, UserRateLimit(logger, options.RateLimiter, handler))
limited := UserRateLimit(logger, options.RateLimiter, handler)
return onboardingCustomerAuth(options.OnboardingService, BearerAuth(options.TokenVerifier, limited), limited)
}
mux.Handle("GET /api/v1/ops/alert-routing-policies", authenticated(ListOpsAlertRoutingPoliciesHandler(options.AlertService)))
mux.Handle("POST /api/v1/ops/alert-routing-policies", authenticated(CreateOpsAlertRoutingPolicyHandler(options.AlertService)))
@@ -356,6 +388,9 @@ func NewRouter(options RouterOptions) http.Handler {
mux.Handle("POST /device/v3/agent-sessions/{id}/turns:batch", activeDevice(SubmitConversationTurnsHandler(options.ConversationTranscriptSubmitter)))
}
}
if options.ServiceName == "callbacks" && options.CallMemoryService != nil {
mux.HandleFunc("POST /callbacks/v1/volcengine/chat", options.CallMemoryService.DirectCallback)
}
if options.ServiceName == "callbacks" && len(options.ProviderCallbackSecret) > 0 && options.ProviderCallbackProcessor != nil {
if len(options.NativeSubtitleSecret) >= 32 && options.NativeSubtitleProcessor != nil {
mux.Handle("POST /callbacks/v1/volcengine/subtitles", NativeSubtitleCallbackHandler(options.NativeSubtitleSecret, options.NativeSubtitleProcessor))