package orgpolicy import ( "context" "encoding/json" "github.com/jackc/pgx/v5" ) // MeetPostActions configures transcript delivery after a meeting. type MeetPostActions struct { EmailEnabled bool `json:"email_enabled"` EmailRecipients string `json:"email_recipients"` EmailCustomAddresses string `json:"email_custom_addresses"` DriveEnabled bool `json:"drive_enabled"` DriveFolderPath string `json:"drive_folder_path"` LLMEnabled bool `json:"llm_enabled"` LLMProviderID string `json:"llm_provider_id"` LLMPrompt string `json:"llm_prompt"` LLMThenEmail bool `json:"llm_then_email"` LLMThenDrive bool `json:"llm_then_drive"` } // MeetPolicy is the organisation UltiMeet / transcription policy. type MeetPolicy struct { TranscriptionEnabled bool `json:"transcription_enabled"` TranscriptionMode string `json:"transcription_mode"` TranscriptionEngine string `json:"transcription_engine"` SkynetURL string `json:"skynet_url"` WhisperModel string `json:"whisper_model"` ExternalAPIURL string `json:"external_api_url"` ExternalAPIKey string `json:"external_api_key"` ExternalAPIProvider string `json:"external_api_provider"` AutoStartTranscription bool `json:"auto_start_transcription"` PostActions MeetPostActions `json:"post_actions"` } // PublicMeetPolicy is exposed to authenticated clients (no secrets). type PublicMeetPolicy struct { TranscriptionEnabled bool `json:"transcription_enabled"` TranscriptionMode string `json:"transcription_mode"` AutoStartTranscription bool `json:"auto_start_transcription"` } func defaultMeetPolicy() map[string]any { return map[string]any{ "transcription_enabled": false, "transcription_mode": "live", "transcription_engine": "faster_whisper_local", "skynet_url": "http://skynet:8000", "whisper_model": "tiny", "external_api_url": "", "external_api_provider": "openai_compatible", "external_api_key": "", "auto_start_transcription": false, "post_actions": map[string]any{ "email_enabled": false, "email_recipients": "organizer", "email_custom_addresses": "", "drive_enabled": true, "drive_folder_path": "/UltiMeet/Transcripts", "llm_enabled": false, "llm_provider_id": "", "llm_prompt": "Résume cette réunion en français : points clés, décisions et actions à suivre.", "llm_then_email": true, "llm_then_drive": true, }, } } func (l *Loader) MeetPolicy(ctx context.Context) (MeetPolicy, error) { var raw []byte err := l.db.QueryRow(ctx, ` SELECT settings FROM org_settings WHERE id = $1 `, orgSettingsSingletonID).Scan(&raw) if err != nil && err != pgx.ErrNoRows { return MeetPolicy{}, err } stored := map[string]any{} if len(raw) > 0 { if err := json.Unmarshal(raw, &stored); err != nil { return MeetPolicy{}, err } } meet, _ := stored["meet"].(map[string]any) if meet == nil { meet = defaultMeetPolicy() } postRaw, _ := meet["post_actions"].(map[string]any) if postRaw == nil { postRaw, _ = defaultMeetPolicy()["post_actions"].(map[string]any) } mode, _ := meet["transcription_mode"].(string) if mode == "" { mode = "live" } engine, _ := meet["transcription_engine"].(string) if engine == "" { engine = "faster_whisper_local" } skynetURL, _ := meet["skynet_url"].(string) if skynetURL == "" { skynetURL = "http://skynet:8000" } whisperModel, _ := meet["whisper_model"].(string) if whisperModel == "" { whisperModel = "tiny" } provider, _ := meet["external_api_provider"].(string) if provider == "" { provider = "openai_compatible" } driveFolder, _ := postRaw["drive_folder_path"].(string) if driveFolder == "" { driveFolder = "/UltiMeet/Transcripts" } emailRecipients, _ := postRaw["email_recipients"].(string) if emailRecipients == "" { emailRecipients = "organizer" } llmPrompt, _ := postRaw["llm_prompt"].(string) if llmPrompt == "" { llmPrompt = "Résume cette réunion en français : points clés, décisions et actions à suivre." } return MeetPolicy{ TranscriptionEnabled: boolField(meet, "transcription_enabled"), TranscriptionMode: mode, TranscriptionEngine: engine, SkynetURL: skynetURL, WhisperModel: whisperModel, ExternalAPIURL: stringValue(meet["external_api_url"]), ExternalAPIKey: stringValue(meet["external_api_key"]), ExternalAPIProvider: provider, AutoStartTranscription: boolField(meet, "auto_start_transcription"), PostActions: MeetPostActions{ EmailEnabled: boolField(postRaw, "email_enabled"), EmailRecipients: emailRecipients, EmailCustomAddresses: stringValue(postRaw["email_custom_addresses"]), DriveEnabled: boolField(postRaw, "drive_enabled"), DriveFolderPath: driveFolder, LLMEnabled: boolField(postRaw, "llm_enabled"), LLMProviderID: stringValue(postRaw["llm_provider_id"]), LLMPrompt: llmPrompt, LLMThenEmail: boolField(postRaw, "llm_then_email"), LLMThenDrive: boolField(postRaw, "llm_then_drive"), }, }, nil } func (l *Loader) PublicMeetPolicy(ctx context.Context) (PublicMeetPolicy, error) { policy, err := l.MeetPolicy(ctx) if err != nil { return PublicMeetPolicy{}, err } return PublicMeetPolicy{ TranscriptionEnabled: policy.TranscriptionEnabled, TranscriptionMode: policy.TranscriptionMode, AutoStartTranscription: policy.AutoStartTranscription, }, nil } func (p MeetPolicy) LiveTranscriptionJWT() bool { return p.TranscriptionEnabled && p.TranscriptionMode == "live" }