163 lines
5.7 KiB
Go
163 lines
5.7 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/eric/narratio/internal/artifactpolicy"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/pathsafe"
|
|
)
|
|
|
|
func validateCampaign(cfg *CampaignConfig) error {
|
|
if cfg == nil {
|
|
return fmt.Errorf("campaign config is required")
|
|
}
|
|
if CampaignID(cfg) == "" {
|
|
return fmt.Errorf("campaign.campaign_id is required")
|
|
}
|
|
if err := pathsafe.ValidateOpaqueSegment(cfg.CampaignID); err != nil {
|
|
return fmt.Errorf("campaign.campaign_id: %w", err)
|
|
}
|
|
if strings.TrimSpace(cfg.Inputs.SpeakersFile) == "" {
|
|
return fmt.Errorf("campaign.inputs.speakers_file is required")
|
|
}
|
|
if strings.TrimSpace(cfg.Inputs.AutocorrectFile) == "" {
|
|
return fmt.Errorf("campaign.inputs.autocorrect_file is required")
|
|
}
|
|
if strings.TrimSpace(cfg.Inputs.GlossaryFile) == "" {
|
|
return fmt.Errorf("campaign.inputs.glossary_file is required")
|
|
}
|
|
if strings.TrimSpace(cfg.Inputs.PartyFile) == "" {
|
|
return fmt.Errorf("campaign.inputs.party_file is required")
|
|
}
|
|
if cfg.Inputs.SpellCatalogFile != "" && strings.TrimSpace(cfg.Inputs.SpellCatalogFile) == "" {
|
|
return fmt.Errorf("campaign.inputs.spell_catalog_file must be non-empty when provided")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateSecrets(cfg *SecretsConfig) error {
|
|
if cfg == nil {
|
|
return nil
|
|
}
|
|
if strings.TrimSpace(cfg.EnvDir) == "" {
|
|
return fmt.Errorf("pipeline.secrets.env_dir must be non-empty when pipeline.secrets is configured")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateSpool(cfg SpoolConfig) error {
|
|
return nil
|
|
}
|
|
|
|
func validateCache(cfg CacheConfig) error {
|
|
if cfg.S3Audio != nil && *cfg.S3Audio && strings.TrimSpace(cfg.Root) == "" {
|
|
return fmt.Errorf("pipeline.cache.root is required when pipeline.cache.s3_audio is true")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateSession(cfg *SessionConfig) error {
|
|
if err := validateSessionIdentifier("session.session_id", cfg.SessionID, true); err != nil {
|
|
return err
|
|
}
|
|
if err := validateSessionIdentifier("session.previous_session_id", cfg.PreviousSessionID, false); err != nil {
|
|
return err
|
|
}
|
|
if strings.TrimSpace(cfg.PreviousSessionID) != "" && strings.TrimSpace(cfg.PreviousSessionID) == strings.TrimSpace(cfg.SessionID) {
|
|
return fmt.Errorf("session.previous_session_id must not equal session.session_id")
|
|
}
|
|
if strings.TrimSpace(cfg.Campaign) == "" {
|
|
return fmt.Errorf("session.campaign is required")
|
|
}
|
|
if err := pathsafe.ValidateOpaqueSegment(cfg.Campaign); err != nil {
|
|
return fmt.Errorf("session.campaign: %w", err)
|
|
}
|
|
|
|
if strings.TrimSpace(cfg.Inputs.SpeakersFile) == "" {
|
|
return fmt.Errorf("session.inputs.speakers_file is required")
|
|
}
|
|
if strings.TrimSpace(cfg.Inputs.AutocorrectFile) == "" {
|
|
return fmt.Errorf("session.inputs.autocorrect_file is required")
|
|
}
|
|
if strings.TrimSpace(cfg.Inputs.GlossaryFile) == "" {
|
|
return fmt.Errorf("session.inputs.glossary_file is required")
|
|
}
|
|
if cfg.Inputs.SpellCatalogFile != "" && strings.TrimSpace(cfg.Inputs.SpellCatalogFile) == "" {
|
|
return fmt.Errorf("session.inputs.spell_catalog_file must be non-empty when provided")
|
|
}
|
|
|
|
hasAudioDir := strings.TrimSpace(cfg.Inputs.AudioDir) != ""
|
|
hasAudioFiles := len(cfg.Inputs.AudioFiles) > 0
|
|
hasAudioS3 := cfg.Inputs.AudioS3 != nil
|
|
if hasAudioS3 {
|
|
if strings.TrimSpace(cfg.Inputs.AudioS3.Prefix) == "" {
|
|
return fmt.Errorf("session.inputs.audio_s3.prefix is required when session.inputs.audio_s3 is configured")
|
|
}
|
|
if err := validateRelativeSafePath("session.inputs.audio_s3.prefix", cfg.Inputs.AudioS3.Prefix); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if hasAudioS3 && (hasAudioDir || hasAudioFiles) {
|
|
return fmt.Errorf("session.inputs.audio_dir/audio_files and session.inputs.audio_s3 are mutually exclusive")
|
|
}
|
|
if !hasAudioDir && !hasAudioFiles && !hasAudioS3 {
|
|
return fmt.Errorf("session.inputs requires audio_dir, at least one audio_files entry, or audio_s3")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateResolvedPartyInputs(cfg *Config) error {
|
|
if cfg == nil || cfg.Session == nil {
|
|
return fmt.Errorf("session config is required")
|
|
}
|
|
if cfg.Party.Canonical == nil {
|
|
return fmt.Errorf("canonical party data is required")
|
|
}
|
|
if cfg.Party.Source.Source != "campaign_config" {
|
|
return fmt.Errorf("canonical party must be sourced by campaign_config")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateSessionIdentifier(fieldName, value string, required bool) error {
|
|
if strings.TrimSpace(value) == "" {
|
|
if required {
|
|
return fmt.Errorf("%s is required", fieldName)
|
|
}
|
|
return nil
|
|
}
|
|
if err := pathsafe.ValidateOpaqueSegment(value); err != nil {
|
|
return fmt.Errorf("%s: %w", fieldName, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateCrossConfig(pipeline *PipelineConfig, session *SessionConfig, stableInputs ResolvedStableInputs) error {
|
|
if pipeline == nil || session == nil {
|
|
return nil
|
|
}
|
|
if pipeline.Notarius != nil && pipeline.Notarius.Enabled {
|
|
for _, selector := range sortedStringKeys(pipeline.Notarius.References) {
|
|
source := pipeline.Notarius.References[selector]
|
|
if source == artifactpolicy.SourceInputSpellCatalog && strings.TrimSpace(stableInputs.SpellCatalogFile.Path) == "" {
|
|
return fmt.Errorf("pipeline.notarius.references.%s requires campaign.inputs.spell_catalog_file or session.inputs.spell_catalog_file", selector)
|
|
}
|
|
}
|
|
}
|
|
|
|
audioS3Enabled := session.Inputs.AudioS3 != nil
|
|
publishUploadEnabled := publishUploadConfiguredForS3(pipeline)
|
|
if audioS3Enabled && !strings.EqualFold(strings.TrimSpace(pipeline.Storage.Backend), StorageBackendS3) {
|
|
return fmt.Errorf("pipeline.storage.backend must be s3 when session.inputs.audio_s3 is configured")
|
|
}
|
|
if !audioS3Enabled && !publishUploadEnabled {
|
|
return nil
|
|
}
|
|
if pipeline.Storage.S3 == nil || strings.TrimSpace(pipeline.Storage.S3.Bucket) == "" {
|
|
return fmt.Errorf("pipeline.storage.s3.bucket is required when S3 session audio or publish upload is enabled")
|
|
}
|
|
return nil
|
|
}
|