diff --git a/core/imageroot/etc/systemd/system/support.service b/core/imageroot/etc/systemd/system/support.service index 224ec1c63..d1d496909 100644 --- a/core/imageroot/etc/systemd/system/support.service +++ b/core/imageroot/etc/systemd/system/support.service @@ -24,10 +24,17 @@ ExecStart=runagent -m node podman run \ ${SUPPORT_IMAGE} ExecStartPost=runagent -m node bash -c "nsenter -t $$(podman ps --format='{{.Pid}}' --filter=id=$$(< %t/support.cid)) -n -- nft -f $${AGENT_INSTALL_DIR}/etc/support/nft.conf" ExecStartPost=runagent -m cluster support-clusteradminctl add +ExecStartPost=/usr/bin/systemd-run \ + --collect \ + --unit=%N-expire.timer \ + --on-active=24h \ + /usr/bin/systemctl stop %n ExecStop=/usr/bin/podman stop --ignore --cidfile %t/support.cid -t 10 ExecStopPost=runagent -m node support-sshkeyctl remove ExecStopPost=runagent -m cluster support-clusteradminctl remove ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/support.cid +ExecStopPost=-/usr/bin/systemctl --no-block stop %N-expire.timer PIDFile=%t/support.pid Type=forking SyslogIdentifier=support +RuntimeMaxSec=7d diff --git a/core/imageroot/var/lib/nethserver/node/actions/get-support-session/20get_support_session b/core/imageroot/var/lib/nethserver/node/actions/get-support-session/20get_support_session index b82291bb8..9ab2d920b 100755 --- a/core/imageroot/var/lib/nethserver/node/actions/get-support-session/20get_support_session +++ b/core/imageroot/var/lib/nethserver/node/actions/get-support-session/20get_support_session @@ -9,6 +9,13 @@ if [[ "$(systemctl is-active support.service)" == "active" ]]; then jactive=true + if [[ "$(systemctl is-active support-expire.timer)" == "active" ]]; then + # 24 hours expiry + expires_at=$(date --utc -d "$(systemctl show --timestamp=utc -P ActiveEnterTimestamp support-expire.timer) + 24 hours" --iso-8601=seconds) + else + # 7 days expiry + expires_at=$(date --utc -d "$(systemctl show --timestamp=utc -P ActiveEnterTimestamp support.service) + 7 days" --iso-8601=seconds) + fi else jactive=false fi @@ -16,4 +23,5 @@ fi jq -c -n \ --argjson active "${jactive}" \ --arg session_id "$(grep VPN_PASSWORD support.env | cut -f 2 -d =)" \ - '{"session_id": $session_id, "active": $active}' + --arg expires_at "${expires_at}" \ + '{"session_id": $session_id, "active": $active, "expires_at": $expires_at}' diff --git a/core/imageroot/var/lib/nethserver/node/actions/get-support-session/validate-output.json b/core/imageroot/var/lib/nethserver/node/actions/get-support-session/validate-output.json index 66025b8d4..e0b46b073 100644 --- a/core/imageroot/var/lib/nethserver/node/actions/get-support-session/validate-output.json +++ b/core/imageroot/var/lib/nethserver/node/actions/get-support-session/validate-output.json @@ -6,10 +6,12 @@ "examples": [ { "session_id": "811630de-67f4-5b6d-8b2f-7cc01f592b1b", + "expires_at": "2026-03-03T15:53:22+00:00", "active": true }, { "session_id": "811630de-67f4-5b6d-8b2f-7cc01f592b1b", + "expires_at": "", "active": false } ], @@ -23,6 +25,10 @@ "type": "string", "description": "Last opened session identifier for the support team" }, + "expires_at": { + "type":"string", + "description": "Scheduled and automatic session stop time, in ISO 8601 format. An empty string is returned if support is not active." + }, "active": { "type": "boolean", "description": "If the opened session is still active or not" diff --git a/core/imageroot/var/lib/nethserver/node/bin/support-sshkeyctl b/core/imageroot/var/lib/nethserver/node/bin/support-sshkeyctl index fde9d9de0..437e628d8 100755 --- a/core/imageroot/var/lib/nethserver/node/bin/support-sshkeyctl +++ b/core/imageroot/var/lib/nethserver/node/bin/support-sshkeyctl @@ -16,7 +16,7 @@ pubkey_file=${3:-${AGENT_INSTALL_DIR:?}/etc/support/ssh-rsa.pub} function add_ssh_key { local keyopts - keyopts=$(printf 'expiry-time="%s",from="127.0.0.1"' "$(date -d 'next month' +'%Y%m%dZ')") + keyopts=$(printf 'expiry-time="%s",from="127.0.0.1"' "$(date --utc -d '7 days' +'%Y%m%d%H%M%SZ')") umask 077 printf "%s %s %s\n" "${keyopts}" "$(cat "${pubkey_file}")" "${key_comment}" >> "${authkeys_file}" } diff --git a/core/ui/public/i18n/en/translation.json b/core/ui/public/i18n/en/translation.json index d0afa9188..7f6831f1b 100644 --- a/core/ui/public/i18n/en/translation.json +++ b/core/ui/public/i18n/en/translation.json @@ -731,7 +731,7 @@ "start_session_support": "Start session", "session_id": "Session ID", "paste_session_id_to_support_team": "Paste the session ID to the support team", - "remote_support_in_progress": "Remote support session in progress", + "remote_support_in_progress": "The remote support session will end automatically in {time}. You can end it manually at any time.", "stop_session_support": "End session", "remove_cluster_subscription_title": "Remove the cluster subscription", "remove_cluster_subscription_description": "You are going to remove the subscription plan. This will disable the subscription features.", diff --git a/core/ui/public/i18n/it/translation.json b/core/ui/public/i18n/it/translation.json index 9582a52f1..35e817852 100644 --- a/core/ui/public/i18n/it/translation.json +++ b/core/ui/public/i18n/it/translation.json @@ -1615,7 +1615,7 @@ "remove_cluster_subscription_title": "Rimuovi la subscription del cluster", "no_expiration": "Senza scadenza", "paste_session_id_to_support_team": "Incolla l'ID di sessione al team di supporto", - "remote_support_in_progress": "Sessione di supporto remoto in corso", + "remote_support_in_progress": "La sessione di supporto remoto terminerà automaticamente tra {time}. Puoi terminarla manualmente in qualsiasi momento.", "remote_support": "Supporto remoto", "plan_name": "Piano", "request_subscription": "Registra", diff --git a/core/ui/src/i18n/index.js b/core/ui/src/i18n/index.js index e86c7877e..14313a05a 100644 --- a/core/ui/src/i18n/index.js +++ b/core/ui/src/i18n/index.js @@ -16,3 +16,27 @@ export async function loadLanguage(lang) { } } } + +export async function getDateFnsLocale() { + const lang = navigator.language; + try { + const mod = await import( + /* webpackChunkName: "date-fns-locale-[request]" */ + `date-fns/locale/${lang}` + ); + return mod.default || mod; + } catch { + // try base language (e.g. "it" from "it-IT") + const baseLang = lang.split("-")[0]; + try { + const mod = await import( + /* webpackChunkName: "date-fns-locale-[request]" */ + `date-fns/locale/${baseLang}` + ); + return mod.default || mod; + } catch { + const mod = await import("date-fns/locale/en-US"); + return mod.default || mod; + } + } +} \ No newline at end of file diff --git a/core/ui/src/views/settings/SettingsSubscription.vue b/core/ui/src/views/settings/SettingsSubscription.vue index 637c8ec09..b09f5b2ae 100644 --- a/core/ui/src/views/settings/SettingsSubscription.vue +++ b/core/ui/src/views/settings/SettingsSubscription.vue @@ -295,7 +295,9 @@