Skip to content

Commit b76c6a2

Browse files
committed
feat: add locales console handler and update templates for addon display
1 parent 40d6157 commit b76c6a2

17 files changed

Lines changed: 139 additions & 16 deletions

File tree

locales/en.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,8 @@ status_offline = "Offline"
346346
config_sections_label = "Extension configuration sections"
347347

348348
[routing]
349+
filter_from_trunk = "Filter from trunk"
350+
349351
title = "Routing"
350352
new_route = "New Route"
351353
route_name = "Route Name"
@@ -2067,3 +2069,16 @@ escalation_timeline = "Escalation timeline"
20672069
escalation_after = "after"
20682070
add_escalation_step = "Add escalation step"
20692071
escalation_skill_group_placeholder = "Skill group ID"
2072+
2073+
2074+
[sip]
2075+
internal = "Internal SIP"
2076+
2077+
[carrier]
2078+
"provider.net" = "Provider network"
2079+
2080+
[rtp]
2081+
"provider.net" = "Provider network (RTP)"
2082+
2083+
[sbc]
2084+
health_check = "SBC Health Check"

locales/zh.toml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,8 @@ status_offline = "离线"
346346
config_sections_label = "分机配置栏目"
347347

348348
[routing]
349+
filter_from_trunk = "过滤来源中继"
350+
349351
title = "路由管理"
350352
new_route = "新建路由"
351353
route_name = "路由名称"
@@ -2039,8 +2041,6 @@ export = "导出配置"
20392041
exporting = "导出中..."
20402042
reload = "重载配置"
20412043
reloading = "重载中..."
2042-
diagnostics = "运行诊断"
2043-
testing = "测试中..."
20442044
confirm_delete_title = "删除策略"
20452045
confirm_delete_desc = "确定要删除此策略吗?此操作无法撤销。"
20462046
saved_with_backup = "已保存到 cc/acd.toml;并生成备份 cc/acd.toml.bak"
@@ -2052,3 +2052,15 @@ escalation_timeline = "升级时间线"
20522052
escalation_after = "后升级到"
20532053
add_escalation_step = "添加升级步骤"
20542054
escalation_skill_group_placeholder = "技能组 ID"
2055+
2056+
[sip]
2057+
internal = "内部 SIP"
2058+
2059+
[carrier]
2060+
"provider.net" = "运营商网络"
2061+
2062+
[rtp]
2063+
"provider.net" = "运营商网络 (RTP)"
2064+
2065+
[sbc]
2066+
health_check = "SBC 健康检查"

src/addons/transcript/handlers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,7 @@ pub async fn download_model(
933933
// Build download command
934934
let mut cmd = tokio::process::Command::new(command);
935935
cmd.arg("--models-path").arg(models_path);
936-
cmd.arg("--download-model");
936+
cmd.arg("--download-only");
937937

938938
// Add HF endpoint if provided
939939
if let Some(endpoint) = payload.hf_endpoint

src/call/app/ivr/tree_app.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ impl IvrApp {
166166
final_result: final_result.to_string(),
167167
completion_time: chrono::Utc::now().to_rfc3339(),
168168
final_routing_target: target.map(|s| s.to_string()),
169+
extra: None,
169170
});
170171
}
171172

@@ -377,6 +378,7 @@ impl IvrApp {
377378
callee_name: Some(ctx.call_info.callee.clone()),
378379
routing_target: Some(menu_key.to_string()),
379380
previous_node_id: previous_node,
381+
extra: None,
380382
});
381383
let menu = self
382384
.definition
@@ -460,6 +462,7 @@ impl IvrApp {
460462
next_node_id: None,
461463
hangup_reason: None,
462464
call_result: None,
465+
extra: None,
463466
});
464467
}
465468
match action {

src/console/handlers/locales.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use crate::console::ConsoleState;
2+
use axum::{
3+
extract::{Path, State},
4+
http::{header, StatusCode},
5+
response::{IntoResponse, Response},
6+
Router, routing::get,
7+
};
8+
use std::sync::Arc;
9+
10+
pub fn api_urls() -> Router<Arc<ConsoleState>> {
11+
Router::new().route("/locales/{lang}", get(get_locale_js))
12+
}
13+
14+
async fn get_locale_js(
15+
State(state): State<Arc<ConsoleState>>,
16+
Path(lang): Path<String>,
17+
) -> Response {
18+
let lang = lang.strip_suffix(".js").unwrap_or(&lang);
19+
let json = state.i18n.get_translations_json(lang);
20+
let js = format!("window.__i18n_t = {};", json);
21+
22+
(
23+
StatusCode::OK,
24+
[
25+
(header::CONTENT_TYPE, "application/javascript; charset=utf-8"),
26+
(header::CACHE_CONTROL, "public, max-age=604800, immutable"),
27+
],
28+
js,
29+
)
30+
.into_response()
31+
}

src/console/handlers/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub mod diagnostics;
1212
pub mod extension;
1313
pub mod forms;
1414
pub mod licenses;
15+
pub mod locales;
1516
pub mod metrics;
1617
pub mod notifications;
1718
pub mod presence;
@@ -68,6 +69,7 @@ pub fn router(state: Arc<ConsoleState>) -> Router {
6869
// API routes (nested under api_prefix)
6970
let api_routes = Router::new()
7071
.route("/pending-reloads", get(pending_reloads_handler))
72+
.merge(locales::api_urls())
7173
.merge(presence::api_urls())
7274
.merge(notifications::api_urls())
7375
.merge(metrics::api_urls())

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub mod utils;
2828
pub mod version;
2929

3030
#[cfg(test)]
31-
#[ctor::ctor]
31+
#[ctor::ctor(unsafe)]
3232
fn init_rustls_crypto_provider() {
3333
rustls::crypto::ring::default_provider()
3434
.install_default()

src/rwi/event.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ pub struct IvrNodeEntered {
528528
pub app_id: String, pub entry_time: String,
529529
pub caller_name: Option<String>, pub callee_name: Option<String>,
530530
pub routing_target: Option<String>, pub previous_node_id: Option<String>,
531+
pub extra: Option<serde_json::Value>,
531532
}
532533
rwi_event!(IvrNodeEntered, "ivr_node_entered");
533534

@@ -537,6 +538,7 @@ pub struct IvrNodeExited {
537538
pub result_value: Option<String>, pub duration_ms: u32, pub exit_time: String,
538539
pub next_node_id: Option<String>, pub hangup_reason: Option<String>,
539540
pub call_result: Option<String>,
541+
pub extra: Option<serde_json::Value>,
540542
}
541543
rwi_event!(IvrNodeExited, "ivr_node_exited");
542544

@@ -546,6 +548,7 @@ pub struct IvrFlowCompleted {
546548
pub total_nodes_traversed: u32, pub total_duration_ms: u32,
547549
pub final_result: String, pub completion_time: String,
548550
pub final_routing_target: Option<String>,
551+
pub extra: Option<serde_json::Value>,
549552
}
550553
rwi_event!(IvrFlowCompleted, "ivr_flow_completed");
551554

@@ -564,3 +567,60 @@ pub struct IvrStepTrace {
564567
pub sip_headers: Option<std::collections::HashMap<String, String>>,
565568
}
566569
rwi_event!(IvrStepTrace, "ivr_step_trace");
570+
571+
#[cfg(test)]
572+
mod tests {
573+
use super::*;
574+
575+
#[test]
576+
fn ivr_events_serialize_extra_field() {
577+
let entered = to_flat_payload(
578+
&IvrNodeEntered {
579+
call_id: "call-1".into(),
580+
node_id: "root".into(),
581+
node_name: "Root".into(),
582+
node_type: "menu".into(),
583+
app_id: "ivr-main".into(),
584+
entry_time: "2026-01-01T00:00:00Z".into(),
585+
caller_name: None,
586+
callee_name: None,
587+
routing_target: None,
588+
previous_node_id: None,
589+
extra: None,
590+
},
591+
None,
592+
);
593+
let exited = to_flat_payload(
594+
&IvrNodeExited {
595+
call_id: "call-1".into(),
596+
node_id: "root".into(),
597+
node_name: "Root".into(),
598+
result_value: None,
599+
duration_ms: 10,
600+
exit_time: "2026-01-01T00:00:01Z".into(),
601+
next_node_id: None,
602+
hangup_reason: None,
603+
call_result: None,
604+
extra: None,
605+
},
606+
None,
607+
);
608+
let completed = to_flat_payload(
609+
&IvrFlowCompleted {
610+
call_id: "call-1".into(),
611+
app_id: "ivr-main".into(),
612+
total_nodes_traversed: 1,
613+
total_duration_ms: 10,
614+
final_result: "completed".into(),
615+
completion_time: "2026-01-01T00:00:02Z".into(),
616+
final_routing_target: None,
617+
extra: None,
618+
},
619+
None,
620+
);
621+
622+
assert!(entered.get("extra").is_some());
623+
assert!(exited.get("extra").is_some());
624+
assert!(completed.get("extra").is_some());
625+
}
626+
}

templates/console/addon_detail.html

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,11 +363,10 @@ <h3 class="text-lg font-medium text-slate-900">{{ "addon_detail.screenshots" | t
363363
</div>
364364
</div>
365365

366-
<script type="application/json" id="__addonDetailT">{{ t | json | safe }}</script>
367366
<script>
368367
document.addEventListener('alpine:init', () => {
369-
const translations = JSON.parse(document.getElementById('__addonDetailT').textContent || '{}');
370-
const i18n = (key) => translations[key] || key;
368+
const translations = window.__i18n_t || {};
369+
const i18n = (key) => translations[key] || key;
371370

372371
Alpine.data('addonDetail', (id, licenseExpired, licenseUnlicensed) => ({
373372
licenseKey: '',

templates/console/addons.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,9 +422,8 @@ <h3 class="mt-4 text-base font-semibold text-slate-900">No license keys configur
422422
</div>
423423
</div>
424424

425-
<script type="application/json" id="__addonsT">{{ t | json | safe }}</script>
426425
<script>
427-
const addonsTranslations = JSON.parse(document.getElementById('__addonsT').textContent || '{}');
426+
const addonsTranslations = window.__i18n_t || {};
428427
const i18n = (key) => addonsTranslations[key] || key;
429428

430429
document.addEventListener('alpine:init', () => {

0 commit comments

Comments
 (0)