@@ -1023,6 +1238,7 @@ type SettingsForm = SystemSettings & {
smtp_password: string
turnstile_secret_key: string
linuxdo_connect_client_secret: string
+ sora_cache_allowed_hosts_text: string
}
const form = reactive({
@@ -1067,6 +1283,25 @@ const form = reactive({
// Identity patch (Claude -> Gemini)
enable_identity_patch: true,
identity_patch_prompt: '',
+ // Sora
+ sora_base_url: 'https://sora.chatgpt.com/backend',
+ sora_timeout: 120,
+ sora_max_retries: 3,
+ sora_poll_interval: 2.5,
+ sora_call_logic_mode: 'default',
+ sora_cache_enabled: false,
+ sora_cache_base_dir: 'tmp/sora',
+ sora_cache_video_dir: 'data/video',
+ sora_cache_max_bytes: 0,
+ sora_cache_allowed_hosts: [],
+ sora_cache_user_dir_enabled: true,
+ sora_watermark_free_enabled: false,
+ sora_watermark_free_parse_method: 'third_party',
+ sora_watermark_free_custom_parse_url: '',
+ sora_watermark_free_custom_parse_token: '',
+ sora_watermark_free_fallback_on_failure: true,
+ sora_token_refresh_enabled: false,
+ sora_cache_allowed_hosts_text: '',
// Ops monitoring (vNext)
ops_monitoring_enabled: true,
ops_realtime_monitoring_enabled: true,
@@ -1136,6 +1371,7 @@ async function loadSettings() {
form.smtp_password = ''
form.turnstile_secret_key = ''
form.linuxdo_connect_client_secret = ''
+ form.sora_cache_allowed_hosts_text = (settings.sora_cache_allowed_hosts || []).join('\n')
} catch (error: any) {
appStore.showError(
t('admin.settings.failedToLoad') + ': ' + (error.message || t('common.unknownError'))
@@ -1148,6 +1384,11 @@ async function loadSettings() {
async function saveSettings() {
saving.value = true
try {
+ const soraAllowedHosts = form.sora_cache_allowed_hosts_text
+ .split(/\r?\n/)
+ .map((value) => value.trim())
+ .filter((value) => value.length > 0)
+
const payload: UpdateSettingsRequest = {
registration_enabled: form.registration_enabled,
email_verify_enabled: form.email_verify_enabled,
@@ -1182,13 +1423,31 @@ async function saveSettings() {
fallback_model_gemini: form.fallback_model_gemini,
fallback_model_antigravity: form.fallback_model_antigravity,
enable_identity_patch: form.enable_identity_patch,
- identity_patch_prompt: form.identity_patch_prompt
+ identity_patch_prompt: form.identity_patch_prompt,
+ sora_base_url: form.sora_base_url,
+ sora_timeout: form.sora_timeout,
+ sora_max_retries: form.sora_max_retries,
+ sora_poll_interval: form.sora_poll_interval,
+ sora_call_logic_mode: form.sora_call_logic_mode,
+ sora_cache_enabled: form.sora_cache_enabled,
+ sora_cache_base_dir: form.sora_cache_base_dir,
+ sora_cache_video_dir: form.sora_cache_video_dir,
+ sora_cache_max_bytes: form.sora_cache_max_bytes,
+ sora_cache_allowed_hosts: soraAllowedHosts,
+ sora_cache_user_dir_enabled: form.sora_cache_user_dir_enabled,
+ sora_watermark_free_enabled: form.sora_watermark_free_enabled,
+ sora_watermark_free_parse_method: form.sora_watermark_free_parse_method,
+ sora_watermark_free_custom_parse_url: form.sora_watermark_free_custom_parse_url,
+ sora_watermark_free_custom_parse_token: form.sora_watermark_free_custom_parse_token,
+ sora_watermark_free_fallback_on_failure: form.sora_watermark_free_fallback_on_failure,
+ sora_token_refresh_enabled: form.sora_token_refresh_enabled
}
const updated = await adminAPI.settings.updateSettings(payload)
Object.assign(form, updated)
form.smtp_password = ''
form.turnstile_secret_key = ''
form.linuxdo_connect_client_secret = ''
+ form.sora_cache_allowed_hosts_text = (updated.sora_cache_allowed_hosts || []).join('\n')
// Refresh cached public settings so sidebar/header update immediately
await appStore.fetchPublicSettings(true)
appStore.showSuccess(t('admin.settings.settingsSaved'))
diff --git a/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue b/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue
index f2a7d787..493cb346 100644
--- a/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue
+++ b/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue
@@ -111,6 +111,7 @@ const platformOptions = computed(() => [
{ value: 'openai', label: 'OpenAI' },
{ value: 'anthropic', label: 'Anthropic' },
{ value: 'gemini', label: 'Gemini' },
+ { value: 'sora', label: 'Sora' },
{ value: 'antigravity', label: 'Antigravity' }
])
diff --git a/frontend/src/views/user/KeysView.vue b/frontend/src/views/user/KeysView.vue
index b72ae9ad..b7e3d166 100644
--- a/frontend/src/views/user/KeysView.vue
+++ b/frontend/src/views/user/KeysView.vue
@@ -916,6 +916,7 @@ const executeCcsImport = (row: ApiKey, clientType: 'claude' | 'gemini') => {
} else {
switch (platform) {
case 'openai':
+ case 'sora':
app = 'codex'
endpoint = baseUrl
break