feat: add remote management identity features and update localization files for multiple languages

This commit is contained in:
Ivan 2026-07-16 11:43:46 -05:00
parent dd0e68b33d
commit afa2cdbc33
No known key found for this signature in database
12 changed files with 259 additions and 37 deletions

View file

@ -22,6 +22,7 @@ COPY patches ./patches
COPY scripts/fetch-micron-wasm.mjs scripts/fetch-micron-wasm.mjs
COPY scripts/micron-wasm-resolve-bundled.mjs scripts/micron-wasm-resolve-bundled.mjs
COPY scripts/micron-parser-go-version.mjs scripts/micron-parser-go-version.mjs
COPY scripts/build-visualiser-wasm.mjs scripts/build-visualiser-wasm.mjs
COPY scripts/sync-meshchatx-docs.js scripts/sync-meshchatx-docs.js
COPY scripts/pip_rns_remotes.py scripts/pip_rns_remotes.py
COPY scripts/build/fetch_reticulum_manual.py scripts/build/fetch_reticulum_manual.py

Binary file not shown.

View file

@ -45,7 +45,10 @@ def _identity_info(path: str, name: str) -> dict[str, Any] | None:
if identity is None:
return None
try:
identity_hash = bytes(identity.hash).hex()
identity_hash_raw = identity.hash
if identity_hash_raw is None:
return None
identity_hash = bytes(identity_hash_raw).hex()
except Exception:
return None
return {

View file

@ -490,7 +490,11 @@
"battery_unavailable": "Akkustatus nicht verfügbar",
"battery_level_title": "Akku {percent}",
"battery_charging_title": "Lädt {percent}",
"battery_discharging_title": "Akku {percent}"
"battery_discharging_title": "Akku {percent}",
"remote_management_allowed": "Remote management allow-list",
"remote_management_allowed_description": "Identity hashes allowed to query this instance remotely with rnstatus/rnpath. One 32-character hex hash per line.",
"remote_management_allowed_placeholder": "9fb6d773498fb3feda407ed8ef2c3229",
"remote_management_allowed_save": "Save allow-list"
},
"common": {
"open": "Öffnen",
@ -2758,7 +2762,14 @@
"blackhole_publishing": "Veröffentlicht",
"blackhole_inactive": "Inaktiv",
"failed_refresh": "RNStatus konnte nicht aktualisiert werden",
"reloading": "RNS wird neu geladen..."
"reloading": "RNS wird neu geladen...",
"remote_query": "Remote query",
"remote_query_hint": "Query a remote transport instance that has remote management enabled and your management identity on its allow-list.",
"remote_transport_hash": "Remote transport identity hash",
"remote_transport_placeholder": "32-character hex hash",
"remote_timeout": "Timeout (seconds)",
"remote_active": "Showing remote status for {hash}",
"use_local": "Use local instance"
},
"translator": {
"text_translation": "Textübersetzung",
@ -3686,6 +3697,20 @@
"failed_to_copy_address": "Adresse konnte nicht kopiert werden",
"config_dir": "Reticulum-Konfigurationsverzeichnis",
"config_dir_placeholder": "Standard ist die Reticulum-Konfiguration der App",
"config_dir_hint": "Leer lassen, um die MeshChatX-Reticulum-Instanz zu teilen. Nur überschreiben, um ein separates Reticulum-Konfigurationsverzeichnis zu nutzen."
"config_dir_hint": "Leer lassen, um die MeshChatX-Reticulum-Instanz zu teilen. Nur überschreiben, um ein separates Reticulum-Konfigurationsverzeichnis zu nutzen.",
"interactive": "Interactive mode (-x)",
"timeout": "Connect timeout (seconds)",
"result_timeout": "Result download timeout (seconds)",
"stdout_limit": "Stdout limit (bytes)",
"stderr_limit": "Stderr limit (bytes)"
},
"remote_mgmt": {
"management_identity": "Management identity",
"select_identity": "Select identity file",
"create_identity": "Create",
"create_identity_prompt": "Name for the new management identity file",
"identity_created": "Management identity created",
"failed_load_identities": "Failed to load management identities",
"failed_create_identity": "Failed to create management identity"
}
}

View file

@ -490,7 +490,11 @@
"battery_unavailable": "Estado de batería no disponible",
"battery_level_title": "Batería {percent}",
"battery_charging_title": "Cargando {percent}",
"battery_discharging_title": "En batería {percent}"
"battery_discharging_title": "En batería {percent}",
"remote_management_allowed": "Remote management allow-list",
"remote_management_allowed_description": "Identity hashes allowed to query this instance remotely with rnstatus/rnpath. One 32-character hex hash per line.",
"remote_management_allowed_placeholder": "9fb6d773498fb3feda407ed8ef2c3229",
"remote_management_allowed_save": "Save allow-list"
},
"common": {
"open": "Abierto",
@ -2940,7 +2944,14 @@
"blackhole_publishing": "Publicando",
"blackhole_inactive": "Inactivo",
"failed_refresh": "Error al actualizar RNStatus",
"reloading": "Recargando RNS..."
"reloading": "Recargando RNS...",
"remote_query": "Remote query",
"remote_query_hint": "Query a remote transport instance that has remote management enabled and your management identity on its allow-list.",
"remote_transport_hash": "Remote transport identity hash",
"remote_transport_placeholder": "32-character hex hash",
"remote_timeout": "Timeout (seconds)",
"remote_active": "Showing remote status for {hash}",
"use_local": "Use local instance"
},
"translator": {
"text_translation": "Traducción de texto",
@ -3686,6 +3697,20 @@
"failed_to_copy_address": "No se pudo copiar la dirección",
"config_dir": "Directorio de configuración de Reticulum",
"config_dir_placeholder": "Por defecto usa la configuración Reticulum de la app",
"config_dir_hint": "Déjalo en blanco para compartir la instancia Reticulum de MeshChatX. Sobrescribe solo para usar un directorio de configuración Reticulum aparte."
"config_dir_hint": "Déjalo en blanco para compartir la instancia Reticulum de MeshChatX. Sobrescribe solo para usar un directorio de configuración Reticulum aparte.",
"interactive": "Interactive mode (-x)",
"timeout": "Connect timeout (seconds)",
"result_timeout": "Result download timeout (seconds)",
"stdout_limit": "Stdout limit (bytes)",
"stderr_limit": "Stderr limit (bytes)"
},
"remote_mgmt": {
"management_identity": "Management identity",
"select_identity": "Select identity file",
"create_identity": "Create",
"create_identity_prompt": "Name for the new management identity file",
"identity_created": "Management identity created",
"failed_load_identities": "Failed to load management identities",
"failed_create_identity": "Failed to create management identity"
}
}

View file

@ -490,7 +490,11 @@
"battery_unavailable": "Akun tila ei ole saatavilla",
"battery_level_title": "Akku {percent}",
"battery_charging_title": "Lataa {percent}",
"battery_discharging_title": "Akulla {percent}"
"battery_discharging_title": "Akulla {percent}",
"remote_management_allowed": "Remote management allow-list",
"remote_management_allowed_description": "Identity hashes allowed to query this instance remotely with rnstatus/rnpath. One 32-character hex hash per line.",
"remote_management_allowed_placeholder": "9fb6d773498fb3feda407ed8ef2c3229",
"remote_management_allowed_save": "Save allow-list"
},
"common": {
"open": "Avaa",
@ -3130,7 +3134,14 @@
"blackhole_publishing": "Julkaistaan",
"blackhole_inactive": "Epäaktiivinen",
"failed_refresh": "RNStatusin päivitys epäonnistui",
"reloading": "Ladataan RNS uudelleen..."
"reloading": "Ladataan RNS uudelleen...",
"remote_query": "Remote query",
"remote_query_hint": "Query a remote transport instance that has remote management enabled and your management identity on its allow-list.",
"remote_transport_hash": "Remote transport identity hash",
"remote_transport_placeholder": "32-character hex hash",
"remote_timeout": "Timeout (seconds)",
"remote_active": "Showing remote status for {hash}",
"use_local": "Use local instance"
},
"translator": {
"text_translation": "Tekstin käännös",
@ -3686,6 +3697,20 @@
"failed_to_copy_address": "Osoitteen kopiointi epäonnistui",
"config_dir": "Reticulum-asetushakemisto",
"config_dir_placeholder": "Oletuksena sovelluksen Reticulum-asetukset",
"config_dir_hint": "Jätä tyhjäksi jakaaksesi MeshChatX:n Reticulum-instanssin. Korvaa vain, jos käytät erillistä Reticulum-asetushakemistoa."
"config_dir_hint": "Jätä tyhjäksi jakaaksesi MeshChatX:n Reticulum-instanssin. Korvaa vain, jos käytät erillistä Reticulum-asetushakemistoa.",
"interactive": "Interactive mode (-x)",
"timeout": "Connect timeout (seconds)",
"result_timeout": "Result download timeout (seconds)",
"stdout_limit": "Stdout limit (bytes)",
"stderr_limit": "Stderr limit (bytes)"
},
"remote_mgmt": {
"management_identity": "Management identity",
"select_identity": "Select identity file",
"create_identity": "Create",
"create_identity_prompt": "Name for the new management identity file",
"identity_created": "Management identity created",
"failed_load_identities": "Failed to load management identities",
"failed_create_identity": "Failed to create management identity"
}
}

View file

@ -490,7 +490,11 @@
"battery_unavailable": "État de la batterie indisponible",
"battery_level_title": "Batterie {percent}",
"battery_charging_title": "En charge {percent}",
"battery_discharging_title": "Sur batterie {percent}"
"battery_discharging_title": "Sur batterie {percent}",
"remote_management_allowed": "Remote management allow-list",
"remote_management_allowed_description": "Identity hashes allowed to query this instance remotely with rnstatus/rnpath. One 32-character hex hash per line.",
"remote_management_allowed_placeholder": "9fb6d773498fb3feda407ed8ef2c3229",
"remote_management_allowed_save": "Save allow-list"
},
"common": {
"open": "Ouvrir",
@ -2940,7 +2944,14 @@
"blackhole_publishing": "Publication",
"blackhole_inactive": "Inactif",
"failed_refresh": "Échec de l'actualisation de RNStatus",
"reloading": "Rechargement de RNS..."
"reloading": "Rechargement de RNS...",
"remote_query": "Remote query",
"remote_query_hint": "Query a remote transport instance that has remote management enabled and your management identity on its allow-list.",
"remote_transport_hash": "Remote transport identity hash",
"remote_transport_placeholder": "32-character hex hash",
"remote_timeout": "Timeout (seconds)",
"remote_active": "Showing remote status for {hash}",
"use_local": "Use local instance"
},
"translator": {
"text_translation": "Traduction textuelle",
@ -3686,6 +3697,20 @@
"failed_to_copy_address": "Échec de la copie de l'adresse",
"config_dir": "Répertoire de configuration Reticulum",
"config_dir_placeholder": "Par défaut: configuration Reticulum de l'app",
"config_dir_hint": "Laissez vide pour partager l'instance Reticulum de MeshChatX. Remplacez uniquement pour utiliser un répertoire de configuration Reticulum séparé."
"config_dir_hint": "Laissez vide pour partager l'instance Reticulum de MeshChatX. Remplacez uniquement pour utiliser un répertoire de configuration Reticulum séparé.",
"interactive": "Interactive mode (-x)",
"timeout": "Connect timeout (seconds)",
"result_timeout": "Result download timeout (seconds)",
"stdout_limit": "Stdout limit (bytes)",
"stderr_limit": "Stderr limit (bytes)"
},
"remote_mgmt": {
"management_identity": "Management identity",
"select_identity": "Select identity file",
"create_identity": "Create",
"create_identity_prompt": "Name for the new management identity file",
"identity_created": "Management identity created",
"failed_load_identities": "Failed to load management identities",
"failed_create_identity": "Failed to create management identity"
}
}

View file

@ -490,7 +490,11 @@
"battery_unavailable": "Stato batteria non disponibile",
"battery_level_title": "Batteria {percent}",
"battery_charging_title": "In carica {percent}",
"battery_discharging_title": "A batteria {percent}"
"battery_discharging_title": "A batteria {percent}",
"remote_management_allowed": "Remote management allow-list",
"remote_management_allowed_description": "Identity hashes allowed to query this instance remotely with rnstatus/rnpath. One 32-character hex hash per line.",
"remote_management_allowed_placeholder": "9fb6d773498fb3feda407ed8ef2c3229",
"remote_management_allowed_save": "Save allow-list"
},
"common": {
"open": "Apri",
@ -2992,7 +2996,14 @@
"blackhole_publishing": "Pubblicazione",
"blackhole_inactive": "Inattivo",
"failed_refresh": "Impossibile aggiornare RNStatus",
"reloading": "Ricaricamento RNS..."
"reloading": "Ricaricamento RNS...",
"remote_query": "Remote query",
"remote_query_hint": "Query a remote transport instance that has remote management enabled and your management identity on its allow-list.",
"remote_transport_hash": "Remote transport identity hash",
"remote_transport_placeholder": "32-character hex hash",
"remote_timeout": "Timeout (seconds)",
"remote_active": "Showing remote status for {hash}",
"use_local": "Use local instance"
},
"translator": {
"text_translation": "Traduzione Testo",
@ -3686,6 +3697,20 @@
"failed_to_copy_address": "Copia indirizzo non riuscita",
"config_dir": "Directory di configurazione Reticulum",
"config_dir_placeholder": "Impostazione predefinita: configurazione Reticulum dell'app",
"config_dir_hint": "Lascia vuoto per condividere l'istanza Reticulum di MeshChatX. Sovrascrivi solo per usare una directory di configurazione Reticulum separata."
"config_dir_hint": "Lascia vuoto per condividere l'istanza Reticulum di MeshChatX. Sovrascrivi solo per usare una directory di configurazione Reticulum separata.",
"interactive": "Interactive mode (-x)",
"timeout": "Connect timeout (seconds)",
"result_timeout": "Result download timeout (seconds)",
"stdout_limit": "Stdout limit (bytes)",
"stderr_limit": "Stderr limit (bytes)"
},
"remote_mgmt": {
"management_identity": "Management identity",
"select_identity": "Select identity file",
"create_identity": "Create",
"create_identity_prompt": "Name for the new management identity file",
"identity_created": "Management identity created",
"failed_load_identities": "Failed to load management identities",
"failed_create_identity": "Failed to create management identity"
}
}

View file

@ -490,7 +490,11 @@
"battery_unavailable": "Batterijstatus niet beschikbaar",
"battery_level_title": "Batterij {percent}",
"battery_charging_title": "Opladen {percent}",
"battery_discharging_title": "Op batterij {percent}"
"battery_discharging_title": "Op batterij {percent}",
"remote_management_allowed": "Remote management allow-list",
"remote_management_allowed_description": "Identity hashes allowed to query this instance remotely with rnstatus/rnpath. One 32-character hex hash per line.",
"remote_management_allowed_placeholder": "9fb6d773498fb3feda407ed8ef2c3229",
"remote_management_allowed_save": "Save allow-list"
},
"common": {
"open": "Openen",
@ -2940,7 +2944,14 @@
"blackhole_publishing": "Publiceren",
"blackhole_inactive": "Inactief",
"failed_refresh": "RNStatus vernieuwen mislukt",
"reloading": "RNS opnieuw laden..."
"reloading": "RNS opnieuw laden...",
"remote_query": "Remote query",
"remote_query_hint": "Query a remote transport instance that has remote management enabled and your management identity on its allow-list.",
"remote_transport_hash": "Remote transport identity hash",
"remote_transport_placeholder": "32-character hex hash",
"remote_timeout": "Timeout (seconds)",
"remote_active": "Showing remote status for {hash}",
"use_local": "Use local instance"
},
"translator": {
"text_translation": "Tekstvertaling",
@ -3686,6 +3697,20 @@
"failed_to_copy_address": "Adres kopiëren mislukt",
"config_dir": "Reticulum-configuratiemap",
"config_dir_placeholder": "Standaard: Reticulum-config van de app",
"config_dir_hint": "Laat leeg om de MeshChatX-Reticulum-instantie te delen. Overschrijf alleen om een aparte Reticulum-configuratiemap te gebruiken."
"config_dir_hint": "Laat leeg om de MeshChatX-Reticulum-instantie te delen. Overschrijf alleen om een aparte Reticulum-configuratiemap te gebruiken.",
"interactive": "Interactive mode (-x)",
"timeout": "Connect timeout (seconds)",
"result_timeout": "Result download timeout (seconds)",
"stdout_limit": "Stdout limit (bytes)",
"stderr_limit": "Stderr limit (bytes)"
},
"remote_mgmt": {
"management_identity": "Management identity",
"select_identity": "Select identity file",
"create_identity": "Create",
"create_identity_prompt": "Name for the new management identity file",
"identity_created": "Management identity created",
"failed_load_identities": "Failed to load management identities",
"failed_create_identity": "Failed to create management identity"
}
}

View file

@ -490,7 +490,11 @@
"battery_unavailable": "Состояние батареи недоступно",
"battery_level_title": "Батарея {percent}",
"battery_charging_title": "Зарядка {percent}",
"battery_discharging_title": "От батареи {percent}"
"battery_discharging_title": "От батареи {percent}",
"remote_management_allowed": "Remote management allow-list",
"remote_management_allowed_description": "Identity hashes allowed to query this instance remotely with rnstatus/rnpath. One 32-character hex hash per line.",
"remote_management_allowed_placeholder": "9fb6d773498fb3feda407ed8ef2c3229",
"remote_management_allowed_save": "Save allow-list"
},
"common": {
"open": "Открыть",
@ -2758,7 +2762,14 @@
"blackhole_publishing": "Публикация",
"blackhole_inactive": "Неактивно",
"failed_refresh": "Не удалось обновить RNStatus",
"reloading": "Перезагрузка RNS..."
"reloading": "Перезагрузка RNS...",
"remote_query": "Remote query",
"remote_query_hint": "Query a remote transport instance that has remote management enabled and your management identity on its allow-list.",
"remote_transport_hash": "Remote transport identity hash",
"remote_transport_placeholder": "32-character hex hash",
"remote_timeout": "Timeout (seconds)",
"remote_active": "Showing remote status for {hash}",
"use_local": "Use local instance"
},
"translator": {
"text_translation": "Перевод текста",
@ -3686,6 +3697,20 @@
"failed_to_copy_address": "Не удалось скопировать адрес",
"config_dir": "Каталог конфигурации Reticulum",
"config_dir_placeholder": "По умолчанию: конфигурация Reticulum приложения",
"config_dir_hint": "Оставьте пустым, чтобы использовать общий экземпляр Reticulum MeshChatX. Указывайте путь только для отдельного каталога конфигурации Reticulum."
"config_dir_hint": "Оставьте пустым, чтобы использовать общий экземпляр Reticulum MeshChatX. Указывайте путь только для отдельного каталога конфигурации Reticulum.",
"interactive": "Interactive mode (-x)",
"timeout": "Connect timeout (seconds)",
"result_timeout": "Result download timeout (seconds)",
"stdout_limit": "Stdout limit (bytes)",
"stderr_limit": "Stderr limit (bytes)"
},
"remote_mgmt": {
"management_identity": "Management identity",
"select_identity": "Select identity file",
"create_identity": "Create",
"create_identity_prompt": "Name for the new management identity file",
"identity_created": "Management identity created",
"failed_load_identities": "Failed to load management identities",
"failed_create_identity": "Failed to create management identity"
}
}

View file

@ -490,7 +490,11 @@
"battery_unavailable": "无法获取电池状态",
"battery_level_title": "电池 {percent}",
"battery_charging_title": "充电中 {percent}",
"battery_discharging_title": "使用电池 {percent}"
"battery_discharging_title": "使用电池 {percent}",
"remote_management_allowed": "Remote management allow-list",
"remote_management_allowed_description": "Identity hashes allowed to query this instance remotely with rnstatus/rnpath. One 32-character hex hash per line.",
"remote_management_allowed_placeholder": "9fb6d773498fb3feda407ed8ef2c3229",
"remote_management_allowed_save": "Save allow-list"
},
"common": {
"open": "打开",
@ -2940,7 +2944,14 @@
"blackhole_publishing": "发布中",
"blackhole_inactive": "未激活",
"failed_refresh": "刷新 RNStatus 失败",
"reloading": "正在重新加载 RNS..."
"reloading": "正在重新加载 RNS...",
"remote_query": "Remote query",
"remote_query_hint": "Query a remote transport instance that has remote management enabled and your management identity on its allow-list.",
"remote_transport_hash": "Remote transport identity hash",
"remote_transport_placeholder": "32-character hex hash",
"remote_timeout": "Timeout (seconds)",
"remote_active": "Showing remote status for {hash}",
"use_local": "Use local instance"
},
"translator": {
"text_translation": "文本翻译",
@ -3686,6 +3697,20 @@
"failed_to_copy_address": "复制地址失败",
"config_dir": "Reticulum 配置目录",
"config_dir_placeholder": "默认为应用的 Reticulum 配置",
"config_dir_hint": "留空以共享 MeshChatX 的 Reticulum 实例。仅在需要单独的 Reticulum 配置目录时覆盖。"
"config_dir_hint": "留空以共享 MeshChatX 的 Reticulum 实例。仅在需要单独的 Reticulum 配置目录时覆盖。",
"interactive": "Interactive mode (-x)",
"timeout": "Connect timeout (seconds)",
"result_timeout": "Result download timeout (seconds)",
"stdout_limit": "Stdout limit (bytes)",
"stderr_limit": "Stderr limit (bytes)"
},
"remote_mgmt": {
"management_identity": "Management identity",
"select_identity": "Select identity file",
"create_identity": "Create",
"create_identity_prompt": "Name for the new management identity file",
"identity_created": "Management identity created",
"failed_load_identities": "Failed to load management identities",
"failed_create_identity": "Failed to create management identity"
}
}

View file

@ -52,6 +52,9 @@ describe("RNStatusPage.vue", () => {
},
});
}
if (url === "/api/v1/reticulum/management-identities") {
return Promise.resolve({ data: { identities: [] } });
}
return Promise.resolve({ data: {} });
});
});
@ -83,14 +86,22 @@ describe("RNStatusPage.vue", () => {
});
it("labels disabled blackhole as Inactive", async () => {
axiosMock.get.mockResolvedValueOnce({
data: {
interfaces: [],
link_count: 0,
blackhole_enabled: false,
blackhole_count: 0,
blackhole_sources: [],
},
axiosMock.get.mockImplementation((url) => {
if (url === "/api/v1/rnstatus") {
return Promise.resolve({
data: {
interfaces: [],
link_count: 0,
blackhole_enabled: false,
blackhole_count: 0,
blackhole_sources: [],
},
});
}
if (url === "/api/v1/reticulum/management-identities") {
return Promise.resolve({ data: { identities: [] } });
}
return Promise.resolve({ data: {} });
});
const wrapper = mountRNStatusPage();
await vi.waitFor(() => expect(wrapper.vm.isLoading).toBe(false));
@ -123,12 +134,19 @@ describe("RNStatusPage.vue", () => {
});
it("toasts on refresh failure", async () => {
axiosMock.get.mockRejectedValueOnce({
response: { data: { message: "RNS stack is reloading" }, status: 503 },
axiosMock.get.mockImplementation((url) => {
if (url === "/api/v1/rnstatus") {
return Promise.reject({
response: { data: { message: "RNS stack is reloading" }, status: 503 },
});
}
if (url === "/api/v1/reticulum/management-identities") {
return Promise.resolve({ data: { identities: [] } });
}
return Promise.resolve({ data: {} });
});
const wrapper = mountRNStatusPage();
await vi.waitFor(() => expect(wrapper.vm.isLoading).toBe(false));
expect(ToastUtils.error).toHaveBeenCalled();
await vi.waitFor(() => expect(ToastUtils.error).toHaveBeenCalled());
const msg = ToastUtils.error.mock.calls[0][0];
expect(msg).toContain("Failed to refresh RNStatus");
expect(msg).toContain("RNS stack is reloading");