Remove AI config approval flow

This commit is contained in:
npc0-hue
2026-09-22 15:33:32 +08:00
parent 20008b4043
commit a8b5d483a3
54 changed files with 364 additions and 875 deletions
+3 -142
View File
@@ -13,10 +13,6 @@ import (
"browser.local/platform/validator"
)
const (
aiConfigDiffTTL = 30 * time.Minute
)
func (svc *CoreService) ListPluginLifecyclesForSession(sessionID string, filter domain.PluginLifecycleFilter) ([]domain.PluginLifecycleInstallation, error) {
user, err := svc.GetCurrentUser(sessionID)
if err != nil {
@@ -128,84 +124,6 @@ func (svc *CoreService) RunPluginLifecycleForSession(sessionID string, request d
return domain.CopyPluginLifecycleResult(domain.PluginLifecycleResult{Installation: installation, Job: job, Status: "queued"}), nil
}
func (svc *CoreService) ListAIConfigDiffsForSession(sessionID string, filter domain.AIConfigDiffFilter) ([]domain.AIConfigDiffPreview, error) {
user, err := svc.GetCurrentUser(sessionID)
if err != nil {
return nil, err
}
previews, err := svc.store.AIConfigDiffs().List(filter)
if err != nil {
return nil, err
}
visible := make([]domain.AIConfigDiffPreview, 0, len(previews))
for _, preview := range previews {
instance, err := svc.store.ServerInstances().Get(preview.ServerInstanceID)
if err == nil && canAccessServer(user, instance) {
visible = append(visible, preview)
}
}
sort.Slice(visible, func(i, j int) bool { return visible[i].CreatedAt.After(visible[j].CreatedAt) })
return domain.CopyAIConfigDiffPreviews(visible), nil
}
func (svc *CoreService) ApproveAIConfigDiffForSession(sessionID string, request domain.AIConfigDiffApprovalRequest) (domain.AIConfigDiffApprovalResult, error) {
if err := validator.ValidateAIConfigDiffApprovalRequest(request); err != nil {
return domain.AIConfigDiffApprovalResult{}, err
}
user, err := svc.GetCurrentUser(sessionID)
if err != nil {
return domain.AIConfigDiffApprovalResult{}, err
}
svc.pluginOperationsMu.Lock()
defer svc.pluginOperationsMu.Unlock()
preview, err := svc.store.AIConfigDiffs().Get(request.DiffID)
if err != nil {
return domain.AIConfigDiffApprovalResult{}, err
}
instance, err := svc.store.ServerInstances().Get(preview.ServerInstanceID)
if err != nil {
return domain.AIConfigDiffApprovalResult{}, err
}
if !canAccessServer(user, instance) || (!isPlatformAdmin(user) && preview.CreatedBy != user.ID) {
return domain.AIConfigDiffApprovalResult{}, ErrForbidden
}
if preview.State == domain.AIConfigDiffStateApproved {
if preview.ApprovalIdempotencyKey != request.IdempotencyKey {
return domain.AIConfigDiffApprovalResult{}, validationError("AI config diff is already approved with another idempotency key")
}
job, err := svc.store.Jobs().Get(preview.JobID)
if err != nil {
return domain.AIConfigDiffApprovalResult{}, err
}
dispatch := domain.ServerConfigWriteDispatch{Job: job, Status: "queued"}
return domain.CopyAIConfigDiffApprovalResult(domain.AIConfigDiffApprovalResult{Preview: preview, Dispatch: dispatch}), nil
}
if preview.State != domain.AIConfigDiffStatePending {
return domain.AIConfigDiffApprovalResult{}, validationError("AI config diff is not pending approval")
}
if !preview.ExpiresAt.After(svc.now()) {
preview.State = domain.AIConfigDiffStateExpired
preview.UpdatedAt = svc.now()
_ = svc.store.AIConfigDiffs().Update(preview)
return domain.AIConfigDiffApprovalResult{}, validationError("AI config diff has expired")
}
dispatch, err := svc.ApproveServerConfigWriteForSession(sessionID, domain.ServerConfigWriteApproval{ServerInstanceID: preview.ServerInstanceID, ExpectedConfigVersion: preview.ConfigVersion, ExpectedChecksum: preview.CurrentConfigChecksum, Key: preview.Key, ProposedContent: preview.ProposedConfig, IdempotencyKey: request.IdempotencyKey})
if err != nil {
return domain.AIConfigDiffApprovalResult{}, err
}
stamp := svc.now()
preview.State = domain.AIConfigDiffStateApproved
preview.ApprovedBy = user.ID
preview.ApprovedAt = stamp
preview.ApprovalIdempotencyKey = request.IdempotencyKey
preview.JobID = dispatch.Job.ID
preview.UpdatedAt = stamp
if err := svc.store.AIConfigDiffs().Update(preview); err != nil {
return domain.AIConfigDiffApprovalResult{}, err
}
return domain.CopyAIConfigDiffApprovalResult(domain.AIConfigDiffApprovalResult{Preview: preview, Dispatch: dispatch}), nil
}
func (svc *CoreService) projectPluginOperationsJobResult(job domain.Job, stamp time.Time) error {
if !strings.HasPrefix(job.ID, "job-plugin-lifecycle-") || job.ExecutionInput.LifecycleOperation == "" || job.ExecutionInput.PluginID == "" {
return nil
@@ -230,63 +148,6 @@ func (svc *CoreService) projectPluginOperationsJobResult(job domain.Job, stamp t
return svc.store.PluginLifecycles().Update(installation)
}
func (svc *CoreService) persistAIConfigDiff(actorID string, provider domain.AIProvider, request domain.AIInvocationRequest, result domain.AIProviderInvocationResult) (domain.AIConfigDiffPreview, error) {
config, err := svc.getServerConfigForUser(actorID, request.ServerInstanceID)
if err != nil {
return domain.AIConfigDiffPreview{}, err
}
stamp := svc.now()
preview := domain.AIConfigDiffPreview{ID: aiConfigDiffID(request.RequestID, request.ServerInstanceID), RequestID: request.RequestID, CreatedBy: actorID, ServerInstanceID: request.ServerInstanceID, PluginID: request.PluginID, ProviderID: provider.ID, Model: result.Usage.Model, Key: config.Key, ConfigVersion: config.ConfigVersion, CurrentConfigChecksum: config.Checksum, ProposedConfig: result.SuggestedConfig, DiffSummary: "review required before config write dispatch", State: domain.AIConfigDiffStatePending, ExpiresAt: stamp.Add(aiConfigDiffTTL), CreatedAt: stamp, UpdatedAt: stamp}
if err := validator.ValidateAIConfigDiffPreview(preview); err != nil {
return domain.AIConfigDiffPreview{}, err
}
if err := svc.store.AIConfigDiffs().Create(preview); err != nil {
if !errors.Is(err, repo.ErrDuplicate) {
return domain.AIConfigDiffPreview{}, err
}
existing, getErr := svc.store.AIConfigDiffs().Get(preview.ID)
if getErr != nil {
return domain.AIConfigDiffPreview{}, getErr
}
if existing.CreatedBy != actorID || existing.ServerInstanceID != request.ServerInstanceID || existing.PluginID != request.PluginID || existing.ProposedConfig != result.SuggestedConfig {
return domain.AIConfigDiffPreview{}, validationError("requestId is already used for a different AI config recommendation")
}
return existing, nil
}
return preview, nil
}
func (svc *CoreService) getServerConfigForUser(userID, serverInstanceID string) (domain.ServerConfig, error) {
instance, err := svc.store.ServerInstances().Get(serverInstanceID)
if err != nil {
return domain.ServerConfig{}, err
}
user, err := svc.store.Users().Get(userID)
if err != nil {
return domain.ServerConfig{}, err
}
if !canAccessServer(user, instance) {
return domain.ServerConfig{}, ErrForbidden
}
config := domain.ServerConfig{ServerInstanceID: instance.ID, ConfigVersion: instance.ConfigVersion, Format: "properties", Key: instance.ConfigKey, Content: instance.ConfigContent, Checksum: instance.ConfigChecksum, Source: "platform-derived", UpdatedAt: instance.ConfigUpdatedAt}
if config.ConfigVersion <= 0 {
config.ConfigVersion = 1
}
if config.Key == "" {
config.Key = "server.properties"
}
if config.Content == "" {
config.Content = buildLogicalServerConfig(instance)
}
if config.Checksum == "" {
config.Checksum = validator.BytesChecksum([]byte(config.Content))
}
if config.UpdatedAt.IsZero() {
config.UpdatedAt = svc.now()
}
return config, validator.ValidateServerConfig(config)
}
func (svc *CoreService) pluginLifecycleDenied(actorID string, instance domain.ServerInstance, plugin domain.GamePlugin, request domain.PluginLifecycleRequest, reason string) (domain.PluginLifecycleResult, error) {
svc.pluginOperationsMu.Lock()
defer svc.pluginOperationsMu.Unlock()
@@ -436,7 +297,7 @@ func pluginLifecycleInstallationID(pluginID, serverInstanceID string) string {
return "plugin-lifecycle-" + hex.EncodeToString(sum[:12])
}
func aiConfigDiffID(requestID, serverInstanceID string) string {
sum := sha256.Sum256([]byte(requestID + "\x00" + serverInstanceID))
return "ai-config-diff-" + hex.EncodeToString(sum[:12])
func aiConfigWriteIdempotencyKey(requestID, serverInstanceID string) string {
sum := sha256.Sum256([]byte("ai-config-write\x00" + requestID + "\x00" + serverInstanceID))
return "ai-config-write-" + hex.EncodeToString(sum[:16])
}