From 877b2b4ff310a3c0e5e9e04cfe4b708bed4b624a Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Sat, 26 Jul 2025 19:16:02 -0400 Subject: [PATCH 001/128] build: 2025.07.26 (#22) * build: 2025.07.26 * ci: template plugin files --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- plugin/tailscale-preview.plg | 36 +++++++++++++++++---------------- plugin/tailscale-trunk.plg | 36 +++++++++++++++++---------------- plugin/tailscale.plg | 36 +++++++++++++++++---------------- tools/plugin/files/CHANGELOG.md | 10 +++++---- tools/plugin/tailscale.json | 10 ++++----- 5 files changed, 68 insertions(+), 60 deletions(-) diff --git a/plugin/tailscale-preview.plg b/plugin/tailscale-preview.plg index 4a77bad..8be9aff 100644 --- a/plugin/tailscale-preview.plg +++ b/plugin/tailscale-preview.plg @@ -4,7 +4,7 @@ - -https://pkgs.tailscale.com/stable/tailscale_1.84.0_amd64.tgz -c91eb43a92c209108bfaf1237696ac2089cc3d8fcf35d570d348cbfb19d8fb31 + +https://pkgs.tailscale.com/stable/tailscale_1.86.0_amd64.tgz +077a0f82c9e1b9aa5a93a489c09def42bbb49b15036a868cfd3232015eb90cef - -https://github.com/unraid/unraid-tailscale-utils/releases/download/4.1.0/unraid-tailscale-utils-4.1.0-noarch-1.txz -97b01db93921e0b2ee58c7b47fa6246eff2c81c872f70043cf85bf33f6572a15 + +https://github.com/unraid/unraid-tailscale-utils/releases/download/4.2.0/unraid-tailscale-utils-4.2.0-noarch-1.txz +681de5861155c4b5590e30f12a1b7625ee1e872c897f175560d449449f448bef + + + + + + + + + + + + + + diff --git a/plugin/plugin.json b/plugin/plugin.json new file mode 100644 index 0000000..067edd2 --- /dev/null +++ b/plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "tailscale", + "package_name": "unraid-tailscale-utils", + "author": "Derek Kaser", + "min": "7.0.0", + "support": "https://forums.unraid.net/topic/136889-plugin-tailscale/", + "launch": "Settings/Tailscale", + "tailscaleVersion": "tailscale_1.86.2_amd64", + "tailscaleSHA256": "3b122d9f1fbf281cf3c79fd61e84c89af9a3647c464859cbee8f133ffa74e8f8", + "packageVersion": "4.2.0", + "packageSHA256": "681de5861155c4b5590e30f12a1b7625ee1e872c897f175560d449449f448bef" +} \ No newline at end of file diff --git a/tools/plugin/tailscale.json b/tools/plugin/tailscale.json index f4dde29..ed91742 100644 --- a/tools/plugin/tailscale.json +++ b/tools/plugin/tailscale.json @@ -3,8 +3,8 @@ "author": "Derek Kaser", "githubRepository": "unraid/unraid-tailscale", "version": "2025.07.28", - "tailscaleVersion": "tailscale_1.84.0_amd64", - "tailscaleSHA256": "c91eb43a92c209108bfaf1237696ac2089cc3d8fcf35d570d348cbfb19d8fb31", + "tailscaleVersion": "tailscale_1.86.2_amd64", + "tailscaleSHA256": "3b122d9f1fbf281cf3c79fd61e84c89af9a3647c464859cbee8f133ffa74e8f8", "packageVersion": "4.2.0", "packageSHA256": "681de5861155c4b5590e30f12a1b7625ee1e872c897f175560d449449f448bef", "pluginDirectory": "/usr/local/emhttp/plugins/tailscale", From 65547f2c58a12ca719c7e522387809e529851032 Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Wed, 30 Jul 2025 01:58:58 +0000 Subject: [PATCH 004/128] fix: variables in template file --- plugin/plugin.j2 | 9 +++++++++ tools/plugin/files/install.sh | 4 ---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/plugin/plugin.j2 b/plugin/plugin.j2 index 0a45e5c..1d6256b 100644 --- a/plugin/plugin.j2 +++ b/plugin/plugin.j2 @@ -1,6 +1,15 @@ +{% set pluginDirectory = "/usr/local/emhttp/plugins/tailscale" -%} +{% set configDirectory = "/boot/config/plugins/tailscale" -%} +{% set version = env['PLUGIN_VERSION'] -%} +{% if env['PLUGIN_RELEASE'] == "-preview" -%} +{% set branch = "preview" -%} +{% else -%} +{% set branch = "main" -%} +{% endif -%} + {{ configDirectory }}/.gitignore ln -s {{ pluginDirectory }}/bin/tailscale /usr/local/sbin/tailscale ln -s {{ pluginDirectory }}/bin/tailscaled /usr/local/sbin/tailscaled -mkdir -p /var/local/emhttp/plugins/tailscale -echo "VERSION={{ version }}" > /var/local/emhttp/plugins/tailscale/tailscale.ini -echo "BRANCH={{ branch }}" >> /var/local/emhttp/plugins/tailscale/tailscale.ini - # remove other branches (e.g., if switching from main to preview) {% if branch != 'main' -%} rm -f /boot/config/plugins/tailscale.plg From bd5c4fb60f882693e76310a89c68b8d75ef6c419 Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Wed, 30 Jul 2025 02:11:33 +0000 Subject: [PATCH 005/128] fix: don't include files --- plugin/plugin.j2 | 84 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/plugin/plugin.j2 b/plugin/plugin.j2 index 1d6256b..02bee0c 100644 --- a/plugin/plugin.j2 +++ b/plugin/plugin.j2 @@ -15,7 +15,7 @@ author="{{ author }}" version="{{ env['PLUGIN_VERSION'] }}" {% if launch is defined %}launch="{{ launch }}"{% endif %} - pluginURL="https://raw.githubusercontent.com/{{ env['GITHUB_REPOSITORY'] }}/main/plugin/{{ name }}.plg" + pluginURL="https://raw.githubusercontent.com/{{ env['GITHUB_REPOSITORY'] }}/{{ branch }}/plugin/{{ name }}.plg" {% if support is defined %}support="{{ support }}"{% endif %} {% if min is defined %}min="{{ min }}"{% endif %} {% if max is defined %}max="{{ max }}"{% endif %} @@ -46,7 +46,75 @@ The 'install' script. {{ configDirectory }}/.gitignore + +ln -s {{ pluginDirectory }}/bin/tailscale /usr/local/sbin/tailscale +ln -s {{ pluginDirectory }}/bin/tailscaled /usr/local/sbin/tailscaled + +# remove other branches (e.g., if switching from main to preview) +{% if branch != 'main' -%} +rm -f /boot/config/plugins/tailscale.plg +rm -f /var/log/plugins/tailscale.plg +{% endif -%} +{% if branch != 'preview' -%} +rm -f /boot/config/plugins/tailscale-preview.plg +rm -f /var/log/plugins/tailscale-preview.plg +{% endif -%} +{% if branch != 'trunk' -%} +rm -f /boot/config/plugins/tailscale-trunk.plg +rm -f /var/log/plugins/tailscale-trunk.plg +{% endif %} + +{% if branch != 'main' -%} +# Update plugin name for non-main branches +sed -i "s/Tailscale\*\*/Tailscale ({{ branch.capitalize() }})**/" {{ pluginDirectory }}/README.md +{% endif %} + +# start tailscaled +{{ pluginDirectory }}/restart.sh + +# Bash completion +tailscale completion bash > /etc/bash_completion.d/tailscale + +# cleanup old versions +rm -f /boot/config/plugins/{{ name }}/tailscale-utils-*.txz +rm -f $(ls /boot/config/plugins/{{ name }}/unraid-tailscale-utils-*.txz 2>/dev/null | grep -v '{{ packageVersion }}') +rm -f $(ls /boot/config/plugins/{{ name }}/unraid-plugin-diagnostics-*.txz 2>/dev/null) +rm -f $(ls /boot/config/plugins/{{ name }}/*.tgz 2>/dev/null | grep -v '{{ tailscaleVersion }}') + +# check to see if the state file has been backed up to Unraid Connect +if [ -d "/boot/.git" ]; then + if git --git-dir /boot/.git log --all --name-only --diff-filter=A -- config/plugins/tailscale/state/tailscaled.state | grep -q . ; then + echo "******************************" + echo "* WARNING *" + echo "******************************" + echo " " + echo "The Tailscale state file has been backed up to Unraid Connect via Flash backup." + echo " " + echo "To remove this backup, please perform the following steps:" + echo "1. Go to Settings -> Management Access". + echo "2. Under Unraid Connect, deactivate flash backup. Select the option to also delete cloud backup." + echo "3. Reactivate flash backup." + + /usr/local/emhttp/webGui/scripts/notify -l '/Settings/ManagementAccess' -i 'alert' -e 'Tailscale State' -s 'Tailscale state backed up to Unraid connect.' -d 'The Tailscale state file has been backed up to Unraid connect. This is a potential security risk. From the Management Settings page, deactivate flash backup and delete cloud backups, then reactivate flash backup.' + fi +fi + +echo "" +echo "----------------------------------------------------" +echo " {{ name }} has been installed." +echo " Version: {{ version }}" +echo "----------------------------------------------------" +echo "" ]]> @@ -57,7 +125,17 @@ The 'remove' script. /dev/null + +rm /usr/local/sbin/tailscale +rm /usr/local/sbin/tailscaled + +removepkg unraid-tailscale-utils + +rm -rf {{ pluginDirectory }} +rm -f {{ configDirectory }}/*.tgz +rm -f {{ configDirectory }}/*.txz ]]> From d9c013f583ecb7e63013ea01ed8919be8d088f19 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 02:16:57 +0000 Subject: [PATCH 006/128] release: update plugin files for 2025.07.29 --- plugin/tailscale-preview.plg | 36 +++++++++++++++++------------------- plugin/tailscale.plg | 36 +++++++++++++++++------------------- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/plugin/tailscale-preview.plg b/plugin/tailscale-preview.plg index 453474c..366033b 100644 --- a/plugin/tailscale-preview.plg +++ b/plugin/tailscale-preview.plg @@ -4,36 +4,38 @@ - -https://pkgs.tailscale.com/stable/tailscale_1.84.0_amd64.tgz -c91eb43a92c209108bfaf1237696ac2089cc3d8fcf35d570d348cbfb19d8fb31 + +https://pkgs.tailscale.com/stable/tailscale_1.86.2_amd64.tgz +3b122d9f1fbf281cf3c79fd61e84c89af9a3647c464859cbee8f133ffa74e8f8 @@ -54,17 +56,13 @@ fi upgradepkg --install-new --reinstall /boot/config/plugins/tailscale/unraid-tailscale-utils-4.2.0-noarch-1.txz mkdir -p /usr/local/emhttp/plugins/tailscale/bin -tar xzf /boot/config/plugins/tailscale/tailscale_1.84.0_amd64.tgz --strip-components 1 -C /usr/local/emhttp/plugins/tailscale/bin +tar xzf /boot/config/plugins/tailscale/tailscale_1.86.2_amd64.tgz --strip-components 1 -C /usr/local/emhttp/plugins/tailscale/bin echo "state" > /boot/config/plugins/tailscale/.gitignore ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscale /usr/local/sbin/tailscale ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscaled /usr/local/sbin/tailscaled -mkdir -p /var/local/emhttp/plugins/tailscale -echo "VERSION=2025.07.28" > /var/local/emhttp/plugins/tailscale/tailscale.ini -echo "BRANCH=preview" >> /var/local/emhttp/plugins/tailscale/tailscale.ini - # remove other branches (e.g., if switching from main to preview) rm -f /boot/config/plugins/tailscale.plg rm -f /var/log/plugins/tailscale.plg @@ -86,7 +84,7 @@ tailscale completion bash > /etc/bash_completion.d/tailscale rm -f /boot/config/plugins/tailscale/tailscale-utils-*.txz rm -f $(ls /boot/config/plugins/tailscale/unraid-tailscale-utils-*.txz 2>/dev/null | grep -v '4.2.0') rm -f $(ls /boot/config/plugins/tailscale/unraid-plugin-diagnostics-*.txz 2>/dev/null) -rm -f $(ls /boot/config/plugins/tailscale/*.tgz 2>/dev/null | grep -v 'tailscale_1.84.0_amd64') +rm -f $(ls /boot/config/plugins/tailscale/*.tgz 2>/dev/null | grep -v 'tailscale_1.86.2_amd64') # check to see if the state file has been backed up to Unraid Connect if [ -d "/boot/.git" ]; then @@ -109,7 +107,7 @@ fi echo "" echo "----------------------------------------------------" echo " tailscale has been installed." -echo " Version: 2025.07.28" +echo " Version: 2025.07.29" echo "----------------------------------------------------" echo "" ]]> diff --git a/plugin/tailscale.plg b/plugin/tailscale.plg index 8822ba3..25d6aa4 100644 --- a/plugin/tailscale.plg +++ b/plugin/tailscale.plg @@ -4,36 +4,38 @@ - -https://pkgs.tailscale.com/stable/tailscale_1.84.0_amd64.tgz -c91eb43a92c209108bfaf1237696ac2089cc3d8fcf35d570d348cbfb19d8fb31 + +https://pkgs.tailscale.com/stable/tailscale_1.86.2_amd64.tgz +3b122d9f1fbf281cf3c79fd61e84c89af9a3647c464859cbee8f133ffa74e8f8 @@ -54,17 +56,13 @@ fi upgradepkg --install-new --reinstall /boot/config/plugins/tailscale/unraid-tailscale-utils-4.2.0-noarch-1.txz mkdir -p /usr/local/emhttp/plugins/tailscale/bin -tar xzf /boot/config/plugins/tailscale/tailscale_1.84.0_amd64.tgz --strip-components 1 -C /usr/local/emhttp/plugins/tailscale/bin +tar xzf /boot/config/plugins/tailscale/tailscale_1.86.2_amd64.tgz --strip-components 1 -C /usr/local/emhttp/plugins/tailscale/bin echo "state" > /boot/config/plugins/tailscale/.gitignore ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscale /usr/local/sbin/tailscale ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscaled /usr/local/sbin/tailscaled -mkdir -p /var/local/emhttp/plugins/tailscale -echo "VERSION=2025.07.28" > /var/local/emhttp/plugins/tailscale/tailscale.ini -echo "BRANCH=main" >> /var/local/emhttp/plugins/tailscale/tailscale.ini - # remove other branches (e.g., if switching from main to preview) rm -f /boot/config/plugins/tailscale-preview.plg rm -f /var/log/plugins/tailscale-preview.plg @@ -84,7 +82,7 @@ tailscale completion bash > /etc/bash_completion.d/tailscale rm -f /boot/config/plugins/tailscale/tailscale-utils-*.txz rm -f $(ls /boot/config/plugins/tailscale/unraid-tailscale-utils-*.txz 2>/dev/null | grep -v '4.2.0') rm -f $(ls /boot/config/plugins/tailscale/unraid-plugin-diagnostics-*.txz 2>/dev/null) -rm -f $(ls /boot/config/plugins/tailscale/*.tgz 2>/dev/null | grep -v 'tailscale_1.84.0_amd64') +rm -f $(ls /boot/config/plugins/tailscale/*.tgz 2>/dev/null | grep -v 'tailscale_1.86.2_amd64') # check to see if the state file has been backed up to Unraid Connect if [ -d "/boot/.git" ]; then @@ -107,7 +105,7 @@ fi echo "" echo "----------------------------------------------------" echo " tailscale has been installed." -echo " Version: 2025.07.28" +echo " Version: 2025.07.29" echo "----------------------------------------------------" echo "" ]]> From 4b8aa51eb0d1b2020eedba556c82bca0ebbdd6e6 Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Wed, 30 Jul 2025 02:24:11 +0000 Subject: [PATCH 007/128] fix: add release channel to pluginURL --- plugin/plugin.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/plugin.j2 b/plugin/plugin.j2 index 02bee0c..646831b 100644 --- a/plugin/plugin.j2 +++ b/plugin/plugin.j2 @@ -15,7 +15,7 @@ author="{{ author }}" version="{{ env['PLUGIN_VERSION'] }}" {% if launch is defined %}launch="{{ launch }}"{% endif %} - pluginURL="https://raw.githubusercontent.com/{{ env['GITHUB_REPOSITORY'] }}/{{ branch }}/plugin/{{ name }}.plg" + pluginURL="https://raw.githubusercontent.com/{{ env['GITHUB_REPOSITORY'] }}/{{ branch }}/plugin/{{ name }}{{ env['PLUGIN_RELEASE'] }}.plg" {% if support is defined %}support="{{ support }}"{% endif %} {% if min is defined %}min="{{ min }}"{% endif %} {% if max is defined %}max="{{ max }}"{% endif %} From 5227fba4c4219afde4e251426b64c236a623b4ca Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Wed, 30 Jul 2025 02:32:40 +0000 Subject: [PATCH 008/128] ci: allow release workflow to bypass rules --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df44bfe..5461bb9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,3 +16,4 @@ jobs: plg_branch: trunk build_package: false build_prereleases: true + ssh_key: ${{ secrets.DEPLOY_KEY }} From f2eda41a1cf0b2493c1f61184461ec5288b06e6d Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 02:33:36 +0000 Subject: [PATCH 009/128] release: update plugin files for 2025.07.29 --- plugin/tailscale-preview.plg | 7 ++++--- plugin/tailscale.plg | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/plugin/tailscale-preview.plg b/plugin/tailscale-preview.plg index 366033b..749111a 100644 --- a/plugin/tailscale-preview.plg +++ b/plugin/tailscale-preview.plg @@ -6,7 +6,7 @@ author="Derek Kaser" version="2025.07.29" launch="Settings/Tailscale" - pluginURL="https://raw.githubusercontent.com/unraid/unraid-tailscale/preview/plugin/tailscale.plg" + pluginURL="https://raw.githubusercontent.com/unraid/unraid-tailscale/preview/plugin/tailscale-preview.plg" support="https://forums.unraid.net/topic/136889-plugin-tailscale/" min="7.0.0" @@ -22,11 +22,12 @@ ### 2025.07.28 -* build: 2025.07.28 by @dkaser in https://github.com/unraid/unraid-tailscale/pull/23 +- Revert Tailscale to 1.84.0 (1.86.0 removed upstream) ### 2025.07.26 -* build: 2025.07.26 by @dkaser in https://github.com/unraid/unraid-tailscale/pull/22 +- Update Tailscale to 1.86.0 +- Updates for Unraid 7.2 For older releases, see https://github.com/unraid/unraid-tailscale/releases diff --git a/plugin/tailscale.plg b/plugin/tailscale.plg index 25d6aa4..5409298 100644 --- a/plugin/tailscale.plg +++ b/plugin/tailscale.plg @@ -22,11 +22,12 @@ ### 2025.07.28 -* build: 2025.07.28 by @dkaser in https://github.com/unraid/unraid-tailscale/pull/23 +- Revert Tailscale to 1.84.0 (1.86.0 removed upstream) ### 2025.07.26 -* build: 2025.07.26 by @dkaser in https://github.com/unraid/unraid-tailscale/pull/22 +- Update Tailscale to 1.86.0 +- Updates for Unraid 7.2 For older releases, see https://github.com/unraid/unraid-tailscale/releases From 131ae873dd12e9e7d4d477db8525ac8b736c83c6 Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Wed, 30 Jul 2025 02:38:38 +0000 Subject: [PATCH 010/128] ci: add release as acceptable commit type --- commitlint.config.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/commitlint.config.js b/commitlint.config.js index 8567d51..6ce4b8d 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1 +1,8 @@ -module.exports = { extends: ["@commitlint/config-conventional"] }; \ No newline at end of file +module.exports = { + extends: ["@commitlint/config-conventional"], + rules: { + 'type-enum': [2, 'always', [ + 'build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'style', 'test', 'release' + ]] + } +}; \ No newline at end of file From 883dfcef0c7f0132c1d4db6f68f8a8aa5f196ab6 Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Wed, 30 Jul 2025 02:42:31 +0000 Subject: [PATCH 011/128] chore: cleanup --- .github/api/createCommitOnBranch.gql | 25 ---------- .github/workflows/jinja.yml | 41 ----------------- plugin/tailscale-trunk.plg | 35 +++++++------- requirements.txt | 9 ---- tools/build-plugin.sh | 5 -- tools/plugin/files/CHANGELOG.md | 14 ------ tools/plugin/files/install.sh | 69 ---------------------------- tools/plugin/files/remove.sh | 11 ----- tools/plugin/tailscale.j2 | 52 --------------------- tools/plugin/tailscale.json | 13 ------ 10 files changed, 16 insertions(+), 258 deletions(-) delete mode 100644 .github/api/createCommitOnBranch.gql delete mode 100644 .github/workflows/jinja.yml delete mode 100644 requirements.txt delete mode 100755 tools/build-plugin.sh delete mode 100644 tools/plugin/files/CHANGELOG.md delete mode 100644 tools/plugin/files/install.sh delete mode 100644 tools/plugin/files/remove.sh delete mode 100644 tools/plugin/tailscale.j2 delete mode 100644 tools/plugin/tailscale.json diff --git a/.github/api/createCommitOnBranch.gql b/.github/api/createCommitOnBranch.gql deleted file mode 100644 index 63302d0..0000000 --- a/.github/api/createCommitOnBranch.gql +++ /dev/null @@ -1,25 +0,0 @@ -mutation ( - $githubRepository: String! - $branchName: String! - $expectedHeadOid: GitObjectID! - $commitMessage: String! - $pluginFile: FileAddition! - $previewFile: FileAddition! - $trunkFile: FileAddition! -) { - createCommitOnBranch( - input: { - branch: { - repositoryNameWithOwner: $githubRepository - branchName: $branchName - } - message: { headline: $commitMessage } - fileChanges: { additions: [$pluginFile, $previewFile, $trunkFile] } - expectedHeadOid: $expectedHeadOid - } - ) { - commit { - url - } - } -} diff --git a/.github/workflows/jinja.yml b/.github/workflows/jinja.yml deleted file mode 100644 index 9cdabcc..0000000 --- a/.github/workflows/jinja.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Template plugin files - -on: - push: - branches-ignore: - - trunk - -permissions: - contents: write - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-python@v5 - with: - python-version: "3.12" - cache: pip - - - name: Template - run: | - pip install jinja-cli - cd tools - ./build-plugin.sh - - - name: Commit plugin file - run: | - gh api graphql \ - -F githubRepository=$GITHUB_REPOSITORY \ - -F branchName=$BRANCH \ - -F expectedHeadOid=$(git rev-parse HEAD) \ - -F commitMessage="ci: template plugin files" \ - -F pluginFile[path]="plugin/tailscale.plg" -F pluginFile[contents]=$(base64 -w0 plugin/tailscale.plg) \ - -F previewFile[path]="plugin/tailscale-preview.plg" -F previewFile[contents]=$(base64 -w0 plugin/tailscale-preview.plg) \ - -F trunkFile[path]="plugin/tailscale-trunk.plg" -F trunkFile[contents]=$(base64 -w0 plugin/tailscale-trunk.plg) \ - -F 'query=@.github/api/createCommitOnBranch.gql' - env: - GH_TOKEN: ${{ github.token }} - BRANCH: ${{ github.ref }} diff --git a/plugin/tailscale-trunk.plg b/plugin/tailscale-trunk.plg index 8355988..a602a0e 100644 --- a/plugin/tailscale-trunk.plg +++ b/plugin/tailscale-trunk.plg @@ -4,15 +4,20 @@ - -https://pkgs.tailscale.com/stable/tailscale_1.84.0_amd64.tgz -c91eb43a92c209108bfaf1237696ac2089cc3d8fcf35d570d348cbfb19d8fb31 + +https://pkgs.tailscale.com/stable/tailscale_1.86.2_amd64.tgz +3b122d9f1fbf281cf3c79fd61e84c89af9a3647c464859cbee8f133ffa74e8f8 @@ -54,26 +55,22 @@ fi upgradepkg --install-new --reinstall /boot/config/plugins/tailscale/unraid-tailscale-utils-4.2.0-noarch-1.txz mkdir -p /usr/local/emhttp/plugins/tailscale/bin -tar xzf /boot/config/plugins/tailscale/tailscale_1.84.0_amd64.tgz --strip-components 1 -C /usr/local/emhttp/plugins/tailscale/bin +tar xzf /boot/config/plugins/tailscale/tailscale_1.86.2_amd64.tgz --strip-components 1 -C /usr/local/emhttp/plugins/tailscale/bin echo "state" > /boot/config/plugins/tailscale/.gitignore ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscale /usr/local/sbin/tailscale ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscaled /usr/local/sbin/tailscaled -mkdir -p /var/local/emhttp/plugins/tailscale -echo "VERSION=2025.07.28" > /var/local/emhttp/plugins/tailscale/tailscale.ini -echo "BRANCH=trunk" >> /var/local/emhttp/plugins/tailscale/tailscale.ini - # remove other branches (e.g., if switching from main to preview) rm -f /boot/config/plugins/tailscale.plg rm -f /var/log/plugins/tailscale.plg -rm -f /boot/config/plugins/tailscale-preview.plg -rm -f /var/log/plugins/tailscale-preview.plg +rm -f /boot/config/plugins/tailscale-trunk.plg +rm -f /var/log/plugins/tailscale-trunk.plg # Update plugin name for non-main branches -sed -i "s/Tailscale\*\*/Tailscale (Trunk)**/" /usr/local/emhttp/plugins/tailscale/README.md +sed -i "s/Tailscale\*\*/Tailscale (Preview)**/" /usr/local/emhttp/plugins/tailscale/README.md # start tailscaled @@ -86,7 +83,7 @@ tailscale completion bash > /etc/bash_completion.d/tailscale rm -f /boot/config/plugins/tailscale/tailscale-utils-*.txz rm -f $(ls /boot/config/plugins/tailscale/unraid-tailscale-utils-*.txz 2>/dev/null | grep -v '4.2.0') rm -f $(ls /boot/config/plugins/tailscale/unraid-plugin-diagnostics-*.txz 2>/dev/null) -rm -f $(ls /boot/config/plugins/tailscale/*.tgz 2>/dev/null | grep -v 'tailscale_1.84.0_amd64') +rm -f $(ls /boot/config/plugins/tailscale/*.tgz 2>/dev/null | grep -v 'tailscale_1.86.2_amd64') # check to see if the state file has been backed up to Unraid Connect if [ -d "/boot/.git" ]; then @@ -109,7 +106,7 @@ fi echo "" echo "----------------------------------------------------" echo " tailscale has been installed." -echo " Version: 2025.07.28" +echo " Version: 2025.07.29" echo "----------------------------------------------------" echo "" ]]> diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 1c9d419..0000000 --- a/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -argparse-ext==1.4.2 -jinja-cli==1.2.2 -Jinja2==3.1.6 -MarkupSafe==3.0.1 -numpy==2.1.2 -PyYAML==6.0.2 -setuptools==78.1.1 -wheel==0.44.0 -xmltodict==0.14.1 diff --git a/tools/build-plugin.sh b/tools/build-plugin.sh deleted file mode 100755 index d37e214..0000000 --- a/tools/build-plugin.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -FILENAME=tailscale; jinja -d plugin/tailscale.json -D filename $FILENAME -D branch main plugin/tailscale.j2 > ../plugin/$FILENAME.plg -FILENAME=tailscale-preview; jinja -d plugin/tailscale.json -D filename $FILENAME -D branch preview plugin/tailscale.j2 > ../plugin/$FILENAME.plg -FILENAME=tailscale-trunk; jinja -d plugin/tailscale.json -D filename $FILENAME -D branch trunk plugin/tailscale.j2 > ../plugin/$FILENAME.plg \ No newline at end of file diff --git a/tools/plugin/files/CHANGELOG.md b/tools/plugin/files/CHANGELOG.md deleted file mode 100644 index a74b4ba..0000000 --- a/tools/plugin/files/CHANGELOG.md +++ /dev/null @@ -1,14 +0,0 @@ -### 2025.07.28 - -- Revert Tailscale to 1.84.0 (1.86.0 removed upstream) - -### 2025.07.26 - -- Update Tailscale to 1.86.0 -- Updates for Unraid 7.2 - -### 2025.05.25 - -- Remove usage reporting - -For older releases, see https://github.com/unraid/unraid-tailscale/releases diff --git a/tools/plugin/files/install.sh b/tools/plugin/files/install.sh deleted file mode 100644 index eae7fdb..0000000 --- a/tools/plugin/files/install.sh +++ /dev/null @@ -1,69 +0,0 @@ -if [ -d "{{ pluginDirectory }}" ]; then - rm -rf {{ pluginDirectory }} -fi - -upgradepkg --install-new --reinstall {{ configDirectory }}/unraid-tailscale-utils-{{ packageVersion }}-noarch-1.txz - -mkdir -p {{ pluginDirectory }}/bin -tar xzf {{ configDirectory }}/{{ tailscaleVersion }}.tgz --strip-components 1 -C {{ pluginDirectory }}/bin - -echo "state" > {{ configDirectory }}/.gitignore - -ln -s {{ pluginDirectory }}/bin/tailscale /usr/local/sbin/tailscale -ln -s {{ pluginDirectory }}/bin/tailscaled /usr/local/sbin/tailscaled - -# remove other branches (e.g., if switching from main to preview) -{% if branch != 'main' -%} -rm -f /boot/config/plugins/tailscale.plg -rm -f /var/log/plugins/tailscale.plg -{% endif -%} -{% if branch != 'preview' -%} -rm -f /boot/config/plugins/tailscale-preview.plg -rm -f /var/log/plugins/tailscale-preview.plg -{% endif -%} -{% if branch != 'trunk' -%} -rm -f /boot/config/plugins/tailscale-trunk.plg -rm -f /var/log/plugins/tailscale-trunk.plg -{% endif %} - -{% if branch != 'main' -%} -# Update plugin name for non-main branches -sed -i "s/Tailscale\*\*/Tailscale ({{ branch.capitalize() }})**/" {{ pluginDirectory }}/README.md -{% endif %} - -# start tailscaled -{{ pluginDirectory }}/restart.sh - -# Bash completion -tailscale completion bash > /etc/bash_completion.d/tailscale - -# cleanup old versions -rm -f /boot/config/plugins/{{ name }}/tailscale-utils-*.txz -rm -f $(ls /boot/config/plugins/{{ name }}/unraid-tailscale-utils-*.txz 2>/dev/null | grep -v '{{ packageVersion }}') -rm -f $(ls /boot/config/plugins/{{ name }}/unraid-plugin-diagnostics-*.txz 2>/dev/null) -rm -f $(ls /boot/config/plugins/{{ name }}/*.tgz 2>/dev/null | grep -v '{{ tailscaleVersion }}') - -# check to see if the state file has been backed up to Unraid Connect -if [ -d "/boot/.git" ]; then - if git --git-dir /boot/.git log --all --name-only --diff-filter=A -- config/plugins/tailscale/state/tailscaled.state | grep -q . ; then - echo "******************************" - echo "* WARNING *" - echo "******************************" - echo " " - echo "The Tailscale state file has been backed up to Unraid Connect via Flash backup." - echo " " - echo "To remove this backup, please perform the following steps:" - echo "1. Go to Settings -> Management Access". - echo "2. Under Unraid Connect, deactivate flash backup. Select the option to also delete cloud backup." - echo "3. Reactivate flash backup." - - /usr/local/emhttp/webGui/scripts/notify -l '/Settings/ManagementAccess' -i 'alert' -e 'Tailscale State' -s 'Tailscale state backed up to Unraid connect.' -d 'The Tailscale state file has been backed up to Unraid connect. This is a potential security risk. From the Management Settings page, deactivate flash backup and delete cloud backups, then reactivate flash backup.' - fi -fi - -echo "" -echo "----------------------------------------------------" -echo " {{ name }} has been installed." -echo " Version: {{ version }}" -echo "----------------------------------------------------" -echo "" \ No newline at end of file diff --git a/tools/plugin/files/remove.sh b/tools/plugin/files/remove.sh deleted file mode 100644 index fcb1bb1..0000000 --- a/tools/plugin/files/remove.sh +++ /dev/null @@ -1,11 +0,0 @@ -# Stop service -/etc/rc.d/rc.tailscale stop 2>/dev/null - -rm /usr/local/sbin/tailscale -rm /usr/local/sbin/tailscaled - -removepkg unraid-tailscale-utils - -rm -rf {{ pluginDirectory }} -rm -f {{ configDirectory }}/*.tgz -rm -f {{ configDirectory }}/*.txz \ No newline at end of file diff --git a/tools/plugin/tailscale.j2 b/tools/plugin/tailscale.j2 deleted file mode 100644 index b8a4390..0000000 --- a/tools/plugin/tailscale.j2 +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - -https://pkgs.tailscale.com/stable/{{ tailscaleVersion }}.tgz -{{ tailscaleSHA256 }} - - - -https://github.com/unraid/unraid-tailscale-utils/releases/download/{{ packageVersion }}/unraid-tailscale-utils-{{ packageVersion }}-noarch-1.txz -{{ packageSHA256 }} - - - - - - - - - - - - - - - - - diff --git a/tools/plugin/tailscale.json b/tools/plugin/tailscale.json deleted file mode 100644 index ed91742..0000000 --- a/tools/plugin/tailscale.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "tailscale", - "author": "Derek Kaser", - "githubRepository": "unraid/unraid-tailscale", - "version": "2025.07.28", - "tailscaleVersion": "tailscale_1.86.2_amd64", - "tailscaleSHA256": "3b122d9f1fbf281cf3c79fd61e84c89af9a3647c464859cbee8f133ffa74e8f8", - "packageVersion": "4.2.0", - "packageSHA256": "681de5861155c4b5590e30f12a1b7625ee1e872c897f175560d449449f448bef", - "pluginDirectory": "/usr/local/emhttp/plugins/tailscale", - "configDirectory": "/boot/config/plugins/tailscale", - "minver": "7.0.0" -} \ No newline at end of file From 72ec6fc12b9bcaef5808032ca27c1877f4a4a10c Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Wed, 30 Jul 2025 02:51:52 +0000 Subject: [PATCH 012/128] chore: cleanup --- plugin/tailscale-trunk.plg | 137 ------------------------------------- 1 file changed, 137 deletions(-) delete mode 100644 plugin/tailscale-trunk.plg diff --git a/plugin/tailscale-trunk.plg b/plugin/tailscale-trunk.plg deleted file mode 100644 index a602a0e..0000000 --- a/plugin/tailscale-trunk.plg +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - -https://pkgs.tailscale.com/stable/tailscale_1.86.2_amd64.tgz -3b122d9f1fbf281cf3c79fd61e84c89af9a3647c464859cbee8f133ffa74e8f8 - - - -https://github.com/unraid/unraid-tailscale-utils/releases/download/4.2.0/unraid-tailscale-utils-4.2.0-noarch-1.txz -681de5861155c4b5590e30f12a1b7625ee1e872c897f175560d449449f448bef - - - - - - /boot/config/plugins/tailscale/.gitignore - -ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscale /usr/local/sbin/tailscale -ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscaled /usr/local/sbin/tailscaled - -# remove other branches (e.g., if switching from main to preview) -rm -f /boot/config/plugins/tailscale.plg -rm -f /var/log/plugins/tailscale.plg -rm -f /boot/config/plugins/tailscale-trunk.plg -rm -f /var/log/plugins/tailscale-trunk.plg - - -# Update plugin name for non-main branches -sed -i "s/Tailscale\*\*/Tailscale (Preview)**/" /usr/local/emhttp/plugins/tailscale/README.md - - -# start tailscaled -/usr/local/emhttp/plugins/tailscale/restart.sh - -# Bash completion -tailscale completion bash > /etc/bash_completion.d/tailscale - -# cleanup old versions -rm -f /boot/config/plugins/tailscale/tailscale-utils-*.txz -rm -f $(ls /boot/config/plugins/tailscale/unraid-tailscale-utils-*.txz 2>/dev/null | grep -v '4.2.0') -rm -f $(ls /boot/config/plugins/tailscale/unraid-plugin-diagnostics-*.txz 2>/dev/null) -rm -f $(ls /boot/config/plugins/tailscale/*.tgz 2>/dev/null | grep -v 'tailscale_1.86.2_amd64') - -# check to see if the state file has been backed up to Unraid Connect -if [ -d "/boot/.git" ]; then - if git --git-dir /boot/.git log --all --name-only --diff-filter=A -- config/plugins/tailscale/state/tailscaled.state | grep -q . ; then - echo "******************************" - echo "* WARNING *" - echo "******************************" - echo " " - echo "The Tailscale state file has been backed up to Unraid Connect via Flash backup." - echo " " - echo "To remove this backup, please perform the following steps:" - echo "1. Go to Settings -> Management Access". - echo "2. Under Unraid Connect, deactivate flash backup. Select the option to also delete cloud backup." - echo "3. Reactivate flash backup." - - /usr/local/emhttp/webGui/scripts/notify -l '/Settings/ManagementAccess' -i 'alert' -e 'Tailscale State' -s 'Tailscale state backed up to Unraid connect.' -d 'The Tailscale state file has been backed up to Unraid connect. This is a potential security risk. From the Management Settings page, deactivate flash backup and delete cloud backups, then reactivate flash backup.' - fi -fi - -echo "" -echo "----------------------------------------------------" -echo " tailscale has been installed." -echo " Version: 2025.07.29" -echo "----------------------------------------------------" -echo "" -]]> - - - - - - -/dev/null - -rm /usr/local/sbin/tailscale -rm /usr/local/sbin/tailscaled - -removepkg unraid-tailscale-utils - -rm -rf /usr/local/emhttp/plugins/tailscale -rm -f /boot/config/plugins/tailscale/*.tgz -rm -f /boot/config/plugins/tailscale/*.txz -]]> - - - - From 88b77a7170b44c638862a410723d889c40192bb2 Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Thu, 31 Jul 2025 17:55:20 -0400 Subject: [PATCH 013/128] chore: migrate package from unraid-tailscale-utils repo (#24) --- .coderabbit.yaml | 16 + .github/workflows/latest-tag.yml | 36 - .../workflows/{commitlint.yml => lint.yml} | 28 +- .github/workflows/release.yml | 2 +- .github/workflows/translate.yml | 22 + .gitignore | 3 +- .php-cs-fixer.dist.php | 45 + CONTRIBUTING.md | 45 + README.md | 33 +- commitlint.config.js | 9 +- composer | 3 + crowdin.yml | 9 + phpstan.neon | 9 + plugin/plugin.j2 | 14 +- plugin/plugin.json | 4 +- src/etc/logrotate.d/tailscale | 17 + src/install/doinst.sh | 12 + src/install/slack-desc | 8 + .../local/emhttp/plugins/tailscale/README.md | 3 + .../tailscale/Tailscale-1-Settings.page | 18 + .../plugins/tailscale/Tailscale-2-Status.page | 18 + .../plugins/tailscale/Tailscale-3-Lock.page | 18 + .../plugins/tailscale/Tailscale-4-Info.page | 18 + .../plugins/tailscale/Tailscale-5-Log.page | 18 + .../emhttp/plugins/tailscale/Tailscale.page | 18 + .../tailscale/Tailscale_dashboard.page | 15 + .../plugins/tailscale/approve-nodes.php | 16 + .../local/emhttp/plugins/tailscale/daily.php | 34 + .../local/emhttp/plugins/tailscale/daily.sh | 3 + .../emhttp/plugins/tailscale/diagnostics.json | 76 + .../local/emhttp/plugins/tailscale/erase.sh | 13 + .../emhttp/plugins/tailscale/event/README | 1 + .../tailscale/include/Pages/Dashboard.php | 52 + .../plugins/tailscale/include/Pages/Error.php | 4 + .../plugins/tailscale/include/Pages/Info.php | 69 + .../plugins/tailscale/include/Pages/Lock.php | 58 + .../plugins/tailscale/include/Pages/Log.php | 82 + .../plugins/tailscale/include/Pages/Main.php | 49 + .../tailscale/include/Pages/Settings.php | 308 ++ .../tailscale/include/Pages/Status.php | 93 + .../tailscale/include/Pages/Tailscale.php | 188 ++ .../plugins/tailscale/include/common.php | 29 + .../plugins/tailscale/include/data/Config.php | 340 +++ .../plugins/tailscale/include/data/Lock.php | 78 + .../plugins/tailscale/include/data/Status.php | 127 + .../plugins/tailscale/include/get_log.php | 52 + .../emhttp/plugins/tailscale/include/page.php | 53 + .../include/tailscale-lock/disabled.php | 32 + .../include/tailscale-lock/locked.php | 45 + .../include/tailscale-lock/signed.php | 47 + .../include/tailscale-lock/signing.php | 80 + .../plugins/tailscale/locales/de_DE.json | 132 + .../plugins/tailscale/locales/en_US.json | 132 + .../plugins/tailscale/locales/es_ES.json | 132 + .../plugins/tailscale/locales/fr_FR.json | 132 + .../plugins/tailscale/locales/it_IT.json | 132 + .../plugins/tailscale/locales/ja_JP.json | 132 + .../plugins/tailscale/locales/ko_KR.json | 132 + .../plugins/tailscale/locales/nl_NL.json | 132 + .../plugins/tailscale/locales/no_NO.json | 132 + .../plugins/tailscale/locales/pl_PL.json | 132 + .../plugins/tailscale/locales/pt_BR.json | 132 + .../plugins/tailscale/locales/pt_PT.json | 132 + .../plugins/tailscale/locales/sv_SE.json | 132 + .../plugins/tailscale/locales/uk_UA.json | 132 + .../plugins/tailscale/locales/zh_CN.json | 132 + src/usr/local/emhttp/plugins/tailscale/log.sh | 7 + .../emhttp/plugins/tailscale/pre-startup.php | 43 + .../emhttp/plugins/tailscale/rc.tailscale | 46 + .../local/emhttp/plugins/tailscale/restart.sh | 6 + .../emhttp/plugins/tailscale/settings.json | 34 + .../local/emhttp/plugins/tailscale/style.css | 15 + .../plugins/tailscale/tailscale-watcher.php | 10 + .../emhttp/plugins/tailscale/tailscale.png | Bin 0 -> 13626 bytes .../plugins/tailscale/update-settings.sh | 30 + .../php/unraid-tailscale-utils/composer.json | 33 + .../php/unraid-tailscale-utils/composer.lock | 2647 +++++++++++++++++ .../unraid-tailscale-utils/Config.php | 60 + .../unraid-tailscale-utils/ConnectionInfo.php | 35 + .../unraid-tailscale-utils/DashboardInfo.php | 30 + .../unraid-tailscale-utils/Info.php | 474 +++ .../unraid-tailscale-utils/LocalAPI.php | 138 + .../unraid-tailscale-utils/LockInfo.php | 28 + .../unraid-tailscale-utils/PeerStatus.php | 44 + .../unraid-tailscale-utils/ServeConfig.php | 60 + .../unraid-tailscale-utils/StatusInfo.php | 33 + .../unraid-tailscale-utils/System.php | 321 ++ .../unraid-tailscale-utils/Utils.php | 189 ++ .../unraid-tailscale-utils/Warning.php | 32 + .../unraid-tailscale-utils/Watcher.php | 99 + 90 files changed, 8694 insertions(+), 70 deletions(-) create mode 100644 .coderabbit.yaml delete mode 100644 .github/workflows/latest-tag.yml rename .github/workflows/{commitlint.yml => lint.yml} (56%) create mode 100644 .github/workflows/translate.yml create mode 100644 .php-cs-fixer.dist.php create mode 100644 CONTRIBUTING.md create mode 100755 composer create mode 100644 crowdin.yml create mode 100644 phpstan.neon create mode 100644 src/etc/logrotate.d/tailscale create mode 100755 src/install/doinst.sh create mode 100644 src/install/slack-desc create mode 100644 src/usr/local/emhttp/plugins/tailscale/README.md create mode 100644 src/usr/local/emhttp/plugins/tailscale/Tailscale-1-Settings.page create mode 100644 src/usr/local/emhttp/plugins/tailscale/Tailscale-2-Status.page create mode 100644 src/usr/local/emhttp/plugins/tailscale/Tailscale-3-Lock.page create mode 100644 src/usr/local/emhttp/plugins/tailscale/Tailscale-4-Info.page create mode 100644 src/usr/local/emhttp/plugins/tailscale/Tailscale-5-Log.page create mode 100644 src/usr/local/emhttp/plugins/tailscale/Tailscale.page create mode 100644 src/usr/local/emhttp/plugins/tailscale/Tailscale_dashboard.page create mode 100755 src/usr/local/emhttp/plugins/tailscale/approve-nodes.php create mode 100755 src/usr/local/emhttp/plugins/tailscale/daily.php create mode 100755 src/usr/local/emhttp/plugins/tailscale/daily.sh create mode 100644 src/usr/local/emhttp/plugins/tailscale/diagnostics.json create mode 100755 src/usr/local/emhttp/plugins/tailscale/erase.sh create mode 100644 src/usr/local/emhttp/plugins/tailscale/event/README create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/Pages/Dashboard.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/Pages/Error.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/Pages/Info.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/Pages/Lock.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/Pages/Log.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/Pages/Main.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/Pages/Settings.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/Pages/Status.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/Pages/Tailscale.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/common.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/data/Config.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/data/Lock.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/data/Status.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/get_log.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/page.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/disabled.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/locked.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/signed.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/signing.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/en_US.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/log.sh create mode 100755 src/usr/local/emhttp/plugins/tailscale/pre-startup.php create mode 100755 src/usr/local/emhttp/plugins/tailscale/rc.tailscale create mode 100755 src/usr/local/emhttp/plugins/tailscale/restart.sh create mode 100644 src/usr/local/emhttp/plugins/tailscale/settings.json create mode 100644 src/usr/local/emhttp/plugins/tailscale/style.css create mode 100755 src/usr/local/emhttp/plugins/tailscale/tailscale-watcher.php create mode 100644 src/usr/local/emhttp/plugins/tailscale/tailscale.png create mode 100755 src/usr/local/emhttp/plugins/tailscale/update-settings.sh create mode 100644 src/usr/local/php/unraid-tailscale-utils/composer.json create mode 100644 src/usr/local/php/unraid-tailscale-utils/composer.lock create mode 100644 src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Config.php create mode 100644 src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/ConnectionInfo.php create mode 100644 src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/DashboardInfo.php create mode 100644 src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Info.php create mode 100644 src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LocalAPI.php create mode 100644 src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LockInfo.php create mode 100644 src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/PeerStatus.php create mode 100644 src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/ServeConfig.php create mode 100644 src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/StatusInfo.php create mode 100644 src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/System.php create mode 100644 src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Utils.php create mode 100644 src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Warning.php create mode 100644 src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Watcher.php diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..994af84 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,16 @@ +language: en-US +early_access: true +enable_free_tier: true +reviews: + profile: chill + poem: false + finishing_touches: + docstrings: + enabled: false + unit_tests: + enabled: false + tools: + phpstan: + enabled: false + level: default + diff --git a/.github/workflows/latest-tag.yml b/.github/workflows/latest-tag.yml deleted file mode 100644 index 1cc313b..0000000 --- a/.github/workflows/latest-tag.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Add main tag to new release -on: - release: - types: [published, edited] - -jobs: - run: - name: Run local action - runs-on: ubuntu-latest - if: github.event.release.draft == false - steps: - - name: Checkout repository - uses: actions/checkout@master - - - name: Get full release object - run: | - RELEASE_JSON=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ - "https://api.github.com/repos/${{ github.repository }}/releases/${{ github.event.release.id }}") - - echo "Latest release name: $RELEASE_JSON" - - - name: Run latest-tag - uses: EndBug/latest-tag@v1 - if: | - github.event.release.prerelease == false - with: - ref: main - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Run latest-tag - uses: EndBug/latest-tag@v1 - with: - ref: preview - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/commitlint.yml b/.github/workflows/lint.yml similarity index 56% rename from .github/workflows/commitlint.yml rename to .github/workflows/lint.yml index 5e692d7..de4b989 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/lint.yml @@ -3,14 +3,36 @@ name: Commit Quality on: push: branches: - - trunk + - main pull_request: jobs: + php-cs-fixer: + name: PHP-CS-Fixer + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: PHP-CS-Fixer + uses: docker://oskarstark/php-cs-fixer-ga + with: + args: --diff --dry-run + + phpstan: + name: PHPStan Analysis + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: php-actions/composer@v6 + with: + working_dir: "src/usr/local/php/unraid-tailscale-utils" + - run: "vendor/bin/phpstan" + commitlint: + name: Commitlint runs-on: ubuntu-22.04 steps: - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 18 @@ -19,7 +41,7 @@ jobs: npm install -g commitlint npm install -g "@commitlint/config-conventional" - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5461bb9..b87bfb3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,6 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} plg_branch: trunk - build_package: false build_prereleases: true ssh_key: ${{ secrets.DEPLOY_KEY }} + composer_dir: src/usr/local/php/unraid-tailscale-utils/ diff --git a/.github/workflows/translate.yml b/.github/workflows/translate.yml new file mode 100644 index 0000000..5e616d4 --- /dev/null +++ b/.github/workflows/translate.yml @@ -0,0 +1,22 @@ +name: Upload for translation + +on: + push: + branches: + - 'main' + +jobs: + synchronize-with-crowdin: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: crowdin action + uses: crowdin/github-action@v2 + with: + upload_sources: true + env: + CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} diff --git a/.gitignore b/.gitignore index eab10c5..4640046 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store ._.DS_Store -.php-cs-fixer.cache \ No newline at end of file +.php-cs-fixer.cache +vendor \ No newline at end of file diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..7ebe048 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,45 @@ +files() + ->in(__DIR__) + ->name('*.php') + ->name('*.page') + ->exclude(['vendor']) +; + +$config = new PhpCsFixer\Config(); +return $config + ->setRiskyAllowed(true) + ->setRules([ + '@PSR12' => true, + 'no_empty_comment' => true, + 'multiline_comment_opening_closing' => true, + 'single_line_comment_spacing' => true, + 'single_line_comment_style' => true, + 'heredoc_indentation' => true, + 'include' => true, + 'no_alternative_syntax' => true, + 'single_space_around_construct' => true, + 'binary_operator_spaces' => ['default' => 'align_single_space_minimal'], + 'concat_space' => ['spacing' => 'one'], + 'linebreak_after_opening_tag' => true, + 'increment_style' => ['style' => 'post'], + 'logical_operators' => true, + 'no_useless_concat_operator' => ['juggle_simple_strings' => true], + 'not_operator_with_space' => true, + 'object_operator_without_whitespace' => true, + 'standardize_increment' => true, + 'standardize_not_equals' => true, + 'no_useless_return' => true, + 'no_empty_statement' => true, + 'semicolon_after_instruction' => true, + 'explicit_string_variable' => true, + 'simple_to_complex_string_variable' => true, + 'method_chaining_indentation' => true, + 'no_extra_blank_lines' => ['tokens' => ['attribute', 'break', 'case', 'continue', 'curly_brace_block', 'default', 'extra', 'parenthesis_brace_block', 'return', 'square_brace_block', 'switch', 'throw', 'use']], + ]) + ->setIndent(' ') + ->setLineEnding("\n") + ->setFinder($finder) +; diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5bf2d43 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,45 @@ +# Contributing to unraid-tailscale-utils + +Thank you for your interest in contributing to **unraid-tailscale-utils**! + +## How to Contribute + +- **Bug Reports & Feature Requests:** + Please open an issue describing your bug or feature request with as much detail as possible. + +- **Pull Requests:** + 1. Fork the repository and create your branch from `main`. + 2. Make your changes, following the existing code style. + 3. Add or update tests as appropriate. + 4. Ensure your code passes all checks (see below). + 5. Submit a pull request with a clear description of your changes. + +## Localization + +New strings should be added to `src/usr/local/emhttp/plugins/tailscale/locales/en_US.json`. + +Translations are managed via Crowdin (https://translate.edac.dev/) + +## Code Quality & Checks + +This repository uses automated code checks via GitHub Actions ([.github/workflows/lint.yml](.github/workflows/lint.yml)): + +- **Static Analysis:** + Run `vendor/bin/phpstan` after running `./composer install` in the repository root. + +- **Code Formatting:** + Run `vendor/bin/php-cs-fixer fix` to automatically apply formatting rules. + +- **Commit Message Linting:** + All commits must follow the [Conventional Commits](https://www.conventionalcommits.org/) specification. + Example: + ``` + feat: add advanced log filtering + fix: resolve colorization bug in syslog view + ``` + +These checks are run automatically on every push and pull request. Please ensure your code passes locally before submitting. + +## License + +By contributing to this repository, you agree that your contributions will be licensed under the [GNU General Public License v3.0 or later](LICENSE). diff --git a/README.md b/README.md index 6f921f8..c96777d 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,30 @@ # unraid-tailscale -![Current Release](https://img.shields.io/github/v/release/dkaser/unraid-tailscale) -![Last Release](https://img.shields.io/github/release-date/dkaser/unraid-tailscale) -![Last Commit](https://img.shields.io/github/last-commit/dkaser/unraid-tailscale/trunk) -![License](https://img.shields.io/github/license/dkaser/unraid-tailscale) -![Issues](https://img.shields.io/github/issues-raw/dkaser/unraid-tailscale) -![Sponsors](https://img.shields.io/github/sponsors/dkaser) +[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](LICENSE) +[![GitHub Releases](https://img.shields.io/github/v/release/unraid/unraid-tailscale)](https://github.com/unraid/unraid-tailscale/releases) +[![Last Commit](https://img.shields.io/github/last-commit/unraid/unraid-tailscale)](https://github.com/unraid/unraid-tailscale/commits/trunk/) +[![Code Style: PHP-CS-Fixer](https://img.shields.io/badge/code%20style-php--cs--fixer-brightgreen.svg)](https://github.com/FriendsOfPHP/PHP-CS-Fixer) +![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/unraid/unraid-tailscale/total) +![GitHub Downloads (all assets, latest release)](https://img.shields.io/github/downloads/unraid/unraid-tailscale/latest/total) +## Features +- Easy Tailscale installation and management on Unraid +- Helper scripts for authentication and status +- Example configurations for common use cases -## Overview +## Development -This installs Tailscale as an Unraid plugin. +### Requirements -Taildrop can be enabled by specifying a destination folder in the plugin configuration. +- [Composer](https://getcomposer.org/) for dependency management -Support is available on the Unraid forums: https://forums.unraid.net/topic/136889-plugin-tailscale/ +### Testing +1. Clone the repository. +2. Run `./composer install` to install dependencies. +3. For local testing, copy the contents of `src/` (except for the `install` directory) to the root of the Unraid test system. + +### Contributing +Pull requests and issues are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines, including code checks, commit message conventions, and licensing. You can also open an issue to discuss your idea. + +## License +This project is licensed under the GNU General Public License v3.0 or later. See [LICENSE](LICENSE) for details. diff --git a/commitlint.config.js b/commitlint.config.js index 6ce4b8d..8567d51 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1,8 +1 @@ -module.exports = { - extends: ["@commitlint/config-conventional"], - rules: { - 'type-enum': [2, 'always', [ - 'build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'style', 'test', 'release' - ]] - } -}; \ No newline at end of file +module.exports = { extends: ["@commitlint/config-conventional"] }; \ No newline at end of file diff --git a/composer b/composer new file mode 100755 index 0000000..cfc8363 --- /dev/null +++ b/composer @@ -0,0 +1,3 @@ +#!/bin/bash + +composer --working-dir src/usr/local/php/unraid-tailscale-utils/ "$@" \ No newline at end of file diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 0000000..3215382 --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,9 @@ +project_id_env: CROWDIN_PROJECT_ID +api_token_env: CROWDIN_PERSONAL_TOKEN + +preserve_hierarchy: true + +files: + - source: /src/usr/local/emhttp/plugins/tailscale/locales/en_US.json + dest: /unraid-tailscale-utils/en_US.json + translation: /src/usr/local/emhttp/plugins/tailscale/locales/%locale_with_underscore%.json diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..796848c --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,9 @@ +parameters: + level: 9 + fileExtensions: + - php + - page + paths: + - src + excludePaths: + - */vendor/* \ No newline at end of file diff --git a/plugin/plugin.j2 b/plugin/plugin.j2 index 646831b..edc5fa0 100644 --- a/plugin/plugin.j2 +++ b/plugin/plugin.j2 @@ -35,9 +35,9 @@ For older releases, see https://github.com/{{ env['GITHUB_REPOSITORY'] }}/releas {{ tailscaleSHA256 }} - -https://github.com/unraid/unraid-tailscale-utils/releases/download/{{ packageVersion }}/unraid-tailscale-utils-{{ packageVersion }}-noarch-1.txz -{{ packageSHA256 }} + +https://github.com/{{ env['GITHUB_REPOSITORY'] }}/releases/download/{{ env['PLUGIN_VERSION'] }}/{{ package_name }}-{{ env['PLUGIN_VERSION'] }}-noarch-1.txz +{{ env['PLUGIN_CHECKSUM'] }} +getTailscaleLockWarning()); ?> +getTailscaleNetbiosWarning()); ?> +getKeyExpirationWarning()); ?> + +

+

+

+ + + + +
+ +
\ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/include/common.php b/src/usr/local/emhttp/plugins/tailscale/include/common.php new file mode 100644 index 0000000..e165648 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/include/common.php @@ -0,0 +1,29 @@ +. +*/ + +namespace Tailscale; + +define(__NAMESPACE__ . "\PLUGIN_ROOT", dirname(dirname(__FILE__))); +define(__NAMESPACE__ . "\PLUGIN_NAME", "tailscale-utils"); + +// @phpstan-ignore requireOnce.fileNotFound +require_once "/usr/local/php/unraid-tailscale-utils/vendor/autoload.php"; + +$utils = new Utils(PLUGIN_NAME); +$utils->setPHPDebug(); diff --git a/src/usr/local/emhttp/plugins/tailscale/include/data/Config.php b/src/usr/local/emhttp/plugins/tailscale/include/data/Config.php new file mode 100644 index 0000000..d970dc1 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/include/data/Config.php @@ -0,0 +1,340 @@ +. +*/ + +use EDACerton\PluginUtils\Translator; + +try { + require_once dirname(dirname(__FILE__)) . "/common.php"; + + if ( ! defined(__NAMESPACE__ . '\PLUGIN_ROOT') || ! defined(__NAMESPACE__ . '\PLUGIN_NAME')) { + throw new \RuntimeException("Common file not loaded."); + } + + $tr = $tr ?? new Translator(PLUGIN_ROOT); + $utils = $utils ?? new Utils(PLUGIN_NAME); + + $tailscaleConfig = $tailscaleConfig ?? new Config(); + + if ( ! $tailscaleConfig->Enable) { + echo("{}"); + return; + } + + $localAPI = new LocalAPI(); + $tailscaleInfo = $tailscaleInfo ?? new Info($tr); + + switch ($_POST['action']) { + case 'get': + $connectionRows = ""; + $configRows = ""; + $routes = "
"; + $config = "
"; + + if ($tailscaleInfo->needsLogin()) { + $connectionRows = "{$tr->tr("needs_login")}"; + } else { + $tailscaleStatusInfo = $tailscaleInfo->getStatusInfo(); + $tailscaleConInfo = $tailscaleInfo->getConnectionInfo(); + + $acceptDNSButton = $tailscaleInfo->acceptsDNS() ? "" : + ( + $tailscaleConfig->AllowDNS ? "" : + "" + ); + + $acceptRoutesButton = $tailscaleInfo->acceptsRoutes() ? "" : + ( + $tailscaleConfig->AllowRoutes ? "" : + "" + ); + + $sshButton = $tailscaleInfo->runsSSH() ? + "" : + ""; + + $advertiseExitButton = $tailscaleInfo->usesExitNode() ? "" : + ( + $tailscaleInfo->advertisesExitNode() ? + "" : + "" + ); + + $exitLocalButton = $tailscaleInfo->exitNodeLocalAccess() ? + "" : + ""; + + $connectionRows = <<{$tr->tr("info.hostname")}{$tailscaleConInfo->HostName} + {$tr->tr("info.dns")}{$tailscaleConInfo->DNSName} + {$tr->tr("info.ip")}{$tailscaleConInfo->TailscaleIPs} + {$tr->tr("info.magicdns")}{$tailscaleConInfo->MagicDNSSuffix} + {$tr->tr("tailnet")}{$tailscaleInfo->getTailnetName()} + EOT; + + $exitDisabled = $tailscaleInfo->advertisesExitNode() ? "disabled" : ""; + $currentExit = $tailscaleInfo->getCurrentExitNode(); + + $exitSelect = ""; + + $configRows = <<{$tr->tr("info.accept_routes")}{$tailscaleConInfo->AcceptRoutes}{$acceptRoutesButton} + {$tr->tr("info.accept_dns")}{$tailscaleConInfo->AcceptDNS}{$acceptDNSButton} + {$tr->tr("info.run_ssh")}{$tailscaleConInfo->RunSSH}{$sshButton} + {$tr->tr("info.advertise_exit_node")}{$tailscaleConInfo->AdvertiseExitNode}{$advertiseExitButton} + {$tr->tr("info.use_exit_node")} {$exitSelect} + {$tr->tr("info.exit_node_local")}{$tailscaleConInfo->ExitNodeLocal}{$exitLocalButton} + + EOT; + + if (Utils::isFunnelAllowed() && $tailscaleConfig->AllowFunnel) { + // Create a list of ports similar to the one used by the exit node selection. + // Available ports can be obtained with $tailscaleInfo->getAllowedFunnelPorts + // Any port that is returned by Utils::get_assigned_ports should not be selectable + $funnelPorts = $tailscaleInfo->getAllowedFunnelPorts(); + $assignedPorts = $utils->get_assigned_ports(); + + $utils->logmsg("Funnel ports: " . implode(", ", $funnelPorts)); + $utils->logmsg("Assigned ports: " . implode(", ", $assignedPorts)); + + $funnelSelect = ""; + $configRows .= <<{$tr->tr("info.funnel_port")} {$funnelSelect} + EOT; + } + + $routesRows = ""; + + foreach ($tailscaleInfo->getAdvertisedRoutes() as $route) { + $approved = $tailscaleInfo->isApprovedRoute($route) ? "" : $tr->tr("info.unapproved"); + $routesRows .= "{$route}{$approved}"; + } + + $routes = << + + + {$tr->tr('info.routes')} +   +   + + + + {$routesRows} +   + + + EOT; + + $config = << + + + {$tr->tr('configuration')} +   +   + + + + {$configRows} + + + EOT; + } + + $connection = << + + + {$tr->tr('connection')} +   +   + + + + {$connectionRows} + + + EOT; + + $rtn = array(); + $rtn['config'] = $config; + $rtn['routes'] = $routes; + $rtn['connection'] = $connection; + + echo json_encode($rtn); + break; + case 'set-feature': + $features = [ + 'dns' => 'CorpDNS', + 'routes' => 'RouteAll', + 'ssh' => 'RunSSH', + 'exit-allow-local' => 'ExitNodeAllowLANAccess' + ]; + + if ( ! (isset($features[$_POST['feature']]))) { + throw new \Exception("Invalid feature: {$_POST['feature']}"); + } + + if ( ! isset($_POST['enable'])) { + throw new \Exception("Missing enable parameter"); + } + + $enable = filter_var($_POST['enable'], FILTER_VALIDATE_BOOLEAN); + $utils->logmsg("Setting feature: {$features[$_POST['feature']]} to " . ($enable ? "true" : "false")); + + $localAPI->patchPref($features[$_POST['feature']], $enable); + break; + case 'set-advertise-exit-node': + if ( ! isset($_POST['enable'])) { + throw new \Exception("Missing enable parameter"); + } + + $enable = filter_var($_POST['enable'], FILTER_VALIDATE_BOOLEAN); + $utils->logmsg("Setting advertise exit node to " . ($enable ? "true" : "false")); + + $prefs = $localAPI->getPrefs(); + $routes = $prefs->AdvertiseRoutes ?? array(); + $exitRoutes = Utils::getExitRoutes(); + + if ($enable) { + $routes = array_unique(array_merge($routes, $exitRoutes)); + } else { + $routes = array_diff($routes, $exitRoutes); + } + + $localAPI->patchPref("AdvertiseRoutes", array_values($routes)); + break; + case 'up': + $utils->logmsg("Getting Auth URL"); + $authURL = $tailscaleInfo->getAuthURL(); + if ($authURL == "") { + $localAPI->postLoginInteractive(); + $retries = 0; + while ($retries < 60) { + $tailscaleInfo = new Info($tr); + $authURL = $tailscaleInfo->getAuthURL(); + if ($authURL != "") { + break; + } + usleep(500000); + $retries++; + } + } + echo $authURL; + break; + case 'remove-route': + if ( ! isset($_POST['route'])) { + throw new \Exception("Missing route parameter"); + } + + $utils->logmsg("Removing route: {$_POST['route']}"); + + $advertisedRoutes = $tailscaleInfo->getAdvertisedRoutes(); + $advertisedRoutes = array_diff($advertisedRoutes, [$_POST['route']]); + + $localAPI->patchPref("AdvertiseRoutes", array_values($advertisedRoutes)); + break; + case 'add-route': + if ( ! isset($_POST['route'])) { + throw new \Exception("Missing route parameter"); + } + + if ( ! Utils::validateCidr($_POST['route'])) { + throw new \Exception("Invalid route: {$_POST['route']}"); + } + + $utils->logmsg("Adding route: {$_POST['route']}"); + + $advertisedRoutes = $tailscaleInfo->getAdvertisedRoutes(); + $advertisedRoutes[] = $_POST['route']; + + $localAPI->patchPref("AdvertiseRoutes", array_values($advertisedRoutes)); + break; + case 'expire-key': + if ($tailscaleInfo->connectedViaTS()) { + throw new \Exception("Cannot expire key while connected via Tailscale"); + } + $utils->logmsg("Expiring node key"); + $localAPI->expireKey(); + break; + case 'exit-node': + if ( ! isset($_POST['node'])) { + throw new \Exception("Missing node parameter"); + } + + $exitNodes = $tailscaleInfo->getExitNodes(); + if (( ! isset($exitNodes[$_POST['node']])) && ($_POST['node'] != '')) { + throw new \Exception("Invalid node parameter"); + } + + $utils->logmsg("Setting exit node: {$_POST['node']}"); + + $localAPI->patchPref("ExitNodeID", $_POST['node']); + break; + case 'funnel-port': + if ( ! isset($_POST['port'])) { + throw new \Exception("Missing port parameter"); + } + + // If port is empty, reset the serve config, this disables funnel + if ($_POST['port'] == '') { + $utils->logmsg("Resetting funnel port"); + $localAPI->resetServeConfig(); + break; + } + + $identCfg = parse_ini_file("/boot/config/ident.cfg", false, INI_SCANNER_RAW) ?: array(); + if ( ! isset($identCfg['PORT'])) { + throw new \Exception("Ident configuration does not contain PORT"); + } + + $serveConfig = new ServeConfig( + trim($tailscaleInfo->getDNSName(), "."), + $_POST['port'], + "http://localhost:" . $identCfg['PORT'] + ); + + $utils->logmsg("Object: " . json_encode($serveConfig->getConfig(), JSON_UNESCAPED_SLASHES)); + $localAPI->setServeConfig($serveConfig); + break; + } +} catch (\Throwable $e) { + file_put_contents("/var/log/tailscale-error.log", print_r($e, true) . PHP_EOL, FILE_APPEND); + echo "{}"; +} diff --git a/src/usr/local/emhttp/plugins/tailscale/include/data/Lock.php b/src/usr/local/emhttp/plugins/tailscale/include/data/Lock.php new file mode 100644 index 0000000..6bcb3c3 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/include/data/Lock.php @@ -0,0 +1,78 @@ +. +*/ + +use EDACerton\PluginUtils\Translator; + +try { + require_once dirname(dirname(__FILE__)) . "/common.php"; + + if ( ! defined(__NAMESPACE__ . '\PLUGIN_ROOT') || ! defined(__NAMESPACE__ . '\PLUGIN_NAME')) { + throw new \RuntimeException("Common file not loaded."); + } + + $tr = $tr ?? new Translator(PLUGIN_ROOT); + + $tailscaleConfig = $tailscaleConfig ?? new Config(); + + if ( ! $tailscaleConfig->Enable) { + echo("{}"); + return; + } + + switch ($_POST['action']) { + case 'get': + $tailscaleInfo = $tailscaleInfo ?? new Info($tr); + $rows = ""; + + $mullvad = filter_var($_POST['mullvad'] ?? false, FILTER_VALIDATE_BOOLEAN); + + foreach ($tailscaleInfo->getTailscaleLockPending() as $lockHost => $lockKey) { + if ( ! $mullvad && str_contains($lockHost, 'mullvad.ts.net')) { + continue; + } + + $rows .= "{$lockHost}{$lockKey}"; + } + + $output = << + + +   + Name + Key + + + + {$rows} + + + EOT; + + $rtn = array(); + $rtn['html'] = $output; + echo json_encode($rtn); + break; + } +} catch (\Throwable $e) { + file_put_contents("/var/log/tailscale-error.log", print_r($e, true) . PHP_EOL, FILE_APPEND); + echo "{}"; +} diff --git a/src/usr/local/emhttp/plugins/tailscale/include/data/Status.php b/src/usr/local/emhttp/plugins/tailscale/include/data/Status.php new file mode 100644 index 0000000..cbf2415 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/include/data/Status.php @@ -0,0 +1,127 @@ +. +*/ + +use EDACerton\PluginUtils\Translator; + +try { + require_once dirname(dirname(__FILE__)) . "/common.php"; + + if ( ! defined(__NAMESPACE__ . '\PLUGIN_ROOT') || ! defined(__NAMESPACE__ . '\PLUGIN_NAME')) { + throw new \RuntimeException("Common file not loaded."); + } + + $tr = $tr ?? new Translator(PLUGIN_ROOT); + $utils = $utils ?? new Utils(PLUGIN_NAME); + + $tailscaleConfig = $tailscaleConfig ?? new Config(); + + if ( ! $tailscaleConfig->Enable) { + echo("{}"); + return; + } + + switch ($_POST['action']) { + case 'get': + $tailscaleInfo = $tailscaleInfo ?? new Info($tr); + $rows = ""; + + $mullvad = filter_var($_POST['mullvad'] ?? false, FILTER_VALIDATE_BOOLEAN); + $shared = filter_var($_POST['shared'] ?? false, FILTER_VALIDATE_BOOLEAN); + + foreach ($tailscaleInfo->getPeerStatus() as $peer) { + if ($peer->Mullvad && ! $mullvad && ! $peer->Active) { + continue; + } + if ($peer->SharedUser && ! $shared && ! $peer->Active) { + continue; + } + + $user = $peer->SharedUser ? $tr->tr('status_page.shared') : $peer->Name; + $online = $peer->Online ? ($peer->Active ? $tr->tr('status_page.active') : $tr->tr('status_page.idle')) : $tr->tr('status_page.offline'); + $exitNode = $peer->ExitNodeActive ? $tr->tr('status_page.exit_active') : ($peer->ExitNodeAvailable ? ($peer->Mullvad ? "Mullvad" : $tr->tr('status_page.exit_available')) : ""); + $connection = $peer->Active ? ($peer->Relayed ? $tr->tr('status_page.relay') : $tr->tr('status_page.direct')) : ""; + $active = $peer->Active ? $peer->Address : ""; + $txBytes = $peer->Traffic ? $peer->TxBytes : ""; + $rxBytes = $peer->Traffic ? $peer->RxBytes : ""; + $pingHost = ($peer->SharedUser || $peer->Active || ! $peer->Online || $peer->Mullvad) ? "" : ""; + $ips = implode("
", $peer->IP); + + $rows .= << + {$user} + {$ips} + {$peer->LoginName} + {$online} + {$exitNode} + {$connection} + {$active} + {$txBytes} + {$rxBytes} + {$pingHost} + + EOT; + } + + $output = << + + + {$tr->tr('info.dns')} + {$tr->tr('info.ip')} + {$tr->tr('status_page.login_name')} + {$tr->tr('status')} + {$tr->tr('status_page.exit_node')} + {$tr->tr('status_page.connection_type')} + {$tr->tr('status_page.connection_addr')} + {$tr->tr('status_page.tx_bytes')} + {$tr->tr('status_page.rx_bytes')} + {$tr->tr('status_page.action')} + + + + {$rows} + + + EOT; + + $rtn = array(); + $rtn['html'] = $output; + echo json_encode($rtn); + break; + case 'ping': + $tailscaleInfo = $tailscaleInfo ?? new Info($tr); + $out = "Could not find host."; + + foreach ($tailscaleInfo->getPeerStatus() as $peer) { + if ($peer->Name == $_POST['host']) { + $peerIP = escapeshellarg($peer->IP[0]); + $out = implode("
", $utils->run_command("tailscale ping {$peerIP}")); + break; + } + } + + echo $out; + break; + } +} catch (\Throwable $e) { + file_put_contents("/var/log/tailscale-error.log", print_r($e, true) . PHP_EOL, FILE_APPEND); + echo "{}"; +} diff --git a/src/usr/local/emhttp/plugins/tailscale/include/get_log.php b/src/usr/local/emhttp/plugins/tailscale/include/get_log.php new file mode 100644 index 0000000..cc0503c --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/include/get_log.php @@ -0,0 +1,52 @@ +. +*/ + +function getLog(string $log, int $max): void +{ + $allowed_files = ["/var/log/tailscale.log", "/var/log/tailscale-utils.log"]; + + if ( ! in_array($log, $allowed_files)) { + return; + } + + if ( ! file_exists($log)) { + echo '', htmlspecialchars($log), " not found."; + return; + } + + $lines = array_reverse(array_slice(file($log) ?: array(), -$max)); + + foreach ($lines as $line) { + echo '', htmlspecialchars($line), ""; + } +} + +ini_set('memory_limit', '512M'); // Increase memory limit + +try { + if ( ! is_string($_POST['log']) || ! is_numeric($_POST['max'])) { + throw new InvalidArgumentException("Invalid input"); + } + + getLog($_POST['log'], intval($_POST['max'])); +} catch (Throwable $e) { + echo '', htmlspecialchars($e->getMessage()), ""; +} + +ini_restore('memory_limit'); // Restore original memory limit diff --git a/src/usr/local/emhttp/plugins/tailscale/include/page.php b/src/usr/local/emhttp/plugins/tailscale/include/page.php new file mode 100644 index 0000000..3786ec4 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/include/page.php @@ -0,0 +1,53 @@ +. +*/ + +namespace Tailscale; + +/** +* @param array $params +*/ +function getPage(string $filename, bool $niceError = true, array $params = array()): string +{ + try { + require_once dirname(__FILE__) . "/common.php"; + return includePage(dirname(__FILE__) . "/Pages/{$filename}.php", $params); + } catch (\Throwable $e) { + if ($niceError) { + file_put_contents("/var/log/tailscale-error.log", print_r($e, true) . PHP_EOL, FILE_APPEND); + return includePage(dirname(__FILE__) . "/Pages/Error.php", array("e" => $e)); + } else { + throw $e; + } + } +} + +/** +* @param array $params +*/ +function includePage(string $filename, array $params = array()): string +{ + extract($params); + + if (is_file($filename)) { + ob_start(); + include $filename; + return ob_get_clean() ?: ""; + } + return ""; +} diff --git a/src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/disabled.php b/src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/disabled.php new file mode 100644 index 0000000..3470a45 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/disabled.php @@ -0,0 +1,32 @@ +. +*/ + +namespace Tailscale; + +use EDACerton\PluginUtils\Translator; + +if ( ! defined(__NAMESPACE__ . '\PLUGIN_ROOT') || ! defined(__NAMESPACE__ . '\PLUGIN_NAME')) { + throw new \RuntimeException("Common file not loaded."); +} + +$tr = $tr ?? new Translator(PLUGIN_ROOT); +?> +

tr("tailscale_lock"); ?>

+ +

tr("lock.disabled"); ?>

\ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/locked.php b/src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/locked.php new file mode 100644 index 0000000..61aeef8 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/locked.php @@ -0,0 +1,45 @@ +. +*/ + +namespace Tailscale; + +use EDACerton\PluginUtils\Translator; + +if ( ! defined(__NAMESPACE__ . '\PLUGIN_ROOT') || ! defined(__NAMESPACE__ . '\PLUGIN_NAME')) { + throw new \RuntimeException("Common file not loaded."); +} + +$tr = $tr ?? new Translator(PLUGIN_ROOT); +?> +

tr("tailscale_lock"); ?>

+ +

+tr('lock.unsigned'); ?>. +

+ +

tr('lock.unsigned_instructions'); ?>

+ + + +
getTailscaleLockNodekey(); ?>
\ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/signed.php b/src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/signed.php new file mode 100644 index 0000000..3fc998b --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/signed.php @@ -0,0 +1,47 @@ +. +*/ + +namespace Tailscale; + +use EDACerton\PluginUtils\Translator; + +if ( ! defined(__NAMESPACE__ . '\PLUGIN_ROOT') || ! defined(__NAMESPACE__ . '\PLUGIN_NAME')) { + throw new \RuntimeException("Common file not loaded."); +} + +$tr = $tr ?? new Translator(PLUGIN_ROOT); +?> +

tr("tailscale_lock"); ?>

+ +

+ tr('lock.signed_node'); ?> +

+ +

+tr('lock.make_signing'); ?> +

+ + + +
getTailscaleLockPubkey(); ?>
diff --git a/src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/signing.php b/src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/signing.php new file mode 100644 index 0000000..f0e38c3 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/include/tailscale-lock/signing.php @@ -0,0 +1,80 @@ +. +*/ + +namespace Tailscale; + +use EDACerton\PluginUtils\Translator; + +if ( ! defined(__NAMESPACE__ . '\PLUGIN_ROOT') || ! defined(__NAMESPACE__ . '\PLUGIN_NAME')) { + throw new \RuntimeException("Common file not loaded."); +} + +$tr = $tr ?? new Translator(PLUGIN_ROOT); +?> +

tr("tailscale_lock"); ?>

+

tr("lock.sign"); ?>

+

+ tr("lock.signing_node"); ?> +

+

+tr("lock.signing_instructions"); ?> +

+ + + + +
+ +
 

+ + +Display unsigned Mullvad nodes +
diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json b/src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json new file mode 100644 index 0000000..63e8c86 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json @@ -0,0 +1,132 @@ +{ + "yes": "Ja", + "no": "Nein", + "ignore": "Ignorieren", + "erase": "Löschen", + "confirm": "Bestätigen", + "download": "Herunterladen", + "restart": "Neustart", + "apply": "Übernehmen", + "back": "Zurück", + "status": "Status", + "connection": "Verbindung", + "unknown": "Unbekannt", + "none": "Keine", + "disabled": "Deaktiviert", + "enabled": "Aktiviert", + "disable": "Deaktivieren", + "enable": "Aktivieren", + "cancel": "Abbrechen", + "accept": "Akzeptieren", + "sign": "Signieren", + "login": "Anmelden", + "add": "Hinzufügen", + "remove": "Entfernen", + "tailnet": "Tailnet", + "configuration": "Konfiguration", + "needs_login": "Dieses Gerät muss bei Tailscale angemeldet sein. Aktualisieren Sie diese Seite nach der Anmeldung, um den Status anzuzeigen.", + "tailscale_disabled": "Tailscale ist derzeit deaktiviert. Es kann über die Registerkarte Einstellungen aktiviert werden.", + "tailscale_lock": "Tailscale Lock", + "warning": "Warnung", + "settings": { + "basic": "Einfache Ansicht", + "advanced": "Erweiterte Ansicht", + "system_settings": "Systemeinstellungen", + "enable_tailscale": "Tailscale aktivieren", + "unraid_listen": "Unraid-Dienste verfügbar via Tailscale IP", + "ip_forward": "IP Forwarding aktivieren", + "taildrop": "Taildrop Speicherort", + "services": "Tailscale-Dienste", + "wireguard": "Wireguard-Port", + "outbound_network": "Erweiterte Netzwerkoptionen", + "subnets": "Tailscale-Subnetze zulassen", + "dns": "Tailscale-DNS-Einstellungen zulassen", + "save": "Einstellungen speichern", + "restart": "Tailscale neu starten", + "erase": "Tailscale Konfiguration löschen", + "diagnostics": "Plugin-Diagnose", + "reauthenticate": "Erneut authentifizieren", + "funnel": "Tailscale Funnel zulassen", + "context": { + "unraid_listen": "Konfiguriert Unraid-Dienste (SSH, WebGUI, SMB usw.) so, dass sie auf Tailscale-Adressen verfügbar sind.", + "ip_forward": "Setzt net.ipv4.ip_forward und net.ipv6.conf.all.forwarding in sysctl auf 1. Diese Änderung tritt sofort nach der Aktivierung auf.", + "taildrop": "Geben Sie den Pfad für eingehende Taildrop-Dateien an.", + "dns": "Erlaubt das Aktivieren von Tailscale-DNS über die CLI oder das WebGUI. Wenn deaktiviert, wird Tailscale-DNS deaktiviert, wenn Tailscale startet.", + "subnets": "Erlaubt das Aktivieren von Tailscale-Routen über die CLI oder das WebGUI. Wenn deaktiviert, werden die Tailscale-Routen beim Start von Tailscale deaktiviert.", + "wireguard": "Port, der für Wireguard-Verbindungen verwendet wird. Dies muss in der Regel nicht geändert werden.", + "erase": "Meldet sich von Tailscale ab und setzt alle Einstellungen auf die Standardeinstellungen zurück", + "restart": "Starten Sie den Tailscale-Daemon neu, und wenden Sie die Einstellungen erneut an.", + "diagnostics": "Diagnose Daten herunterladen (dies kann einige Zeit in Anspruch nehmen)", + "save": "Tailscale wird neu gestartet, damit Änderungen übernommen werden", + "reauthenticate": "Erzwingt eine erneute Authentifizierung von Tailscale. Dabei wird Tailscale getrennt, bis die Authentifizierung abgeschlossen ist.", + "funnel": "Erlaubt das Aktivieren von Tailscale Funnel über das WebGUI. Wenn deaktiviert, wird Tailscale Funnel deaktiviert." + } + }, + "info": { + "online": "Online", + "version": "Tailscale Version", + "health": "Tailscale-Zustand", + "login": "Angemeldet", + "netmap": "In der Netzwerkübersicht", + "key_expire": "Ablaufdatum des Schlüssels", + "tags": "Tags", + "hostname": "Hostname", + "dns": "DNS-Name", + "ip": "Tailscale-IPs", + "magicdns": "MagicDNS-Suffix", + "routes": "Veröffentlichte Routen", + "accept_routes": "Routen akzeptieren", + "accept_dns": "DNS akzeptieren", + "run_ssh": "Tailscale-SSH", + "advertise_exit_node": "Als Exit-Node betreiben", + "use_exit_node": "Exit-Node verwenden", + "exit_node_local": "LAN-Zugriff beim Verwenden des Exit-Nodes erlauben", + "unapproved": "Genehmigung in der Admin-Konsole erforderlich", + "connected_via": "Verbunden über Tailscale", + "funnel_port": "Funnel-Port für WebGUI", + "port_in_use": "Verwendeter Port", + "lock": { + "node_key": "Node Schlüssel", + "public_key": "Öffentlicher Schlüssel", + "signed": "Node Key Signiert", + "signing": "Ist ein Signing-Node" + } + }, + "warnings": { + "key_expiration": "Der Tailscale Schlüssel wird in %s Tagen um %s ablaufen.", + "netbios": "NetBIOS ist in den SMB-Einstellungen aktiviert. Dies kann verhindern, dass über Tailscale auf Freigaben zugegriffen werden kann.", + "lock": "Für das Tailnet ist die Sperre aktiviert, aber dieser Node wurde nicht signiert. Es wird nicht in der Lage sein, mit dem Tailnet zu kommunizieren.", + "funnel": "Das Aktivieren von Tailscale Funnel für das Unraid WebGUI macht Ihren Server über das Internet zugänglich und erhöht das Risiko unbefugten Zugriffs und potenzieller Angriffe erheblich. Diese Funktion sollte nur aktiviert werden, wenn Sie die Sicherheitsauswirkungen vollständig verstehen. Die empfohlene/​sichere Methode für den Fernzugriff ist, Tailscale auf den Geräten zu installieren, mit denen Sie auf das WebGUI zugreifen möchten, anstatt Funnel zu aktivieren.", + "subnet": "Das Akzeptieren von Tailscale-Subnetzen auf Ihrem Unraid-Server kann in bestimmten Netzwerkkonfigurationen die lokale Netzwerkverbindung stören. Das Akzeptieren von Routen ist nicht erforderlich, damit Ihr Unraid-Server als Subnetz-Router oder Exit-Node fungiert. Aktivieren Sie diese Option nur, wenn Ihr Unraid-Server eine Verbindung zu von anderen Geräten angekündigten entfernten Subnetzen herstellen muss.", + "dns": "Das Aktivieren von Tailscale MagicDNS auf Ihrem Server kann die Namensauflösung innerhalb von Docker-Containern stören und möglicherweise zu Verbindungsproblemen für Anwendungen führen. MagicDNS muss auf dem Server nicht aktiviert sein, damit Sie mit anderen Geräten über MagicDNS-Namen (z. B. \"unraid.tailnet.ts.net\") auf Ihren Unraid-Server zugreifen können.", + "caution": "Gehen Sie mit Vorsicht vor und stellen Sie sicher, dass Sie die Auswirkungen dieser Änderung verstehen, bevor Sie fortfahren.", + "more_info": "Weitere Informationen:" + }, + "lock": { + "sign": "Nodes signieren", + "signing_node": "Dies ist ein Signaturknoten für das Tailnet.", + "signing_instructions": "Die folgenden Knoten sind derzeit aus dem Tailnet ausgeschlossen. Aktivieren Sie das Kontrollkästchen für alle Knoten, die Sie hinzufügen möchten, und klicken Sie dann auf die Schaltfläche Signieren, um den Knoten zum Tailnet hinzuzufügen.", + "signed_node": "In Ihrem Tailnet ist die Sperre aktiviert, und der aktuelle Knoten ist signiert. Dieser Knoten kann mit dem Tailnet kommunizieren.", + "make_signing": "Wenn Sie dies zu einem Signaturknoten machen möchten, müssen Sie dem folgenden Schlüssel von einem Signaturknoten vertrauen:", + "unsigned": "In Ihrem Tailnet ist die Sperre aktiviert, und der aktuelle Knoten ist nicht signiert. Dieser Knoten kann nicht mit dem Tailnet kommunizieren.", + "unsigned_instructions": "Damit dieser Knoten kommunizieren kann, müssen Sie dem folgenden Schlüssel von einem Signaturknoten vertrauen", + "disabled": "Für Ihr Tailnet ist die Sperre nicht aktiviert." + }, + "status_page": { + "exit_node": "Exit-Node", + "connection_type": "Verbindungstyp", + "connection_addr": "Verbindung", + "tx_bytes": "Tx-Bytes", + "rx_bytes": "Rx-Bytes", + "relay": "Relay", + "direct": "Direkt", + "exit_active": "Aktiv", + "exit_available": "Verfügbar", + "idle": "Leerlauf", + "offline": "Offline", + "active": "Aktiv", + "login_name": "Anmeldename", + "shared": "Shared-In-Benutzer", + "action": "Aktion" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/en_US.json b/src/usr/local/emhttp/plugins/tailscale/locales/en_US.json new file mode 100644 index 0000000..1b57eb9 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/en_US.json @@ -0,0 +1,132 @@ +{ + "yes": "Yes", + "no": "No", + "ignore": "Ignore", + "erase": "Erase", + "confirm": "Confirm", + "download": "Download", + "restart": "Restart", + "apply": "Apply", + "back": "Back", + "status": "Status", + "connection": "Connection", + "unknown": "Unknown", + "none": "None", + "disabled": "Disabled", + "enabled": "Enabled", + "disable": "Disable", + "enable": "Enable", + "cancel": "Cancel", + "accept": "Accept", + "sign": "Sign", + "login": "Login", + "add": "Add", + "remove": "Remove", + "tailnet": "Tailnet", + "configuration": "Configuration", + "needs_login": "This device needs to be logged in to Tailscale. Refresh this page after logging in to view the status.", + "tailscale_disabled": "Tailscale is currently disabled. It can be enabled via the Settings tab.", + "tailscale_lock": "Tailscale Lock", + "warning": "Warning", + "settings": { + "basic": "Basic View", + "advanced": "Advanced View", + "system_settings": "System Settings", + "enable_tailscale": "Enable Tailscale", + "unraid_listen": "Unraid services listen on Tailscale IP", + "ip_forward": "Enable IP Forwarding", + "taildrop": "Taildrop Location", + "services": "Tailscale Services", + "wireguard": "Wireguard Port", + "outbound_network": "Advanced Network Options", + "subnets": "Allow Tailscale Subnets", + "dns": "Allow Tailscale DNS settings", + "save": "Save Settings", + "restart": "Restart Tailscale", + "erase": "Erase Tailscale Configuration", + "diagnostics": "Plugin Diagnostics", + "reauthenticate": "Reauthenticate", + "funnel": "Allow Tailscale Funnel", + "context": { + "unraid_listen": "Configures Unraid services (SSH, WebGUI, SMB, etc.) to listen on Tailscale addresses.", + "ip_forward": "Sets net.ipv4.ip_forward and net.ipv6.conf.all.forwarding to 1 in sysctl. This change occurs immediately when being enabled.", + "taildrop": "Specify the path for incoming Taildrop files.", + "dns": "Allows Tailscale DNS to be enabled via the CLI or WebGUI. If disabled, Tailscale DNS will be disabled when Tailscale starts.", + "subnets": "Allows Tailscale routes to be enabled via the CLI or WebGUI. If disabled, Tailscale routes will be disabled when Tailscale starts.", + "wireguard": "Change the port used for Wireguard conections. This usually does not need to be changed.", + "erase": "Logs out of Tailscale and resets all settings to defaults", + "restart": "Restart the Tailscale daemon and reapply settings.", + "diagnostics": "Download diagnostics (this may take some time to complete)", + "save": "Tailscale will be restarted when changes are applied", + "reauthenticate": "Force a Tailscale reauthentication. This will disconnect Tailscale until the authentication is completed.", + "funnel": "Allows Tailscale funnel to be enabled via the WebGUI. If disabled, Tailscale funnel will be disabled." + } + }, + "info": { + "online": "Online", + "version": "Tailscale Version", + "health": "Tailscale Health", + "login": "Logged In", + "netmap": "In Network Map", + "key_expire": "Key Expiration", + "tags": "Tags", + "hostname": "Hostname", + "dns": "DNS Name", + "ip": "Tailscale IPs", + "magicdns": "MagicDNS Suffix", + "routes": "Advertised Routes", + "accept_routes": "Accept Routes", + "accept_dns": "Accept DNS", + "run_ssh": "Tailscale SSH", + "advertise_exit_node": "Run as Exit Node", + "use_exit_node": "Use Exit Node", + "exit_node_local": "Allow LAN Access while using Exit Node", + "unapproved": "Needs approval in admin console", + "connected_via": "Connected via Tailscale", + "funnel_port": "Funnel Port for WebGUI", + "port_in_use": "Port in Use", + "lock": { + "node_key": "Node Key", + "public_key": "Public Key", + "signed": "Node Key Signed", + "signing": "Is Signing Node" + } + }, + "warnings": { + "key_expiration": "The Tailscale key will expire in %s days on %s.", + "netbios": "NetBIOS is enabled in SMB settings - this can prevent shares from being accessed via Tailscale.", + "lock": "The tailnet has lock enabled, but this node has not been signed. It will not be able to communicate with the tailnet.", + "funnel": "Enabling Tailscale Funnel for the Unraid WebGUI exposes your server to the internet, significantly increasing the risk of unauthorized access and potential attacks. This feature should only be enabled if you fully understand the security impact. The recommended/secure approach for remote access is to install Tailscale on the devices you plan to use to access the WebGUI instead of enabling Funnel.", + "subnet": "Accepting Tailscale subnets on your Unraid server can disrupt local network connectivity in certain network configurations. Accepting routes is not required for your Unraid server to function as a subnet router or exit node. Only enable this option if your Unraid server needs to connect to remote subnets advertised by other devices.", + "dns": "Enabling Tailscale MagicDNS on your server can disrupt name resolution inside Docker containers, potentially causing connectivity issues for applications. MagicDNS does not need to be enabled on the server for you to access your Unraid server using MagicDNS names (e.g., 'unraid.tailnet.ts.net') from other devices.", + "caution": "Proceed with caution and ensure you understand the implications of this change before continuing.", + "more_info": "For more information:" + }, + "lock": { + "sign": "Sign Nodes", + "signing_node": "This is a signing node for the tailnet.", + "signing_instructions": "The following nodes are currently locked out of the tailnet. Check the box for any nodes you wish to add, then click the Sign button, to add the node to the tailnet.", + "signed_node": "Your tailnet has lock enabled and the current node is signed. This node can communicate with the tailnet.", + "make_signing": "If you wish to make this a signing node, you will need to trust the following key from a signing node:", + "unsigned": "Your tailnet has lock enabled and the current node is not signed. This node cannot communicate with the tailnet.", + "unsigned_instructions": "To allow this node to communicate, you will need to trust the following key from a signing node", + "disabled": "Your tailnet does not have lock enabled." + }, + "status_page": { + "exit_node": "Exit Node", + "connection_type": "Connection Type", + "connection_addr": "Connection", + "tx_bytes": "Tx Bytes", + "rx_bytes": "Rx Bytes", + "relay": "Relay", + "direct": "Direct", + "exit_active": "Active", + "exit_available": "Available", + "idle": "Idle", + "offline": "Offline", + "active": "Active", + "login_name": "Login Name", + "shared": "Shared-In User", + "action": "Action" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json b/src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json new file mode 100644 index 0000000..aa28915 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json @@ -0,0 +1,132 @@ +{ + "yes": "Sí", + "no": "No", + "ignore": "Ignorar", + "erase": "Borrar", + "confirm": "Confirmar", + "download": "Descargar", + "restart": "Reiniciar", + "apply": "Aplicar", + "back": "Atrás", + "status": "Estado", + "connection": "Conexión", + "unknown": "Desconocido", + "none": "Nada", + "disabled": "Desactivado", + "enabled": "Activado", + "disable": "Desactivar", + "enable": "Activar", + "cancel": "Cancelar", + "accept": "Aceptar", + "sign": "Firma", + "login": "Iniciar sesión", + "add": "Agregar", + "remove": "Eliminar", + "tailnet": "Tailnet", + "configuration": "Configuración", + "needs_login": "Este dispositivo necesita iniciar sesión en Tailscale. Actualice esta página después de iniciar sesión para ver el estado.", + "tailscale_disabled": "Tailscale está desactivada actualmente. Se puede activar a través de la pestaña de ajustes.", + "tailscale_lock": "Bloqueo de Tailscale", + "warning": "Advertencia", + "settings": { + "basic": "Vista básica", + "advanced": "Vista avanzada", + "system_settings": "Configuración del Sistema", + "enable_tailscale": "Activar Tailscale", + "unraid_listen": "Los servicios de Unraid escuchan en la IP de Tailscale", + "ip_forward": "Activar reenvío IP", + "taildrop": "Ubicación de Taildrop", + "services": "Servicios de Tailscale", + "wireguard": "Puerto Wireguard", + "outbound_network": "Opciones de red avanzadas", + "subnets": "Permitir subredes de Tailscale", + "dns": "Permitir configuración de DNS de Tailscale", + "save": "Guardar configuración", + "restart": "Reiniciar Tailscale", + "erase": "Borrar configuración de Tailscale", + "diagnostics": "Diagnósticos de plugin", + "reauthenticate": "Reautentificar", + "funnel": "Permitir Tailscale Funnel", + "context": { + "unraid_listen": "Configura los servicios de Unraid (SSH, WebGUI, SMB, etc.) para escuchar las direcciones de Tailscale.", + "ip_forward": "Establece net.ipv4.ip_forward y net.ipv6.conf.all.forwarding a 1 en sysctl. Este cambio ocurre inmediatamente cuando se activa.", + "taildrop": "Especifique la ruta para los archivos entrantes de Taildrop.", + "dns": "Permite habilitar el DNS de Tailscale vía CLI o WebGUI. Si está deshabilitado, el DNS de Tailscale se deshabilitará al iniciar Tailscale.", + "subnets": "Permite habilitar las rutas de Tailscale vía CLI o WebGUI. Si está deshabilitado, las rutas de Tailscale se deshabilitarán al iniciar Tailscale.", + "wireguard": "Cambie el puerto utilizado para las conexiones de Wireguard. Por lo general, no es necesario cambiarlo.", + "erase": "Cierra sesión en Tailscale y restablece todos los ajustes a los valores predeterminados", + "restart": "Reinicie el daemon Tailscale y vuelva a aplicar la configuración.", + "diagnostics": "Descargar diagnóstico (esto puede tardar algún tiempo en completarse)", + "save": "Tailscale se reiniciará cuando se apliquen los cambios", + "reauthenticate": "Forzar una reautenticación de Tailscale. Esto desconectará Tailscale hasta que se complete la autenticación.", + "funnel": "Permite que el funnel de Tailscale se habilite través del WebGUI. Si está deshabilitado, el funnel de Tailscale se deshabilitará." + } + }, + "info": { + "online": "En línea", + "version": "Versión de Tailscale", + "health": "Salud a Tailscale", + "login": "Conectado", + "netmap": "En el mapa de red", + "key_expire": "Caducidad de la clave", + "tags": "Etiquetas", + "hostname": "Nombre de host", + "dns": "Nombre DNS", + "ip": "IP de Tailscale", + "magicdns": "Sufijo MagicDNS", + "routes": "Rutas anunciadas", + "accept_routes": "Aceptar rutas", + "accept_dns": "Aceptar DNS", + "run_ssh": "SSH de Tailscale", + "advertise_exit_node": "Ejecutar como nodo de salida", + "use_exit_node": "Usar nodo de salida", + "exit_node_local": "Permitir acceso LAN al usar nodo de salida", + "unapproved": "Requiere aprobación en la consola de administración", + "connected_via": "Conectado a través de Tailscale", + "funnel_port": "Puerto del Funnel para WebGUI", + "port_in_use": "Puerto en uso", + "lock": { + "node_key": "Clave de nodo", + "public_key": "Clave pública", + "signed": "La clave de nodo está firmada", + "signing": "Es el nodo de firma" + } + }, + "warnings": { + "key_expiration": "La clave de Tailscale expirará en %s días el %s.", + "netbios": "NetBIOS está habilitado en configuración de SMB, lo que puede impedir que se acceda a los recursos compartidos a través de Tailscale.", + "lock": "La tailnet tiene habilitado el bloqueo, pero este nodo no se ha firmado. No podrá comunicarse con la tailnet.", + "funnel": "Habilitar Tailscale Funnel para el WebGUI de Unraid expone su servidor a internet, aumentando significativamente el riesgo de acceso no autorizado y posibles ataques. Esta función solo debe habilitarse si comprende completamente el impacto en la seguridad. El método recomendado/seguro para el acceso remoto es instalar Tailscale en los dispositivos con los que planea acceder al WebGUI en lugar de habilitar Funnel.", + "subnet": "Aceptar subredes de Tailscale en su servidor Unraid puede interrumpir la conectividad de red local en ciertas configuraciones de red. Aceptar rutas no es necesario para que su servidor Unraid funcione como enrutador de subred o nodo de salida. Solo habilite esta opción si su servidor Unraid necesita conectarse a subredes remotas anunciadas por otros dispositivos.", + "dns": "Habilitar MagicDNS de Tailscale en su servidor puede interrumpir la resolución de nombres dentro de los contenedores Docker, lo que puede causar problemas de conectividad para las aplicaciones. No es necesario habilitar MagicDNS en el servidor para que usted acceda a su servidor Unraid usando nombres de MagicDNS (por ejemplo, 'unraid.tailnet.ts.net') desde otros dispositivos.", + "caution": "Proceda con precaución y asegúrese de comprender las implicaciones de este cambio antes de continuar.", + "more_info": "Para más información:" + }, + "lock": { + "sign": "Firma nodos", + "signing_node": "Este es un nodo firmante para la tailnet.", + "signing_instructions": "Los siguientes nodos están actualmente bloqueados fuera de la tailnet. Marque la casilla de los nodos que desee agregar y, a continuación, haga clic en el botón Firmar para agregar el nodo.", + "signed_node": "Su tailnet tiene habilitado el bloqueo y el nodo actual está firmado. Este nodo puede comunicarse con la tailnet.", + "make_signing": "Si desea hacer esto un nodo firmante, tendrá que usar un nodo firmante a confiar en la siguiente clave:", + "unsigned": "La tailnet tiene habilitado el bloqueo y el nodo actual no está firmado. Este nodo no puede comunicarse con la tailnet.", + "unsigned_instructions": "Para permitir que este nodo se comunique, necesitará confiar en la siguiente clave de un nodo firmante", + "disabled": "Tu tailnet no tiene bloqueo activado." + }, + "status_page": { + "exit_node": "Nodo de salida", + "connection_type": "Tipo de conexión", + "connection_addr": "Conexión", + "tx_bytes": "Bytes Tx", + "rx_bytes": "Bytes Rx", + "relay": "Relevo", + "direct": "Directo", + "exit_active": "Activo", + "exit_available": "Hay disponible", + "idle": "Inactivo", + "offline": "Desconectado", + "active": "Activo", + "login_name": "Nombre de usuario", + "shared": "Usuario compartido entrante", + "action": "Acción" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json b/src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json new file mode 100644 index 0000000..50a3c22 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json @@ -0,0 +1,132 @@ +{ + "yes": "Oui", + "no": "Non", + "ignore": "Ignorer", + "erase": "Effacer", + "confirm": "Confirmer", + "download": "Télécharger", + "restart": "Redémarrer", + "apply": "Appliquer", + "back": "Retour", + "status": "État", + "connection": "Connexion", + "unknown": "Inconnu", + "none": "Aucun", + "disabled": "Désactivé", + "enabled": "Activé", + "disable": "Désactiver", + "enable": "Activer", + "cancel": "Annuler", + "accept": "Accepter", + "sign": "Se connecter", + "login": "Connexion", + "add": "Ajouter", + "remove": "Supprimer", + "tailnet": "Tailnet", + "configuration": "Configuration", + "needs_login": "Cet appareil doit être connecté à Tailscale. Actualisez cette page après la connexion pour afficher le statut.", + "tailscale_disabled": "Tailscale est actuellement désactivé. Il peut être activé via l'onglet Paramètres.", + "tailscale_lock": "Tailscale Lock", + "warning": "Avertissement", + "settings": { + "basic": "Affichage de base", + "advanced": "Affichage avancé", + "system_settings": "Paramètres Système", + "enable_tailscale": "Activer Tailscale", + "unraid_listen": "Les services Unraid écoutent sur l'IP de Tailscale", + "ip_forward": "Activer l'IP Forwarding", + "taildrop": "Emplacement de Taildrop", + "services": "Services de Tailscale", + "wireguard": "Port Wireguard", + "outbound_network": "Options réseau avancées", + "subnets": "Autoriser les sous-réseaux Tailscale", + "dns": "Autoriser les paramètres DNS Tailscale", + "save": "Sauvegarde des Paramètres", + "restart": "Redémarrer Tailscale", + "erase": "Effacer la configuration de Tailscale", + "diagnostics": "Diagnostics du Plugin", + "reauthenticate": "Réanthentifier", + "funnel": "Autoriser Tailscale Funnel", + "context": { + "unraid_listen": "Configure les services Unraid (SSH, WebGUI, SMB, etc.) pour écouter sur les adresses de Tailscale.", + "ip_forward": "Définit net.ipv4.ip_forward et net.ipv6.conf.all.forwarding à 1 dans sysctl. Ce changement survient immédiatement après son activation.", + "taildrop": "Spécifie le chemin pour les fichiers Taildrop entrants.", + "dns": "Permet d'activer le DNS Tailscale via la CLI ou le WebGUI. Si désactivé, le DNS Tailscale sera désactivé lors du démarrage de Tailscale.", + "subnets": "Permet d'activer les routes Tailscale via la CLI ou le WebGUI. Si désactivé, les routes Tailscale seront désactivées lors du démarrage de Tailscale.", + "wireguard": "Change le port utilisé pour les connexions Wireguard. Cela n'a généralement pas besoin d'être changé.", + "erase": "Se déconnecte de Tailscale et réinitialise tous les paramètres par défaut", + "restart": "Redémarre le daemon Tailscale et réapplique les paramètres.", + "diagnostics": "Télécharger les diagnostics (cela peut prendre un certain temps)", + "save": "Tailscale sera redémarré lorsque les modifications seront appliquées", + "reauthenticate": "Forcer une ré-authentification Tailscale. Cela déconnectera Tailscale jusqu'à ce que l'authentification soit complétée.", + "funnel": "Permet d'activer Tailscale Funnel via le WebGUI. Si désactivé, Tailscale Funnel sera désactivé." + } + }, + "info": { + "online": "Connecté", + "version": "Version de Tailscale", + "health": "Santé de Tailscale", + "login": "Identifié", + "netmap": "Dans la carte du réseau", + "key_expire": "Expiration de clé", + "tags": "Tags", + "hostname": "Nom d'hôte", + "dns": "Nom DNS", + "ip": "Adresses IP de Tailscale", + "magicdns": "Suffixe MagicDNS", + "routes": "Routes annoncées", + "accept_routes": "Accepter les routes", + "accept_dns": "Accepter le DNS", + "run_ssh": "SSH Tailscale", + "advertise_exit_node": "Agir en tant que nœud de sortie", + "use_exit_node": "Utiliser un nœud de sortie", + "exit_node_local": "Autoriser l'accès LAN lors de l'utilisation d'un nœud de sortie", + "unapproved": "Nécessite une approbation dans la console d'administration", + "connected_via": "Connecté via Tailscale", + "funnel_port": "Port Funnel pour le WebGUI", + "port_in_use": "Port utilisé", + "lock": { + "node_key": "Clé du nœud", + "public_key": "Clé publique", + "signed": "Clé du nœud signée", + "signing": "Est un nœud de signature" + } + }, + "warnings": { + "key_expiration": "La clé Tailscale expirera dans %s jours sur %s.", + "netbios": "NetBIOS est activé dans les paramètres SMB - cela peut empêcher l'accès aux partages via Tailscale.", + "lock": "Le verrouillage du tailnet est activé, mais ce nœud n’a pas été signé. Il ne pourra pas communiquer avec le tailnet.", + "funnel": "L’activation de Tailscale Funnel pour le WebGUI Unraid expose votre serveur à Internet, augmentant considérablement le risque d’accès non autorisé et d’attaques potentielles. Cette fonctionnalité ne doit être activée que si vous comprenez parfaitement l’impact sur la sécurité. L’approche recommandée/sécurisée pour l’accès à distance consiste à installer Tailscale sur les appareils que vous prévoyez d’utiliser pour accéder au WebGUI, au lieu d’activer Funnel.", + "subnet": "L’acceptation des sous-réseaux Tailscale sur votre serveur Unraid peut perturber la connectivité du réseau local dans certaines configurations réseau. L’acceptation des routes n’est pas nécessaire pour que votre serveur Unraid fonctionne comme routeur de sous-réseaux ou nœud de sortie. Activez cette option uniquement si votre serveur Unraid doit se connecter à des sous-réseaux distants annoncés par d’autres appareils.", + "dns": "L’activation de MagicDNS de Tailscale sur votre serveur peut perturber la résolution de noms à l’intérieur des conteneurs Docker, ce qui peut entraîner des problèmes de connectivité pour les applications. Il n’est pas nécessaire d’activer MagicDNS sur le serveur pour accéder à votre serveur Unraid en utilisant les noms MagicDNS (par exemple, « unraid.tailnet.ts.net ») depuis d’autres appareils.", + "caution": "Procédez avec prudence et assurez-vous de bien comprendre les implications de ce changement avant de continuer.", + "more_info": "Pour plus d'informations :" + }, + "lock": { + "sign": "Nœuds de signature", + "signing_node": "Il s'agit d'un nœud de signature pour le tailnet.", + "signing_instructions": "Les nœuds suivants sont actuellement exclus du tailnet. Cochez la case correspondant aux nœuds que vous souhaitez ajouter, puis cliquez sur le bouton Signer pour ajouter le nœud au tailnet.", + "signed_node": "Le verrouillage de votre tailnet est activé et le nœud actuel est signé. Ce nœud peut communiquer avec le tailnet.", + "make_signing": "Si vous souhaitez en faire un nœud de signature, vous devez approuver la clé suivante depuis un nœud de signature :", + "unsigned": "Le verrouillage de votre tailnet est activé et le nœud actuel n’est pas signé. Ce nœud ne peut pas communiquer avec le tailnet.", + "unsigned_instructions": "Pour permettre à ce nœud de communiquer, vous devez approuver la clé suivante depuis un nœud de signature", + "disabled": "Le verrouillage n’est pas activé sur votre tailnet." + }, + "status_page": { + "exit_node": "Nœud de sortie", + "connection_type": "Type de connexion", + "connection_addr": "Connexion", + "tx_bytes": "Octets Tx", + "rx_bytes": "Octets Rx", + "relay": "Relais", + "direct": "Direct", + "exit_active": "Actif", + "exit_available": "Disponible", + "idle": "Inactif", + "offline": "Hors ligne", + "active": "Actif", + "login_name": "Nom de connexion", + "shared": "Utilisateur partagé", + "action": "Action" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json b/src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json new file mode 100644 index 0000000..01154a6 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json @@ -0,0 +1,132 @@ +{ + "yes": "Sì", + "no": "No", + "ignore": "Ignora", + "erase": "Elimina", + "confirm": "Conferma", + "download": "Scarica", + "restart": "Riavvia", + "apply": "Applica", + "back": "Indietro", + "status": "Stato", + "connection": "Connessione", + "unknown": "Sconosciuto", + "none": "Nessuno", + "disabled": "Disabilitato", + "enabled": "Abilitato", + "disable": "Disabilita", + "enable": "Abilita", + "cancel": "Annulla", + "accept": "Accetta", + "sign": "Firma", + "login": "Login", + "add": "Aggiungi", + "remove": "Rimuovi", + "tailnet": "Tailnet", + "configuration": "Configurazione", + "needs_login": "Questo dispositivo deve accedere a Tailscale. Aggiorna questa pagina dopo aver effettuato il login per visualizzare lo stato.", + "tailscale_disabled": "Tailscale è attualmente disabilitato. Può essere abilitato tramite la scheda Impostazioni.", + "tailscale_lock": "Blocco Tailscale", + "warning": "Avviso", + "settings": { + "basic": "Vista di base", + "advanced": "Vista avanzata", + "system_settings": "Impostazioni di sistema", + "enable_tailscale": "Abilita Tailscale", + "unraid_listen": "I servizi Unraid ascoltano sull'indirizzo IP di Tailscale", + "ip_forward": "Abilita inoltro IP", + "taildrop": "Percorso Taildrop", + "services": "Servizi Tailscale", + "wireguard": "Porta Wireguard", + "outbound_network": "Opzioni di rete avanzate", + "subnets": "Consenti subnet Tailscale", + "dns": "Consenti impostazioni DNS di Tailscale", + "save": "Salva impostazioni", + "restart": "Riavvia Tailscale", + "erase": "Elimina configurazione di Tailscale", + "diagnostics": "Diagnostica plugin", + "reauthenticate": "Reautentica", + "funnel": "Consenti Tailscale Funnel", + "context": { + "unraid_listen": "Configura i servizi di Unraid (SSH, WebGUI, SMB, ecc.) per ascoltare sugli indirizzi Tailscale.", + "ip_forward": "Imposta net.ipv4.ip_forward e net.ipv6.conf.all.forwarding su 1 in sysctl. Questa modifica viene applicata immediatamente quando attivata.", + "taildrop": "Specifica il percorso per i file Taildrop in arrivo.", + "dns": "Consente di abilitare il DNS di Tailscale tramite CLI o WebGUI. Se disabilitato, il DNS di Tailscale sarà disattivato quando Tailscale viene avviato.", + "subnets": "Consente di abilitare le rotte Tailscale tramite CLI o WebGUI. Se disabilitato, le rotte Tailscale saranno disattivate quando Tailscale viene avviato.", + "wireguard": "Cambia la porta utilizzata per le connessioni Wireguard. Di solito non è necessario cambiarla.", + "erase": "Effettua il logout da Tailscale e ripristina tutte le impostazioni ai valori predefiniti", + "restart": "Riavvia il demone Tailscale e riapplica le impostazioni.", + "diagnostics": "Scarica la diagnostica (questa operazione può richiedere del tempo per essere completata)", + "save": "Tailscale verrà riavviato quando le modifiche saranno applicate", + "reauthenticate": "Forza una nuova autenticazione con Tailscale. Questo disconnetterà Tailscale finché l'autenticazione non sarà completata.", + "funnel": "Consente di abilitare Tailscale Funnel tramite la WebGUI. Se disabilitato, Tailscale Funnel sarà disattivato." + } + }, + "info": { + "online": "Online", + "version": "Versione Tailscale", + "health": "Stato di Tailscale", + "login": "Connesso", + "netmap": "Nella mappa di rete", + "key_expire": "Scadenza chiave", + "tags": "Tag", + "hostname": "Nome host", + "dns": "Nome DNS", + "ip": "IP Tailscale", + "magicdns": "Suffisso MagicDNS", + "routes": "Rotte pubblicizzate", + "accept_routes": "Accetta rotte", + "accept_dns": "Accetta DNS", + "run_ssh": "Tailscale SSH", + "advertise_exit_node": "Esegui come Exit Node", + "use_exit_node": "Usa Exit Node", + "exit_node_local": "Consenti accesso LAN durante l\\'uso di Exit Node", + "unapproved": "Richiede approvazione nella console di amministrazione", + "connected_via": "Connesso tramite Tailscale", + "funnel_port": "Porta Funnel per WebGUI", + "port_in_use": "Porta in uso", + "lock": { + "node_key": "Chiave nodo", + "public_key": "Chiave pubblica", + "signed": "Chiave nodo firmata", + "signing": "È nodo firmatario" + } + }, + "warnings": { + "key_expiration": "La chiave Tailscale scadrà tra %s giorni il %s.", + "netbios": "NetBIOS è abilitato nelle impostazioni SMB - questo può impedire l'accesso alle condivisioni tramite Tailscale.", + "lock": "La tailnet ha il blocco attivato, ma questo nodo non è stato firmato. Non potrà comunicare con la tailnet.", + "funnel": "Abilitare Tailscale Funnel per la WebGUI di Unraid espone il server a Internet, aumentando significativamente il rischio di accessi non autorizzati e potenziali attacchi. Questa funzionalità dovrebbe essere attivata solo se si comprende appieno l'impatto sulla sicurezza. L'approccio consigliato/sicuro per l'accesso remoto è installare Tailscale sui dispositivi che si intende utilizzare per accedere alla WebGUI invece di abilitare Funnel.", + "subnet": "Accettare le subnet Tailscale sul tuo server Unraid può interrompere la connettività di rete locale in alcune configurazioni di rete. L'accettazione delle route non è necessaria affinché il tuo server Unraid funzioni come router di subnet o exit node. Abilita questa opzione solo se il tuo server Unraid deve connettersi a subnet remote pubblicizzate da altri dispositivi.", + "dns": "Abilitare MagicDNS di Tailscale sul tuo server può interrompere la risoluzione dei nomi all'interno dei container Docker, causando potenzialmente problemi di connettività per le applicazioni. Non è necessario abilitare MagicDNS sul server per accedere al tuo server Unraid utilizzando i nomi MagicDNS (ad esempio, \"unraid.tailnet.ts.net\") da altri dispositivi.", + "caution": "Procedi con cautela e assicurati di comprendere le implicazioni di questa modifica prima di continuare.", + "more_info": "Per ulteriori informazioni:" + }, + "lock": { + "sign": "Firma nodi", + "signing_node": "Questo è un nodo firmatario per la tailnet.", + "signing_instructions": "I seguenti nodi sono attualmente esclusi dalla tailnet. Seleziona quelli che vuoi aggiungere, poi clicca sul pulsante Firma, per aggiungerli alla tailnet.", + "signed_node": "La tua tailnet ha il blocco abilitato e il nodo attuale è firmato. Questo nodo può comunicare con la tailnet.", + "make_signing": "Se vuoi rendere questo nodo firmatario, dovrai fidarti della seguente chiave da un nodo firmatario:", + "unsigned": "La tua tailnet ha il blocco abilitato e il nodo attuale non è firmato. Questo nodo non può comunicare con la tailnet.", + "unsigned_instructions": "Per permettere la comunicazione di questo nodo, dovrai fidarti della seguente chiave da un nodo firmatario", + "disabled": "La tua tailnet non ha il blocco abilitato." + }, + "status_page": { + "exit_node": "Exit Node", + "connection_type": "Tipo di connessione", + "connection_addr": "Connessione", + "tx_bytes": "Byte trasmessi", + "rx_bytes": "Byte ricevuti", + "relay": "Relay", + "direct": "Diretto", + "exit_active": "Attivo", + "exit_available": "Disponibile", + "idle": "Inattivo", + "offline": "Offline", + "active": "Attivo", + "login_name": "Nome di accesso", + "shared": "Utente condiviso con", + "action": "Azione" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json b/src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json new file mode 100644 index 0000000..926dcaa --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json @@ -0,0 +1,132 @@ +{ + "yes": "はい", + "no": "いいえ", + "ignore": "無視", + "erase": "消去", + "confirm": "確認", + "download": "ダウンロード", + "restart": "再起動", + "apply": "適用", + "back": "戻る", + "status": "ステータス", + "connection": "接続", + "unknown": "不明", + "none": "なし", + "disabled": "無効", + "enabled": "有効", + "disable": "無効にする", + "enable": "有効にする", + "cancel": "キャンセル", + "accept": "承認", + "sign": "署名", + "login": "ログイン", + "add": "追加", + "remove": "削除", + "tailnet": "tailnet", + "configuration": "構成", + "needs_login": "このデバイスはTailscaleへのログインが必要です。ログイン完了後、このページをリロードしてステータスを確認してください。", + "tailscale_disabled": "Tailscaleは現在無効です。設定タブから有効化できます。", + "tailscale_lock": "Tailscaleロック", + "warning": "警告", + "settings": { + "basic": "基本表示", + "advanced": "詳細表示", + "system_settings": "システム設定", + "enable_tailscale": "Tailscaleを有効にする", + "unraid_listen": "UnraidサービスがTailscale IPでリッスンする", + "ip_forward": "IPフォワーディングを有効にする", + "taildrop": "Taildropの保存先", + "services": "Tailscaleサービス", + "wireguard": "Wireguardポート", + "outbound_network": "高度なネットワークオプション", + "subnets": "Tailscaleサブネット許可", + "dns": "Tailscale DNS設定を許可", + "save": "設定を保存", + "restart": "Tailscaleを再起動", + "erase": "Tailscale構成を消去", + "diagnostics": "プラグイン診断情報", + "reauthenticate": "再認証", + "funnel": "Tailscale Funnel を許可", + "context": { + "unraid_listen": "Unraidサービス(SSH、WebGUI、SMBなど)がTailscaleアドレスでリッスンするように設定します。", + "ip_forward": "net.ipv4.ip_forwardおよびnet.ipv6.conf.all.forwardingをsysctlで1に設定します。この変更は有効化時にすぐに適用されます。", + "taildrop": "Taildropから受信するファイルのパスを指定します。", + "dns": "CLIまたはWebGUIからTailscale DNSの有効化が可能です。無効の場合、Tailscale起動時にTailscale DNSが無効になります。", + "subnets": "CLIまたはWebGUIからTailscaleルートの有効化が可能です。無効の場合、Tailscale起動時にTailscaleルートが無効になります。", + "wireguard": "Wireguard接続で使用するポートを変更します。通常この設定を変更する必要はありません。", + "erase": "Tailscaleからログアウトし、すべての設定をデフォルトにリセットします", + "restart": "Tailscaleデーモンを再起動し、設定を再適用します。", + "diagnostics": "診断情報をダウンロードします(完了までに時間がかかる場合があります)", + "save": "設定を適用するとTailscaleが再起動します", + "reauthenticate": "Tailscaleの再認証を強制します。認証が完了するまでTailscaleは切断されます。", + "funnel": "WebGUI から Tailscale Funnel を有効化できます。無効にすると、Tailscale Funnel は無効になります。" + } + }, + "info": { + "online": "オンライン", + "version": "Tailscaleバージョン", + "health": "Tailscaleヘルス", + "login": "ログイン済み", + "netmap": "ネットワークマップ内", + "key_expire": "キー有効期限", + "tags": "タグ", + "hostname": "ホスト名", + "dns": "DNS名", + "ip": "Tailscale IPアドレス", + "magicdns": "MagicDNSサフィックス", + "routes": "広告されたルート", + "accept_routes": "ルートを受け入れる", + "accept_dns": "DNSを受け入れる", + "run_ssh": "Tailscale SSH", + "advertise_exit_node": "エグジットノードとして動作", + "use_exit_node": "エグジットノードを使用", + "exit_node_local": "エグジットノード使用中にLANアクセスを許可", + "unapproved": "管理コンソールでの承認が必要です", + "connected_via": "Tailscale経由で接続済み", + "funnel_port": "WebGUI 用 Funnel ポート", + "port_in_use": "使用中のポート", + "lock": { + "node_key": "ノードキー", + "public_key": "公開鍵", + "signed": "ノードキー署名済み", + "signing": "署名ノードである" + } + }, + "warnings": { + "key_expiration": "Tailscaleキーは%s日後(%s)に期限切れとなります。", + "netbios": "SMB設定でNetBIOSが有効になっています - これによりTailscale経由で共有へアクセスできなくなる場合があります。", + "lock": "tailnetでロックが有効ですが、このノードは署名されていません。tailnetと通信できません。", + "funnel": "Unraid WebGUI で Tailscale Funnel を有効にすると、サーバーがインターネットに公開され、認可されていないアクセスや攻撃のリスクが大幅に増加します。この機能は、セキュリティへの影響を十分に理解している場合のみ有効にしてください。リモートアクセスの推奨/安全な方法は、Funnel を有効にせず、WebGUI にアクセスする予定の端末すべてに Tailscale をインストールすることです。", + "subnet": "Unraid サーバーで Tailscale サブネットの許可を有効にすると、特定のネットワーク構成においてローカルネットワーク接続が障害される可能性があります。Unraid サーバーをサブネットルーターや出口ノードとして機能させるためにルートの許可は必要ありません。他の端末から通知されたリモートサブネットに Unraid サーバーが接続する必要がある場合のみ、このオプションを有効にしてください。", + "dns": "サーバー上で Tailscale MagicDNS を有効にすると、Docker コンテナ内で名前解決が妨げられ、アプリケーションの接続に問題が発生する可能性があります。他の端末から MagicDNS 名(例: \"unraid.tailnet.ts.net\")を使って Unraid サーバーへアクセスする場合、サーバー上で MagicDNS を有効にする必要はありません。", + "caution": "十分に注意して進め、この変更の影響を理解したうえで続行してください。", + "more_info": "詳細はこちら:" + }, + "lock": { + "sign": "ノードに署名", + "signing_node": "これはtailnetの署名ノードです。", + "signing_instructions": "以下のノードは現在tailnetからロックアウトされています。追加したいノードのチェックボックスを選択し、「署名」ボタンをクリックしてtailnetに追加してください。", + "signed_node": "お使いのtailnetでロックが有効になっており、現在のノードは署名済みです。このノードはtailnetと通信可能です。", + "make_signing": "このノードを署名ノードにする場合は、以下の署名ノードのキーを信頼する必要があります。", + "unsigned": "お使いのtailnetでロックが有効になっており、現在のノードは署名されていません。このノードはtailnetと通信できません。", + "unsigned_instructions": "このノードが通信できるようにするには、以下の署名ノードのキーを信頼する必要があります。", + "disabled": "お使いのtailnetではロックが有効になっていません。" + }, + "status_page": { + "exit_node": "エグジットノード", + "connection_type": "接続タイプ", + "connection_addr": "接続", + "tx_bytes": "送信バイト数", + "rx_bytes": "受信バイト数", + "relay": "リレー", + "direct": "ダイレクト", + "exit_active": "アクティブ", + "exit_available": "利用可能", + "idle": "アイドル", + "offline": "オフライン", + "active": "アクティブ", + "login_name": "ログイン名", + "shared": "共有ユーザー", + "action": "操作" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json b/src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json new file mode 100644 index 0000000..d873436 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json @@ -0,0 +1,132 @@ +{ + "yes": "예", + "no": "아니오", + "ignore": "무시", + "erase": "지우기", + "confirm": "확인", + "download": "다운로드", + "restart": "다시 시작", + "apply": "적용", + "back": "뒤로", + "status": "상태", + "connection": "연결", + "unknown": "알 수 없음", + "none": "없음", + "disabled": "비활성화됨", + "enabled": "활성화됨", + "disable": "비활성화", + "enable": "활성화", + "cancel": "취소", + "accept": "수락", + "sign": "서명", + "login": "로그인", + "add": "추가", + "remove": "제거", + "tailnet": "Tailnet", + "configuration": "구성", + "needs_login": "이 장치는 Tailscale에 로그인해야 합니다. 로그인 후 이 페이지를 새로고침하여 상태를 확인하세요.", + "tailscale_disabled": "Tailscale이(가) 현재 비활성화되어 있습니다. 설정 탭에서 활성화할 수 있습니다.", + "tailscale_lock": "Tailscale 잠금", + "warning": "경고", + "settings": { + "basic": "기본 보기", + "advanced": "고급 보기", + "system_settings": "시스템 설정", + "enable_tailscale": "Tailscale 활성화", + "unraid_listen": "Unraid 서비스가 Tailscale IP로 수신 대기", + "ip_forward": "IP 포워딩 활성화", + "taildrop": "Taildrop 위치", + "services": "Tailscale 서비스", + "wireguard": "Wireguard 포트", + "outbound_network": "고급 네트워크 옵션", + "subnets": "Tailscale 서브넷 허용", + "dns": "Tailscale DNS 설정 허용", + "save": "설정 저장", + "restart": "Tailscale 다시 시작", + "erase": "Tailscale 구성 삭제", + "diagnostics": "플러그인 진단", + "reauthenticate": "다시 인증", + "funnel": "Tailscale Funnel 허용", + "context": { + "unraid_listen": "Unraid 서비스(SSH, WebGUI, SMB 등)가 Tailscale 주소에서 수신 대기하도록 구성합니다.", + "ip_forward": "sysctl에서 net.ipv4.ip_forward 및 net.ipv6.conf.all.forwarding을 1로 설정합니다. 이 설정은 활성화 시 즉시 적용됩니다.", + "taildrop": "수신되는 Taildrop 파일의 경로를 지정하세요.", + "dns": "CLI 또는 WebGUI를 통해 Tailscale DNS를 활성화할 수 있습니다. 비활성화하면 Tailscale 시작 시 Tailscale DNS가 비활성화됩니다.", + "subnets": "CLI 또는 WebGUI를 통해 Tailscale 라우트를 활성화할 수 있습니다. 비활성화하면 Tailscale 시작 시 Tailscale 라우트가 비활성화됩니다.", + "wireguard": "Wireguard 연결에 사용할 포트를 변경합니다. 일반적으로 이 설정을 변경할 필요는 없습니다.", + "erase": "Tailscale에서 로그아웃하고 모든 설정을 기본값으로 초기화합니다.", + "restart": "Tailscale 데몬을 다시 시작하고 설정을 재적용합니다.", + "diagnostics": "진단을 다운로드합니다(완료까지 시간이 걸릴 수 있습니다).", + "save": "변경 사항이 적용되면 Tailscale이 다시 시작됩니다.", + "reauthenticate": "Tailscale 재인증을 강제합니다. 인증이 완료될 때까지 Tailscale 연결이 끊깁니다.", + "funnel": "WebGUI를 통해 Tailscale funnel을 활성화할 수 있습니다. 비활성화하면 Tailscale funnel이 비활성화됩니다." + } + }, + "info": { + "online": "온라인", + "version": "Tailscale 버전", + "health": "Tailscale 상태", + "login": "로그인됨", + "netmap": "네트워크 맵에 포함됨", + "key_expire": "키 만료", + "tags": "태그", + "hostname": "호스트 이름", + "dns": "DNS 이름", + "ip": "Tailscale IP", + "magicdns": "MagicDNS 접미사", + "routes": "광고된 라우트", + "accept_routes": "라우트 수락", + "accept_dns": "DNS 수락", + "run_ssh": "Tailscale SSH", + "advertise_exit_node": "Exit Node로 실행", + "use_exit_node": "Exit Node 사용", + "exit_node_local": "Exit Node 사용 중 LAN 접근 허용", + "unapproved": "관리 콘솔에서 승인이 필요합니다.", + "connected_via": "Tailscale을 통해 연결됨", + "funnel_port": "WebGUI용 Funnel 포트", + "port_in_use": "사용 중인 포트", + "lock": { + "node_key": "노드 키", + "public_key": "공개 키", + "signed": "노드 키 서명됨", + "signing": "서명 노드 여부" + } + }, + "warnings": { + "key_expiration": "Tailscale 키가 %s일 뒤 %s에 만료됩니다.", + "netbios": "SMB 설정에서 NetBIOS가 활성화되어 있습니다 - 이로 인해 공유를 Tailscale을 통해 액세스할 수 없을 수 있습니다.", + "lock": "Tailnet에 잠금이 활성화되었지만, 이 노드는 서명되지 않았습니다. Tailnet과 통신할 수 없습니다.", + "funnel": "Unraid WebGUI에 대해 Tailscale Funnel을 활성화하면 서버가 인터넷에 노출되어 무단 액세스 및 잠재적인 공격의 위험이 크게 증가합니다. 이 기능은 보안상의 영향을 완전히 이해하는 경우에만 활성화해야 합니다. 원격 액세스를 위한 권장/안전한 방법은 Funnel을 활성화하는 대신 WebGUI에 액세스할 장치에 Tailscale을 설치하는 것입니다.", + "subnet": "Unraid 서버에서 Tailscale 서브넷을 수락하면 일부 네트워크 구성에서 로컬 네트워크 연결이 방해받을 수 있습니다. Unraid 서버가 서브넷 라우터 또는 종료 노드로 동작하기 위해 라우트 수락은 필요하지 않습니다. Unraid 서버에서 다른 장치가 광고하는 원격 서브넷에 연결해야 할 때만 이 옵션을 활성화하세요.", + "dns": "서버에서 Tailscale MagicDNS를 활성화하면 Docker 컨테이너 내의 이름 해석이 방해받아 애플리케이션 연결 문제가 발생할 수 있습니다. MagicDNS는 다른 장치에서 'unraid.tailnet.ts.net'과 같은 MagicDNS 이름으로 Unraid 서버에 액세스하기 위해 서버에서 활성화할 필요가 없습니다.", + "caution": "신중하게 진행하시고 계속하기 전에 이 변경의 영향을 충분히 이해했는지 확인하세요.", + "more_info": "자세한 정보:" + }, + "lock": { + "sign": "노드 서명", + "signing_node": "이 노드는 Tailnet의 서명 노드입니다.", + "signing_instructions": "다음 노드들이 현재 Tailnet에서 잠겨져 있습니다. 추가할 노드를 선택한 후, 서명 버튼을 클릭하여 Tailnet에 노드를 추가하세요.", + "signed_node": "Tailnet에 잠금이 활성화되어 있고 현재 노드는 서명되었습니다. 이 노드는 Tailnet과 통신할 수 있습니다.", + "make_signing": "이 노드를 서명 노드로 만들려면 서명 노드에서 다음 키를 신뢰해야 합니다:", + "unsigned": "Tailnet에 잠금이 활성화되어 있고 현재 노드는 서명되지 않았습니다. 이 노드는 Tailnet과 통신할 수 없습니다.", + "unsigned_instructions": "이 노드가 통신할 수 있도록 하려면 서명 노드에서 다음 키를 신뢰해야 합니다.", + "disabled": "Tailnet에 잠금이 활성화되어 있지 않습니다." + }, + "status_page": { + "exit_node": "종료 노드", + "connection_type": "연결 유형", + "connection_addr": "연결", + "tx_bytes": "Tx 바이트", + "rx_bytes": "Rx 바이트", + "relay": "릴레이", + "direct": "직접 연결", + "exit_active": "활성화됨", + "exit_available": "사용 가능", + "idle": "유휴", + "offline": "오프라인", + "active": "활성화됨", + "login_name": "로그인 이름", + "shared": "공유된 사용자", + "action": "동작" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json b/src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json new file mode 100644 index 0000000..7ad16fa --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json @@ -0,0 +1,132 @@ +{ + "yes": "Ja", + "no": "Nee", + "ignore": "Negeren", + "erase": "Wissen", + "confirm": "Bevestigen", + "download": "Download", + "restart": "Herstart", + "apply": "Toepassen", + "back": "Terug", + "status": "Status", + "connection": "Verbinding", + "unknown": "Onbekend", + "none": "Geen", + "disabled": "Uitgeschakeld", + "enabled": "Ingeschakeld", + "disable": "Uitschakelen", + "enable": "Inschakelen", + "cancel": "Annuleren", + "accept": "Accepteren", + "sign": "Teken", + "login": "Login", + "add": "Toevoegen", + "remove": "Verwijderen", + "tailnet": "Tailnet", + "configuration": "Configuratie", + "needs_login": "Dit apparaat moet worden ingelogd op Tailscale. Vernieuw deze pagina na het inloggen om de status te bekijken.", + "tailscale_disabled": "Tailscale is momenteel uitgeschakeld. Deze kan worden ingeschakeld via het tabblad Instellingen.", + "tailscale_lock": "Tailscale vergrendeling", + "warning": "Waarschuwing", + "settings": { + "basic": "Eenvoudige weergave", + "advanced": "Geavanceerde weergave", + "system_settings": "Systeeminstellingen", + "enable_tailscale": "Tailscale inschakelen", + "unraid_listen": "Unraid diensten die luisteren op Tailscale IP", + "ip_forward": "IP Forwarding inschakelen", + "taildrop": "Taildrop locatie", + "services": "Tailscale Diensten", + "wireguard": "Wireguard poort", + "outbound_network": "Geavanceerde netwerkopties", + "subnets": "Sta Tailscale-subnetten toe", + "dns": "Sta Tailscale DNS-instellingen toe", + "save": "Instellingen Opslaan", + "restart": "Herstart Tailscale", + "erase": "Verwijder Tailscale Configuratie", + "diagnostics": "Plugin diagnostiek", + "reauthenticate": "Opnieuw verifiëren", + "funnel": "Sta Tailscale Funnel toe", + "context": { + "unraid_listen": "Configureert Unraid diensten (SSH, WebGUI, SMB, etc.) om te luisteren op Tailscale adressen.", + "ip_forward": "Zet net.ipv4.ip_forward en net.ipv6.conf.all.forwarding naar 1 in sysctl. Deze wijziging vindt onmiddellijk plaats wanneer deze wordt ingeschakeld.", + "taildrop": "Specificeer het pad voor inkomende Taildrop bestanden.", + "dns": "Staat toe dat Tailscale DNS via de CLI of WebGUI wordt ingeschakeld. Als dit is uitgeschakeld, wordt Tailscale DNS uitgeschakeld wanneer Tailscale wordt gestart.", + "subnets": "Staat toe dat Tailscale-routes via de CLI of WebGUI worden ingeschakeld. Indien uitgeschakeld, worden Tailscale-routes uitgeschakeld wanneer Tailscale wordt gestart.", + "wireguard": "Wijzig de poort die wordt gebruikt voor Wireguard connecties. Dit hoeft meestal niet gewijzigd worden.", + "erase": "Log uit op Tailscale en herstel alle instellingen naar startwaarden", + "restart": "Herstart de Tailscale daemon en pas de instellingen opnieuw toe.", + "diagnostics": "Download diagnostiek (dit kan enige tijd duren om te voltooien)", + "save": "Tailscale zal opnieuw worden opgestart wanneer wijzigingen worden toegepast", + "reauthenticate": "Forceer een Tailscale herauthenticatie. Tailscale wordt hierdoor losgekoppeld totdat de authenticatie voltooid is.", + "funnel": "Staat toe dat Tailscale Funnel via de WebGUI kan worden ingeschakeld. Indien uitgeschakeld, wordt Tailscale Funnel uitgeschakeld." + } + }, + "info": { + "online": "Online", + "version": "Tailscale Versie", + "health": "Tailscale Gezondheid", + "login": "Ingelogd", + "netmap": "In Netwerkkaart", + "key_expire": "Sleutel Geldigheid", + "tags": "Tags", + "hostname": "Hostnaam", + "dns": "DNS-naam", + "ip": "Tailscale IP's", + "magicdns": "MagicDNS-achtervoegsel", + "routes": "Geadverteerde routes", + "accept_routes": "Accepteer routes", + "accept_dns": "Accepteer DNS", + "run_ssh": "Tailscale SSH", + "advertise_exit_node": "Draaien als Exit Node", + "use_exit_node": "Gebruik Exit Node", + "exit_node_local": "Sta LAN-toegang toe bij gebruik van Exit Node", + "unapproved": "Behoeft goedkeuring in beheerconsole", + "connected_via": "Verbonden via Tailscale", + "funnel_port": "Funnel-poort voor WebGUI", + "port_in_use": "Poort in gebruik", + "lock": { + "node_key": "Node sleutel", + "public_key": "Publieke sleutel", + "signed": "Node-sleutel ondertekend", + "signing": "Is ondertekenende node" + } + }, + "warnings": { + "key_expiration": "De Tailscale key vervalt in %s dagen op %s.", + "netbios": "NetBIOS is ingeschakeld in SMB instellingen - dit kan voorkomen dat gedeelde mappen benaderd kunnen worden via Tailscale.", + "lock": "Het tailnet heeft lock ingeschakeld, maar deze node is niet ondertekend. Het zal niet met het tailnet kunnen communiceren.", + "funnel": "Het inschakelen van Tailscale Funnel voor de Unraid WebGUI stelt uw server bloot aan het internet, wat het risico op ongeautoriseerde toegang en mogelijke aanvallen aanzienlijk vergroot. Schakel deze functie alleen in als u volledig begrijpt wat de gevolgen voor de beveiliging zijn. De aanbevolen/veilige methode voor externe toegang is om Tailscale te installeren op de apparaten waarmee u de WebGUI wilt benaderen, in plaats van Funnel in te schakelen.", + "subnet": "Het accepteren van Tailscale-subnetten op uw Unraid-server kan de lokale netwerkconnectiviteit verstoren bij bepaalde netwerkconfiguraties. Het accepteren van routes is niet vereist voor uw Unraid-server om als subnetrouter of exit node te functioneren. Schakel deze optie alleen in als uw Unraid-server verbinding moet maken met externe subnetten die door andere apparaten worden geadverteerd.", + "dns": "Het inschakelen van Tailscale MagicDNS op uw server kan naamresolutie binnen Docker-containers verstoren, waardoor mogelijk verbindingsproblemen voor applicaties ontstaan. MagicDNS hoeft niet ingeschakeld te zijn op de server om uw Unraid-server te kunnen benaderen via MagicDNS-namen (bijv. 'unraid.tailnet.ts.net') vanaf andere apparaten.", + "caution": "Ga voorzichtig verder en zorg ervoor dat u de gevolgen van deze wijziging begrijpt voordat u doorgaat.", + "more_info": "Voor meer informatie:" + }, + "lock": { + "sign": "Nodes ondertekenen", + "signing_node": "Dit is een ondertekenende node voor het tailnet.", + "signing_instructions": "De volgende nodes zijn momenteel uitgesloten van het tailnet. Vink het vakje aan bij de nodes die u wilt toevoegen en klik dan op de knop Ondertekenen om de node toe te voegen aan het tailnet.", + "signed_node": "Uw tailnet heeft lock ingeschakeld en de huidige node is ondertekend. Deze node kan communiceren met het tailnet.", + "make_signing": "Als u hiervan een ondertekenende node wilt maken, moet u de volgende sleutel van een ondertekenende node vertrouwen:", + "unsigned": "Uw tailnet heeft lock ingeschakeld en de huidige node is niet ondertekend. Deze node kan niet communiceren met het tailnet.", + "unsigned_instructions": "Om deze node toe te staan te communiceren, moet u de volgende sleutel van een ondertekenende node vertrouwen", + "disabled": "Uw tailnet heeft lock niet ingeschakeld." + }, + "status_page": { + "exit_node": "Exit Node", + "connection_type": "Verbindingstype", + "connection_addr": "Verbinding", + "tx_bytes": "Tx-bytes", + "rx_bytes": "Rx-bytes", + "relay": "Relay", + "direct": "Direct", + "exit_active": "Actief", + "exit_available": "Beschikbaar", + "idle": "Inactief", + "offline": "Offline", + "active": "Actief", + "login_name": "Loginnaam", + "shared": "Shared-In gebruiker", + "action": "Actie" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json b/src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json new file mode 100644 index 0000000..f15a198 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json @@ -0,0 +1,132 @@ +{ + "yes": "Ja", + "no": "Nei", + "ignore": "Ignorer", + "erase": "Slett", + "confirm": "Bekreft", + "download": "Last ned", + "restart": "Start på nytt", + "apply": "Bruk", + "back": "Tilbake", + "status": "Status", + "connection": "Tilkobling", + "unknown": "Ukjent", + "none": "Ingen", + "disabled": "Deaktivert", + "enabled": "Aktivert", + "disable": "Deaktiver", + "enable": "Aktiver", + "cancel": "Avbryt", + "accept": "Godta", + "sign": "Signer", + "login": "Innlogging", + "add": "Legg til", + "remove": "Fjern", + "tailnet": "Tailnet", + "configuration": "Konfigurasjon", + "needs_login": "Denne enheten må logges inn på Tailscale. Oppdater denne siden etter innlogging for å se status.", + "tailscale_disabled": "Tailscale er for øyeblikket deaktivert. Det kan aktiveres via fanen Innstillinger.", + "tailscale_lock": "Tailscale-lås", + "warning": "Advarsel", + "settings": { + "basic": "Grunnleggende visning", + "advanced": "Avansert visning", + "system_settings": "Systeminnstillinger", + "enable_tailscale": "Aktiver Tailscale", + "unraid_listen": "Unraid-tjenester lytter på Tailscale-IP", + "ip_forward": "Aktiver IP-videresending", + "taildrop": "Taildrop-plassering", + "services": "Tailscale-tjenester", + "wireguard": "Wireguard-port", + "outbound_network": "Avanserte nettverksalternativer", + "subnets": "Tillat Tailscale-subnett", + "dns": "Tillat Tailscale DNS-innstillinger", + "save": "Lagre innstillinger", + "restart": "Start Tailscale på nytt", + "erase": "Slett Tailscale-konfigurasjon", + "diagnostics": "Plugin-diagnostikk", + "reauthenticate": "Reautentiser", + "funnel": "Tillat Tailscale Funnel", + "context": { + "unraid_listen": "Konfigurerer Unraid-tjenester (SSH, WebGUI, SMB, osv.) til å lytte på Tailscale-adresser.", + "ip_forward": "Setter net.ipv4.ip_forward og net.ipv6.conf.all.forwarding til 1 i sysctl. Denne endringen skjer umiddelbart når den aktiveres.", + "taildrop": "Angi stien for innkommende Taildrop-filer.", + "dns": "Tillater at Tailscale DNS kan aktiveres via CLI eller WebGUI. Hvis deaktivert, deaktiveres Tailscale DNS når Tailscale starter.", + "subnets": "Tillater at Tailscale-ruter kan aktiveres via CLI eller WebGUI. Hvis deaktivert, deaktiveres Tailscale-ruter når Tailscale starter.", + "wireguard": "Endre porten som brukes for Wireguard-tilkoblinger. Dette trenger vanligvis ikke endres.", + "erase": "Logger ut av Tailscale og tilbakestiller alle innstillinger til standard.", + "restart": "Starter Tailscale-daemonen på nytt og bruker innstillingene på nytt.", + "diagnostics": "Last ned diagnostikk (dette kan ta litt tid å fullføre)", + "save": "Tailscale vil bli startet på nytt når endringer brukes", + "reauthenticate": "Tving Tailscale-reauthentisering. Dette vil koble fra Tailscale til autentisering er fullført.", + "funnel": "Tillater at Tailscale Funnel kan aktiveres via WebGUI. Hvis deaktivert, vil Tailscale Funnel være deaktivert." + } + }, + "info": { + "online": "Online", + "version": "Tailscale-versjon", + "health": "Tailscale-helse", + "login": "Innlogget", + "netmap": "I nettverkskart", + "key_expire": "Nøkkelutløp", + "tags": "Tagger", + "hostname": "Vertsnavn", + "dns": "DNS-navn", + "ip": "Tailscale-IP-er", + "magicdns": "MagicDNS-suffiks", + "routes": "Annonserte ruter", + "accept_routes": "Godta ruter", + "accept_dns": "Godta DNS", + "run_ssh": "Tailscale SSH", + "advertise_exit_node": "Kjør som exit node", + "use_exit_node": "Bruk exit node", + "exit_node_local": "Tillat LAN-tilgang ved bruk av exit node", + "unapproved": "Trenger godkjenning i administrasjonskonsollen", + "connected_via": "Tilkoblet via Tailscale", + "funnel_port": "Funnel-port for WebGUI", + "port_in_use": "Port i bruk", + "lock": { + "node_key": "Node-nøkkel", + "public_key": "Offentlig nøkkel", + "signed": "Node-nøkkel signert", + "signing": "Er signernode" + } + }, + "warnings": { + "key_expiration": "Tailscale-nøkkelen utløper om %s dager, den %s.", + "netbios": "NetBIOS er aktivert i SMB-innstillinger - dette kan forhindre tilgang til delinger via Tailscale.", + "lock": "Tailnettet har lås aktivert, men denne noden er ikke signert. Den vil ikke kunne kommunisere med tailnettet.", + "funnel": "Hvis du aktiverer Tailscale Funnel for Unraid WebGUI, eksponeres serveren din mot internett, noe som øker risikoen for uautorisert tilgang og potensielle angrep betydelig. Denne funksjonen bør kun aktiveres hvis du fullt ut forstår sikkerhetskonsekvensene. Den anbefalte/sikre metoden for ekstern tilgang er å installere Tailscale på enhetene du bruker til å få tilgang til WebGUI i stedet for å aktivere Funnel.", + "subnet": "Å godta Tailscale-subnett på Unraid-serveren din kan forstyrre lokal nettverkstilkobling i visse nettverkskonfigurasjoner. Å godta ruter er ikke nødvendig for at Unraid-serveren din skal fungere som en subnettruter eller exit node. Aktiver kun dette alternativet hvis Unraid-serveren din må koble til eksterne subnett annonsert av andre enheter.", + "dns": "Å aktivere Tailscale MagicDNS på serveren din kan forstyrre navneoppløsning inne i Docker-containere, noe som potensielt kan føre til tilkoblingsproblemer for applikasjoner. MagicDNS trenger ikke å være aktivert på serveren for at du skal kunne få tilgang til Unraid-serveren ved å bruke MagicDNS-navn (for eksempel \"unraid.tailnet.ts.net\") fra andre enheter.", + "caution": "Fortsett med forsiktighet og sørg for at du forstår konsekvensene av denne endringen før du går videre.", + "more_info": "For mer informasjon:" + }, + "lock": { + "sign": "Signer noder", + "signing_node": "Dette er en signernode for tailnettet.", + "signing_instructions": "Følgende noder er for øyeblikket låst ute fra tailnettet. Huk av for de nodene du ønsker å legge til, og klikk deretter på Signer-knappen for å legge noden til tailnettet.", + "signed_node": "Tailnettet ditt har lås aktivert, og denne noden er signert. Denne noden kan kommunisere med tailnettet.", + "make_signing": "Hvis du ønsker å gjøre dette til en signernode, må du stole på følgende nøkkel fra en signernode:", + "unsigned": "Tailnettet ditt har lås aktivert, og denne noden er ikke signert. Denne noden kan ikke kommunisere med tailnettet.", + "unsigned_instructions": "For å tillate at denne noden kommuniserer, må du stole på følgende nøkkel fra en signernode", + "disabled": "Tailnettet ditt har ikke lås aktivert." + }, + "status_page": { + "exit_node": "Utgangspunkt", + "connection_type": "Tilkoblingstype", + "connection_addr": "Tilkobling", + "tx_bytes": "Tx-bytes", + "rx_bytes": "Rx-bytes", + "relay": "Relé", + "direct": "Direkte", + "exit_active": "Aktiv", + "exit_available": "Tilgjengelig", + "idle": "Tomgang", + "offline": "Frakoblet", + "active": "Aktiv", + "login_name": "Innloggingsnavn", + "shared": "Delt bruker", + "action": "Handling" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json b/src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json new file mode 100644 index 0000000..c7b8136 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json @@ -0,0 +1,132 @@ +{ + "yes": "Tak", + "no": "Nie", + "ignore": "Ignoruj", + "erase": "Usuń", + "confirm": "Potwierdź", + "download": "Pobierz", + "restart": "Uruchom ponownie", + "apply": "Zastosuj", + "back": "Wstecz", + "status": "Status", + "connection": "Połączenie", + "unknown": "Nieznane", + "none": "Brak", + "disabled": "Wyłączone", + "enabled": "Włączone", + "disable": "Wyłącz", + "enable": "Włącz", + "cancel": "Anuluj", + "accept": "Akceptuj", + "sign": "Podpisz", + "login": "Zaloguj", + "add": "Dodaj", + "remove": "Usuń", + "tailnet": "Tailnet", + "configuration": "Konfiguracja", + "needs_login": "To urządzenie musi być zalogowane do Tailscale. Odśwież tę stronę po zalogowaniu, aby zobaczyć status.", + "tailscale_disabled": "Tailscale jest obecnie wyłączony. Można go włączyć w zakładce Ustawienia.", + "tailscale_lock": "Blokada Tailscale", + "warning": "Ostrzeżenie", + "settings": { + "basic": "Widok podstawowy", + "advanced": "Widok zaawansowany", + "system_settings": "Ustawienia systemowe", + "enable_tailscale": "Włącz Tailscale", + "unraid_listen": "Usługi Unraid nasłuchują na adresie IP Tailscale", + "ip_forward": "Włącz przekazywanie IP", + "taildrop": "Lokalizacja Taildrop", + "services": "Usługi Tailscale", + "wireguard": "Port Wireguard", + "outbound_network": "Zaawansowane opcje sieciowe", + "subnets": "Zezwól na podsieci Tailscale", + "dns": "Zezwól na ustawienia DNS Tailscale", + "save": "Zapisz ustawienia", + "restart": "Restartuj Tailscale", + "erase": "Usuń konfigurację Tailscale", + "diagnostics": "Diagnostyka wtyczki", + "reauthenticate": "Ponowna autoryzacja", + "funnel": "Zezwól na Tailscale Funnel", + "context": { + "unraid_listen": "Konfiguruje usługi Unraid (SSH, WebGUI, SMB itd.), aby nasłuchiwały na adresach Tailscale.", + "ip_forward": "Ustawia net.ipv4.ip_forward oraz net.ipv6.conf.all.forwarding na 1 w sysctl. Ta zmiana następuje natychmiast po włączeniu.", + "taildrop": "Określ ścieżkę dla przychodzących plików Taildrop.", + "dns": "Pozwala na włączenie DNS Tailscale przez CLI lub WebGUI. Po wyłączeniu, DNS Tailscale będzie wyłączony po uruchomieniu Tailscale.", + "subnets": "Pozwala na włączenie tras Tailscale przez CLI lub WebGUI. Po wyłączeniu, trasy Tailscale będą wyłączone po uruchomieniu Tailscale.", + "wireguard": "Zmień port używany do połączeń Wireguard. Zazwyczaj nie ma potrzeby go zmieniać.", + "erase": "Wylogowuje z Tailscale i przywraca wszystkie ustawienia do domyślnych", + "restart": "Restartuje demona Tailscale i ponownie stosuje ustawienia.", + "diagnostics": "Pobierz diagnostykę (to może chwilę potrwać)", + "save": "Tailscale zostanie zrestartowany po zastosowaniu zmian", + "reauthenticate": "Wymusza ponowną autoryzację Tailscale. Spowoduje to rozłączenie Tailscale do czasu zakończenia uwierzytelniania.", + "funnel": "Pozwala na włączenie Tailscale Funnel przez WebGUI. Po wyłączeniu, Tailscale Funnel zostanie wyłączony." + } + }, + "info": { + "online": "Online", + "version": "Wersja Tailscale", + "health": "Stan Tailscale", + "login": "Zalogowano", + "netmap": "W mapie sieci", + "key_expire": "Wygaśnięcie klucza", + "tags": "Tagi", + "hostname": "Nazwa hosta", + "dns": "Nazwa DNS", + "ip": "Adresy IP Tailscale", + "magicdns": "Sufiks MagicDNS", + "routes": "Reklamowane trasy", + "accept_routes": "Akceptuj trasy", + "accept_dns": "Akceptuj DNS", + "run_ssh": "Tailscale SSH", + "advertise_exit_node": "Działaj jako węzeł wyjściowy", + "use_exit_node": "Użyj węzła wyjściowego", + "exit_node_local": "Zezwól na dostęp do LAN podczas korzystania z węzła wyjściowego", + "unapproved": "Wymaga zatwierdzenia w konsoli administracyjnej", + "connected_via": "Połączono przez Tailscale", + "funnel_port": "Port Tailscale Funnel dla WebGUI", + "port_in_use": "Port w użyciu", + "lock": { + "node_key": "Klucz węzła", + "public_key": "Klucz publiczny", + "signed": "Klucz węzła podpisany", + "signing": "Czy jest to węzeł podpisujący" + } + }, + "warnings": { + "key_expiration": "Klucz Tailscale wygaśnie za %s dni, dnia %s.", + "netbios": "NetBIOS jest włączony w ustawieniach SMB - może to uniemożliwić dostęp do udziałów przez Tailscale.", + "lock": "Sieć tailnet ma włączoną blokadę, ale ten węzeł nie został podpisany. Nie będzie mógł komunikować się z tailnetem.", + "funnel": "Włączenie Tailscale Funnel dla WebGUI Unraid wystawia Twój serwer na internet, znacząco zwiększając ryzyko nieautoryzowanego dostępu oraz potencjalnych ataków. Funkcję tę należy włączać jedynie, jeśli w pełni rozumiesz jej wpływ na bezpieczeństwo. Zalecaną i bezpieczniejszą metodą zdalnego dostępu jest instalacja Tailscale na urządzeniach, z których planujesz korzystać z WebGUI, zamiast włączać Tailscale Funnel.", + "subnet": "Akceptowanie podsieci Tailscale na Twoim serwerze Unraid może zakłócić łączność w sieci lokalnej w niektórych konfiguracjach. Akceptacja tras nie jest wymagana, aby Unraid działał jako router podsieci lub węzeł wyjściowy. Włącz tę opcję tylko wtedy, gdy Twój serwer Unraid potrzebuje połączyć się z podsieciami reklamowanymi przez inne urządzenia.", + "dns": "Włączenie MagicDNS Tailscale na serwerze może zakłócić rozwiązywanie nazw w kontenerach Docker, potencjalnie powodując problemy z łącznością aplikacji. Nie musisz włączać MagicDNS na serwerze, aby uzyskiwać dostęp do Unraid przy użyciu nazw MagicDNS (np. \"unraid.tailnet.ts.net\") z innych urządzeń.", + "caution": "Zachowaj ostrożność i upewnij się, że rozumiesz konsekwencje tej zmiany przed kontynuacją.", + "more_info": "Więcej informacji:" + }, + "lock": { + "sign": "Podpisz węzły", + "signing_node": "To jest węzeł podpisujący dla tailnet.", + "signing_instructions": "Następujące węzły są aktualnie zablokowane w tailnet. Zaznacz dowolne węzły, które chcesz dodać, a następnie kliknij przycisk Podpisz, aby dodać węzeł do tailnet.", + "signed_node": "Twój tailnet ma włączoną blokadę i bieżący węzeł jest podpisany. Ten węzeł może komunikować się z tailnet.", + "make_signing": "Jeśli chcesz uczynić ten węzeł podpisującym, musisz zaufać następującemu kluczowi z węzła podpisującego:", + "unsigned": "Twój tailnet ma włączoną blokadę i bieżący węzeł nie jest podpisany. Ten węzeł nie może komunikować się z tailnet.", + "unsigned_instructions": "Aby ten węzeł mógł się komunikować, musisz zaufać następującemu kluczowi z węzła podpisującego", + "disabled": "Twój tailnet nie ma włączonej blokady." + }, + "status_page": { + "exit_node": "Węzeł wyjściowy", + "connection_type": "Typ połączenia", + "connection_addr": "Połączenie", + "tx_bytes": "Bajty Tx", + "rx_bytes": "Bajty Rx", + "relay": "Przekaźnik", + "direct": "Bezpośrednio", + "exit_active": "Aktywny", + "exit_available": "Dostępny", + "idle": "Bezczynny", + "offline": "Offline", + "active": "Aktywny", + "login_name": "Nazwa logowania", + "shared": "Użytkownik z udostępnionym dostępem", + "action": "Akcja" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json b/src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json new file mode 100644 index 0000000..8fd7676 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json @@ -0,0 +1,132 @@ +{ + "yes": "Sim", + "no": "Não", + "ignore": "Ignorar", + "erase": "Apagar", + "confirm": "Confirmar", + "download": "Baixar", + "restart": "Reiniciar", + "apply": "Aplicar", + "back": "Voltar", + "status": "Status", + "connection": "Conexão", + "unknown": "Desconhecido", + "none": "Nenhum", + "disabled": "Desativado", + "enabled": "Ativado", + "disable": "Desativar", + "enable": "Ativar", + "cancel": "Cancelar", + "accept": "Aceitar", + "sign": "Assinar", + "login": "Login", + "add": "Adicionar", + "remove": "Remover", + "tailnet": "Tailnet", + "configuration": "Configuração", + "needs_login": "Este dispositivo precisa estar logado no Tailscale. Recarregue esta página após o login para ver o status.", + "tailscale_disabled": "Tailscale está atualmente desativado. Pode ser ativado através da aba Configurações.", + "tailscale_lock": "Bloqueio do Tailscale", + "warning": "Aviso", + "settings": { + "basic": "Visualização Básica", + "advanced": "Visualização Avançada", + "system_settings": "Configurações do Sistema", + "enable_tailscale": "Ativar Tailscale", + "unraid_listen": "Serviços Unraid escutam no IP do Tailscale", + "ip_forward": "Ativar Encaminhamento de IP", + "taildrop": "Localização do Taildrop", + "services": "Serviços do Tailscale", + "wireguard": "Porta do Wireguard", + "outbound_network": "Opções Avançadas de Rede", + "subnets": "Permitir Sub-redes do Tailscale", + "dns": "Permitir configurações de DNS do Tailscale", + "save": "Salvar Configurações", + "restart": "Reiniciar Tailscale", + "erase": "Apagar Configuração do Tailscale", + "diagnostics": "Diagnóstico do Plugin", + "reauthenticate": "Reautenticar", + "funnel": "Permitir Funnel do Tailscale", + "context": { + "unraid_listen": "Configura os serviços Unraid (SSH, WebGUI, SMB, etc.) para escutarem nos endereços do Tailscale.", + "ip_forward": "Define net.ipv4.ip_forward e net.ipv6.conf.all.forwarding como 1 no sysctl. Esta alteração ocorre imediatamente ao ser ativada.", + "taildrop": "Especifique o caminho para os arquivos recebidos do Taildrop.", + "dns": "Permite ativar o DNS do Tailscale via CLI ou WebGUI. Se desativado, o DNS do Tailscale será desativado ao iniciar o Tailscale.", + "subnets": "Permite ativar rotas do Tailscale via CLI ou WebGUI. Se desativado, as rotas do Tailscale serão desativadas ao iniciar o Tailscale.", + "wireguard": "Altera a porta usada para conexões Wireguard. Normalmente não é necessário alterar.", + "erase": "Faz logout do Tailscale e redefine todas as configurações para o padrão", + "restart": "Reinicia o daemon do Tailscale e reaplica as configurações.", + "diagnostics": "Baixar diagnóstico (isso pode levar algum tempo para completar)", + "save": "O Tailscale será reiniciado ao aplicar as alterações", + "reauthenticate": "Força uma reautenticação do Tailscale. Isso desconectará o Tailscale até que a autenticação seja concluída.", + "funnel": "Permite que o Funnel do Tailscale seja ativado pela WebGUI. Se desativado, o Funnel do Tailscale será desativado." + } + }, + "info": { + "online": "Online", + "version": "Versão do Tailscale", + "health": "Estado do Tailscale", + "login": "Logado", + "netmap": "No Mapa de Rede", + "key_expire": "Expiração da Chave", + "tags": "Tags", + "hostname": "Nome do Host", + "dns": "Nome DNS", + "ip": "IPs do Tailscale", + "magicdns": "Sufixo MagicDNS", + "routes": "Rotas Anunciadas", + "accept_routes": "Aceitar Rotas", + "accept_dns": "Aceitar DNS", + "run_ssh": "Tailscale SSH", + "advertise_exit_node": "Atuar como Nó de Saída", + "use_exit_node": "Usar Nó de Saída", + "exit_node_local": "Permitir acesso à LAN ao usar Nó de Saída", + "unapproved": "Precisa de aprovação no console de administração", + "connected_via": "Conectado via Tailscale", + "funnel_port": "Porta do Funnel para WebGUI", + "port_in_use": "Porta em uso", + "lock": { + "node_key": "Chave do Nó", + "public_key": "Chave Pública", + "signed": "Chave do Nó Assinada", + "signing": "É Nó de Assinatura" + } + }, + "warnings": { + "key_expiration": "A chave do Tailscale irá expirar em %s dias em %s.", + "netbios": "O NetBIOS está ativado em Configurações SMB - isto pode impedir o acesso a compartilhamentos via Tailscale.", + "lock": "O tailnet possui bloqueio ativado, mas este nó não foi assinado. Ele não poderá se comunicar com o tailnet.", + "funnel": "Ativar o Funnel do Tailscale na WebGUI do Unraid expõe seu servidor à internet, aumentando significativamente o risco de acesso não autorizado e possíveis ataques. Esse recurso só deve ser ativado se você compreender totalmente o impacto na segurança. A abordagem recomendada/segura para acesso remoto é instalar o Tailscale nos dispositivos que você pretende usar para acessar a WebGUI, em vez de ativar o Funnel.", + "subnet": "Aceitar sub-redes do Tailscale no seu servidor Unraid pode afetar a conectividade da rede local em determinadas configurações. Aceitar rotas não é necessário para que seu servidor Unraid funcione como um roteador de sub-rede ou nó de saída. Só ative esta opção se seu servidor Unraid precisar se conectar a sub-redes remotas anunciadas por outros dispositivos.", + "dns": "Ativar o MagicDNS do Tailscale no seu servidor pode prejudicar a resolução de nomes dentro dos containers Docker, causando possíveis problemas de conectividade para aplicativos. Não é necessário ativar o MagicDNS no servidor para acessar o seu Unraid usando nomes MagicDNS (por exemplo, 'unraid.tailnet.ts.net') a partir de outros dispositivos.", + "caution": "Proceda com cautela e certifique-se de que você entende as implicações desta alteração antes de continuar.", + "more_info": "Para mais informações:" + }, + "lock": { + "sign": "Assinar Nós", + "signing_node": "Este é um nó de assinatura para o tailnet.", + "signing_instructions": "Os seguintes nós estão atualmente bloqueados fora do tailnet. Marque a caixa dos nós que você deseja adicionar e clique no botão Assinar para adicionar o nó ao tailnet.", + "signed_node": "Seu tailnet possui bloqueio ativado e o nó atual está assinado. Este nó pode se comunicar com o tailnet.", + "make_signing": "Se desejar tornar este um nó de assinatura, será necessário confiar na chave abaixo de um nó de assinatura:", + "unsigned": "Seu tailnet possui bloqueio ativado e o nó atual não está assinado. Este nó não pode se comunicar com o tailnet.", + "unsigned_instructions": "Para permitir que este nó se comunique, você precisará confiar na chave abaixo de um nó de assinatura", + "disabled": "Seu tailnet não possui bloqueio ativado." + }, + "status_page": { + "exit_node": "Nó de Saída", + "connection_type": "Tipo de Conexão", + "connection_addr": "Conexão", + "tx_bytes": "Bytes de Envio (Tx)", + "rx_bytes": "Bytes de Recepção (Rx)", + "relay": "Relé", + "direct": "Direto", + "exit_active": "Ativo", + "exit_available": "Disponível", + "idle": "Ocioso", + "offline": "Offline", + "active": "Ativo", + "login_name": "Nome de Login", + "shared": "Usuário Compartilhado", + "action": "Ação" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json b/src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json new file mode 100644 index 0000000..6ebc450 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json @@ -0,0 +1,132 @@ +{ + "yes": "Sim", + "no": "Não", + "ignore": "Ignorar", + "erase": "Apagar", + "confirm": "Confirmar", + "download": "Baixar", + "restart": "Reiniciar", + "apply": "Aplicar", + "back": "Voltar", + "status": "Status", + "connection": "Conexão", + "unknown": "Desconhecido", + "none": "Nenhum", + "disabled": "Desativado", + "enabled": "Ativado", + "disable": "Desativar", + "enable": "Ativar", + "cancel": "Cancelar", + "accept": "Aceitar", + "sign": "Assinar", + "login": "Login", + "add": "Adicionar", + "remove": "Remover", + "tailnet": "Tailnet", + "configuration": "Configuração", + "needs_login": "Este dispositivo precisa estar logado no Tailscale. Atualize esta página após o login para ver o status.", + "tailscale_disabled": "Tailscale está atualmente desativado. Ele pode ser ativado pela guia Configurações.", + "tailscale_lock": "Bloqueio do Tailscale", + "warning": "Aviso", + "settings": { + "basic": "Visualização Básica", + "advanced": "Visualização Avançada", + "system_settings": "Configurações do Sistema", + "enable_tailscale": "Ativar Tailscale", + "unraid_listen": "Serviços Unraid ouvem no IP do Tailscale", + "ip_forward": "Ativar Reencaminhamento de IP", + "taildrop": "Localização do Taildrop", + "services": "Serviços do Tailscale", + "wireguard": "Porta Wireguard", + "outbound_network": "Opções avançadas de rede", + "subnets": "Permitir Sub-redes do Tailscale", + "dns": "Permitir configurações de DNS do Tailscale", + "save": "Salvar Configurações", + "restart": "Reiniciar Tailscale", + "erase": "Apagar Configuração do Tailscale", + "diagnostics": "Diagnóstico do Plugin", + "reauthenticate": "Reautenticar", + "funnel": "Permitir Funnel do Tailscale", + "context": { + "unraid_listen": "Configura serviços do Unraid (SSH, WebGUI, SMB, etc.) para ouvir nos endereços do Tailscale.", + "ip_forward": "Define net.ipv4.ip_forward e net.ipv6.conf.all.forwarding como 1 no sysctl. Essa alteração ocorre imediatamente ao ser ativada.", + "taildrop": "Especifique o caminho para arquivos recebidos do Taildrop.", + "dns": "Permite que o DNS do Tailscale seja ativado via CLI ou WebGUI. Se desativado, o DNS do Tailscale estará desativado quando o Tailscale iniciar.", + "subnets": "Permite que rotas do Tailscale sejam ativadas via CLI ou WebGUI. Se desativado, rotas do Tailscale serão desativadas quando iniciar.", + "wireguard": "Altere a porta usada para conexões Wireguard. Normalmente isso não precisa ser alterado.", + "erase": "Faz logout do Tailscale e restaura todas as configurações para os padrões", + "restart": "Reinicia o daemon do Tailscale e reaplica as configurações.", + "diagnostics": "Baixar diagnósticos (isso pode demorar um pouco para concluir)", + "save": "O Tailscale será reiniciado quando as alterações forem aplicadas", + "reauthenticate": "Força uma reautenticação do Tailscale. Isso desconectará o Tailscale até que a autenticação seja concluída.", + "funnel": "Permite que o Funnel do Tailscale seja ativado através da WebGUI. Se desativado, o Funnel do Tailscale será desativado." + } + }, + "info": { + "online": "Online", + "version": "Versão do Tailscale", + "health": "Status do Tailscale", + "login": "Logado", + "netmap": "No Mapa de Rede", + "key_expire": "Expiração da Chave", + "tags": "Tags", + "hostname": "Nome do Host", + "dns": "Nome DNS", + "ip": "IPs do Tailscale", + "magicdns": "Sufixo MagicDNS", + "routes": "Rotas Anunciadas", + "accept_routes": "Aceitar Rotas", + "accept_dns": "Aceitar DNS", + "run_ssh": "Tailscale SSH", + "advertise_exit_node": "Anunciar como Exit Node", + "use_exit_node": "Usar Exit Node", + "exit_node_local": "Permitir acesso LAN ao usar Exit Node", + "unapproved": "Necessita aprovação no painel de administração", + "connected_via": "Conectado via Tailscale", + "funnel_port": "Porta do Funnel para WebGUI", + "port_in_use": "Porta em Uso", + "lock": { + "node_key": "Chave do Nó", + "public_key": "Chave Pública", + "signed": "Chave do Nó Assinada", + "signing": "É Nó de Assinatura" + } + }, + "warnings": { + "key_expiration": "A chave do Tailscale irá expirar em %s dias, em %s.", + "netbios": "O NetBIOS está ativado em configurações SMB - isso pode impedir acesso às compartilhamentos através do Tailscale.", + "lock": "O tailnet está com bloqueio ativado, mas este nó não foi assinado. Ele não poderá se comunicar com o tailnet.", + "funnel": "Ativar o Funnel do Tailscale para o WebGUI do Unraid expõe seu servidor à Internet, aumentando significativamente o risco de acesso não autorizado e potenciais ataques. Este recurso só deve ser ativado se você compreender totalmente o impacto na segurança. A abordagem recomendada/segura para acesso remoto é instalar o Tailscale nos dispositivos que você pretende usar para acessar o WebGUI, em vez de ativar o Funnel.", + "subnet": "Aceitar sub-redes do Tailscale no seu servidor Unraid pode interromper a conectividade da rede local em certas configurações de rede. Aceitar rotas não é necessário para que seu Unraid funcione como roteador de sub-rede ou nó de saída. Ative esta opção apenas se seu Unraid precisar se conectar a sub-redes remotas anunciadas por outros dispositivos.", + "dns": "Ativar o MagicDNS do Tailscale no seu servidor pode causar interrupções na resolução de nomes dentro de containers Docker, causando possivelmente problemas de conectividade para aplicações. O MagicDNS não precisa estar ativado no servidor para que você acesse seu Unraid usando nomes MagicDNS (exemplo: 'unraid.tailnet.ts.net') de outros dispositivos.", + "caution": "Prossiga com cautela e certifique-se de entender as implicações desta alteração antes de continuar.", + "more_info": "Para mais informações:" + }, + "lock": { + "sign": "Assinar Nós", + "signing_node": "Este é um nó de assinatura para a tailnet.", + "signing_instructions": "Os seguintes nós estão atualmente bloqueados fora do tailnet. Marque a caixa dos nós que deseja adicionar e clique no botão Assinar para adicioná-lo à tailnet.", + "signed_node": "Seu tailnet está com bloqueio ativado e o nó atual está assinado. Este nó pode se comunicar com o tailnet.", + "make_signing": "Se deseja tornar este um nó de assinatura, você deverá confiar na seguinte chave a partir de um nó de assinatura:", + "unsigned": "Seu tailnet está com bloqueio ativado e o nó atual não está assinado. Este nó não pode se comunicar com o tailnet.", + "unsigned_instructions": "Para permitir que este nó se comunique, você deverá confiar na seguinte chave a partir de um nó de assinatura", + "disabled": "Seu tailnet não possui bloqueio ativado." + }, + "status_page": { + "exit_node": "Exit Node", + "connection_type": "Tipo de Conexão", + "connection_addr": "Conexão", + "tx_bytes": "Bytes Tx", + "rx_bytes": "Bytes Rx", + "relay": "Relé", + "direct": "Direto", + "exit_active": "Ativo", + "exit_available": "Disponível", + "idle": "Inativo", + "offline": "Offline", + "active": "Ativo", + "login_name": "Nome de Login", + "shared": "Usuário Compartilhado", + "action": "Ação" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json b/src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json new file mode 100644 index 0000000..a202ff5 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json @@ -0,0 +1,132 @@ +{ + "yes": "Ja", + "no": "Nej", + "ignore": "Ignorera", + "erase": "Radera", + "confirm": "Bekräfta", + "download": "Ladda ner", + "restart": "Starta om", + "apply": "Tillämpa", + "back": "Tillbaka", + "status": "Status", + "connection": "Anslutning", + "unknown": "Okänd", + "none": "Ingen", + "disabled": "Inaktiverad", + "enabled": "Aktiverad", + "disable": "Inaktivera", + "enable": "Aktivera", + "cancel": "Avbryt", + "accept": "Acceptera", + "sign": "Signera", + "login": "Inloggning", + "add": "Lägg till", + "remove": "Ta bort", + "tailnet": "Tailnet", + "configuration": "Konfiguration", + "needs_login": "Den här enheten måste vara inloggad i Tailscale. Uppdatera denna sidan efter inloggning för att visa status.", + "tailscale_disabled": "Tailscale är för närvarande inaktiverat. Det kan aktiveras via fliken Inställningar.", + "tailscale_lock": "Tailscale-lås", + "warning": "Varning", + "settings": { + "basic": "Grundvy", + "advanced": "Avancerad vy", + "system_settings": "Systeminställningar", + "enable_tailscale": "Aktivera Tailscale", + "unraid_listen": "Unraid-tjänster lyssnar på Tailscale IP", + "ip_forward": "Aktivera IP-vidarebefordran", + "taildrop": "Taildrop-plats", + "services": "Tailscale-tjänster", + "wireguard": "Wireguard-port", + "outbound_network": "Avancerade nätverksalternativ", + "subnets": "Tillåt Tailscale-subnät", + "dns": "Tillåt Tailscale DNS-inställningar", + "save": "Spara inställningar", + "restart": "Starta om Tailscale", + "erase": "Radera Tailscale-konfiguration", + "diagnostics": "Plugin-diagnostik", + "reauthenticate": "Tvinga Tailscale att autentisera om.", + "funnel": "Tillåt Tailscale Funnel", + "context": { + "unraid_listen": "Konfigurerar Unraid-tjänster (SSH, WebGUI, SMB, etc.) att lyssna på Tailscale-adresser.", + "ip_forward": "Ställer in net.ipv4.ip_forward och net.ipv6.conf.all.forwarding till 1 i sysctl. Denna ändring sker omedelbart när den aktiveras.", + "taildrop": "Ange sökvägen för inkommande Taildrop-filer.", + "dns": "Tillåter att Tailscale DNS kan aktiveras via CLI eller WebGUI. Om det är inaktiverat kommer Tailscale DNS att inaktiveras när Tailscale startar.", + "subnets": "Tillåter att Tailscale-rutter kan aktiveras via CLI eller WebGUI. Om det är inaktiverat kommer Tailscale-rutter att inaktiveras när Tailscale startar.", + "wireguard": "Ändra porten som används för Wireguard-anslutningar. Detta behöver vanligtvis inte ändras.", + "erase": "Loggar ut från Tailscale och återställer alla inställningar till standardvärdena", + "restart": "Starta om Tailscale-daemonen och tillämpa inställningar på nytt.", + "diagnostics": "Ladda ner diagnostik (detta kan ta en stund att slutföra)", + "save": "Tailscale kommer att startas om när ändringar tillämpas", + "reauthenticate": "Tvinga en Tailscale-autentisering. Detta kommer att koppla bort Tailscale tills autentiseringen har slutförts.", + "funnel": "Tillåter att Tailscale Funnel kan aktiveras via WebGUI. Om det är inaktiverat kommer Tailscale Funnel att inaktiveras." + } + }, + "info": { + "online": "Online", + "version": "Tailscale-version", + "health": "Tailscale-status", + "login": "Inloggad", + "netmap": "I nätverkskartan", + "key_expire": "Nyckelutgång", + "tags": "Taggar", + "hostname": "Värdnamn", + "dns": "DNS-namn", + "ip": "Tailscale-IP:er", + "magicdns": "MagicDNS-suffix", + "routes": "Annonserade rutter", + "accept_routes": "Acceptera rutter", + "accept_dns": "Acceptera DNS", + "run_ssh": "Tailscale SSH", + "advertise_exit_node": "Kör som exit-nod", + "use_exit_node": "Använd exit-nod", + "exit_node_local": "Tillåt LAN-åtkomst när exit-nod används", + "unapproved": "Behöver godkännande i admin-konsolen", + "connected_via": "Ansluten via Tailscale", + "funnel_port": "Funnel-port för WebGUI", + "port_in_use": "Port används", + "lock": { + "node_key": "Nodnyckel", + "public_key": "Publik nyckel", + "signed": "Nodnyckel signerad", + "signing": "Är signeringsnod" + } + }, + "warnings": { + "key_expiration": "Tailscale-nyckeln kommer att utgå om %s dagar den %s.", + "netbios": "NetBIOS är aktiverat i SMB-inställningar - detta kan förhindra åtkomst till delade mappar via Tailscale.", + "lock": "Tailnet har lås aktiverat, men denna nod är inte signerad. Den kommer inte kunna kommunicera med tailnet.", + "funnel": "Att aktivera Tailscale Funnel för Unraid WebGUI exponerar din server mot internet och ökar avsevärt risken för obehörig åtkomst och potentiella attacker. Denna funktion bör endast aktiveras om du fullt ut förstår säkerhetsriskerna. Det rekommenderade/säkra sättet för fjärråtkomst är att installera Tailscale på de enheter du planerar att använda för att komma åt WebGUI, istället för att aktivera Funnel.", + "subnet": "Att acceptera Tailscale-subnät på din Unraid-server kan störa den lokala nätverksanslutningen i vissa nätverkskonfigurationer. Att acceptera rutter krävs inte för att din Unraid-server ska fungera som en subnätsrouter eller exitnod. Aktivera endast detta alternativ om din Unraid-server behöver ansluta till fjärrsubnät som annonseras av andra enheter.", + "dns": "Att aktivera Tailscale MagicDNS på din server kan störa namnuppslagning i Docker-containrar och potentiellt orsaka anslutningsproblem för applikationer. MagicDNS behöver inte vara aktiverat på servern för att du ska kunna nå din Unraid-server med MagicDNS-namn (t.ex. \\\"unraid.tailnet.ts.net\\\") från andra enheter.", + "caution": "Fortsätt med försiktighet och säkerställ att du förstår konsekvenserna av denna ändring innan du fortsätter.", + "more_info": "Mer information:" + }, + "lock": { + "sign": "Signera noder", + "signing_node": "Detta är en signeringsnod för tailnet.", + "signing_instructions": "Följande noder är för närvarande utestängda från tailnet. Markera kryssrutan för de noder du vill lägga till, och klicka sedan på Signera för att lägga till noden i tailnet.", + "signed_node": "Ditt tailnet har lås aktiverat och den aktuella noden är signerad. Denna nod kan kommunicera med tailnet.", + "make_signing": "Om du vill göra denna till en signeringsnod måste du lita på följande nyckel från en signeringsnod:", + "unsigned": "Ditt tailnet har lås aktiverat och den aktuella noden är inte signerad. Denna nod kan inte kommunicera med tailnet.", + "unsigned_instructions": "För att tillåta denna nod att kommunicera måste du lita på följande nyckel från en signeringsnod", + "disabled": "Ditt tailnet har inte lås aktiverat." + }, + "status_page": { + "exit_node": "Exit-nod", + "connection_type": "Anslutningstyp", + "connection_addr": "Anslutning", + "tx_bytes": "Tx-bytes", + "rx_bytes": "Rx-bytes", + "relay": "Relä", + "direct": "Direkt", + "exit_active": "Aktiv", + "exit_available": "Tillgänglig", + "idle": "Vilande", + "offline": "Offline", + "active": "Aktiv", + "login_name": "Inloggningsnamn", + "shared": "Delad användare", + "action": "Åtgärd" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json b/src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json new file mode 100644 index 0000000..a1681ac --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json @@ -0,0 +1,132 @@ +{ + "yes": "Так", + "no": "Ні", + "ignore": "Ігнорувати", + "erase": "Стерти", + "confirm": "Підтвердити", + "download": "Завантажити", + "restart": "Перезапустити", + "apply": "Застосувати", + "back": "Назад", + "status": "Статус", + "connection": "З'єднання", + "unknown": "Невідомо", + "none": "Відсутньо", + "disabled": "Вимкнено", + "enabled": "Увімкнено", + "disable": "Вимкнути", + "enable": "Увімкнути", + "cancel": "Скасувати", + "accept": "Прийняти", + "sign": "Підписати", + "login": "Вхід", + "add": "Додати", + "remove": "Видалити", + "tailnet": "Tailnet", + "configuration": "Конфігурація", + "needs_login": "Цьому пристрою потрібно увійти в Tailscale. Оновіть цю сторінку після входу для перегляду статусу.", + "tailscale_disabled": "Tailscale наразі вимкнений. Його можна увімкнути через вкладку Налаштування.", + "tailscale_lock": "Блокування Tailscale", + "warning": "Попередження", + "settings": { + "basic": "Базовий вигляд", + "advanced": "Розширений вигляд", + "system_settings": "Системні налаштування", + "enable_tailscale": "Увімкнути Tailscale", + "unraid_listen": "Служби Unraid слухають на IP Tailscale", + "ip_forward": "Увімкнути проброс IP", + "taildrop": "Розташування Taildrop", + "services": "Служби Tailscale", + "wireguard": "Порт Wireguard", + "outbound_network": "Розширені мережеві параметри", + "subnets": "Дозволити підмережі Tailscale", + "dns": "Дозволити налаштування DNS Tailscale", + "save": "Зберегти налаштування", + "restart": "Перезапустити Tailscale", + "erase": "Стерти конфігурацію Tailscale", + "diagnostics": "Діагностика плагіна", + "reauthenticate": "Перевірити автентифікацію ще раз", + "funnel": "Дозволити Tailscale Funnel", + "context": { + "unraid_listen": "Налаштовує служби Unraid (SSH, WebGUI, SMB тощо) для прослуховування на адресах Tailscale.", + "ip_forward": "Встановлює net.ipv4.ip_forward та net.ipv6.conf.all.forwarding у 1 в sysctl. Зміни застосовуються негайно після увімкнення.", + "taildrop": "Укажіть шлях для вхідних файлів Taildrop.", + "dns": "Дозволяє включити DNS Tailscale через CLI або WebGUI. Якщо вимкнено, DNS Tailscale буде вимкнено при старті Tailscale.", + "subnets": "Дозволяє включити маршрути Tailscale через CLI або WebGUI. Якщо вимкнено, маршрути Tailscale будуть вимкнені при старті Tailscale.", + "wireguard": "Змініть порт, який використовується для з'єднань Wireguard. Зазвичай цю опцію змінювати не потрібно.", + "erase": "Вихід з Tailscale і скидання всіх налаштувань за замовчуванням", + "restart": "Перезапустіть демон Tailscale і повторно застосуйте налаштування.", + "diagnostics": "Завантажити діагностичну інформацію (це може зайняти певний час)", + "save": "Tailscale буде перезапущено під час застосування змін", + "reauthenticate": "Примусити повторну автентифікацію Tailscale. Це призведе до відключення Tailscale до завершення автентифікації.", + "funnel": "Дозволяє увімкнути Tailscale Funnel через WebGUI. Якщо вимкнено, Tailscale Funnel буде вимкнено." + } + }, + "info": { + "online": "Онлайн", + "version": "Версія Tailscale", + "health": "Стан Tailscale", + "login": "Увійшов у систему", + "netmap": "У мережевій карті", + "key_expire": "Термін дії ключа", + "tags": "Теги", + "hostname": "Ім'я хоста", + "dns": "DNS-ім'я", + "ip": "IP-адреси Tailscale", + "magicdns": "Суфікс MagicDNS", + "routes": "Рекламовані маршрути", + "accept_routes": "Приймати маршрути", + "accept_dns": "Приймати DNS", + "run_ssh": "Tailscale SSH", + "advertise_exit_node": "Використовувати як вихідний вузол", + "use_exit_node": "Використовувати вихідний вузол", + "exit_node_local": "Дозволити LAN-доступ під час використання вихідного вузла", + "unapproved": "Потрібне підтвердження в адмін-консолі", + "connected_via": "Підключено через Tailscale", + "funnel_port": "Порт Funnel для WebGUI", + "port_in_use": "Порт використовується", + "lock": { + "node_key": "Ключ вузла", + "public_key": "Публічний ключ", + "signed": "Ключ вузла підписаний", + "signing": "Підписуючий вузол" + } + }, + "warnings": { + "key_expiration": "Ключ Tailscale закінчиться через %s днiв %s.", + "netbios": "NetBIOS увімкнений у налаштуваннях SMB – це може завадити доступу до спільних папок через Tailscale.", + "lock": "У tailnet увімкнено блокування, але цей вузол не підписано. Він не зможе спілкуватися з tailnet.", + "funnel": "Увімкнення Tailscale Funnel для WebGUI Unraid відкриває ваш сервер для доступу з Інтернету, що суттєво підвищує ризик несанкціонованого доступу та можливих атак. Увімкніть цю функцію лише в тому разі, якщо ви повністю розумієте наслідки для безпеки. Рекомендований/безпечний підхід до віддаленого доступу — встановити Tailscale на пристрої, за допомогою яких ви плануєте отримувати доступ до WebGUI, замість увімкнення Funnel.", + "subnet": "Прийняття підмереж Tailscale на вашому сервері Unraid може порушити локальну мережеву підключуваність у певних мережевих конфігураціях. Приймати маршрути необовʼязково для роботи вашого сервера Unraid як маршрутизатора підмережі чи вузла виходу. Увімкніть цю опцію лише якщо ваш сервер Unraid має підключатися до віддалених підмереж, що рекламуються іншими пристроями.", + "dns": "Увімкнення MagicDNS Tailscale на вашому сервері може порушити вирішення імен у контейнерах Docker, що потенційно може спричинити проблеми з підключенням для застосунків. Вам не обовʼязково вмикати MagicDNS на сервері для доступу до сервера Unraid за іменами MagicDNS (наприклад, 'unraid.tailnet.ts.net') з інших пристроїв.", + "caution": "Дійте обережно і переконайтесь, що ви розумієте наслідки цієї зміни перед тим, як продовжити.", + "more_info": "Детальніше:" + }, + "lock": { + "sign": "Підписати вузли", + "signing_node": "Це підписуючий вузол для tailnet.", + "signing_instructions": "Наступні вузли наразі заблоковані від tailnet. Відмітьте будь-який вузол, який хочете додати, а потім натисніть кнопку \"Підписати\", щоб додати цей вузол до tailnet.", + "signed_node": "У вашому tailnet увімкнено блокування, і поточний вузол підписано. Цей вузол може спілкуватися з tailnet.", + "make_signing": "Щоб зробити це підписуючим вузлом, потрібно засвідчити наступний ключ з підписуючого вузла:", + "unsigned": "У вашому tailnet увімкнено блокування, а поточний вузол не підписано. Цей вузол не може спілкуватися з tailnet.", + "unsigned_instructions": "Щоб дозволити цьому вузлу спілкуватися, потрібно засвідчити наступний ключ з підписуючого вузла", + "disabled": "У вашому tailnet блокування не увімкнено." + }, + "status_page": { + "exit_node": "Вихідний вузол", + "connection_type": "Тип з'єднання", + "connection_addr": "З'єднання", + "tx_bytes": "Передано байтів", + "rx_bytes": "Прийнято байтів", + "relay": "Ретранслятор", + "direct": "Прямий", + "exit_active": "Активно", + "exit_available": "Доступно", + "idle": "Бездіяльний", + "offline": "Офлайн", + "active": "Активний", + "login_name": "Ім'я для входу", + "shared": "Користувач із спільним доступом", + "action": "Дія" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json b/src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json new file mode 100644 index 0000000..1ab45c2 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json @@ -0,0 +1,132 @@ +{ + "yes": "是", + "no": "否", + "ignore": "忽略", + "erase": "清除", + "confirm": "确认", + "download": "下载", + "restart": "重新启动", + "apply": "应用", + "back": "返回", + "status": "状态", + "connection": "连接", + "unknown": "未知的", + "none": "无", + "disabled": "已禁用", + "enabled": "已启用", + "disable": "禁用", + "enable": "启用", + "cancel": "取消", + "accept": "接受", + "sign": "登录", + "login": "登录", + "add": "添加", + "remove": "移除", + "tailnet": "Tailnet", + "configuration": "配置", + "needs_login": "此设备需要登录 Tailscale。登录后请刷新此页面以查看状态。", + "tailscale_disabled": "Tailscale 当前已禁用。可以通过“设置选项卡”启用。", + "tailscale_lock": "Tailscale 锁", + "warning": "警告", + "settings": { + "basic": "基本视图", + "advanced": "高级视图", + "system_settings": "系统设置", + "enable_tailscale": "启用 Tailscale", + "unraid_listen": "Unraid 服务监听 Tailscale IP", + "ip_forward": "启用 IP 转发", + "taildrop": "Taildrop 位置", + "services": "Tailscale 服务", + "wireguard": "Wireguard 端口", + "outbound_network": "高级网络选项", + "subnets": "允许 Tailscale 子网", + "dns": "允许 Tailscale DNS 设置", + "save": "保存设置", + "restart": "重启 Tailscale", + "erase": "清除 Tailscale 配置", + "diagnostics": "插件诊断", + "reauthenticate": "重新认证", + "funnel": "允许 Tailscale Funnel", + "context": { + "unraid_listen": "将Unraid服务(如SSH、WebGUI、SMB等)配置为Tailscale监听地址", + "ip_forward": "在 sysctl 中将 net.ipv4.ip_forward 和 net.ipv6.conf.all.forwarding 设置为 1。启用后,此更改会立即发生。", + "taildrop": "指定传入 Taildrop 文件的路径。", + "dns": "允许通过 CLI 或 WebGUI 启用 Tailscale DNS。如果禁用,当 Tailscale 启动时,Tailscale DNS 也将被禁用。", + "subnets": "允许通过 CLI 或 WebGUI 启用 Tailscale 路由。如果禁用,当 Tailscale 启动时,Tailscale 路由也将被禁用。", + "wireguard": "更改用于 Wireguard 连接的端口。这项通常不需要更改。", + "erase": "注销 Tailscale 并重置所有设置为默认值", + "restart": "重新启动 Tailscale 守护程序并重新应用设置。", + "diagnostics": "下载诊断记录(这可能需要一些时间才能完成)", + "save": "应用更改时,Tailscale 将重新启动", + "reauthenticate": "强制进行 Tailscale 重新认证。这将在认证完成之前断开 Tailscale 的连接。", + "funnel": "允许通过 WebGUI 启用 Tailscale Funnel。如果禁用,Tailscale Funnel 将被禁用。" + } + }, + "info": { + "online": "在线", + "version": "Tailscale 版本", + "health": "Tailscale 健康状态", + "login": "已登录", + "netmap": "在网络图中", + "key_expire": "密钥过期", + "tags": "标签", + "hostname": "主机名", + "dns": "DNS 名称", + "ip": "Tailscale IP 地址", + "magicdns": "MagicDNS 后缀", + "routes": "广告路由", + "accept_routes": "接受路由", + "accept_dns": "接受 DNS", + "run_ssh": "Tailscale SSH", + "advertise_exit_node": "作为出口节点运行", + "use_exit_node": "使用出口节点", + "exit_node_local": "使用出口节点时允许 LAN 访问", + "unapproved": "需要在管理控制台批准", + "connected_via": "通过 Tailscale 连接", + "funnel_port": "WebGUI 的 Funnel 端口", + "port_in_use": "端口正在使用", + "lock": { + "node_key": "节点密钥", + "public_key": "公开密钥", + "signed": "节点密钥签名", + "signing": "正在签署节点" + } + }, + "warnings": { + "key_expiration": "Tailscale 密钥将在 %s 天内在 %s 过期。", + "netbios": "NetBIOS 在 SMB 设置中已启用 - 这可以阻止通过 Tailscale 访问共享。", + "lock": "尾网(tailnet)已启用锁定,但该节点尚未签名。 它将无法与尾网通信。", + "funnel": "为 Unraid WebGUI 启用 Tailscale Funnel 会使您的服务器暴露在 Internet 上,极大增加未经授权访问和潜在攻击的风险。只有在您充分理解安全影响的情况下才应启用此功能。建议和安全的远程访问方式是,在您计划用于访问 WebGUI 的设备上安装 Tailscale,而不是启用 Funnel。", + "subnet": "在您的 Unraid 服务器上接受 Tailscale 子网会在某些网络配置下破坏本地网络连接。作为子网路由器或出口节点,您的 Unraid 服务器并不需要接受路由。只有当您的 Unraid 服务器需要连接其他设备所发布的远程子网时,才应启用该选项。", + "dns": "在服务器上启用 Tailscale MagicDNS 可能会干扰 Docker 容器中的名称解析,可能导致应用程序连通性问题。您无需在服务器上启用 MagicDNS,也能从其他设备使用 MagicDNS 名称(如 'unraid.tailnet.ts.net')访问您的 Unraid 服务器。", + "caution": "请谨慎操作,并确保在继续前已了解此更改的影响。", + "more_info": "详细信息:" + }, + "lock": { + "sign": "为节点签名", + "signing_node": "这是尾网的签名节点。", + "signing_instructions": "以下节点当前被锁定在尾网之外。 选中您要添加的任何节点的复选框,然后点击“签名”按钮,将该节点添加到尾网。", + "signed_node": "您的尾网已启用锁定,且当前节点已签名。 该节点可与尾网通信。", + "make_signing": "如您希望将其设为签名节点,则需信任以下来自签名节点的密钥:", + "unsigned": "您的尾网已启用锁定,且当前节点未签名。 该节点无法与尾网通信。", + "unsigned_instructions": "要允许该节点通信,您需要信任以下来自签名节点的密钥", + "disabled": "您的 tailnet 未启用锁定。" + }, + "status_page": { + "exit_node": "出口节点", + "connection_type": "连接类型", + "connection_addr": "连接", + "tx_bytes": "发送字节 (Tx Bytes)", + "rx_bytes": "接收字节 (Rx Bytes)", + "relay": "中继", + "direct": "直连", + "exit_active": "已激活", + "exit_available": "可用", + "idle": "空闲", + "offline": "离线", + "active": "活动", + "login_name": "登录名", + "shared": "共享用户", + "action": "操作" + } +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/log.sh b/src/usr/local/emhttp/plugins/tailscale/log.sh new file mode 100644 index 0000000..da3609d --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/log.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +log() { + LOG_TIME=`date '+%Y/%m/%d %H:%M:%S'` + CALLER=`basename "$0"` + echo "$LOG_TIME $CALLER: $1" >> /var/log/tailscale-utils.log +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/pre-startup.php b/src/usr/local/emhttp/plugins/tailscale/pre-startup.php new file mode 100755 index 0000000..c29bedc --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/pre-startup.php @@ -0,0 +1,43 @@ +#!/usr/bin/php -q +. +*/ + +namespace Tailscale; + +$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp'; +require_once "{$docroot}/plugins/tailscale/include/common.php"; + +if ( ! defined(__NAMESPACE__ . '\PLUGIN_ROOT') || ! defined(__NAMESPACE__ . '\PLUGIN_NAME')) { + throw new \RuntimeException("Common file not loaded."); +} +$utils = new Utils(PLUGIN_NAME); + +$tailscaleConfig = $tailscaleConfig ?? new Config(); + +$utils->run_task('Tailscale\System::createTailscaledParamsFile', array($tailscaleConfig)); +$utils->run_task('Tailscale\System::applyGRO'); +$utils->run_task('Tailscale\System::setExtraInterface', array($tailscaleConfig)); +$utils->run_task('Tailscale\System::enableIPForwarding', array($tailscaleConfig)); + +if ($tailscaleConfig->Enable) { + $utils->run_command('/etc/rc.d/rc.tailscale restart > /dev/null &'); +} else { + $utils->run_command('/etc/rc.d/rc.tailscale stop'); + $utils->run_command(System::RESTART_COMMAND); +} diff --git a/src/usr/local/emhttp/plugins/tailscale/rc.tailscale b/src/usr/local/emhttp/plugins/tailscale/rc.tailscale new file mode 100755 index 0000000..0ef5f4e --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/rc.tailscale @@ -0,0 +1,46 @@ +#!/bin/sh +# /etc/rc.d/rc.tailscaled - start/stop the tailscaled daemon + +. /usr/local/emhttp/plugins/tailscale/log.sh + +if [ -f /usr/local/emhttp/plugins/tailscale/custom-params.sh ]; then + . /usr/local/emhttp/plugins/tailscale/custom-params.sh +else + TAILSCALE_CUSTOM_PARAMS="" +fi + +start_tailscaled() { + if ! /usr/bin/pgrep --ns $$ --euid root -f "^/usr/local/sbin/tailscaled" 1> /dev/null 2> /dev/null ; then + TAILSCALE_START_CMD="/usr/local/sbin/tailscaled -statedir /boot/config/plugins/tailscale/state -tun tailscale1 $TAILSCALE_CUSTOM_PARAMS" + log "Starting tailscaled: $TAILSCALE_START_CMD" + mkdir -p /boot/config/plugins/tailscale/state + $TAILSCALE_START_CMD 2>&1 | grep -vF "monitor: [unexpected]" >> /var/log/tailscale.log & + nohup /usr/local/emhttp/plugins/tailscale/tailscale-watcher.php 1>/dev/null 2>&1 & + fi +} + +stop_tailscaled() { + log "Stopping tailscaled." + killall --ns $$ --wait tailscale-watcher.php 2> /dev/null + killall --ns $$ --wait tailscaled 2> /dev/null +} + +restart_tailscaled() { + stop_tailscaled + sleep 1 + start_tailscaled +} + +case "$1" in +'start') + start_tailscaled + ;; +'stop') + stop_tailscaled + ;; +'restart') + restart_tailscaled + ;; +*) + echo "usage $0 start|stop|restart" +esac diff --git a/src/usr/local/emhttp/plugins/tailscale/restart.sh b/src/usr/local/emhttp/plugins/tailscale/restart.sh new file mode 100755 index 0000000..b180a95 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/restart.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +. /usr/local/emhttp/plugins/tailscale/log.sh + +log "Restarting Tailscale in 5 seconds" +echo "sleep 5 ; /usr/local/emhttp/plugins/tailscale/update-settings.sh" | at now 2>/dev/null diff --git a/src/usr/local/emhttp/plugins/tailscale/settings.json b/src/usr/local/emhttp/plugins/tailscale/settings.json new file mode 100644 index 0000000..9ada3ee --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/settings.json @@ -0,0 +1,34 @@ +{ + "INCLUDE_INTERFACE": { + "default": "1", + "description": "Adds tailscale interface to Unraid services" + }, + "USAGE": { + "default": "1", + "description": "Allow collection of usage data" + }, + "ACCEPT_DNS": { + "default": "0", + "description": "Use MagicDNS" + }, + "ACCEPT_ROUTES": { + "default": "0", + "description": "Use routes from the tailnet" + }, + "SYSCTL_IP_FORWARD": { + "default": "1", + "description": "Enable IP forwarding in sysctl" + }, + "ENABLE_TAILSCALE": { + "default": "1", + "description": "Tailscale enabled" + }, + "WG_PORT": { + "default": "0", + "description": "Port for Wireguard connections" + }, + "TAILDROP_DIR": { + "default": "", + "description": "Destination for Taildrop files" + } +} diff --git a/src/usr/local/emhttp/plugins/tailscale/style.css b/src/usr/local/emhttp/plugins/tailscale/style.css new file mode 100644 index 0000000..459605c --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/style.css @@ -0,0 +1,15 @@ +.tablesorter .filtered { + display: none; +} +.tablesorter-filter.disabled { + display: none; +} +.fileTree { + width: 300px; + max-height: 150px; + overflow-y: scroll; + overflow-x: hidden; + position: absolute; + z-index: 100; + display: none +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/tailscale-watcher.php b/src/usr/local/emhttp/plugins/tailscale/tailscale-watcher.php new file mode 100755 index 0000000..f89a647 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/tailscale-watcher.php @@ -0,0 +1,10 @@ +#!/usr/bin/php -q +run(); diff --git a/src/usr/local/emhttp/plugins/tailscale/tailscale.png b/src/usr/local/emhttp/plugins/tailscale/tailscale.png new file mode 100644 index 0000000000000000000000000000000000000000..8408ec67b574cf20944050fc3b8b47fafb0953e6 GIT binary patch literal 13626 zcmbWe2RK~c7brRyGP>wdM@e)BBYH51=)HHN6K(WP^xh+S7la^6M3ksW2oj0WYh;K{ z^!85v<@fG=-+k@+rkpuv?X~vWYp=Ce6R)MAL`*$%-w-_Dv;UFx z2=ImP@x&Mf0ufUDePe<03TQzf@UfGgk++e$ngr7QDZjO?yNw;c|5FbD8U&J(@%OMs zy4ZO`ZR{MJ+@x9eI(t~5PPWplh9c@Pbq{$vN2e!&UUs^H8hXe;7o@l?tIQ*)l)nUk z;HjOrHPrv9s~bwfUz+t_cqM@AzqbWgq5lH$c9CY4`%4gNq^<>(clWY`iu1$xkT94a zR8*WF2@`_BZG>#??0KO=Fd<<9m=N&d6BLpVgi8p)q5t_~1t@yi+DqstJpKfq+4>itE%F~Y4<9ere-XDu z3fQ^YJ+*W5Mgg#b|G;`Ux_i5$9NqtKQ2+Dze35n_n2}{64C14`J&;Jt?*f_S<-q!!Gz_v&Udv~v=)&R>+Ppuv71U%dvSfT$c zq=dY?tGgFK7?_>#zh1^h=RbnQI_!N|AV;y z4Tf^J_x7{)vXgTFR_p)6h7tg#FYs3v|ILZO|CPyqJ^LT@_P^kOG5CA=k0kN44N6d&Zu2}#2E9`<5F9WPGkGtheyJ_Lzj1I?;KX$J!aEvExI*F%38%fP~tlLWdb1lIXx6 zyUMAznO93ijx)M+%PL7O$1b-De~1=R3KA2;07GhPKiE*$W^?Q5N9+!`)8au`_D`}X z73Eq@h(Dx0Ly=6v7yNML!mVi)a_rd2@pWqS;+*oxz*tFw8Bf?toaiRu#I?0v`ugG+ z2IjNgJNvyjqyRl{Uta{;$INfR=WIx{8*8N5W4RY=-g{Er|9Zs;3*9(V_eSMK>;~0FFfkxg$oR^hxSh5{v4V8*Q z>bk(oE0l2E>qTEfO`J6?1L%GfAPoaUL-YIhqe}F+BT*y>h}vY-GkMNR7*T%+-WUiW zR+(j#6OCZmv)5^Ij3D!{QN>YG;qo^3%kE3H z$Vpvpzp>$eUJi1uz_QZHSzl;o-=zdw%Tf+9MSLSsQO<`#@I3}ND}_oGu%P5*5zln7 zwItD4WMnFk^#YuzY0+NuWG?~M!%wGp%R}q$HGgZLm9K3rHX(69L*i>G5AKqUVLk4> zcZV1Bz~Q~KU=>AVhFudjlGnj_s@ci-C}*(B+ngEgb6ruorCY60hz%MY8~Yru5NVua z_vmc-S={iNk*TRmM{wS!97;e|upy7#4$Nx064Qszp9z3PtQ;Ivv@y;h0X6fQXT%MX zysQn3XC0w=QZ_9B@eK$>z0GNI^BF9>eP0xWRCFj1?oDL&rvokm&%Nt-ZPOOc#JJZr=damD% zXAil1q=FF;TjNc%UVRT=VfSZPvL=mcWh|=hJSd^^8yahQ#-Uh$q%xzyiB9!FCX5It z%WW}dsS%qDVMU7vDygq0ZsuJZlibs@GCZa-&Joxe^9~hy@r0oS3{#`fUy$|x<QCTp$*Z zp*BAHk&%({Vq=4Ic6Ju~+t)l-BOi8U#Dh$^2boxvS!!x(j-Xo5#LS}aR>R7Nz3R+( zoGPc1-$u*H3iWvv_h3^wOa2 z;_$H9_wc8(*D#}>ETt$qFY_foB5m-+6ZZX2nxW*D*S`%v5e}!;t*cb3LGICRY+c8C z3mweGSogK?V$5%!gQFY@ZY9*p`l9k&drr;@P;q4-Wy+>J0jY;Ufj1H(%~c@BPqxh& zDwSuWqiP$S*5oyy(*-|Eq!nU+VK0FSrLcD+HXSBF^+yVIUl*iuhz?Kji<-^_*i}fC( zEqitsNkjK>etL;h$r|UZFEKD}gsJ5W__yldLYzwXBOXUuqw7_SsII*R+FgFAR1zqr z`y~;PTzLdLU$h^cU+GRxPC835y*3LyU*FieP$mYr zqx%%v89IUbOD9+fZ^-!yP@D-w=Au&?>Kf*b##4z=Wb0M=ZncY{mpgpJ6g43C;5B-z z>HcB>d2T@gC90!984^!S`iD7r*rq(&d1vI5orJzlLDVE7rYopwWlp7}#E82vH#ct) zrvwgzLA!s6ycQ&7l=w+P`*arXY^uF`otBx^tz(_q>ulc|KYVd=;W+h0O3Kn}y}F1( zE~o@fpM;rP*_DEPpL-isnD`0`lvieD?X(#Yy}i1pn!zqSl(e{LvADPh7`qfEX}WM5 z)e2&dpC9jfj2-;U)Li|O-p+V}^4Lb3a=bJv zrBEtBNGO=yMLENY_5r1Fw^j!GQ%MH0$O@erXz_Tz5Z}jUBK1jGF2xSB$E8}~>l*w| zEu8sXq7hi@UiY$e9P{vubF7W256`8zvp+u+ycqN#ARnS-SN;azYSgKLuTvW1sHv-K zkKN0(I~QfZ3_VrlECmPZD^=>q`5B7h;az;4S|rieHxqq>D2)R=4M4JGIt|dGICngJ zkP?e!IJ+t>Bcr09pD1~}n1VJKb21(1W{0agDg4o%rlgkJ);I-Tgq;qFXh4hkl!jS> zcDBT*8XMDE#r3)4hDH0i%)~GytkLYsJ#t)w-@cJYuLa#>PbYvTu+k#QaIO7ZGNtPA zD=8~&0AGd1!k@)=Zx&Zk-zx8c+N5{gNJ9gEQc{u?aJY&E{^9)f>sQ(n_J@DgJ;t;- ze}3&OrwOv`y7NK}Fxryc1W{xax=w#iPp!SZDLFKX2873@o_Td78Szdv&rtysbnv6d z%+B&@fTJ3w@q!QK`~VEjU{CQH%jfpA6@|S}`)1R`dFt&K(heLN-jdEB1cW=AfV1-L zpKq#yVq)RJkM`baizY}j=m^^aE*eaukWNYHg+kq$?RhX0U`eKXZplHirCJaoL)2Z( zN}b%iygt(ITY1=6Nv-pd+AB|1%ID_3TzGfxH04+luGp%wRqK*e@7z_+XngnXq<@l- z&|U38=Ed*d&M&EzW!ruSog_d;^+2zIG?VfO%wRQ{~2Nj&Oe4M;nk) zT%KA+0!mLw5M5Vn`ocE&LE{K?*BP>x}abzZ1OvX1V>_2p@Kdpl_~!gJ0UYfStl)Emo@ElPpfOnCA+ z1!5M=T`|*^B_r3QHL$zTDcFqS@aibIW|SP*l9&?1dkj(R(80s>)>(GZB$V~S%7)pt zaXExeb{7c4%XRo;Z5uH~IQb*%FkswcB_(a%95Qo$@SqKTd3k3OYTy?ob;AlxDW@U# zazXoOjs8l3JY^KTvSk7Ovo0wEr^k6fuV_r;p`rVCp*6kmP z_O}RM-sF=yiI5MdnW`kqT2S8P~Lt% za%AA3N=>pycV0qEI;-c3S`Ih){T^>V6LeMNe9rTvz4GhKi<;Mq_{)|*>Nd7Vp@k~t zW)1#Xxss6N74!-!+%sl(*G-H2ZL8Iq9zFgb=_;uQ85wcGFfPliFpMGVrC$AcB*z-vC8QJpW=yW9pqt|4EE84vs!@~S#izh0f? z*vaXN46F&F$=;y%$Sj9q)=)z%1gzwrz`R3J2cn1VyoB+#eVaN$m~K&l-&Z&X_*NVC zI2=9A-*wQ=Hz-gRIX1#3Ns)9>#2s4<{oCYe=1UjWgO-(0hlvZ6`w4gRHgz05^Hq10 z1s_Kd5wtaecocef#1o&@9<>yHwBSA{jC0NW#%Ny3TfW zWrAqQhT3-Km#t{0b*ok4`S^mAo{RjsHTeQ~I(-B>z$Xj8I_4NVin^oUlw1*s>7;k) zj28`Ww_lz|N5WT+=t3QZKj$yciTdNLgKnZgRwCveNXV81-U`KZqWaEp%1zoL;wj&) zi;5gXJI}|OjjFeWR|~;z_ck8{onk}U1pyJR3Z0;Z2weLt+ zY_E!R5^5U&?*%~#{_cF&eLmu0{hq2_LAjZyYj#Z4)ZAOZM&l-Vwm&)u&E~fNcSHTf z@r!apW||_dIKU-QPfT?{)4Akv<%^4&W6YjV4Wti;3b_}Lc(ql6Opl2+FNERmR5>Bowb=x~fFMh34eay8jHv8v2H*}@`_N9_ zXuLdSTs)S2#OO=?EQ&Hh|KZ<_vsZJW&_;!;MTxs?2E7YL+y@z@+wt=ASzB9U9r*JG zM)57DbH)KK@f%@bvM9%e$}Agud%PH@>5;0kqtd&1o&#&1ytZ$Q%uJeh!hNLmE@OB{ zj7&`W`ViCyMxz!)B;Z3*a&0neIy`%OdpZ~R?H0>f1Y-V|qGL|Vug6f9mR4@&Zo1%W z$tW@{goo=kt#^NTEhnGmTMv(lZ(K>5YF55(BzaYetHUj{_6``>qel^XLi(>G^LstV zN^1#-`g^CR1*eC+#lln=HHK5<&^|9~`?WQ@i6hE?IEV_PfoZ%$mq$N)IpoGPr8~cooR}^)TgvH~3B-tfm zpIM{$a`1wp+lY7ds}gg6oyD;V5h*F#3l)iIT>*=(TBMgV0dhRmIy98&K!E|9iY`9| zA{B8VUifUcp+SN}iFe3VV_Z5Gi5&hQCeOO-k-$A|AN(v7m14gJN~dET5mL~;nvA%) z5r8d)t5~fDo1^Vj8BNqs50OG?mZtI#JL7^`NYy8y$~ybcm%u%3 zHHtNQkAbt-&yXgghTYa-xl)fGX%~PyGsp{i+Zht=1?l(eRlOf17&G$e0Phs8N4B&>JKB?H=_27yc|cZW3Wm5}c1oRVVR89U+~z<`%)!XS zqVMYJeW#z+(*jFvN^~5d{?voB*7c{GdIT}n%iqt$mbYo`haF|maN?|qWU6kOVjL*L zff2)Ke@&ZjbDOt?0VTUG*Col?vGYPA+Ic1RU@7H6Z}R7*;iQ*=1yXShxXU4KG*Pv+ z;j^?A79{ne(%NV9TpMpW8yfoIOqavMl=eYYWYU zclnc5zT3vWR|_0P!*_p#)jh1(>vw+N!FYDlu%((zdbN5cNtzcRsr0BdS$2u$Za8CZ zY*B5J68qw##RPVjgSmSevRX}XVf`KnYRbF${d_xcd|{E?2#!g>=yZJoNRxUT%&!$Tm+ zfFhQ&Ret;+Gos3Aj8f=fswRZcS$iCH<$1GSA_G+z5qPuq(> zhbkZr zk!a`E$fI#d<~{%6IlzOd7Vd9LawrO+Rw7{@+MegVxVX5RIB50a*U-LoYE1TLJ0Ml^ zHzQzZ=84Pyb~)IO{zP|e?d)r&Oy3)sFzSRd8xIfAsEx+)wsIK74H&FP1``pFuHE$r zyqp7kjLAZg7I8+WpSA(-BNiTfs6mT{Gw0%%ZsG|I(fO5_p)JHkor$v~35TSq)pkO! z8CCFkC;L9>ay0pkGX-xW!y>{?PnP6`UYIjs?KgMHkj_3SOgBs+UjQYk_tmk4E zFQdWBsu@x7pYR7-Xm3`SZ$ukHk4&Yu)k+M0=wgYWKEk}`{ric}K8Ktl{rx$>Dmx|< z`Rb2&5w5Z5_;^pVu3abLjGc&yEo_^e(yL5d%Mp%=9-AX(EQq-A@C0r ztdjo4UhX>d=27tP7>fPr(q|egDAF7b>rt=WduiC;)c+?1gw*9ee3_Iw|1(8+3%z*2nnyh_Mp7`Rw4CVQS>x8qWypvOU=mrt_D zo|^Jaa=*Y5O5nt-@s94KZX0=;6(BYslCvH!k~YQIHc8wO<&4$;S9zj7#QW$!ywD0a9; zfe;jyd?spkgZF-GVb96Gr-O{cymx8w#1cs0SP708a{AmP3F;klyUk%&hu!XOd7Q7i z_l}MxZCUK*6fNx^t=s{&OxsVg)rcc@x)1S@w-(6?(&>aVie~;RE}lPrxa0dbMcaCG zS}porGb7N)pf4?+VrvsVjC-9oq=iBBoRq0%0mr8MvxJt3t`AMamWIPvI+r~z7Kn#? zQZluVJq~HTTLq*+VTJyOJyhgUIm zv#j9XUu0%_`f=T0Cy~!JCT*N1O@I=rYy<7)jQ@xsO%?!~Lq2y4ap8{Gy=U08BuOeM}`TNTWb3 zqUICVfK8L^l|OIHW)0W-v(@;R(}(OD7ezrg0cSGUmaiuF4bR`H*?^nA2edR>bo@3@)GVW|QAT!>u~}*%#IN_G40MGhP369zq~xgc#vp~FHimQGjg z6l|SXu>1CM#L;EXv7pQ4VH!pDZOYru{HQ0B#H_3oWVYvuQ7Z*(nVUn^k3x7Xf>);T zgc5PSGnw8l7vJ2ox3Bs-Gt=?tX_3W~s&Wcp;mNb*J2JLQE;g{gb1 z`?qk#qD7j*mQfAO6+aR){7LxNsVV9g@gf>Ib}Py5?+AqRf5PE#2D3FcmRr++*6hJj zdYA84FT2~-CkGnLJ7I!?5s4=|!#z@GC7I+MMV^ac3VlEZ$L()b$FTSe?EB|Ll29RU zS<(dyxLkYv=m3A-qn{Xv2e6-j@UrKz1NMt=@4cS$!+-GL0gyFpnC1GJzPjGxz7#6; zR!*a<>pDgHO>}5QanBJ_mFF3|auhokaN=ye*1Y51H-?{L+V}lCC3iOY;z5cFP%aA# zSLN@9gmiV0vEMy!l`0Cmp=Am@lYD!7bLqJ{kVwnG5W}u|tei<5ML-$jX65XR-RZY8 z6!f}>$lY8~?J+=3H1q5N-IH>M$sbhfTpZ3M7DxVJX=F3c&+M5D zWEJj9L@NDr?WY?t1!I`%IF_H?#QgSXtdIO{@OLY%3hV(JIx!CB-92Wlrs?WZfWkj~u>Ny=ESJF!q^pxsQ_HoC-LK+rXkFYkbn#)TxM&F z{%k&nVm3Tss4ZbSUxrHcewONe*bsC?Xf+<3rS3Y*fUgb#(zrIIAait~c|S647`gSn zq&~ zxWrH#e#GYw{?R67DYLkZ%hr9lsBkWt`zNLu{aMuh$pjH1tza;<&u_0~LeAx#&_|cX z@3E&_%g21B^o4h>KOM?gYA#Jp&3@3!BGdXXQ~WvpLt?8jPyxyv)M_xWrj>w{PU!tu ztibnW@y{O{>YEjNkG4WU z=sXjxNSRodmQ8zcd9f`JJEMqodTU=UOwygHo}KVT*}#@FfgnO>BPKaUbc-}O&(f&(=TFMd*$H!nvyeykDH(d>DTdSCwtDZly}fH_>2rTfW>0itaS_j4 z6ec2_%zU#C>pn=n1E|27qsusSat8l0O|4nux__UGWrCP7+Y9#|GlvSN+N3PN*%g9X zS9kJZwZeQnY!p-Ca|ONm@-9L>Ci4E1jH~Ld5FAUZQLlCx{8}qf)-3WYm6H_+O+gsb zVgO)LO%9SOvU5&Z~QMd#yu|nNRh`!>Aqath746l^|h#}sTcKS8=6P= zT+YpTe83H2ojf@o4`X7!9k~UMw7}p}3@`?U(H>t%N|2eyhXDV<-36_#04IG@Gu?8&fiOO zW0p9Z9v-KuJ?Ir5CJptcU0b8zV{TM{d^oG2@mxz5T}_eP_1JA5`XOOa>br8ap`^K- ze5WDC8mN=)$iKl6rWe~;N?lnY(+J=CypG9Ixi4?>HaW~n1UEN7|0b=}&^=Wmp(1hsmf`kSry2)`rRQI{1y8+zAWxQ zS77yb-IAF=a8)c#=#Dt=`(2;Q{T+AdIIo(x$RmBF_#m>WEsNNdQiw*lWLI~J?nbFL zJx_44$JEcp>IeMRl<$p|yx0(sgi3PXy~Sj6HpwRPz}}2oksxy!to=O0y=Yb*^=cxyPbY}eGTUH7_bd0 z!>*&U1~V1)J&cfcZ8twX9zHS#T3A-S-w=pui7|wx*@W=zWwdn19yTro4@K z)7GL$1Qbum>NXt_i8}QLU$Z~`jlqC&rIScEzx48QQfX&(XLo>NIMBMl{ltH6uY0EZ z>(BXuW1H%_YClj9YLGt7+z3KkV_3KG+e<84^g}$#V{Br=a=;*)Q`vK$M=f^a;C>I^9MY8KOq~=NZXV~b3BGn*rmlQ< zpFVt+Kx^@#z&*08&+EkJf6f&t6|#7!^mu-x9r=LNf53!;=!7SQ^M`6W{8Cdj;rJMj zW=2O*-1`Zv+$7cZlkN2otX?rpj$L-UBUs2|OeN{)vo9PggPj#fL@J_L;y!F$m~Hp| znZ{r>)Hjrx)kOJ@Q|f>Cogn?KEud*F^l(^H&VZt{lwEtk*^TN)!ft`q)H@F`CKazj z-KVBS^;4V$Z@7C^O=qD9G6mR>YC+Sgubx^nFVZRC-i<wdM`1`KALUM`@k+!2Z`S zgeEPP>s&MeYxhZ5kM&ZC4OrzZL+I5VvBNXcuL}!QbdPiFcm)L5I&*#6x|e8Epb4-f zD~)`8Z0vAh3$T`szKD>kFk2dS@Q>zKttE85#l2Pzu@~+LE(l~Og)w$T9A5h{{;oLv znE1CEcEC#kT*y0oRncpxl*MkjtC457q^ZHDOWV^=c=Aidn#AP$ZVzN`MIe=F2xlG= z2R1$g?4j>`;a6|cdLos4Odcvlk30GxUU_tEy5Q>S8Ao{JUPljp#NaEDq3K%Ag!CUe zZ`N0$Ky^vRssk_d30c?p-j_J+CQ)JH29deNmc6=~*OUkTL|)5Ss>#Au46>%C^v#K? z{=YZNfns%58Xl2MH*frejq>{~>Ak_LE2r={*-Yu9?Y?%7l1JyCG*>6em-;96HPAw& zs~^Rbc&YoZ5Xrjp>pxFP_IUz=OzD9>ii64OR7947AJGTqvCMZqXL47C^=CP~=d(GF zvGw)W3y@#nUnYs?etZu{ESi#TcJ%KEpQkW}Va29O;VWmPsZ=kV6kOgIQc*GP?BwPb z50}Swr#iR(c4MTEb{C9Uyb0yy=Ux6b5=uY*=!gjP!MJekk`qE_a3^GyqPUl@eLj-0 z3x`;Dy7WqX%2TC!vMs+ORR*hsn_zZs&U1FXLVoglZ(hI5;GD`04mihYG{UOhSd;S` zb4h1szQY^i1$fdYsN&%_lf6`(;tv++971fHCfXuq$zS4DT+ed-CL*U7>JZ)GnBkO_ z^CrR5Kc2TV_hQ`X@O-;gv&9Zp6W{)6=O4BVy|RCQdmR?FyIa&dyS)5C$rI@1Sy_o? zMriJVT8v(WA_8P@{0Ym)oQOKiNH9e}p47)@W{qi{Md9t28m-di>GGQlF8;Wa={ktIsNYw>}o=x^fjw-+^Ar$xkwKo3LEX+i2(QkUWW zr_436;Gg(XC(}sSF?WoTZ)sb+Ps@whILkU7f=X6SmW|z7W;#XAXlq&{6iKp)sp+#S zr?mU`Mho3t)PfeAUWo*UOC}<~qho5JS>a*6!zqknKEL(OOtlGMQc_7eHSD~6eCCf} z@WV!+&hf^Xf+sYS?`7HHEr|vUNtEqOmE$Dd_CRjO+XFaV6K=BG=!aiF^=)d-=>PqB z^&K2PBc`Z`>0|TvK1Vb7BbHX_yFTb9>g5K3a8@2NyHD|~3Fx5%+=xE7x{JNnm5LlC zQy0((_ewsNBzlN)224auyfP3^fsMJsWUHLd=)DVZUxR9BVJU@_-oRQTA56^SFy>Qv zYZZJs*ja#!0r~K6n|hA($j z*%RGho}NjV{&-nkbOAh+iwia7ry>o!Fzy~88+VVi5(>rVnTBTEE6W>iKtFO~s0)cs3)sF$`=?{KBTpg#T(df}7i^?u7Egb}U(0-_�jOm!g_REqlwih?Fy7B zdyc0}39FpGRc>E>sig~&)&2o=Zn%9Ub*roYWw5>;1r)Phz2tnZ68#qdWI(0(DQp|b zyfz3?nu-xCZ$SnI(uJcghCUN???($P?{@CF-6=^Z`;&81n%)VPiz+qLHpS4~-};iX zL3A+JslC^BXxH^>Up)5bwjEqJ;5XhRyqmb>3TVRdS$DC#q$7{_V8(l`z}+i?He0i( zt0hp>_PD)1r5Fn!MXF^`t*{E{cxPjyvHSLV@3qSj+s^whXP4KXim&i@yG|{X(Z?2`GH9Ip#wPulH*e0yDT?R0Fj3q`q~FR%5&Qbrm!mR@iS=C+|K^SP{%W}WNcD!%NxEHMb=eU z=d#=SI;8$!RCpYS$hr3y)K9U?6v)8abM50m>s7IM$c1-m!xqPLYRql3(J1ro#`Zgl z#QVNpV^(wz+BV=JLj)|~NX|&|1f3eOB2$aEug$onAtm6F?@LQP9%JH|+`Ou2>QLdC z7VO-rcm-B0Poy12Li){~M_(fW1pI?rPAi)5-bg5Z z9m}>2Fd|b(*ZN(QU@|B8rW;j-ab<;@!PB4DD!S?S?Z;onp1qb1`|#AAWke|1xmC!x zKC4*Zd4f!BmZ~Opt~V<;PeKN8X7E+(aPji;LbFvO1lv|b;za<)f$$%Kt)LAw_&x@@ zdK?@aj58`;AWGACvQ-MRzHO>>!1cMFcjK2RWmT{!XWUWL{!OLOWLkotr;1{)ge!CD zgUZMd3W#`ckR4vDbf)>*TYdB*B;Zr5OCgsu$=tx|Ng&E2Q`pUGOr6AHJxTu_64&0? zCC9GbC?{P+9W@5~qdW<#0*&IpIPXUZT3|Q6M-HbTxU+3kRaJLutLxp|JDqS};=6^q+#pl;SkxHYc?=}tR5on3FIyo zD;YLH=S2J)=LG+E%7Bp#Rw70hEfKGLo!DY+t>th{sxn}?%aL|D7i&q_AHVQ%CQO`m z8`qOsOtf&YO(L8ie0GTdq@*p&Sy>=xfU8xa_tDSK;WMz7Q-C}Z23bQ7GmA^g*#f`V z)HU1TzKmyAhN)AaP^c#lGX2Mc7LknNUKK#xx>@FwrwRyI2o#Ot$4R?~B(K!_S)eON z#)(XtCZu~|${;dM2XqXhO_BpeM|Hs>emvtQ6o`4T-}bRv3)|<85EkD6Pq^Odz)`W^ zHA3u>XU}77ppOId=yk{2851>G$jWF9BUlcmJ_!eHM3I?O4%XJz2C}BZ=H}+kJ1|9w zDJdjCANWtNjw+=-cZD`}tnfg*SM~Kk)(?^@eL~Csejm}`l-JbKV*7r-k5I86Cd*Y> zfHL9E2AiYtK`LDL%Q$rqkQzPifo9&z#9sxVaLxp<$HUhZ-)H(#-oA|i8j|m$$(o;= zaitu5f+m!O%=2Qj^r?P0|LAJ-Bc|DIqihS&tq7s*1$3(j?uCOLLOPJ zXggyDkRfF>&hns5`rw-917&gw#q{{HkDQfelW=}z@{^spSfGtX>R^Ck0#2N*GN^vr zmNO&ZjQRcMu#Ria@OeSDrK)%x$l+~)ez>sN7$qUZl}AN}DimcLO;M4}I- R>F<9xDJyCyG$5=Z{|{peKx+U1 literal 0 HcmV?d00001 diff --git a/src/usr/local/emhttp/plugins/tailscale/update-settings.sh b/src/usr/local/emhttp/plugins/tailscale/update-settings.sh new file mode 100755 index 0000000..adfdc59 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/update-settings.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +. /usr/local/emhttp/plugins/tailscale/log.sh + +TS_PLUGIN_CONFIG=/boot/config/plugins/tailscale/tailscale.cfg +TS_PLUGIN_ROOT=/usr/local/emhttp/plugins/tailscale + +# Cleanup Taildrop emulation from pre-1.42.0 +if [ ! -s /etc/config/uLinux.conf ] && [ -f /etc/config/uLinux.conf ]; then + log "Cleaning up Taildrop emulation" + rm /etc/config/uLinux.conf +fi + +if [ -f $TS_PLUGIN_CONFIG ]; then + source $TS_PLUGIN_CONFIG +fi + +if [[ $TAILDROP_DIR && -d "$TAILDROP_DIR" && -x "$TAILDROP_DIR" ]]; then + log "Configuring Taildrop link" + + if [ ! -d "/var/lib/tailscale" ]; then + mkdir /var/lib/tailscale + fi + + ln -sfn "$TAILDROP_DIR" /var/lib/tailscale/Taildrop +fi + +log "Running pre-startup script" +$TS_PLUGIN_ROOT/pre-startup.php + diff --git a/src/usr/local/php/unraid-tailscale-utils/composer.json b/src/usr/local/php/unraid-tailscale-utils/composer.json new file mode 100644 index 0000000..eaa65e8 --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/composer.json @@ -0,0 +1,33 @@ +{ + "name": "dkaser/unraid-tailscale-utils", + "description": "Tailscale configuration", + "type": "library", + "license": "GPL-3.0-or-later", + "autoload": { + "psr-4": { + "Tailscale\\": "unraid-tailscale-utils/" + } + }, + "authors": [ + { + "name": "Derek Kaser" + } + ], + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.64", + "phpstan/phpstan": "^2.1" + }, + "config": { + "sort-packages": true, + "bin-dir": "../../../../../vendor/bin/" + }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/dkaser/unraid-utils.git" + } + ], + "require": { + "edacerton/plugin-utils": "^1.0" + } +} \ No newline at end of file diff --git a/src/usr/local/php/unraid-tailscale-utils/composer.lock b/src/usr/local/php/unraid-tailscale-utils/composer.lock new file mode 100644 index 0000000..afc3d6b --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/composer.lock @@ -0,0 +1,2647 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "aaeccac5bd4a8a475c5cbc25647a8ac9", + "packages": [ + { + "name": "edacerton/plugin-utils", + "version": "1.2.1-stable", + "source": { + "type": "git", + "url": "https://github.com/dkaser/unraid-utils.git", + "reference": "da010094bd0c4ac8f9be2ed03e949544bb0b53bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dkaser/unraid-utils/zipball/da010094bd0c4ac8f9be2ed03e949544bb0b53bc", + "reference": "da010094bd0c4ac8f9be2ed03e949544bb0b53bc", + "shasum": "" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.64", + "phpstan/phpstan": "^2.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "EDACerton\\PluginUtils\\": "src/" + } + }, + "license": [ + "GPL-3.0-or-later" + ], + "authors": [ + { + "name": "Derek Kaser" + } + ], + "description": "Utility classes", + "support": { + "source": "https://github.com/dkaser/unraid-utils/tree/1.2.1-stable", + "issues": "https://github.com/dkaser/unraid-utils/issues" + }, + "time": "2025-06-04T03:29:23+00:00" + } + ], + "packages-dev": [ + { + "name": "clue/ndjson-react", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/clue/reactphp-ndjson.git", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/stream": "^1.2" + }, + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\NDJson\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", + "homepage": "https://github.com/clue/reactphp-ndjson", + "keywords": [ + "NDJSON", + "json", + "jsonlines", + "newline", + "reactphp", + "streaming" + ], + "support": { + "issues": "https://github.com/clue/reactphp-ndjson/issues", + "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" + }, + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-12-23T10:58:28+00:00" + }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.3", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-09-19T14:15:21+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "evenement/evenement", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Evenement\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" + }, + "time": "2023-08-08T05:53:35+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "8520451a140d3f46ac33042715115e290cf5785f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-08-06T10:04:20+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.75.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/399a128ff2fdaf4281e4e79b755693286cdf325c", + "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c", + "shasum": "" + }, + "require": { + "clue/ndjson-react": "^1.0", + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.3", + "ext-filter": "*", + "ext-hash": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "fidry/cpu-core-counter": "^1.2", + "php": "^7.4 || ^8.0", + "react/child-process": "^0.6.5", + "react/event-loop": "^1.0", + "react/promise": "^2.0 || ^3.0", + "react/socket": "^1.0", + "react/stream": "^1.0", + "sebastian/diff": "^4.0 || ^5.1 || ^6.0 || ^7.0", + "symfony/console": "^5.4 || ^6.4 || ^7.0", + "symfony/event-dispatcher": "^5.4 || ^6.4 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.4 || ^7.0", + "symfony/finder": "^5.4 || ^6.4 || ^7.0", + "symfony/options-resolver": "^5.4 || ^6.4 || ^7.0", + "symfony/polyfill-mbstring": "^1.31", + "symfony/polyfill-php80": "^1.31", + "symfony/polyfill-php81": "^1.31", + "symfony/process": "^5.4 || ^6.4 || ^7.2", + "symfony/stopwatch": "^5.4 || ^6.4 || ^7.0" + }, + "require-dev": { + "facile-it/paraunit": "^1.3.1 || ^2.6", + "infection/infection": "^0.29.14", + "justinrainbow/json-schema": "^5.3 || ^6.2", + "keradus/cli-executor": "^2.1", + "mikey179/vfsstream": "^1.6.12", + "php-coveralls/php-coveralls": "^2.7", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", + "phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.12", + "symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.3", + "symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.3" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + }, + "exclude-from-classmap": [ + "src/Fixer/Internal/*" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "keywords": [ + "Static code analysis", + "fixer", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.75.0" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2025-03-31T18:40:42+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "2.1.17", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053", + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2025-05-21T20:55:28+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "react/cache", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2022-11-30T15:59:55+00:00" + }, + { + "name": "react/child-process", + "version": "v0.6.6", + "source": { + "type": "git", + "url": "https://github.com/reactphp/child-process.git", + "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", + "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/event-loop": "^1.2", + "react/stream": "^1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/socket": "^1.16", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\ChildProcess\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven library for executing child processes with ReactPHP.", + "keywords": [ + "event-driven", + "process", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.6" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-01-01T16:37:48+00:00" + }, + { + "name": "react/dns", + "version": "v1.13.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Dns\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.13.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-13T14:18:03+00:00" + }, + { + "name": "react/event-loop", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "suggest": { + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "support": { + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-11-13T13:48:05+00:00" + }, + { + "name": "react/promise", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpstan/phpstan": "1.10.39 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-05-24T10:39:05+00:00" + }, + { + "name": "react/socket", + "version": "v1.16.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.13", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3.3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Socket\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], + "support": { + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.16.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-07-26T10:38:09+00:00" + }, + { + "name": "react/stream", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], + "support": { + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.4.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-11T12:45:25+00:00" + }, + { + "name": "sebastian/diff", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:46+00:00" + }, + { + "name": "symfony/console", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/66c1440edf6f339fd82ed6c7caa76cb006211b44", + "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-05-24T10:34:04+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "497f73ac996a598c92409b44ac43b6690c4f666d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d", + "reference": "497f73ac996a598c92409b44ac43b6690c4f666d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-22T09:11:45+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-25T15:15:23+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/ec2344cf77a48253bbca6939aa3d2477773ea63d", + "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-30T19:00:26+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/afb9a8038025e5dbc657378bfab9198d75f10fca", + "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-04T13:12:05+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-02T08:10:11+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-17T09:11:12+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-25T09:37:31+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", + "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-02-24T10:49:57+00:00" + }, + { + "name": "symfony/string", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125", + "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-20T20:19:01+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Config.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Config.php new file mode 100644 index 0000000..f0825fd --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Config.php @@ -0,0 +1,60 @@ +. +*/ + +namespace Tailscale; + +class Config +{ + public bool $IncludeInterface; + public bool $Usage; + public bool $IPForward; + public bool $Enable; + public bool $SSH; + public bool $AllowDNS; + public bool $AllowRoutes; + public bool $AllowFunnel; + + public int $WgPort; + public string $TaildropDir; + + public function __construct() + { + $config_file = '/boot/config/plugins/tailscale/tailscale.cfg'; + + // Load configuration file + if (file_exists($config_file)) { + $saved_config = parse_ini_file($config_file) ?: array(); + } else { + $saved_config = array(); + } + + $this->IncludeInterface = boolval($saved_config["INCLUDE_INTERFACE"] ?? "1"); + $this->Usage = boolval($saved_config["USAGE"] ?? "1"); + $this->IPForward = boolval($saved_config["SYSCTL_IP_FORWARD"] ?? "1"); + $this->Enable = boolval($saved_config["ENABLE_TAILSCALE"] ?? "1"); + $this->SSH = boolval($saved_config["SSH"] ?? "0"); + $this->AllowDNS = boolval($saved_config["ACCEPT_DNS"] ?? "0"); + $this->AllowRoutes = boolval($saved_config["ACCEPT_ROUTES"] ?? "0"); + $this->AllowFunnel = boolval($saved_config["ALLOW_FUNNEL"] ?? "0"); + + $this->WgPort = intval($saved_config["WG_PORT"] ?? "0"); + + $this->TaildropDir = $saved_config["TAILDROP_DIR"] ?? ""; + } +} diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/ConnectionInfo.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/ConnectionInfo.php new file mode 100644 index 0000000..ae2b7ad --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/ConnectionInfo.php @@ -0,0 +1,35 @@ +. +*/ + +namespace Tailscale; + +class ConnectionInfo +{ + public string $HostName = ""; + public string $DNSName = ""; + public string $TailscaleIPs = ""; + public string $MagicDNSSuffix = ""; + public string $AdvertisedRoutes = ""; + public string $AcceptRoutes = ""; + public string $AcceptDNS = ""; + public string $RunSSH = ""; + public string $ExitNodeLocal = ""; + public string $AdvertiseExitNode = ""; + public string $UseExitNode = ""; +} diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/DashboardInfo.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/DashboardInfo.php new file mode 100644 index 0000000..316f334 --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/DashboardInfo.php @@ -0,0 +1,30 @@ +. +*/ + +namespace Tailscale; + +class DashboardInfo +{ + /** @var array $TailscaleIPs */ + public array $TailscaleIPs = array(); + + public string $HostName = ""; + public string $DNSName = ""; + public string $Online = ""; +} diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Info.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Info.php new file mode 100644 index 0000000..52bd543 --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Info.php @@ -0,0 +1,474 @@ +. +*/ + +namespace Tailscale; + +use EDACerton\PluginUtils\Translator; + +class Info +{ + private string $useNetbios; + private string $smbEnabled; + private Translator $tr; + private LocalAPI $localAPI; + private \stdClass $status; + private \stdClass $prefs; + private \stdClass $lock; + private \stdClass $serve; + + public function __construct(Translator $tr) + { + $share_config = parse_ini_file("/boot/config/share.cfg") ?: array(); + $ident_config = parse_ini_file("/boot/config/ident.cfg") ?: array(); + + $this->localAPI = new LocalAPI(); + + $this->tr = $tr; + $this->smbEnabled = $share_config['shareSMBEnabled'] ?? ""; + $this->useNetbios = $ident_config['USE_NETBIOS'] ?? ""; + $this->status = $this->localAPI->getStatus(); + $this->prefs = $this->localAPI->getPrefs(); + $this->lock = $this->localAPI->getTkaStatus(); + $this->serve = $this->localAPI->getServeConfig(); + } + + public function getStatus(): \stdClass + { + return $this->status; + } + + public function getPrefs(): \stdClass + { + return $this->prefs; + } + + public function getLock(): \stdClass + { + return $this->lock; + } + + private function tr(string $message): string + { + return $this->tr->tr($message); + } + + public function getStatusInfo(): StatusInfo + { + $status = $this->status; + $prefs = $this->prefs; + $lock = $this->lock; + + $statusInfo = new StatusInfo(); + + $statusInfo->TsVersion = isset($status->Version) ? $status->Version : $this->tr("unknown"); + $statusInfo->KeyExpiration = isset($status->Self->KeyExpiry) ? $status->Self->KeyExpiry : $this->tr("disabled"); + $statusInfo->Online = isset($status->Self->Online) ? ($status->Self->Online ? $this->tr("yes") : $this->tr("no")) : $this->tr("unknown"); + $statusInfo->InNetMap = isset($status->Self->InNetworkMap) ? ($status->Self->InNetworkMap ? $this->tr("yes") : $this->tr("no")) : $this->tr("unknown"); + $statusInfo->Tags = isset($status->Self->Tags) ? implode("
", $status->Self->Tags) : ""; + $statusInfo->LoggedIn = isset($prefs->LoggedOut) ? ($prefs->LoggedOut ? $this->tr("no") : $this->tr("yes")) : $this->tr("unknown"); + $statusInfo->TsHealth = isset($status->Health) ? implode("
", $status->Health) : ""; + $statusInfo->LockEnabled = $this->getTailscaleLockEnabled() ? $this->tr("yes") : $this->tr("no"); + + if ($this->getTailscaleLockEnabled()) { + $lockInfo = new LockInfo(); + + $lockInfo->LockSigned = $this->getTailscaleLockSigned() ? $this->tr("yes") : $this->tr("no"); + $lockInfo->LockSigning = $this->getTailscaleLockSigning() ? $this->tr("yes") : $this->tr("no"); + $lockInfo->PubKey = $this->getTailscaleLockPubkey(); + $lockInfo->NodeKey = $this->getTailscaleLockNodekey(); + + $statusInfo->LockInfo = $lockInfo; + } + + return $statusInfo; + } + + public function getConnectionInfo(): ConnectionInfo + { + $status = $this->status; + $prefs = $this->prefs; + + $info = new ConnectionInfo(); + + $info->HostName = isset($status->Self->HostName) ? $status->Self->HostName : $this->tr("unknown"); + $info->DNSName = isset($status->Self->DNSName) ? $status->Self->DNSName : $this->tr("unknown"); + $info->TailscaleIPs = isset($status->TailscaleIPs) ? implode("
", $status->TailscaleIPs) : $this->tr("unknown"); + $info->MagicDNSSuffix = isset($status->MagicDNSSuffix) ? $status->MagicDNSSuffix : $this->tr("unknown"); + $info->AdvertisedRoutes = isset($prefs->AdvertiseRoutes) ? implode("
", $prefs->AdvertiseRoutes) : $this->tr("none"); + $info->AcceptRoutes = isset($prefs->RouteAll) ? ($prefs->RouteAll ? $this->tr("yes") : $this->tr("no")) : $this->tr("unknown"); + $info->AcceptDNS = isset($prefs->CorpDNS) ? ($prefs->CorpDNS ? $this->tr("yes") : $this->tr("no")) : $this->tr("unknown"); + $info->RunSSH = isset($prefs->RunSSH) ? ($prefs->RunSSH ? $this->tr("yes") : $this->tr("no")) : $this->tr("unknown"); + $info->ExitNodeLocal = isset($prefs->ExitNodeAllowLANAccess) ? ($prefs->ExitNodeAllowLANAccess ? $this->tr("yes") : $this->tr("no")) : $this->tr("unknown"); + $info->UseExitNode = $this->usesExitNode() ? $this->tr("yes") : $this->tr("no"); + + if ($this->advertisesExitNode()) { + if ($this->status->Self->ExitNodeOption) { + $info->AdvertiseExitNode = $this->tr("yes"); + } else { + $info->AdvertiseExitNode = $this->tr("info.unapproved"); + } + } else { + $info->AdvertiseExitNode = $this->tr("no"); + } + + return $info; + } + + public function getDashboardInfo(): DashboardInfo + { + $status = $this->status; + + $info = new DashboardInfo(); + + $info->HostName = isset($status->Self->HostName) ? $status->Self->HostName : $this->tr("Unknown"); + $info->DNSName = isset($status->Self->DNSName) ? $status->Self->DNSName : $this->tr("Unknown"); + $info->TailscaleIPs = isset($status->TailscaleIPs) ? $status->TailscaleIPs : array(); + $info->Online = isset($status->Self->Online) ? ($status->Self->Online ? $this->tr("yes") : $this->tr("no")) : $this->tr("unknown"); + + return $info; + } + + public function getKeyExpirationWarning(): ?Warning + { + $status = $this->status; + + if (isset($status->Self->KeyExpiry)) { + $expiryTime = new \DateTime($status->Self->KeyExpiry); + $expiryTime->setTimezone(new \DateTimeZone(date_default_timezone_get())); + + $interval = $expiryTime->diff(new \DateTime('now')); + $expiryPrint = $expiryTime->format(\DateTimeInterface::RFC7231); + $intervalPrint = $interval->format('%a'); + + $warning = new Warning(sprintf($this->tr("warnings.key_expiration"), $intervalPrint, $expiryPrint)); + + switch (true) { + case $interval->days <= 7: + $warning->Priority = 'error'; + break; + case $interval->days <= 30: + $warning->Priority = 'warn'; + break; + default: + $warning->Priority = 'system'; + break; + } + + return $warning; + } + return null; + } + + public function getTailscaleLockEnabled(): bool + { + return $this->lock->Enabled ?? false; + } + + public function getTailscaleLockSigned(): bool + { + if ( ! $this->getTailscaleLockEnabled()) { + return false; + } + + return $this->lock->NodeKeySigned; + } + + public function getTailscaleLockNodekey(): string + { + if ( ! $this->getTailscaleLockEnabled()) { + return ""; + } + + return $this->lock->NodeKey; + } + + public function getTailscaleLockPubkey(): string + { + if ( ! $this->getTailscaleLockEnabled()) { + return ""; + } + + return $this->lock->PublicKey; + } + + public function getTailscaleLockSigning(): bool + { + if ( ! $this->getTailscaleLockSigned()) { + return false; + } + + $isTrusted = false; + $myKey = $this->getTailscaleLockPubkey(); + + foreach ($this->lock->TrustedKeys as $item) { + if ($item->Key == $myKey) { + $isTrusted = true; + } + } + + return $isTrusted; + } + + /** + * @return array + */ + public function getTailscaleLockPending(): array + { + if ( ! $this->getTailscaleLockSigning()) { + return array(); + } + + $pending = array(); + + foreach ($this->lock->FilteredPeers as $item) { + $pending[$item->Name] = $item->NodeKey; + } + + return $pending; + } + + public function getTailscaleLockWarning(): ?Warning + { + if ($this->getTailscaleLockEnabled() && ( ! $this->getTailscaleLockSigned())) { + return new Warning($this->tr("warnings.lock"), "error"); + } + return null; + } + + public function getTailscaleNetbiosWarning(): ?Warning + { + if (($this->useNetbios == "yes") && ($this->smbEnabled != "no")) { + return new Warning($this->tr("warnings.netbios"), "warn"); + } + return null; + } + + /** + * @return array + */ + public function getPeerStatus(): array + { + $result = array(); + + foreach ($this->status->Peer as $node => $status) { + $peer = new PeerStatus(); + + $peer->Name = trim($status->DNSName, "."); + $peer->IP = $status->TailscaleIPs; + + $peer->LoginName = $this->status->User->{$status->UserID}->LoginName; + $peer->SharedUser = isset($status->ShareeNode); + + if ($status->ExitNode) { + $peer->ExitNodeActive = true; + } elseif ($status->ExitNodeOption) { + $peer->ExitNodeAvailable = true; + } + $peer->Mullvad = in_array("tag:mullvad-exit-node", $status->Tags ?? array()); + + if ($status->TxBytes > 0 || $status->RxBytes > 0) { + $peer->Traffic = true; + $peer->TxBytes = $status->TxBytes; + $peer->RxBytes = $status->RxBytes; + } + + if ( ! $status->Online) { + $peer->Online = false; + $peer->Active = false; + } elseif ( ! $status->Active) { + $peer->Online = true; + $peer->Active = false; + } else { + $peer->Online = true; + $peer->Active = true; + + if (($status->Relay != "") && ($status->CurAddr == "")) { + $peer->Relayed = true; + $peer->Address = $status->Relay; + } elseif ($status->CurAddr != "") { + $peer->Relayed = false; + $peer->Address = $status->CurAddr; + } + } + + $result[] = $peer; + } + + return $result; + } + + public function advertisesExitNode(): bool + { + foreach (($this->prefs->AdvertiseRoutes ?? array()) as $net) { + switch ($net) { + case "0.0.0.0/0": + case "::/0": + return true; + } + } + + return false; + } + + public function usesExitNode(): bool + { + if (($this->prefs->ExitNodeID ?? "") || ($this->prefs->ExitNodeIP ?? "")) { + return true; + } + return false; + } + + public function exitNodeLocalAccess(): bool + { + return $this->prefs->ExitNodeAllowLANAccess ?? false; + } + + public function acceptsDNS(): bool + { + return $this->prefs->CorpDNS ?? false; + } + + public function acceptsRoutes(): bool + { + return $this->prefs->RouteAll ?? false; + } + + public function runsSSH(): bool + { + return $this->prefs->RunSSH ?? false; + } + + public function isOnline(): bool + { + return $this->status->Self->Online ?? false; + } + + public function getAuthURL(): string + { + return $this->status->AuthURL ?? ""; + } + + public function needsLogin(): bool + { + return ($this->status->BackendState ?? "") == "NeedsLogin"; + } + + /** + * @return array + */ + public function getAdvertisedRoutes(): array + { + $advertisedRoutes = $this->prefs->AdvertiseRoutes ?? array(); + $exitNodeRoutes = ["0.0.0.0/0", "::/0"]; + return array_diff($advertisedRoutes, $exitNodeRoutes); + } + + public function isApprovedRoute(string $route): bool + { + return in_array($route, $this->status->Self->AllowedIPs ?? array()); + } + + public function getTailnetName(): string + { + return $this->status->CurrentTailnet->Name ?? ""; + } + + /** + * @return array + */ + public function getExitNodes(): array + { + $exitNodes = array(); + + foreach (($this->status->Peer ?? array()) as $node => $status) { + if ($status->ExitNodeOption ?? false) { + $nodeName = $status->DNSName; + if (isset($status->Location->City)) { + $nodeName .= " (" . $status->Location->City . ")"; + } + $exitNodes[$status->ID] = $nodeName; + } + } + + return $exitNodes; + } + + public function getCurrentExitNode(): string + { + foreach (($this->status->Peer ?? array()) as $node => $status) { + if ($status->ExitNode ?? false) { + return $status->ID; + } + } + + return ""; + } + + public function connectedViaTS(): bool + { + return in_array($_SERVER['SERVER_ADDR'] ?? "", $this->status->TailscaleIPs ?? array()); + } + + /** + * @return array + */ + public function getAllowedFunnelPorts(): array + { + $allowedPorts = array(); + + if (isset($this->status->Self->CapMap)) { + $prefix = "https://tailscale.com/cap/funnel-ports?ports="; + foreach ($this->status->Self->CapMap as $cap => $value) { + if (strpos($cap, $prefix) === 0) { + $ports = explode(",", substr($cap, strlen($prefix))); + foreach ($ports as $port) { + $allowedPorts[] = intval($port); + } + break; + } + } + } + return $allowedPorts; + } + + public function getFunnelPort(): ?int + { + if (isset($this->serve->AllowFunnel) && $this->serve->AllowFunnel) { + $funnelKeys = array_keys((array)$this->serve->AllowFunnel); + if (count($funnelKeys) > 0) { + $funnelKey = $funnelKeys[0]; + $parts = explode(":", strval($funnelKey)); + if (count($parts) == 2 && is_numeric($parts[1])) { + return intval($parts[1]); + } + } + } + + return null; // Funnel not enabled + } + + public function getDNSName(): string + { + if ( ! isset($this->status->Self->DNSName)) { + throw new \RuntimeException("DNSName not set in Tailscale status."); + } + + return $this->status->Self->DNSName; + } +} diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LocalAPI.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LocalAPI.php new file mode 100644 index 0000000..2dc4448 --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LocalAPI.php @@ -0,0 +1,138 @@ +. +*/ + +namespace Tailscale; + +enum APIMethods +{ + case GET; + case POST; + case PATCH; +} + +class LocalAPI +{ + private const tailscaleSocket = '/var/run/tailscale/tailscaled.sock'; + private Utils $utils; + + public function __construct() + { + if ( ! defined(__NAMESPACE__ . "\PLUGIN_ROOT") || ! defined(__NAMESPACE__ . "\PLUGIN_NAME")) { + throw new \RuntimeException("Common file not loaded."); + } + $this->utils = new Utils(PLUGIN_NAME); + } + + private function tailscaleLocalAPI(string $url, APIMethods $method = APIMethods::GET, object $body = new \stdClass()): string + { + if (empty($url)) { + throw new \InvalidArgumentException("URL cannot be empty"); + } + + $body_encoded = json_encode($body, JSON_UNESCAPED_SLASHES); + + if ( ! $body_encoded) { + throw new \InvalidArgumentException("Failed to encode JSON"); + } + + $ch = curl_init(); + + $headers = []; + + curl_setopt($ch, CURLOPT_TIMEOUT, 5); + curl_setopt($ch, CURLOPT_UNIX_SOCKET_PATH, $this::tailscaleSocket); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_URL, "http://local-tailscaled.sock/localapi/{$url}"); + + if ($method == APIMethods::POST) { + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $body_encoded); + $this->utils->logmsg("Tailscale Local API: {$url} POST " . $body_encoded); + $headers[] = "Content-Type: application/json"; + } + + if ($method == APIMethods::PATCH) { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH'); + curl_setopt($ch, CURLOPT_POSTFIELDS, $body_encoded); + $this->utils->logmsg("Tailscale Local API: {$url} PATCH " . $body_encoded); + $headers[] = "Content-Type: application/json"; + } + + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + $out = curl_exec($ch) ?: false; + curl_close($ch); + return strval($out); + } + + public function getStatus(): \stdClass + { + return (object) json_decode($this->tailscaleLocalAPI('v0/status')); + } + + public function getPrefs(): \stdClass + { + return (object) json_decode($this->tailscaleLocalAPI('v0/prefs')); + } + + public function getTkaStatus(): \stdClass + { + return (object) json_decode($this->tailscaleLocalAPI('v0/tka/status')); + } + + public function getServeConfig(): \stdClass + { + return (object) json_decode($this->tailscaleLocalAPI('v0/serve-config')); + } + + public function resetServeConfig(): void + { + $this->tailscaleLocalAPI("v0/serve-config", APIMethods::POST, new \stdClass()); + } + + public function setServeConfig(ServeConfig $serveConfig): void + { + $this->tailscaleLocalAPI("v0/serve-config", APIMethods::POST, $serveConfig->getConfig()); + } + + public function postLoginInteractive(): void + { + $this->tailscaleLocalAPI('v0/login-interactive', APIMethods::POST); + } + + public function patchPref(string $key, mixed $value): void + { + $body = []; + $body[$key] = $value; + $body["{$key}Set"] = true; + + $this->tailscaleLocalAPI('v0/prefs', APIMethods::PATCH, (object) $body); + } + + public function postTkaSign(string $key): void + { + $body = ["NodeKey" => $key]; + $this->tailscaleLocalAPI("v0/tka/sign", APIMethods::POST, (object) $body); + } + + public function expireKey(): void + { + $this->tailscaleLocalAPI('v0/set-expiry-sooner?expiry=0', APIMethods::POST); + } +} diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LockInfo.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LockInfo.php new file mode 100644 index 0000000..3e323c6 --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LockInfo.php @@ -0,0 +1,28 @@ +. +*/ + +namespace Tailscale; + +class LockInfo +{ + public string $LockSigned = ""; + public string $LockSigning = ""; + public string $PubKey = ""; + public string $NodeKey = ""; +} diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/PeerStatus.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/PeerStatus.php new file mode 100644 index 0000000..ba63cb0 --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/PeerStatus.php @@ -0,0 +1,44 @@ +. +*/ + +namespace Tailscale; + +class PeerStatus +{ + public string $Name = ""; + public string $LoginName = ""; + public bool $SharedUser = false; + + /** @var string[] */ + public array $IP = array(); + + public string $Address = ""; + + public bool $Online = false; + public bool $Active = false; + public bool $Relayed = false; + + public bool $Traffic = false; + public int $TxBytes = 0; + public int $RxBytes = 0; + + public bool $ExitNodeActive = false; + public bool $ExitNodeAvailable = false; + public bool $Mullvad = false; +} diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/ServeConfig.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/ServeConfig.php new file mode 100644 index 0000000..2aaf36f --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/ServeConfig.php @@ -0,0 +1,60 @@ +. +*/ + +namespace Tailscale; + +class ServeConfig +{ + private \stdClass $config; + + public function __construct(string $hostname, string $port, string $target) + { + // Validate the hostname + if ( ! filter_var($hostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) { + throw new \InvalidArgumentException("Invalid hostname: {$hostname}"); + } + + // Validate the port + if ( ! is_numeric($port) || (int)$port < 1 || (int)$port > 65535) { + throw new \InvalidArgumentException("Invalid port: {$port}"); + } + + $hostAndPort = "{$hostname}:{$port}"; + + $this->config = new \stdClass(); + + $this->config->TCP = new \stdClass(); + $this->config->TCP->{$port} = new \stdClass(); + $this->config->TCP->{$port}->HTTPS = true; + + $this->config->Web = new \stdClass(); + $this->config->Web->{$hostAndPort} = new \stdClass(); + $this->config->Web->{$hostAndPort}->Handlers = new \stdClass(); + $this->config->Web->{$hostAndPort}->Handlers->{'/'} = new \stdClass(); + $this->config->Web->{$hostAndPort}->Handlers->{'/'}->Proxy = $target; + + $this->config->AllowFunnel = new \stdClass(); + $this->config->AllowFunnel->{$hostAndPort} = true; + } + + public function getConfig(): \stdClass + { + return $this->config; + } +} diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/StatusInfo.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/StatusInfo.php new file mode 100644 index 0000000..102bd87 --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/StatusInfo.php @@ -0,0 +1,33 @@ +. +*/ + +namespace Tailscale; + +class StatusInfo +{ + public ?LockInfo $LockInfo = null; + public string $TsVersion = ""; + public string $KeyExpiration = ""; + public string $Online = ""; + public string $InNetMap = ""; + public string $Tags = ""; + public string $LoggedIn = ""; + public string $TsHealth = ""; + public string $LockEnabled = ""; +} diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/System.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/System.php new file mode 100644 index 0000000..23c52c7 --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/System.php @@ -0,0 +1,321 @@ +. +*/ + +namespace Tailscale; + +enum NotificationType: string +{ + case NORMAL = 'normal'; + case WARNING = 'warning'; + case ALERT = 'alert'; +} + +class System +{ + public const RESTART_COMMAND = "/usr/local/emhttp/webGui/scripts/reload_services"; + public const NOTIFY_COMMAND = "/usr/local/emhttp/webGui/scripts/notify"; + + public static function fixLocalSubnetRoutes(): void + { + $ips = parse_ini_file("/boot/config/network.cfg") ?: array(); + if (array_key_exists(('IPADDR'), $ips)) { + $route_table = Utils::runwrap("ip route list table 52", false, false); + + $ipaddr = is_array($ips['IPADDR']) ? $ips['IPADDR'] : array($ips['IPADDR']); + + foreach ($ipaddr as $ip) { + foreach ($route_table as $route) { + $net = explode(' ', $route)[0]; + if (Utils::ip4_in_network($ip, $net)) { + Utils::logwrap("Detected local IP {$ip} in Tailscale route {$net}, removing"); + Utils::runwrap("ip route del '{$net}' dev tailscale1 table 52"); + } + } + } + } + } + + public static function checkWebgui(Config $config, string $tailscale_ipv4): void + { + // Make certain that the WebGUI is listening on the Tailscale interface + if ($config->IncludeInterface) { + $ident_config = parse_ini_file("/boot/config/ident.cfg") ?: array(); + + $connection = @fsockopen($tailscale_ipv4, $ident_config['PORT']); + + if (is_resource($connection)) { + Utils::logwrap("WebGUI listening on {$tailscale_ipv4}:{$ident_config['PORT']}", false, true); + } else { + Utils::logwrap("WebGUI not listening on {$tailscale_ipv4}:{$ident_config['PORT']}, terminating and restarting"); + Utils::runwrap("/etc/rc.d/rc.nginx term"); + sleep(5); + Utils::runwrap("/etc/rc.d/rc.nginx start"); + } + } + } + + public static function checkServeConfig(): void + { + $ident_config = parse_ini_file("/boot/config/ident.cfg") ?: array(); + + $httpPort = isset($ident_config['PORT']) && is_scalar($ident_config['PORT']) + ? intval($ident_config['PORT']) : 80; + $httpsPort = isset($ident_config['PORTSSL']) && is_scalar($ident_config['PORTSSL']) + ? intval($ident_config['PORTSSL']) : 443; + + $localAPI = new LocalAPI(); + $serveConfig = $localAPI->getServeConfig(); + + $tcpConfig = $serveConfig->TCP ?? array(); + + foreach ($tcpConfig as $key => $val) { + $configPort = intval($key); + + if ($configPort == $httpPort || $configPort == $httpsPort) { + Utils::logwrap("Serve TCP Port {$configPort} conflicts with WebGUI, removing"); + self::sendNotification( + "Tailscale Serve Port Conflict", + "Tailscale Serve Port Conflict", + "Port {$configPort} conflicts with WebGUI port. The Tailscale serve config has been reset to remove the conflict.", + NotificationType::ALERT + ); + $localAPI->resetServeConfig(); + Utils::runwrap(self::RESTART_COMMAND); + + return; + } else { + Utils::logwrap("Checked for WebGUI conflict with serve TCP Port {$configPort}", false, true); + } + } + + // Check if serveConfig has an AllowFunnel property. If this exists, but Config->AllowFunnel is false, reset the config. + $config = new Config(); + if (isset($serveConfig->AllowFunnel) && $config->AllowFunnel === false) { + Utils::logwrap("Tailscale funnel is enabled, but config does not allow it, resetting serve config"); + $localAPI->resetServeConfig(); + } + } + + public static function restartSystemServices(Config $config): void + { + if ($config->IncludeInterface) { + self::refreshWebGuiCert(false); + + Utils::runwrap(self::RESTART_COMMAND); + } + } + + public static function enableIPForwarding(Config $config): void + { + if ($config->Enable) { + Utils::logwrap("Enabling IP forwarding"); + $sysctl = "net.ipv4.ip_forward = 1" . PHP_EOL . "net.ipv6.conf.all.forwarding = 1"; + file_put_contents('/etc/sysctl.d/99-tailscale.conf', $sysctl); + Utils::runwrap("sysctl -p /etc/sysctl.d/99-tailscale.conf", true); + } + } + + public static function applyGRO(): void + { + /** @var array> $ip_route */ + $ip_route = (array) json_decode(implode(Utils::runwrap('ip -j route get 8.8.8.8')), true); + + // Check if a device was returned + if ( ! isset($ip_route[0]['dev'])) { + Utils::logwrap("Default interface could not be detected."); + return; + } + + $dev = $ip_route[0]['dev']; + + /** @var array> $ethtool */ + $ethtool = ((array) json_decode(implode(Utils::runwrap("ethtool --json -k {$dev}")), true))[0]; + + if (isset($ethtool['rx-udp-gro-forwarding']) && ! $ethtool['rx-udp-gro-forwarding']['active']) { + Utils::runwrap("ethtool -K {$dev} rx-udp-gro-forwarding on"); + } + + if (isset($ethtool['rx-gro-list']) && $ethtool['rx-gro-list']['active']) { + Utils::runwrap("ethtool -K {$dev} rx-gro-list off"); + } + } + + public static function notifyOnKeyExpiration(): void + { + $localAPI = new LocalAPI(); + $status = $localAPI->getStatus(); + + if (isset($status->Self->KeyExpiry)) { + $expiryTime = new \DateTime($status->Self->KeyExpiry); + $expiryTime->setTimezone(new \DateTimeZone(date_default_timezone_get())); + $interval = $expiryTime->diff(new \DateTime('now')); + + $expiryPrint = $expiryTime->format(\DateTimeInterface::RFC7231); + $intervalPrint = $interval->format('%a'); + + $message = "The Tailscale key will expire in {$intervalPrint} days on {$expiryPrint}."; + Utils::logwrap($message); + + switch (true) { + case $interval->days <= 7: + $priority = NotificationType::ALERT; + break; + case $interval->days <= 30: + $priority = NotificationType::WARNING; + break; + default: + return; + } + + $event = "Tailscale Key Expiration - {$priority->value} - {$expiryTime->format('Ymd')}"; + Utils::logwrap("Sending notification for key expiration: {$event}"); + self::sendNotification($event, "Tailscale key is expiring", $message, $priority); + } else { + Utils::logwrap("Tailscale key expiration is not set."); + } + } + + public static function sendNotification(string $event, string $subject, string $message, NotificationType $priority): void + { + $command = self::NOTIFY_COMMAND . " -l '/Settings/Tailscale' -e " . escapeshellarg($event) . " -s " . escapeshellarg($subject) . " -d " . escapeshellarg("{$message}") . " -i \"{$priority->value}\" -x 2>/dev/null"; + exec($command); + } + + public static function refreshWebGuiCert(bool $restartIfChanged = true): void + { + $localAPI = new LocalAPI(); + $status = $localAPI->getStatus(); + + $certDomains = $status->CertDomains; + + if (count($certDomains ?? array()) === 0) { + Utils::logwrap("Cannot generate certificate for WebGUI -- HTTPS not enabled for Tailnet."); + return; + } + + $dnsName = $certDomains[0]; + + $certFile = "/boot/config/plugins/tailscale/state/certs/{$dnsName}.crt"; + $keyFile = "/boot/config/plugins/tailscale/state/certs/{$dnsName}.key"; + $pemFile = "/boot/config/ssl/certs/ts_bundle.pem"; + + clearstatcache(); + + $pemHash = ''; + if (file_exists($pemFile)) { + $pemHash = sha1_file($pemFile); + } + + Utils::logwrap("Certificate bundle hash: {$pemHash}"); + + Utils::runwrap("tailscale cert --cert-file={$certFile} --key-file={$keyFile} --min-validity=720h {$dnsName}"); + + if ( + file_exists($certFile) && file_exists($keyFile) && filesize($certFile) > 0 && filesize($keyFile) > 0 + ) { + file_put_contents($pemFile, file_get_contents($certFile)); + file_put_contents($pemFile, file_get_contents($keyFile), FILE_APPEND); + + if ((sha1_file($pemFile) != $pemHash) && $restartIfChanged) { + Utils::logwrap("WebGUI certificate has changed, restarting nginx"); + Utils::runwrap("/etc/rc.d/rc.nginx reload"); + } + } else { + Utils::logwrap("Something went wrong when creating WebGUI certificate, skipping nginx update."); + } + } + + public static function setExtraInterface(Config $config): void + { + if (file_exists(self::RESTART_COMMAND)) { + $include_array = array(); + $exclude_interfaces = ""; + $write_file = true; + $network_extra_file = '/boot/config/network-extra.cfg'; + $ifname = 'tailscale1'; + + if (file_exists($network_extra_file)) { + $netExtra = parse_ini_file($network_extra_file); + if ($netExtra['include_interfaces'] ?? false) { + $include_array = explode(' ', $netExtra['include_interfaces']); + } + if ($netExtra['exclude_interfaces'] ?? false) { + $exclude_interfaces = $netExtra['exclude_interfaces']; + } + $write_file = false; + } + + $in_array = in_array($ifname, $include_array); + + if ($in_array != $config->IncludeInterface) { + if ($config->IncludeInterface) { + $include_array[] = $ifname; + Utils::logwrap("{$ifname} added to include_interfaces"); + } else { + $include_array = array_diff($include_array, [$ifname]); + Utils::logwrap("{$ifname} removed from include_interfaces"); + } + $write_file = true; + } + + if ($write_file) { + $include_interfaces = implode(' ', $include_array); + + $file = <<patchPref($flag, false); + } + } + + public static function applyTailscaleConfig(Config $config): void + { + $localAPI = new LocalAPI(); + + self::disableTailscaleFeature($localAPI, $config->AllowRoutes, 'RouteAll'); + self::disableTailscaleFeature($localAPI, $config->AllowDNS, 'CorpDNS'); + + $localAPI->patchPref('NoStatefulFiltering', true); + } + + public static function createTailscaledParamsFile(Config $config): void + { + $custom_params = ""; + + if ($config->WgPort > 0 && $config->WgPort < 65535) { + $custom_params .= "-port {$config->WgPort} "; + } + + file_put_contents('/usr/local/emhttp/plugins/tailscale/custom-params.sh', 'TAILSCALE_CUSTOM_PARAMS="' . $custom_params . '"'); + } +} diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Utils.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Utils.php new file mode 100644 index 0000000..d031320 --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Utils.php @@ -0,0 +1,189 @@ +. +*/ + +namespace Tailscale; + +class Utils extends \EDACerton\PluginUtils\Utils +{ + public function setPHPDebug(): void + { + $version = parse_ini_file('/var/local/emhttp/plugins/tailscale/tailscale.ini'); + + if ( ! $version) { + $this->logmsg("Could not retrieve system data, skipping debug check."); + return; + } + + if ((($version['BRANCH'] ?? "") == "trunk") && ! defined("PLUGIN_DEBUG")) { + error_reporting(E_ALL); + define("PLUGIN_DEBUG", true); + } + } + + public static function printRow(string $title, string $value): string + { + return "{$title}{$value}" . PHP_EOL; + } + + public static function printDash(string $title, string $value): string + { + return "{$title}{$value}" . PHP_EOL; + } + + public static function formatWarning(?Warning $warning): string + { + if ($warning == null) { + return ""; + } + + return "" . $warning->Message . ""; + } + + public static function ip4_in_network(string $ip, string $network): bool + { + if (strpos($network, '/') === false) { + return false; + } + + list($subnet, $mask) = explode('/', $network, 2); + $ip_bin_string = sprintf("%032b", ip2long($ip)); + $net_bin_string = sprintf("%032b", ip2long($subnet)); + + return (substr_compare($ip_bin_string, $net_bin_string, 0, intval($mask)) === 0); + } + + public static function logwrap(string $message, bool $debug = false, bool $rateLimit = false): void + { + if ( ! defined(__NAMESPACE__ . "\PLUGIN_NAME")) { + throw new \RuntimeException("PLUGIN_NAME is not defined."); + } + $utils = new Utils(PLUGIN_NAME); + $utils->logmsg($message, $debug, $rateLimit); + } + + /** + * @return array + */ + public static function runwrap(string $command, bool $alwaysShow = false, bool $show = true): array + { + if ( ! defined(__NAMESPACE__ . "\PLUGIN_NAME")) { + throw new \RuntimeException("PLUGIN_NAME is not defined."); + } + $utils = new Utils(PLUGIN_NAME); + return $utils->run_command($command, $alwaysShow, $show); + } + + public static function validateCidr(string $cidr): bool + { + $parts = explode('/', $cidr); + if (count($parts) != 2) { + return false; + } + + $ip = $parts[0]; + $netmask = $parts[1]; + + if ( ! preg_match("/^\d+$/", $netmask)) { + return false; + } + + $netmask = intval($parts[1]); + + if ($netmask < 0) { + return false; + } + + if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + return $netmask <= 32; + } + + if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + return $netmask <= 128; + } + + return false; + } + + /** + * @return array + */ + public static function getExitRoutes(): array + { + return ["0.0.0.0/0", "::/0"]; + } + + public static function isFunnelAllowed(): bool + { + $directives = ["allow 127.0.0.1;", "allow ::1;"]; + + $nginxConfig = file_get_contents('/etc/nginx/nginx.conf'); + if ($nginxConfig === false) { + return false; // Unable to read the nginx configuration file + } + + // Search $nginxConfig for the allow directives. + foreach ($directives as $directive) { + if (strpos($nginxConfig, $directive) !== false) { + return false; // Directive found, funnel not safe to use + } + } + + return true; + } + + /** + * Get a list of ports that are currently assigned to services. + * This is a best-effort approach, especially since docker might not be running during configuration. + * + * @return array + */ + public function get_assigned_ports(): array + { + $ports = array(); + $identCfg = parse_ini_file("/boot/config/ident.cfg", false, INI_SCANNER_RAW) ?: array(); + if (isset($identCfg['PORT'])) { + $ports[] = intval($identCfg['PORT']); + } + if (isset($identCfg['PORTSSL']) && isset($identCfg['USE_SSL']) && $identCfg['USE_SSL'] === 'yes') { + $ports[] = intval($identCfg['PORTSSL']); + } + if (isset($identCfg['PORTTELNET']) && isset($identCfg['USE_TELNET']) && $identCfg['USE_TELNET'] === 'yes') { + $ports[] = intval($identCfg['PORTTELNET']); + } + if (isset($identCfg['PORTSSH']) && isset($identCfg['USE_SSH']) && $identCfg['USE_SSH'] === 'yes') { + $ports[] = intval($identCfg['PORTSSH']); + } + + // Get any open TCP ports from the system + $netstatOutput = shell_exec("netstat -tuln | grep LISTEN"); + if ($netstatOutput) { + $lines = explode("\n", trim($netstatOutput)); + foreach ($lines as $line) { + if (preg_match('/:(\d+)\s+/', $line, $matches)) { + $port = intval($matches[1]); + if ($port > 0 && $port < 65536) { + $ports[] = $port; + } + } + } + } + + return array_unique($ports); + } +} diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Warning.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Warning.php new file mode 100644 index 0000000..366f900 --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Warning.php @@ -0,0 +1,32 @@ +. +*/ + +namespace Tailscale; + +class Warning +{ + public string $Message; + public string $Priority; + + public function __construct(string $message = "", string $priority = "system") + { + $this->Message = $message; + $this->Priority = $priority; + } +} diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Watcher.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Watcher.php new file mode 100644 index 0000000..5b2b7f7 --- /dev/null +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Watcher.php @@ -0,0 +1,99 @@ +. +*/ + +namespace Tailscale; + +class Watcher +{ + private Config $config; + + public function __construct() + { + $this->config = new Config(); + } + + public function run(): void + { + $timer = 15; + $need_ip = true; + + $tsName = ''; + + if ( ! defined(__NAMESPACE__ . '\PLUGIN_ROOT') || ! defined(__NAMESPACE__ . '\PLUGIN_NAME')) { + throw new \RuntimeException("Common file not loaded."); + } + $utils = new Utils(PLUGIN_NAME); + + $utils->logmsg("Starting tailscale-watcher"); + + // @phpstan-ignore while.alwaysTrue + while (true) { + unset($tailscale_ipv4); + + $interfaces = net_get_interfaces(); + + if (isset($interfaces["tailscale1"]["unicast"])) { + foreach ($interfaces["tailscale1"]["unicast"] as $interface) { + if (isset($interface["address"])) { + if ($interface["family"] == 2) { + $tailscale_ipv4 = $interface["address"]; + $timer = 60; + } + } + } + } + + if (isset($tailscale_ipv4)) { + if ($need_ip) { + $utils->logmsg("Tailscale IP detected, applying configuration"); + $need_ip = false; + + $localAPI = new LocalAPI(); + $status = $localAPI->getStatus(); + $tsName = $status->Self->DNSName; + + $utils->run_task('Tailscale\System::applyTailscaleConfig', array($this->config)); + $utils->run_task('Tailscale\System::applyGRO'); + $utils->run_task('Tailscale\System::restartSystemServices', array($this->config)); + } + + $utils->run_task('Tailscale\System::checkWebgui', array($this->config, $tailscale_ipv4)); + $utils->run_task('Tailscale\System::checkServeConfig'); + $utils->run_task('Tailscale\System::fixLocalSubnetRoutes'); + + // Watch for changes to the DNS name (e.g., if someone changes the tailnet name or the Tailscale name of the server via the admin console) + // If a change happens, refresh the Tailscale WebGUI certificate + $localAPI = new LocalAPI(); + $status = $localAPI->getStatus(); + $newTsName = $status->Self->DNSName; + + if ($newTsName != $tsName) { + $utils->logmsg("Detected DNS name change"); + $tsName = $newTsName; + + $utils->run_task('Tailscale\System::refreshWebGuiCert'); + } + } else { + $utils->logmsg("Waiting for Tailscale IP"); + } + + sleep($timer); + } + } +} From c896bb57a293f544ef07efa08cffed69528948ee Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Sat, 2 Aug 2025 22:54:50 -0400 Subject: [PATCH 014/128] chore: use Enhanced Log plugin for log viewing (#26) * chore: use Enhanced Log plugin for log viewing * fix: black theme * fix: formatting --- .../plugins/tailscale/Tailscale-5-Log.page | 18 ---- .../plugins/tailscale/enhanced-log.json | 8 ++ .../plugins/tailscale/include/Pages/Log.php | 82 ------------------- .../plugins/tailscale/include/Pages/Main.php | 49 ----------- .../tailscale/include/Pages/Tailscale.php | 2 +- .../plugins/tailscale/include/get_log.php | 52 ------------ .../local/emhttp/plugins/tailscale/style.css | 4 + 7 files changed, 13 insertions(+), 202 deletions(-) delete mode 100644 src/usr/local/emhttp/plugins/tailscale/Tailscale-5-Log.page create mode 100644 src/usr/local/emhttp/plugins/tailscale/enhanced-log.json delete mode 100644 src/usr/local/emhttp/plugins/tailscale/include/Pages/Log.php delete mode 100644 src/usr/local/emhttp/plugins/tailscale/include/Pages/Main.php delete mode 100644 src/usr/local/emhttp/plugins/tailscale/include/get_log.php diff --git a/src/usr/local/emhttp/plugins/tailscale/Tailscale-5-Log.page b/src/usr/local/emhttp/plugins/tailscale/Tailscale-5-Log.page deleted file mode 100644 index 19a4437..0000000 --- a/src/usr/local/emhttp/plugins/tailscale/Tailscale-5-Log.page +++ /dev/null @@ -1,18 +0,0 @@ -Menu="Tailscale" -Icon="tailscale.png" -Title="Log" -Tag="file" -Type="xmenu" -Markdown="false" ---- -" . print_r($e, true) . ""; -} - -?> \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/enhanced-log.json b/src/usr/local/emhttp/plugins/tailscale/enhanced-log.json new file mode 100644 index 0000000..8fe5115 --- /dev/null +++ b/src/usr/local/emhttp/plugins/tailscale/enhanced-log.json @@ -0,0 +1,8 @@ +{ + "files": [ + "/var/log/tailscale.log", + "/var/log/tailscale.log.*", + "/var/log/tailscale-utils.log", + "/var/log/tailscale-utils.log.*" + ] +} \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/include/Pages/Log.php b/src/usr/local/emhttp/plugins/tailscale/include/Pages/Log.php deleted file mode 100644 index 09ba71d..0000000 --- a/src/usr/local/emhttp/plugins/tailscale/include/Pages/Log.php +++ /dev/null @@ -1,82 +0,0 @@ -"; - foreach ($logs as $file) { - $select[] = Tailscale\Utils::make_option(false, $file, basename($file)); - } - $select[] = ""; -} -$select = implode($select); -?> - -Log size:   - - " onclick="window.open('/plugins/plugin-diagnostics/download.php?plugin=tailscale','_blank')"> -

\ No newline at end of file
diff --git a/src/usr/local/emhttp/plugins/tailscale/include/Pages/Main.php b/src/usr/local/emhttp/plugins/tailscale/include/Pages/Main.php
deleted file mode 100644
index 68f1554..0000000
--- a/src/usr/local/emhttp/plugins/tailscale/include/Pages/Main.php
+++ /dev/null
@@ -1,49 +0,0 @@
-.
-*/
-
-namespace Tailscale;
-
-use EDACerton\PluginUtils\Translator;
-
-$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
-require_once "{$docroot}/plugins/tailscale/include/common.php";
-
-if ( ! defined(__NAMESPACE__ . '\PLUGIN_ROOT') || ! defined(__NAMESPACE__ . '\PLUGIN_NAME')) {
-    throw new \RuntimeException("Common file not loaded.");
-}
-
-$tr = $tr ?? new Translator(PLUGIN_ROOT);
-
-$tailscaleConfig = $tailscaleConfig ?? new Config();
-?>
-
-
-Enable) {
-    echo($tr->tr("tailscale_disabled"));
-    return;
-}
-
-$tailscaleInfo = $tailscaleInfo ?? new Info($tr);
-?>
-
\ No newline at end of file
diff --git a/src/usr/local/emhttp/plugins/tailscale/include/Pages/Tailscale.php b/src/usr/local/emhttp/plugins/tailscale/include/Pages/Tailscale.php
index e791c3a..db74e60 100644
--- a/src/usr/local/emhttp/plugins/tailscale/include/Pages/Tailscale.php
+++ b/src/usr/local/emhttp/plugins/tailscale/include/Pages/Tailscale.php
@@ -39,7 +39,7 @@
 
 $tailscaleInfo = $tailscaleInfo ?? new Info($tr);
 ?>
-
+
 
 
 
diff --git a/src/usr/local/emhttp/plugins/tailscale/include/get_log.php b/src/usr/local/emhttp/plugins/tailscale/include/get_log.php
deleted file mode 100644
index cc0503c..0000000
--- a/src/usr/local/emhttp/plugins/tailscale/include/get_log.php
+++ /dev/null
@@ -1,52 +0,0 @@
-.
-*/
-
-function getLog(string $log, int $max): void
-{
-    $allowed_files = ["/var/log/tailscale.log", "/var/log/tailscale-utils.log"];
-
-    if ( ! in_array($log, $allowed_files)) {
-        return;
-    }
-
-    if ( ! file_exists($log)) {
-        echo '', htmlspecialchars($log), " not found.";
-        return;
-    }
-
-    $lines = array_reverse(array_slice(file($log) ?: array(), -$max));
-
-    foreach ($lines as $line) {
-        echo '', htmlspecialchars($line), "";
-    }
-}
-
-ini_set('memory_limit', '512M'); // Increase memory limit
-
-try {
-    if ( ! is_string($_POST['log']) || ! is_numeric($_POST['max'])) {
-        throw new InvalidArgumentException("Invalid input");
-    }
-
-    getLog($_POST['log'], intval($_POST['max']));
-} catch (Throwable $e) {
-    echo '', htmlspecialchars($e->getMessage()), "";
-}
-
-ini_restore('memory_limit'); // Restore original memory limit
diff --git a/src/usr/local/emhttp/plugins/tailscale/style.css b/src/usr/local/emhttp/plugins/tailscale/style.css
index 459605c..d4bdbb8 100644
--- a/src/usr/local/emhttp/plugins/tailscale/style.css
+++ b/src/usr/local/emhttp/plugins/tailscale/style.css
@@ -12,4 +12,8 @@
     position: absolute;
     z-index: 100;
     display: none
+}
+
+li.select2-results__option {
+    color: black;
 }
\ No newline at end of file

From 79c1bd305d197c8c97dae500ac6a04944ad46016 Mon Sep 17 00:00:00 2001
From: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Date: Sun, 3 Aug 2025 03:03:59 +0000
Subject: [PATCH 015/128] release: update plugin files for 2025.08.02

---
 plugin/tailscale-preview.plg | 25 +++++++++++--------------
 1 file changed, 11 insertions(+), 14 deletions(-)

diff --git a/plugin/tailscale-preview.plg b/plugin/tailscale-preview.plg
index 749111a..d788661 100644
--- a/plugin/tailscale-preview.plg
+++ b/plugin/tailscale-preview.plg
@@ -4,7 +4,7 @@
 
 
@@ -39,9 +38,9 @@ For older releases, see https://github.com/unraid/unraid-tailscale/releases
 3b122d9f1fbf281cf3c79fd61e84c89af9a3647c464859cbee8f133ffa74e8f8
 
- -https://github.com/unraid/unraid-tailscale-utils/releases/download/4.2.0/unraid-tailscale-utils-4.2.0-noarch-1.txz -681de5861155c4b5590e30f12a1b7625ee1e872c897f175560d449449f448bef + +https://github.com/unraid/unraid-tailscale/releases/download/2025.08.02/unraid-tailscale-utils-2025.08.02-noarch-1.txz +5a86bd59cd99886b6bc902a3dbf3969de629d25d6c454f6e042a289b6f9b1ac9 ## Summary by CodeRabbit * **Chores** * Discord release notifications now use dynamic colors: yellow for prereleases and green for full releases, improving at-a-glance status. * Channel label and preview wording updated for clearer, friendlier display in notifications. * Release link label changed to “:link: Github Release” and now appears as a clickable Markdown link for better readability. --------- Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bec8270..b2c5f80 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,7 +49,7 @@ jobs: avatarUrl: https://raw.githubusercontent.com/unraid/unraid-tailscale/trunk/logo.png thumbnailUrl: https://raw.githubusercontent.com/unraid/unraid-tailscale/trunk/logo.png severity: info - color: "#00ff00" + color: ${{ github.event.release.prerelease && '#ffff00' || '#00ff00' }} title: New Update Available description: | ## :loudspeaker: Tailscale Plugin Update : ${{ github.event.release.name }} @@ -57,7 +57,7 @@ jobs: ${{ github.event.release.body }} fields: | [ - {"name": "Channel", "value": ${{ toJson(github.event.release.prerelease && ':test_tube: Preview' || 'Release') }}, "inline": true}, + {"name": ":tv: Channel", "value": ${{ toJson(github.event.release.prerelease && 'Preview :test_tube:' || 'Release') }}, "inline": true}, {"name": ":label: Tag", "value": ${{ toJson(github.event.release.tag_name) }}, "inline": true}, - {"name": ":link: Release Link", "value": ${{ toJson(github.event.release.html_url) }} } + {"name": ":link: Github Release", "value": ${{ toJson(format('[Link]({0})', github.event.release.html_url)) }}, "inline": true} ] From decb7eb167727f32ca6930e8acaa85ab3493d2a7 Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Thu, 2 Oct 2025 22:14:50 -0400 Subject: [PATCH 047/128] fix: potential race condition with emhttpd during boot (#48) ## Summary by CodeRabbit * **New Features** * Adds a boot-time wait to ensure the system is ready before monitoring starts, improving startup reliability. * Introduces a safeguard that defers WebGUI restarts until explicitly allowed, reducing unintended disruptions. * **Bug Fixes** * Prevents premature WebGUI restarts during early boot. * Improves status handling in the monitoring loop for more stable operation. Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- .../php/unraid-tailscale-utils/composer.lock | 158 +++++++++++++----- .../unraid-tailscale-utils/System.php | 9 +- .../unraid-tailscale-utils/Watcher.php | 12 +- 3 files changed, 133 insertions(+), 46 deletions(-) diff --git a/src/usr/local/php/unraid-tailscale-utils/composer.lock b/src/usr/local/php/unraid-tailscale-utils/composer.lock index bbf5455..c8abaa8 100644 --- a/src/usr/local/php/unraid-tailscale-utils/composer.lock +++ b/src/usr/local/php/unraid-tailscale-utils/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "edacerton/plugin-utils", - "version": "1.3.2-stable", + "version": "1.4.0-stable", "source": { "type": "git", "url": "https://github.com/dkaser/unraid-utils.git", - "reference": "eaddecb73867c1c2858cd9394f51f2b70eb939df" + "reference": "25c68bbea000670520c4ba63ddc0f8a39d2920b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dkaser/unraid-utils/zipball/eaddecb73867c1c2858cd9394f51f2b70eb939df", - "reference": "eaddecb73867c1c2858cd9394f51f2b70eb939df", + "url": "https://api.github.com/repos/dkaser/unraid-utils/zipball/25c68bbea000670520c4ba63ddc0f8a39d2920b7", + "reference": "25c68bbea000670520c4ba63ddc0f8a39d2920b7", "shasum": "" }, "require-dev": { @@ -40,10 +40,10 @@ ], "description": "Utility classes", "support": { - "source": "https://github.com/dkaser/unraid-utils/tree/1.3.2-stable", + "source": "https://github.com/dkaser/unraid-utils/tree/1.4.0-stable", "issues": "https://github.com/dkaser/unraid-utils/issues" }, - "time": "2025-09-21T02:11:15+00:00" + "time": "2025-10-03T01:51:41+00:00" } ], "packages-dev": [ @@ -443,16 +443,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.87.2", + "version": "v3.88.2", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992" + "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992", - "reference": "da5f0a7858c79b56fc0b8c36d3efcfe5f37f0992", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a8d15584bafb0f0d9d938827840060fd4a3ebc99", + "reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99", "shasum": "" }, "require": { @@ -479,12 +479,13 @@ "symfony/polyfill-mbstring": "^1.33", "symfony/polyfill-php80": "^1.33", "symfony/polyfill-php81": "^1.33", + "symfony/polyfill-php84": "^1.33", "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2", "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0" }, "require-dev": { "facile-it/paraunit": "^1.3.1 || ^2.7", - "infection/infection": "^0.29.14", + "infection/infection": "^0.31.0", "justinrainbow/json-schema": "^6.5", "keradus/cli-executor": "^2.2", "mikey179/vfsstream": "^1.6.12", @@ -492,7 +493,6 @@ "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", - "symfony/polyfill-php84": "^1.33", "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2", "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2" }, @@ -535,7 +535,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.87.2" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.2" }, "funding": [ { @@ -543,20 +543,15 @@ "type": "github" } ], - "time": "2025-09-10T09:51:40+00:00" + "time": "2025-09-27T00:24:15+00:00" }, { "name": "phpstan/phpstan", - "version": "2.1.28", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "578fa296a166605d97b94091f724f1257185d278" - }, + "version": "2.1.30", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/578fa296a166605d97b94091f724f1257185d278", - "reference": "578fa296a166605d97b94091f724f1257185d278", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a4a7f159927983dd4f7c8020ed227d80b7f39d7d", + "reference": "a4a7f159927983dd4f7c8020ed227d80b7f39d7d", "shasum": "" }, "require": { @@ -601,7 +596,7 @@ "type": "github" } ], - "time": "2025-09-19T08:58:49+00:00" + "time": "2025-10-02T16:07:52+00:00" }, { "name": "psr/container", @@ -1351,16 +1346,16 @@ }, { "name": "symfony/console", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", - "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", "shasum": "" }, "require": { @@ -1425,7 +1420,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.3" + "source": "https://github.com/symfony/console/tree/v7.3.4" }, "funding": [ { @@ -1445,7 +1440,7 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-22T15:31:00+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2382,18 +2377,98 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-php84", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-24T13:30:11+00:00" + }, { "name": "symfony/process", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", - "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", + "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", "shasum": "" }, "require": { @@ -2425,7 +2500,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.3" + "source": "https://github.com/symfony/process/tree/v7.3.4" }, "funding": [ { @@ -2445,7 +2520,7 @@ "type": "tidelift" } ], - "time": "2025-08-18T09:42:54+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/service-contracts", @@ -2594,16 +2669,16 @@ }, { "name": "symfony/string", - "version": "v7.3.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" + "reference": "f96476035142921000338bad71e5247fbc138872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", - "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", + "reference": "f96476035142921000338bad71e5247fbc138872", "shasum": "" }, "require": { @@ -2618,7 +2693,6 @@ }, "require-dev": { "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -2661,7 +2735,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.3" + "source": "https://github.com/symfony/string/tree/v7.3.4" }, "funding": [ { @@ -2681,7 +2755,7 @@ "type": "tidelift" } ], - "time": "2025-08-25T06:35:40+00:00" + "time": "2025-09-11T14:36:48+00:00" } ], "aliases": [], diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/System.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/System.php index 2fdf171..1e7dfef 100644 --- a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/System.php +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/System.php @@ -88,7 +88,7 @@ public static function fixLocalSubnetRoutes(): void } } - public static function checkWebgui(Config $config, string $tailscale_ipv4): void + public static function checkWebgui(Config $config, string $tailscale_ipv4, bool $allowRestart): bool { // Make certain that the WebGUI is listening on the Tailscale interface if ($config->IncludeInterface) { @@ -99,12 +99,19 @@ public static function checkWebgui(Config $config, string $tailscale_ipv4): void if (is_resource($connection)) { Utils::logwrap("WebGUI listening on {$tailscale_ipv4}:{$ident_config['PORT']}", false, true); } else { + if ( ! $allowRestart) { + Utils::logwrap("WebGUI not listening on {$tailscale_ipv4}:{$ident_config['PORT']}, waiting for next check"); + return true; + } + Utils::logwrap("WebGUI not listening on {$tailscale_ipv4}:{$ident_config['PORT']}, terminating and restarting"); Utils::runwrap("/etc/rc.d/rc.nginx term"); sleep(5); Utils::runwrap("/etc/rc.d/rc.nginx start"); } } + + return false; } public static function checkServeConfig(): void diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Watcher.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Watcher.php index d1e0199..cee327a 100644 --- a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Watcher.php +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Watcher.php @@ -30,8 +30,9 @@ public function __construct() public function run(): void { - $timer = 15; - $need_ip = true; + $timer = 15; + $need_ip = true; + $allow_check_restart = false; $tsName = ''; @@ -42,6 +43,11 @@ public function run(): void $utils->logmsg("Starting tailscale-watcher"); + while ( ! file_exists('/var/local/emhttp/var.ini')) { + $utils->logmsg("Waiting for system to finish booting"); + sleep(10); + } + // @phpstan-ignore while.alwaysTrue while (true) { unset($tailscale_ipv4); @@ -76,7 +82,7 @@ public function run(): void } } - $utils->run_task('Tailscale\System::checkWebgui', array($this->config, $tailscale_ipv4)); + $allow_check_restart = $utils->run_task('Tailscale\System::checkWebgui', array($this->config, $tailscale_ipv4, $allow_check_restart)); $utils->run_task('Tailscale\System::checkServeConfig'); $utils->run_task('Tailscale\System::fixLocalSubnetRoutes'); From 824bdc530d677b98b9954ff2f8304cbe943947ae Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Thu, 2 Oct 2025 22:17:49 -0400 Subject: [PATCH 048/128] ci: update release-drafter (#49) Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 83a07d5..eb039c7 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -20,7 +20,7 @@ jobs: id: set_version run: TZ=UTC date +'%Y.%m.%d.%H%M' | xargs -I{} echo "version={}" >> $GITHUB_ENV - - uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 + - uses: dkaser/release-drafter@8bc802e5816eff4cc92ae7ce2a6f5f09c284bf0d with: version: ${{ env.version }} name: ${{ env.version }} From 792ba81d29ff682d67dd2108450ab61a743ca87f Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 3 Oct 2025 02:18:50 +0000 Subject: [PATCH 049/128] release: update plugin files for 2025.10.03.0217 --- plugin/tailscale-preview.plg | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/plugin/tailscale-preview.plg b/plugin/tailscale-preview.plg index d71f9ec..e9f4984 100644 --- a/plugin/tailscale-preview.plg +++ b/plugin/tailscale-preview.plg @@ -4,7 +4,7 @@ @@ -51,9 +53,9 @@ For older releases, see https://github.com/unraid/unraid-tailscale/releases 360805d74573c6357eb97f918786155b82ebb2146817eb56677430d1b13fccb8 - -https://github.com/unraid/unraid-tailscale/releases/download/2025.09.25.2118/unraid-tailscale-utils-2025.09.25.2118-noarch-1.txz -d3927c37da565ada4d47f2b67b0d6af9804de946ccefcffaae5d8d6e19b27477 + +https://github.com/unraid/unraid-tailscale/releases/download/2025.10.03.0217/unraid-tailscale-utils-2025.10.03.0217-noarch-1.txz +d03457419272c19e5f1f1a8ff425436737086ce155fc54a9ce808f668d67b4a8 ## Summary by CodeRabbit * **Bug Fixes** * Improved reliability when loading the Tailscale plugin page by adding robust error handling. * Prevents partial or corrupted page output if an error occurs during content rendering. * Ensures the page cleans up correctly on failures and returns consistent content on success. * No changes to visible functionality; enhances stability and user experience. Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- src/usr/local/emhttp/plugins/tailscale/include/page.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/usr/local/emhttp/plugins/tailscale/include/page.php b/src/usr/local/emhttp/plugins/tailscale/include/page.php index 3786ec4..34477fc 100644 --- a/src/usr/local/emhttp/plugins/tailscale/include/page.php +++ b/src/usr/local/emhttp/plugins/tailscale/include/page.php @@ -46,8 +46,13 @@ function includePage(string $filename, array $params = array()): string if (is_file($filename)) { ob_start(); - include $filename; - return ob_get_clean() ?: ""; + try { + include $filename; + return ob_get_clean() ?: ""; + } catch (\Throwable $e) { + ob_end_clean(); + throw $e; + } } return ""; } From bde86e11eb2075d3d343212fdd27872a3888054d Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Fri, 24 Oct 2025 22:29:13 -0400 Subject: [PATCH 051/128] feat: update to Tailscale 1.90.2 (#51) ## Summary by CodeRabbit * **Chores** * Updated Tailscale to version 1.90.2 * Modified Tailscale startup configuration settings Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- plugin/plugin.json | 4 ++-- src/usr/local/emhttp/plugins/tailscale/rc.tailscale | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin/plugin.json b/plugin/plugin.json index 3859428..3997d88 100644 --- a/plugin/plugin.json +++ b/plugin/plugin.json @@ -5,6 +5,6 @@ "min": "7.0.0", "support": "https://forums.unraid.net/topic/136889-plugin-tailscale/", "launch": "Settings/Tailscale", - "tailscaleVersion": "tailscale_1.88.3_amd64", - "tailscaleSHA256": "360805d74573c6357eb97f918786155b82ebb2146817eb56677430d1b13fccb8" + "tailscaleVersion": "tailscale_1.90.2_amd64", + "tailscaleSHA256": "e8d1ec2f0eba10c83ba9d170dc79432aca3ef7cc684d5249e9b91dad519582ad" } \ No newline at end of file diff --git a/src/usr/local/emhttp/plugins/tailscale/rc.tailscale b/src/usr/local/emhttp/plugins/tailscale/rc.tailscale index 0ef5f4e..d12450d 100755 --- a/src/usr/local/emhttp/plugins/tailscale/rc.tailscale +++ b/src/usr/local/emhttp/plugins/tailscale/rc.tailscale @@ -11,7 +11,7 @@ fi start_tailscaled() { if ! /usr/bin/pgrep --ns $$ --euid root -f "^/usr/local/sbin/tailscaled" 1> /dev/null 2> /dev/null ; then - TAILSCALE_START_CMD="/usr/local/sbin/tailscaled -statedir /boot/config/plugins/tailscale/state -tun tailscale1 $TAILSCALE_CUSTOM_PARAMS" + TAILSCALE_START_CMD="/usr/local/sbin/tailscaled -statedir /boot/config/plugins/tailscale/state -tun tailscale1 -encrypt-state=false -hardware-attestation=false $TAILSCALE_CUSTOM_PARAMS" log "Starting tailscaled: $TAILSCALE_START_CMD" mkdir -p /boot/config/plugins/tailscale/state $TAILSCALE_START_CMD 2>&1 | grep -vF "monitor: [unexpected]" >> /var/log/tailscale.log & From ef9ebb4a5b1ca3df0c72e6f645a659e1dc272467 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 25 Oct 2025 02:30:29 +0000 Subject: [PATCH 052/128] release: update plugin files for 2025.10.25.0229 --- plugin/tailscale-preview.plg | 40 ++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/plugin/tailscale-preview.plg b/plugin/tailscale-preview.plg index e9f4984..daa474d 100644 --- a/plugin/tailscale-preview.plg +++ b/plugin/tailscale-preview.plg @@ -4,7 +4,7 @@ - -https://pkgs.tailscale.com/stable/tailscale_1.88.3_amd64.tgz -360805d74573c6357eb97f918786155b82ebb2146817eb56677430d1b13fccb8 + +https://pkgs.tailscale.com/stable/tailscale_1.90.2_amd64.tgz +e8d1ec2f0eba10c83ba9d170dc79432aca3ef7cc684d5249e9b91dad519582ad - -https://github.com/unraid/unraid-tailscale/releases/download/2025.10.03.0217/unraid-tailscale-utils-2025.10.03.0217-noarch-1.txz -d03457419272c19e5f1f1a8ff425436737086ce155fc54a9ce808f668d67b4a8 + +https://github.com/unraid/unraid-tailscale/releases/download/2025.10.25.0229/unraid-tailscale-utils-2025.10.25.0229-noarch-1.txz +a301ed6a0d6b43db9c8f93d67385b0e0e3b7f27be12301af6ef87ba1cbab1141 ## Summary by CodeRabbit * **Updates** * Upgraded Tailscale plugin from version 1.90.2 to 1.90.5. Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- plugin/plugin.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugin/plugin.json b/plugin/plugin.json index 3997d88..6ea0d52 100644 --- a/plugin/plugin.json +++ b/plugin/plugin.json @@ -1,10 +1,10 @@ { - "name": "tailscale", - "package_name": "unraid-tailscale-utils", - "author": "Derek Kaser", - "min": "7.0.0", - "support": "https://forums.unraid.net/topic/136889-plugin-tailscale/", - "launch": "Settings/Tailscale", - "tailscaleVersion": "tailscale_1.90.2_amd64", - "tailscaleSHA256": "e8d1ec2f0eba10c83ba9d170dc79432aca3ef7cc684d5249e9b91dad519582ad" -} \ No newline at end of file + "name": "tailscale", + "package_name": "unraid-tailscale-utils", + "author": "Derek Kaser", + "min": "7.0.0", + "support": "https://forums.unraid.net/topic/136889-plugin-tailscale/", + "launch": "Settings/Tailscale", + "tailscaleVersion": "tailscale_1.90.5_amd64", + "tailscaleSHA256": "901d0310d888e471f0e17b1f2a64fd0d77c0d8848e5a63f0a2b29a9da66e8ac2" +} From d795a50bcb6fd4081730585d63fc0e8b7dffc40f Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 04:32:02 +0000 Subject: [PATCH 054/128] release: update plugin files for 2025.10.31.0431 --- plugin/tailscale-preview.plg | 45 +++++++++++++++--------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/plugin/tailscale-preview.plg b/plugin/tailscale-preview.plg index daa474d..b927bc0 100644 --- a/plugin/tailscale-preview.plg +++ b/plugin/tailscale-preview.plg @@ -4,7 +4,7 @@ - -https://pkgs.tailscale.com/stable/tailscale_1.90.2_amd64.tgz -e8d1ec2f0eba10c83ba9d170dc79432aca3ef7cc684d5249e9b91dad519582ad + +https://pkgs.tailscale.com/stable/tailscale_1.90.5_amd64.tgz +901d0310d888e471f0e17b1f2a64fd0d77c0d8848e5a63f0a2b29a9da66e8ac2 - -https://github.com/unraid/unraid-tailscale/releases/download/2025.10.25.0229/unraid-tailscale-utils-2025.10.25.0229-noarch-1.txz -a301ed6a0d6b43db9c8f93d67385b0e0e3b7f27be12301af6ef87ba1cbab1141 + +https://github.com/unraid/unraid-tailscale/releases/download/2025.10.31.0431/unraid-tailscale-utils-2025.10.31.0431-noarch-1.txz +5ff36e07555fce2dbcef01c6941b9a5c7a82edc15766cc690a316afb68db288c ## Summary by CodeRabbit * **New Features** * Relay port configuration UI for peers with numeric input (0–65535), save button and dynamic warning when ACL approval is absent * Async save action for relay port with input validation and persistent preference storage * New backend support to get/set relay port and surface errors on invalid input * New localization strings for relay controls and messaging * **Enhancements** * Added checks for peer-relay approval and packet-filter rule inspection --------- Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- .../tailscale/include/Pages/Tailscale.php | 6 ++++ .../plugins/tailscale/include/data/Config.php | 33 +++++++++++++++++++ .../plugins/tailscale/locales/en_US.json | 5 ++- .../unraid-tailscale-utils/Info.php | 27 +++++++++++++++ .../unraid-tailscale-utils/LocalAPI.php | 5 +++ 5 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/usr/local/emhttp/plugins/tailscale/include/Pages/Tailscale.php b/src/usr/local/emhttp/plugins/tailscale/include/Pages/Tailscale.php index 46fbe40..a40c2d9 100644 --- a/src/usr/local/emhttp/plugins/tailscale/include/Pages/Tailscale.php +++ b/src/usr/local/emhttp/plugins/tailscale/include/Pages/Tailscale.php @@ -121,6 +121,12 @@ function showTailscaleConfig() { var res = await $.post('/plugins/tailscale/include/data/Config.php',{action: 'add-route', route: $('#tailscaleRoute').val()}); showTailscaleConfig(); } +async function setTailscaleRelayPort() { + $('div.spinner.fixed').show('fast'); + tailscaleControlsDisabled(true); + var res = await $.post('/plugins/tailscale/include/data/Config.php',{action: 'set-relay-port', port: $('#tailscaleRelayPort').val()}); + showTailscaleConfig(); +} function isValidCIDR(ip) { if (ip === undefined) { return false; diff --git a/src/usr/local/emhttp/plugins/tailscale/include/data/Config.php b/src/usr/local/emhttp/plugins/tailscale/include/data/Config.php index d970dc1..d6129ea 100644 --- a/src/usr/local/emhttp/plugins/tailscale/include/data/Config.php +++ b/src/usr/local/emhttp/plugins/tailscale/include/data/Config.php @@ -99,6 +99,9 @@ } $exitSelect .= ""; + $relayPort = $tailscaleInfo->getRelayServerPort() ?: ""; + $relayPortWarning = $relayPort !== "" && ! $tailscaleInfo->isApprovedPeerRelay() ? $tr->tr("warnings.peer_relay_no_acl") : " "; + $configRows = <<{$tr->tr("info.accept_routes")}{$tailscaleConInfo->AcceptRoutes}{$acceptRoutesButton} {$tr->tr("info.accept_dns")}{$tailscaleConInfo->AcceptDNS}{$acceptDNSButton} @@ -106,6 +109,14 @@ {$tr->tr("info.advertise_exit_node")}{$tailscaleConInfo->AdvertiseExitNode}{$advertiseExitButton} {$tr->tr("info.use_exit_node")} {$exitSelect} {$tr->tr("info.exit_node_local")}{$tailscaleConInfo->ExitNodeLocal}{$exitLocalButton} + + {$tr->tr("info.peer_relay")} + {$relayPortWarning} + + + + + EOT; @@ -333,6 +344,28 @@ $utils->logmsg("Object: " . json_encode($serveConfig->getConfig(), JSON_UNESCAPED_SLASHES)); $localAPI->setServeConfig($serveConfig); break; + case 'set-relay-port': + if ( ! isset($_POST['port'])) { + throw new \Exception("Missing port parameter"); + } + + $port = null; + + if ($_POST['port'] === '') { + $port = null; + } elseif (ctype_digit($_POST['port'])) { + $port = intval($_POST['port']); + if (($port < 0) || ($port > 65535)) { + throw new \Exception("Port out of range: {$_POST['port']}"); + } + } else { + throw new \Exception("Invalid port: {$_POST['port']}"); + } + + $utils->logmsg("Setting relay server port to: {$port}"); + + $localAPI->patchPref("RelayServerPort", $port); + break; } } catch (\Throwable $e) { file_put_contents("/var/log/tailscale-error.log", print_r($e, true) . PHP_EOL, FILE_APPEND); diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/en_US.json b/src/usr/local/emhttp/plugins/tailscale/locales/en_US.json index edb3bd3..282c111 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/en_US.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/en_US.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale is currently disabled. It can be enabled via the Settings tab.", "tailscale_lock": "Tailscale Lock", "warning": "Warning", + "save": "Save", "settings": { "basic": "Basic View", "advanced": "Advanced View", @@ -87,6 +88,7 @@ "connected_via": "Connected via Tailscale", "funnel_port": "Funnel Port for WebGUI", "port_in_use": "Port in Use", + "peer_relay": "Peer Relay Port", "lock": { "node_key": "Node Key", "public_key": "Public Key", @@ -102,7 +104,8 @@ "subnet": "Accepting Tailscale subnets on your Unraid server can disrupt local network connectivity in certain network configurations. Accepting routes is not required for your Unraid server to function as a subnet router or exit node. Only enable this option if your Unraid server needs to connect to remote subnets advertised by other devices.", "dns": "Enabling Tailscale MagicDNS on your server can disrupt name resolution inside Docker containers, potentially causing connectivity issues for applications. MagicDNS does not need to be enabled on the server for you to access your Unraid server using MagicDNS names (e.g., 'unraid.tailnet.ts.net') from other devices. Enabling 'Add Peers to /etc/hosts' is a safer alternative that allows Tailscale names to be used on the server.", "caution": "Proceed with caution and ensure you understand the implications of this change before continuing.", - "more_info": "For more information:" + "more_info": "For more information:", + "peer_relay_no_acl": "Not configured in Tailnet policy (access controls)." }, "lock": { "sign": "Sign Nodes", diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Info.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Info.php index 52bd543..ac22b95 100644 --- a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Info.php +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Info.php @@ -471,4 +471,31 @@ public function getDNSName(): string return $this->status->Self->DNSName; } + + public function isApprovedPeerRelay(): bool + { + $netmapRules = (array) $this->localAPI->getPacketFilterRules(); + + // Parse the packet filter rules to see if peer relay is approved + // TODO: Get a better way to do this from Tailscale + foreach ($netmapRules as $rule) { + if (isset($rule->CapGrant) && is_array($rule->CapGrant)) { + foreach ($rule->CapGrant as $capGrant) { + if (is_object($capGrant) && isset($capGrant->CapMap) && is_object($capGrant->CapMap) && isset($capGrant->CapMap->{'tailscale.com/cap/relay'})) { + return true; + } + } + } + } + + return false; + } + + public function getRelayServerPort(): int|false + { + if (isset($this->prefs->RelayServerPort) && is_int($this->prefs->RelayServerPort)) { + return $this->prefs->RelayServerPort; + } + return false; + } } diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LocalAPI.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LocalAPI.php index 2dc4448..e1ddc3c 100644 --- a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LocalAPI.php +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LocalAPI.php @@ -101,6 +101,11 @@ public function getServeConfig(): \stdClass return (object) json_decode($this->tailscaleLocalAPI('v0/serve-config')); } + public function getPacketFilterRules(): \stdClass + { + return (object) json_decode($this->tailscaleLocalAPI('v0/debug-packet-filter-rules')); + } + public function resetServeConfig(): void { $this->tailscaleLocalAPI("v0/serve-config", APIMethods::POST, new \stdClass()); From 099219693b76a4304281b362b4f7c71de0cf2f67 Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:23:42 -0500 Subject: [PATCH 057/128] ci: add crowdin download (#54) Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- .github/workflows/translate-download.yml | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/translate-download.yml diff --git a/.github/workflows/translate-download.yml b/.github/workflows/translate-download.yml new file mode 100644 index 0000000..1c555a7 --- /dev/null +++ b/.github/workflows/translate-download.yml @@ -0,0 +1,28 @@ +name: Upload for translation + +on: + schedule: + - cron: "30 2 * * *" + workflow_dispatch: + +jobs: + synchronize-with-crowdin: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: crowdin action + uses: crowdin/github-action@v2 + with: + upload_sources: false + download_translations: true + push_translations: true + commit_message: "chore: update translations from Crowdin" + create_pull_request: true + pull_request_title: "chore: update translations from Crowdin" + env: + CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} From 5c30e45a9e32d78cc5efde373011cfef7d4e1171 Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:33:04 -0500 Subject: [PATCH 058/128] ci: fix workflows (#55) Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- .github/workflows/translate-download.yml | 3 ++- .github/workflows/translate.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/translate-download.yml b/.github/workflows/translate-download.yml index 1c555a7..60344dd 100644 --- a/.github/workflows/translate-download.yml +++ b/.github/workflows/translate-download.yml @@ -1,4 +1,4 @@ -name: Upload for translation +name: Download translations on: schedule: @@ -26,3 +26,4 @@ jobs: env: CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/translate.yml b/.github/workflows/translate.yml index 5e616d4..e174065 100644 --- a/.github/workflows/translate.yml +++ b/.github/workflows/translate.yml @@ -3,7 +3,7 @@ name: Upload for translation on: push: branches: - - 'main' + - "trunk" jobs: synchronize-with-crowdin: From 246f86a4a49345f982c85050d870a7adc791a57f Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:38:06 -0500 Subject: [PATCH 059/128] ci: add pull request permission (#56) Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- .github/workflows/translate-download.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/translate-download.yml b/.github/workflows/translate-download.yml index 60344dd..8253965 100644 --- a/.github/workflows/translate-download.yml +++ b/.github/workflows/translate-download.yml @@ -10,6 +10,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + pull_requests: write steps: - name: Checkout uses: actions/checkout@v4 From 4a32950d753e6bd459155bde0c96d63cd1dda8fc Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Mon, 3 Nov 2025 03:39:54 +0000 Subject: [PATCH 060/128] ci: fix permission Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- .github/workflows/translate-download.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/translate-download.yml b/.github/workflows/translate-download.yml index 8253965..549074e 100644 --- a/.github/workflows/translate-download.yml +++ b/.github/workflows/translate-download.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: write - pull_requests: write + pull-requests: write steps: - name: Checkout uses: actions/checkout@v4 From 773fe68894be92afae42e8566d34be057933ac0d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:52:35 -0500 Subject: [PATCH 061/128] chore: update translations from Crowdin (#57) Co-authored-by: Crowdin Bot --- src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json | 5 ++++- src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json | 5 ++++- src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json | 5 ++++- src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json | 5 ++++- src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json | 5 ++++- src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json | 5 ++++- src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json | 7 +++++-- src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json | 5 ++++- src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json | 5 ++++- src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json | 5 ++++- src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json | 5 ++++- src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json | 5 ++++- src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json | 5 ++++- src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json | 5 ++++- 14 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json b/src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json index 80405af..40a9233 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale ist derzeit deaktiviert. Es kann über die Registerkarte Einstellungen aktiviert werden.", "tailscale_lock": "Tailscale Lock", "warning": "Warnung", + "save": "Speichern", "settings": { "basic": "Einfache Ansicht", "advanced": "Erweiterte Ansicht", @@ -87,6 +88,7 @@ "connected_via": "Verbunden über Tailscale", "funnel_port": "Funnel-Port für WebGUI", "port_in_use": "Verwendeter Port", + "peer_relay": "Peer-Relay-Port", "lock": { "node_key": "Node Schlüssel", "public_key": "Öffentlicher Schlüssel", @@ -102,7 +104,8 @@ "subnet": "Das Akzeptieren von Tailscale-Subnetzen auf Ihrem Unraid-Server kann in bestimmten Netzwerkkonfigurationen die lokale Netzwerkverbindung stören. Das Akzeptieren von Routen ist nicht erforderlich, damit Ihr Unraid-Server als Subnetz-Router oder Exit-Node fungiert. Aktivieren Sie diese Option nur, wenn Ihr Unraid-Server eine Verbindung zu von anderen Geräten angekündigten entfernten Subnetzen herstellen muss.", "dns": "Das Aktivieren von Tailscale MagicDNS auf Ihrem Server kann die Namensauflösung innerhalb von Docker-Containern stören und möglicherweise zu Verbindungsproblemen für Anwendungen führen. MagicDNS muss auf dem Server nicht aktiviert sein, damit Sie mit anderen Geräten über MagicDNS-Namen (z. B. \"unraid.tailnet.ts.net\") auf Ihren Unraid-Server zugreifen können. Das Aktivieren von \"Peers zu /etc/hosts hinzufügen\" ist eine sicherere Alternative, die die Verwendung von Tailscale-Namen auf dem Server ermöglicht.", "caution": "Gehen Sie mit Vorsicht vor und stellen Sie sicher, dass Sie die Auswirkungen dieser Änderung verstehen, bevor Sie fortfahren.", - "more_info": "Weitere Informationen:" + "more_info": "Weitere Informationen:", + "peer_relay_no_acl": "Nicht in der Tailnet-Richtlinie (Zugriffssteuerungen) konfiguriert." }, "lock": { "sign": "Nodes signieren", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json b/src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json index 9adf5dd..8c05cba 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale está desactivada actualmente. Se puede activar a través de la pestaña de ajustes.", "tailscale_lock": "Bloqueo de Tailscale", "warning": "Advertencia", + "save": "Archiva", "settings": { "basic": "Vista básica", "advanced": "Vista avanzada", @@ -87,6 +88,7 @@ "connected_via": "Conectado a través de Tailscale", "funnel_port": "Puerto del Funnel para WebGUI", "port_in_use": "Puerto en uso", + "peer_relay": "Puerto de retransmisión entre pares", "lock": { "node_key": "Clave de nodo", "public_key": "Clave pública", @@ -102,7 +104,8 @@ "subnet": "Aceptar subredes de Tailscale en su servidor Unraid puede interrumpir la conectividad de red local en ciertas configuraciones de red. Aceptar rutas no es necesario para que su servidor Unraid funcione como enrutador de subred o nodo de salida. Solo habilite esta opción si su servidor Unraid necesita conectarse a subredes remotas anunciadas por otros dispositivos.", "dns": "Habilitar MagicDNS de Tailscale en su servidor puede interrumpir la resolución de nombres dentro de los contenedores Docker, lo que puede causar problemas de conectividad para las aplicaciones. No es necesario habilitar MagicDNS en el servidor para que usted acceda a su servidor Unraid usando nombres de MagicDNS (por ejemplo, 'unraid.tailnet.ts.net') desde otros dispositivos. Habilitar 'Agregar pares a /etc/hosts' es una alternativa más segura que permite utilizar los nombres de Tailscale en el servidor.", "caution": "Proceda con precaución y asegúrese de comprender las implicaciones de este cambio antes de continuar.", - "more_info": "Para más información:" + "more_info": "Para más información:", + "peer_relay_no_acl": "No está configurado en la política de Tailnet (controles de acceso)." }, "lock": { "sign": "Firma nodos", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json b/src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json index 4ee0fe1..6ea0fe9 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale est actuellement désactivé. Il peut être activé via l'onglet Paramètres.", "tailscale_lock": "Tailscale Lock", "warning": "Avertissement", + "save": "Enregistrer", "settings": { "basic": "Affichage de base", "advanced": "Affichage avancé", @@ -87,6 +88,7 @@ "connected_via": "Connecté via Tailscale", "funnel_port": "Port Funnel pour le WebGUI", "port_in_use": "Port utilisé", + "peer_relay": "Port de relais pair-à-pair", "lock": { "node_key": "Clé du nœud", "public_key": "Clé publique", @@ -102,7 +104,8 @@ "subnet": "L’acceptation des sous-réseaux Tailscale sur votre serveur Unraid peut perturber la connectivité du réseau local dans certaines configurations réseau. L’acceptation des routes n’est pas nécessaire pour que votre serveur Unraid fonctionne comme routeur de sous-réseaux ou nœud de sortie. Activez cette option uniquement si votre serveur Unraid doit se connecter à des sous-réseaux distants annoncés par d’autres appareils.", "dns": "L’activation de MagicDNS de Tailscale sur votre serveur peut perturber la résolution de noms à l’intérieur des conteneurs Docker, ce qui peut entraîner des problèmes de connectivité pour les applications. Il n’est pas nécessaire d’activer MagicDNS sur le serveur pour accéder à votre serveur Unraid en utilisant les noms MagicDNS (par exemple, « unraid.tailnet.ts.net ») depuis d’autres appareils. Activer « Ajouter des pairs à /etc/hosts » est une alternative plus sûre qui permet d’utiliser les noms Tailscale sur le serveur.", "caution": "Procédez avec prudence et assurez-vous de bien comprendre les implications de ce changement avant de continuer.", - "more_info": "Pour plus d'informations :" + "more_info": "Pour plus d'informations :", + "peer_relay_no_acl": "Non configuré dans la politique Tailnet (contrôles d'accès)." }, "lock": { "sign": "Nœuds de signature", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json b/src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json index a5b3682..82750d9 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale è attualmente disabilitato. Può essere abilitato tramite la scheda Impostazioni.", "tailscale_lock": "Blocco Tailscale", "warning": "Avviso", + "save": "Salva", "settings": { "basic": "Vista di base", "advanced": "Vista avanzata", @@ -87,6 +88,7 @@ "connected_via": "Connesso tramite Tailscale", "funnel_port": "Porta Funnel per WebGUI", "port_in_use": "Porta in uso", + "peer_relay": "Porta di inoltro peer", "lock": { "node_key": "Chiave nodo", "public_key": "Chiave pubblica", @@ -102,7 +104,8 @@ "subnet": "Accettare le subnet Tailscale sul tuo server Unraid può interrompere la connettività di rete locale in alcune configurazioni di rete. L'accettazione delle route non è necessaria affinché il tuo server Unraid funzioni come router di subnet o exit node. Abilita questa opzione solo se il tuo server Unraid deve connettersi a subnet remote pubblicizzate da altri dispositivi.", "dns": "Abilitare MagicDNS di Tailscale sul tuo server può interrompere la risoluzione dei nomi all'interno dei container Docker, causando potenzialmente problemi di connettività per le applicazioni. Non è necessario abilitare MagicDNS sul server per accedere al tuo server Unraid utilizzando i nomi MagicDNS (ad esempio, \"unraid.tailnet.ts.net\") da altri dispositivi. Abilitare \"Aggiungi peer a /etc/hosts\" è un'alternativa più sicura che consente di utilizzare i nomi Tailscale sul server.", "caution": "Procedi con cautela e assicurati di comprendere le implicazioni di questa modifica prima di continuare.", - "more_info": "Per ulteriori informazioni:" + "more_info": "Per ulteriori informazioni:", + "peer_relay_no_acl": "Non configurato nella policy Tailnet (controlli di accesso)." }, "lock": { "sign": "Firma nodi", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json b/src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json index f83a0ed..59dff92 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscaleは現在無効です。設定タブから有効化できます。", "tailscale_lock": "Tailscaleロック", "warning": "警告", + "save": "保存する", "settings": { "basic": "基本表示", "advanced": "詳細表示", @@ -87,6 +88,7 @@ "connected_via": "Tailscale経由で接続済み", "funnel_port": "WebGUI 用 Funnel ポート", "port_in_use": "使用中のポート", + "peer_relay": "ピアリレイポート", "lock": { "node_key": "ノードキー", "public_key": "公開鍵", @@ -102,7 +104,8 @@ "subnet": "Unraid サーバーで Tailscale サブネットの許可を有効にすると、特定のネットワーク構成においてローカルネットワーク接続が障害される可能性があります。Unraid サーバーをサブネットルーターや出口ノードとして機能させるためにルートの許可は必要ありません。他の端末から通知されたリモートサブネットに Unraid サーバーが接続する必要がある場合のみ、このオプションを有効にしてください。", "dns": "サーバー上で Tailscale MagicDNS を有効にすると、Docker コンテナ内で名前解決が妨げられ、アプリケーションの接続に問題が発生する可能性があります。他の端末から MagicDNS 名(例: \"unraid.tailnet.ts.net\")を使って Unraid サーバーへアクセスする場合、サーバー上で MagicDNS を有効にする必要はありません。「/etc/hosts にピアを追加する (Add Peers)」を有効にする方が安全な選択肢であり、サーバー上で Tailscale 名を使用できます。", "caution": "十分に注意して進め、この変更の影響を理解したうえで続行してください。", - "more_info": "詳細はこちら:" + "more_info": "詳細はこちら:", + "peer_relay_no_acl": "Tailnetポリシー(アクセス制御)で設定されていません。" }, "lock": { "sign": "ノードに署名", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json b/src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json index 5c40a20..8123b6f 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale이(가) 현재 비활성화되어 있습니다. 설정 탭에서 활성화할 수 있습니다.", "tailscale_lock": "Tailscale 잠금", "warning": "경고", + "save": "저장하기", "settings": { "basic": "기본 보기", "advanced": "고급 보기", @@ -87,6 +88,7 @@ "connected_via": "Tailscale을 통해 연결됨", "funnel_port": "WebGUI용 Funnel 포트", "port_in_use": "사용 중인 포트", + "peer_relay": "피어 릴레이 포트", "lock": { "node_key": "노드 키", "public_key": "공개 키", @@ -102,7 +104,8 @@ "subnet": "Unraid 서버에서 Tailscale 서브넷을 수락하면 일부 네트워크 구성에서 로컬 네트워크 연결이 방해받을 수 있습니다. Unraid 서버가 서브넷 라우터 또는 종료 노드로 동작하기 위해 라우트 수락은 필요하지 않습니다. Unraid 서버에서 다른 장치가 광고하는 원격 서브넷에 연결해야 할 때만 이 옵션을 활성화하세요.", "dns": "서버에서 Tailscale MagicDNS를 활성화하면 Docker 컨테이너 내의 이름 해석이 방해받아 애플리케이션 연결 문제가 발생할 수 있습니다. MagicDNS는 다른 장치에서 'unraid.tailnet.ts.net' 같은 MagicDNS 이름으로 Unraid 서버에 접근하기 위해 서버에서 활성화할 필요가 없습니다. '피어를 /etc/hosts에 추가하기'를 활성화하면 서버에서 Tailscale 이름을 사용할 수 있어 더욱 안전한 대안이 됩니다.", "caution": "신중하게 진행하시고 계속하기 전에 이 변경의 영향을 충분히 이해했는지 확인하세요.", - "more_info": "자세한 정보:" + "more_info": "자세한 정보:", + "peer_relay_no_acl": "Tailnet 정책(액세스 제어)에서 구성되지 않았습니다." }, "lock": { "sign": "노드 서명", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json b/src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json index 5b47fa7..f70a8a2 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale is momenteel uitgeschakeld. Deze kan worden ingeschakeld via het tabblad Instellingen.", "tailscale_lock": "Tailscale vergrendeling", "warning": "Waarschuwing", + "save": "Opslaan", "settings": { "basic": "Eenvoudige weergave", "advanced": "Geavanceerde weergave", @@ -61,7 +62,7 @@ "save": "Tailscale zal opnieuw worden opgestart wanneer wijzigingen worden toegepast", "reauthenticate": "Forceer een Tailscale herauthenticatie. Tailscale wordt hierdoor losgekoppeld totdat de authenticatie voltooid is.", "funnel": "Staat toe dat Tailscale Funnel via de WebGUI kan worden ingeschakeld. Indien uitgeschakeld, wordt Tailscale Funnel uitgeschakeld.", - "hosts": "" + "hosts": "Voegt Tailscale-peers toe aan /etc/hosts voor naamresolutie. Dit is handig als u Tailnet-DNS-namen wilt gebruiken, zonder de mogelijke complicaties van het inschakelen van Tailscale DNS op de server." } }, "info": { @@ -87,6 +88,7 @@ "connected_via": "Verbonden via Tailscale", "funnel_port": "Funnel-poort voor WebGUI", "port_in_use": "Poort in gebruik", + "peer_relay": "Peer Relay-poort", "lock": { "node_key": "Node sleutel", "public_key": "Publieke sleutel", @@ -102,7 +104,8 @@ "subnet": "Het accepteren van Tailscale-subnetten op uw Unraid-server kan de lokale netwerkconnectiviteit verstoren bij bepaalde netwerkconfiguraties. Het accepteren van routes is niet vereist voor uw Unraid-server om als subnetrouter of exit node te functioneren. Schakel deze optie alleen in als uw Unraid-server verbinding moet maken met externe subnetten die door andere apparaten worden geadverteerd.", "dns": "Het inschakelen van Tailscale MagicDNS op uw server kan naamresolutie binnen Docker-containers verstoren, waardoor mogelijk verbindingsproblemen voor applicaties ontstaan. MagicDNS hoeft niet ingeschakeld te zijn op de server om uw Unraid-server te kunnen benaderen via MagicDNS-namen (bijv. 'unraid.tailnet.ts.net') vanaf andere apparaten. Het inschakelen van 'Peers toevoegen aan /etc/hosts' is een veiliger alternatief waarmee Tailscale-namen op de server kunnen worden gebruikt.", "caution": "Ga voorzichtig verder en zorg ervoor dat u de gevolgen van deze wijziging begrijpt voordat u doorgaat.", - "more_info": "Voor meer informatie:" + "more_info": "Voor meer informatie:", + "peer_relay_no_acl": "Niet geconfigureerd in het Tailnet-beleid (toegangscontroles)." }, "lock": { "sign": "Nodes ondertekenen", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json b/src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json index cbb236a..480b289 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale er for øyeblikket deaktivert. Det kan aktiveres via fanen Innstillinger.", "tailscale_lock": "Tailscale-lås", "warning": "Advarsel", + "save": "Lagre", "settings": { "basic": "Grunnleggende visning", "advanced": "Avansert visning", @@ -87,6 +88,7 @@ "connected_via": "Tilkoblet via Tailscale", "funnel_port": "Funnel-port for WebGUI", "port_in_use": "Port i bruk", + "peer_relay": "Peer-reléport", "lock": { "node_key": "Node-nøkkel", "public_key": "Offentlig nøkkel", @@ -102,7 +104,8 @@ "subnet": "Å godta Tailscale-subnett på Unraid-serveren din kan forstyrre lokal nettverkstilkobling i visse nettverkskonfigurasjoner. Å godta ruter er ikke nødvendig for at Unraid-serveren din skal fungere som en subnettruter eller exit node. Aktiver kun dette alternativet hvis Unraid-serveren din må koble til eksterne subnett annonsert av andre enheter.", "dns": "Å aktivere Tailscale MagicDNS på serveren din kan forstyrre navneoppløsning inne i Docker-containere, noe som potensielt kan føre til tilkoblingsproblemer for applikasjoner. MagicDNS trenger ikke å være aktivert på serveren for at du skal kunne få tilgang til Unraid-serveren ved å bruke MagicDNS-navn (for eksempel \"unraid.tailnet.ts.net\") fra andre enheter. Å aktivere «Legg til Peers i /etc/hosts» er et tryggere alternativ som lar deg bruke Tailscale-navn på serveren.", "caution": "Fortsett med forsiktighet og sørg for at du forstår konsekvensene av denne endringen før du går videre.", - "more_info": "For mer informasjon:" + "more_info": "For mer informasjon:", + "peer_relay_no_acl": "Ikke konfigurert i Tailnet-policyen (tilgangskontroller)." }, "lock": { "sign": "Signer noder", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json b/src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json index 8e14e53..4b36629 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale jest obecnie wyłączony. Można go włączyć w zakładce Ustawienia.", "tailscale_lock": "Blokada Tailscale", "warning": "Ostrzeżenie", + "save": "Zapisz", "settings": { "basic": "Widok podstawowy", "advanced": "Widok zaawansowany", @@ -87,6 +88,7 @@ "connected_via": "Połączono przez Tailscale", "funnel_port": "Port Tailscale Funnel dla WebGUI", "port_in_use": "Port w użyciu", + "peer_relay": "Port przekaźnika peer", "lock": { "node_key": "Klucz węzła", "public_key": "Klucz publiczny", @@ -102,7 +104,8 @@ "subnet": "Akceptowanie podsieci Tailscale na Twoim serwerze Unraid może zakłócić łączność w sieci lokalnej w niektórych konfiguracjach. Akceptacja tras nie jest wymagana, aby Unraid działał jako router podsieci lub węzeł wyjściowy. Włącz tę opcję tylko wtedy, gdy Twój serwer Unraid potrzebuje połączyć się z podsieciami reklamowanymi przez inne urządzenia.", "dns": "Włączenie MagicDNS Tailscale na serwerze może zakłócić rozwiązywanie nazw w kontenerach Docker, co może powodować problemy z łącznością aplikacji. Nie musisz włączać MagicDNS na serwerze, aby uzyskiwać dostęp do serwera Unraid przy użyciu nazw MagicDNS (np. \"unraid.tailnet.ts.net\") z innych urządzeń. Włączenie opcji \"Dodaj peerów do /etc/hosts\" to bezpieczniejsza alternatywa, która pozwala na używanie nazw Tailscale na serwerze.", "caution": "Zachowaj ostrożność i upewnij się, że rozumiesz konsekwencje tej zmiany przed kontynuacją.", - "more_info": "Więcej informacji:" + "more_info": "Więcej informacji:", + "peer_relay_no_acl": "Niezkonfigurowane w polityce Tailnet (kontrola dostępu)." }, "lock": { "sign": "Podpisz węzły", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json b/src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json index 32cddc9..a5e1bc7 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale está atualmente desativado. Pode ser ativado através da aba Configurações.", "tailscale_lock": "Bloqueio do Tailscale", "warning": "Aviso", + "save": "Salvar", "settings": { "basic": "Visualização Básica", "advanced": "Visualização Avançada", @@ -87,6 +88,7 @@ "connected_via": "Conectado via Tailscale", "funnel_port": "Porta do Funnel para WebGUI", "port_in_use": "Porta em uso", + "peer_relay": "Porta de retransmissão de peer", "lock": { "node_key": "Chave do Nó", "public_key": "Chave Pública", @@ -102,7 +104,8 @@ "subnet": "Aceitar sub-redes do Tailscale no seu servidor Unraid pode afetar a conectividade da rede local em determinadas configurações. Aceitar rotas não é necessário para que seu servidor Unraid funcione como um roteador de sub-rede ou nó de saída. Só ative esta opção se seu servidor Unraid precisar se conectar a sub-redes remotas anunciadas por outros dispositivos.", "dns": "Ativar o MagicDNS do Tailscale no seu servidor pode prejudicar a resolução de nomes dentro dos containers Docker, causando possíveis problemas de conectividade para aplicativos. Não é necessário ativar o MagicDNS no servidor para acessar o seu servidor Unraid usando nomes MagicDNS (por exemplo, 'unraid.tailnet.ts.net') a partir de outros dispositivos. Ativar a opção 'Adicionar pares ao /etc/hosts' é uma alternativa mais segura que permite o uso dos nomes do Tailscale no servidor.", "caution": "Proceda com cautela e certifique-se de que você entende as implicações desta alteração antes de continuar.", - "more_info": "Para mais informações:" + "more_info": "Para mais informações:", + "peer_relay_no_acl": "Não configurado na política do Tailnet (controles de acesso)." }, "lock": { "sign": "Assinar Nós", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json b/src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json index e3ca6ad..21e6875 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale está atualmente desativado. Ele pode ser ativado pela guia Configurações.", "tailscale_lock": "Bloqueio do Tailscale", "warning": "Aviso", + "save": "Salvar", "settings": { "basic": "Visualização Básica", "advanced": "Visualização Avançada", @@ -87,6 +88,7 @@ "connected_via": "Conectado via Tailscale", "funnel_port": "Porta do Funnel para WebGUI", "port_in_use": "Porta em Uso", + "peer_relay": "Porta de retransmissão do peer", "lock": { "node_key": "Chave do Nó", "public_key": "Chave Pública", @@ -102,7 +104,8 @@ "subnet": "Aceitar sub-redes do Tailscale no seu servidor Unraid pode interromper a conectividade da rede local em certas configurações de rede. Aceitar rotas não é necessário para que seu Unraid funcione como roteador de sub-rede ou nó de saída. Ative esta opção apenas se seu Unraid precisar se conectar a sub-redes remotas anunciadas por outros dispositivos.", "dns": "Ativar o MagicDNS do Tailscale no seu servidor pode causar interrupções na resolução de nomes dentro de containers Docker, causando possivelmente problemas de conectividade para aplicações. O MagicDNS não precisa estar ativado no servidor para que você acesse seu Unraid usando nomes MagicDNS (exemplo: 'unraid.tailnet.ts.net') de outros dispositivos. Ativar \"Adicionar peers ao /etc/hosts\" é uma alternativa mais segura, pois permite que nomes do Tailscale sejam usados no servidor.", "caution": "Prossiga com cautela e certifique-se de entender as implicações desta alteração antes de continuar.", - "more_info": "Para mais informações:" + "more_info": "Para mais informações:", + "peer_relay_no_acl": "Não configurado na política do Tailnet (controles de acesso)." }, "lock": { "sign": "Assinar Nós", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json b/src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json index c97ef63..51e386e 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale är för närvarande inaktiverat. Det kan aktiveras via fliken Inställningar.", "tailscale_lock": "Tailscale-lås", "warning": "Varning", + "save": "Spara", "settings": { "basic": "Grundvy", "advanced": "Avancerad vy", @@ -87,6 +88,7 @@ "connected_via": "Ansluten via Tailscale", "funnel_port": "Funnel-port för WebGUI", "port_in_use": "Port används", + "peer_relay": "Peer-reläport", "lock": { "node_key": "Nodnyckel", "public_key": "Publik nyckel", @@ -102,7 +104,8 @@ "subnet": "Att acceptera Tailscale-subnät på din Unraid-server kan störa den lokala nätverksanslutningen i vissa nätverkskonfigurationer. Att acceptera rutter krävs inte för att din Unraid-server ska fungera som en subnätsrouter eller exitnod. Aktivera endast detta alternativ om din Unraid-server behöver ansluta till fjärrsubnät som annonseras av andra enheter.", "dns": "Att aktivera Tailscale MagicDNS på din server kan störa namnuppslagning i Docker-containrar och potentiellt orsaka anslutningsproblem för applikationer. MagicDNS behöver inte vara aktiverat på servern för att du ska kunna nå din Unraid-server med MagicDNS-namn (t.ex. \"unraid.tailnet.ts.net\") från andra enheter. Att aktivera \"Lägg till Peers i /etc/hosts\" är ett säkrare alternativ som gör det möjligt att använda Tailscale-namn på servern.", "caution": "Fortsätt med försiktighet och säkerställ att du förstår konsekvenserna av denna ändring innan du fortsätter.", - "more_info": "Mer information:" + "more_info": "Mer information:", + "peer_relay_no_acl": "Inte konfigurerad i Tailnet-policy (åtkomstkontroller)." }, "lock": { "sign": "Signera noder", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json b/src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json index 22395c8..b5ec2ac 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale наразі вимкнений. Його можна увімкнути через вкладку Налаштування.", "tailscale_lock": "Блокування Tailscale", "warning": "Попередження", + "save": "Зберегти", "settings": { "basic": "Базовий вигляд", "advanced": "Розширений вигляд", @@ -87,6 +88,7 @@ "connected_via": "Підключено через Tailscale", "funnel_port": "Порт Funnel для WebGUI", "port_in_use": "Порт використовується", + "peer_relay": "Порт ретрансляції пірів", "lock": { "node_key": "Ключ вузла", "public_key": "Публічний ключ", @@ -102,7 +104,8 @@ "subnet": "Прийняття підмереж Tailscale на вашому сервері Unraid може порушити локальну мережеву підключуваність у певних мережевих конфігураціях. Приймати маршрути необовʼязково для роботи вашого сервера Unraid як маршрутизатора підмережі чи вузла виходу. Увімкніть цю опцію лише якщо ваш сервер Unraid має підключатися до віддалених підмереж, що рекламуються іншими пристроями.", "dns": "Увімкнення MagicDNS Tailscale на вашому сервері може порушити вирішення імен у контейнерах Docker, що потенційно може спричинити проблеми з підключенням для застосунків. MagicDNS не потрібно вмикати на сервері, щоб отримати доступ до вашого сервера Unraid за іменами MagicDNS (наприклад, 'unraid.tailnet.ts.net') з інших пристроїв. Увімкнення \"Додати вузли до /etc/hosts\" є безпечнішою альтернативою, яка дозволяє використовувати імена Tailscale на сервері.", "caution": "Дійте обережно і переконайтесь, що ви розумієте наслідки цієї зміни перед тим, як продовжити.", - "more_info": "Детальніше:" + "more_info": "Детальніше:", + "peer_relay_no_acl": "Не налаштовано в політиці Tailnet (контролі доступу)." }, "lock": { "sign": "Підписати вузли", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json b/src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json index ad26eee..5fab93d 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json @@ -28,6 +28,7 @@ "tailscale_disabled": "Tailscale 当前已禁用。可以通过“设置选项卡”启用。", "tailscale_lock": "Tailscale 锁", "warning": "警告", + "save": "保存", "settings": { "basic": "基本视图", "advanced": "高级视图", @@ -87,6 +88,7 @@ "connected_via": "通过 Tailscale 连接", "funnel_port": "WebGUI 的 Funnel 端口", "port_in_use": "端口正在使用", + "peer_relay": "同行中继端口", "lock": { "node_key": "节点密钥", "public_key": "公开密钥", @@ -102,7 +104,8 @@ "subnet": "在您的 Unraid 服务器上接受 Tailscale 子网会在某些网络配置下破坏本地网络连接。作为子网路由器或出口节点,您的 Unraid 服务器并不需要接受路由。只有当您的 Unraid 服务器需要连接其他设备所发布的远程子网时,才应启用该选项。", "dns": "在服务器上启用 Tailscale MagicDNS 可能会干扰 Docker 容器中的名称解析,可能导致应用程序连通性问题。您无需在服务器上启用 MagicDNS,也能从其他设备使用 MagicDNS 名称(如 'unraid.tailnet.ts.net')访问您的 Unraid 服务器。启用“将对等端添加到 /etc/hosts”是一种更安全的替代方案,可以让服务器上使用 Tailscale 名称。", "caution": "请谨慎操作,并确保在继续前已了解此更改的影响。", - "more_info": "详细信息:" + "more_info": "详细信息:", + "peer_relay_no_acl": "未在 Tailnet 策略(访问控制)中配置。" }, "lock": { "sign": "为节点签名", From 346f8b520e4203a68c79af04ca0890e029fba963 Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:59:33 -0500 Subject: [PATCH 062/128] chore: update Tailscale to 1.90.6 (#58) ## Summary by CodeRabbit * **Chores** * Updated Tailscale plugin to version 1.90.6 Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- plugin/plugin.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugin/plugin.json b/plugin/plugin.json index 6ea0d52..df308ad 100644 --- a/plugin/plugin.json +++ b/plugin/plugin.json @@ -1,10 +1,10 @@ { - "name": "tailscale", - "package_name": "unraid-tailscale-utils", - "author": "Derek Kaser", - "min": "7.0.0", - "support": "https://forums.unraid.net/topic/136889-plugin-tailscale/", - "launch": "Settings/Tailscale", - "tailscaleVersion": "tailscale_1.90.5_amd64", - "tailscaleSHA256": "901d0310d888e471f0e17b1f2a64fd0d77c0d8848e5a63f0a2b29a9da66e8ac2" -} + "name": "tailscale", + "package_name": "unraid-tailscale-utils", + "author": "Derek Kaser", + "min": "7.0.0", + "support": "https://forums.unraid.net/topic/136889-plugin-tailscale/", + "launch": "Settings/Tailscale", + "tailscaleVersion": "tailscale_1.90.6_amd64", + "tailscaleSHA256": "cc51b5b6fe86a25e66b2ef037334e799a61d06dc64fabcadfe6e2a7588a2b2c7" +} \ No newline at end of file From 14237210f5ad4925cb8f21b48344b0490b7eb224 Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Sun, 2 Nov 2025 23:19:17 -0500 Subject: [PATCH 063/128] chore: bump supported version to 7.1 (#59) ## Summary by CodeRabbit ## Release Notes * **Bug Fixes** * Enhanced compatibility for older Unraid versions by automatically detecting the environment and installing the last compatible plugin version, with clear messaging to guide users through the process. Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- plugin/plugin.j2 | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/plugin/plugin.j2 b/plugin/plugin.j2 index edc5fa0..2a32575 100644 --- a/plugin/plugin.j2 +++ b/plugin/plugin.j2 @@ -30,6 +30,19 @@ For older releases, see https://github.com/{{ env['GITHUB_REPOSITORY'] }}/releas ]]> + + + + + + https://pkgs.tailscale.com/stable/{{ tailscaleVersion }}.tgz {{ tailscaleSHA256 }} From 77e32507e5233248f3063d293895cffa57ff8956 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 04:25:32 +0000 Subject: [PATCH 064/128] release: update plugin files for 2025.11.03.0419 --- plugin/tailscale-preview.plg | 55 ++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/plugin/tailscale-preview.plg b/plugin/tailscale-preview.plg index b927bc0..10c8ef9 100644 --- a/plugin/tailscale-preview.plg +++ b/plugin/tailscale-preview.plg @@ -4,7 +4,7 @@ - -https://pkgs.tailscale.com/stable/tailscale_1.90.5_amd64.tgz -901d0310d888e471f0e17b1f2a64fd0d77c0d8848e5a63f0a2b29a9da66e8ac2 + + + + + + + +https://pkgs.tailscale.com/stable/tailscale_1.90.6_amd64.tgz +cc51b5b6fe86a25e66b2ef037334e799a61d06dc64fabcadfe6e2a7588a2b2c7 - -https://github.com/unraid/unraid-tailscale/releases/download/2025.10.31.0431/unraid-tailscale-utils-2025.10.31.0431-noarch-1.txz -5ff36e07555fce2dbcef01c6941b9a5c7a82edc15766cc690a316afb68db288c + +https://github.com/unraid/unraid-tailscale/releases/download/2025.11.03.0419/unraid-tailscale-utils-2025.11.03.0419-noarch-1.txz +47207ecb566ab415927306941d569b6e340f4de97e8209765c06190c814add12 ## Summary by CodeRabbit ## Release Notes * **New Features** * Installer now verifies system compatibility before proceeding with installation. * Automatic fallback support ensures older systems receive compatible plugin versions. * **Bug Fixes** * Enhanced system version detection for more reliable installations. Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- plugin/plugin.j2 | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/plugin/plugin.j2 b/plugin/plugin.j2 index 45c6918..61d0759 100644 --- a/plugin/plugin.j2 +++ b/plugin/plugin.j2 @@ -30,23 +30,38 @@ For older releases, see https://github.com/{{ env['GITHUB_REPOSITORY'] }}/releas ]]> - + - - -exit 1 - - - https://pkgs.tailscale.com/stable/{{ tailscaleVersion }}.tgz {{ tailscaleSHA256 }} From 50ae227b32eedb5700c84cb6d124244e04e147a5 Mon Sep 17 00:00:00 2001 From: Derek Kaser <11674153+dkaser@users.noreply.github.com> Date: Wed, 5 Nov 2025 01:06:59 -0500 Subject: [PATCH 070/128] feat: add configuration option for auto update (#64) ## Summary by CodeRabbit * **New Features** * Added auto-update configuration option to the Tailscale plugin interface, enabling users to toggle automatic updates on or off. The setting is persisted and managed through the plugin's configuration system. Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- .../plugins/tailscale/include/Pages/Tailscale.php | 6 ++++++ .../plugins/tailscale/include/data/Config.php | 15 +++++++++++++++ .../emhttp/plugins/tailscale/locales/en_US.json | 1 + .../unraid-tailscale-utils/ConnectionInfo.php | 1 + .../unraid-tailscale-utils/Info.php | 6 ++++++ .../unraid-tailscale-utils/LocalAPI.php | 9 +++++++++ 6 files changed, 38 insertions(+) diff --git a/src/usr/local/emhttp/plugins/tailscale/include/Pages/Tailscale.php b/src/usr/local/emhttp/plugins/tailscale/include/Pages/Tailscale.php index a40c2d9..e8f290e 100644 --- a/src/usr/local/emhttp/plugins/tailscale/include/Pages/Tailscale.php +++ b/src/usr/local/emhttp/plugins/tailscale/include/Pages/Tailscale.php @@ -81,6 +81,12 @@ function showTailscaleConfig() { var res = await $.post('/plugins/tailscale/include/data/Config.php',{action: 'set-feature', feature: feature, enable: enable}); showTailscaleConfig(); } +async function setAutoUpdate(enable) { + $('div.spinner.fixed').show('fast'); + tailscaleControlsDisabled(true); + var res = await $.post('/plugins/tailscale/include/data/Config.php',{action: 'set-auto-update', enable: enable}); + showTailscaleConfig(); +} async function setAdvertiseExitNode(enable) { $('div.spinner.fixed').show('fast'); tailscaleControlsDisabled(true); diff --git a/src/usr/local/emhttp/plugins/tailscale/include/data/Config.php b/src/usr/local/emhttp/plugins/tailscale/include/data/Config.php index d6129ea..44698cc 100644 --- a/src/usr/local/emhttp/plugins/tailscale/include/data/Config.php +++ b/src/usr/local/emhttp/plugins/tailscale/include/data/Config.php @@ -70,6 +70,10 @@ "" : ""; + $autoUpdateButton = $tailscaleInfo->autoUpdateEnabled() ? + "" : + ""; + $advertiseExitButton = $tailscaleInfo->usesExitNode() ? "" : ( $tailscaleInfo->advertisesExitNode() ? @@ -103,6 +107,7 @@ $relayPortWarning = $relayPort !== "" && ! $tailscaleInfo->isApprovedPeerRelay() ? $tr->tr("warnings.peer_relay_no_acl") : " "; $configRows = <<{$tr->tr("info.auto_update")}{$tailscaleConInfo->AutoUpdate}{$autoUpdateButton} {$tr->tr("info.accept_routes")}{$tailscaleConInfo->AcceptRoutes}{$acceptRoutesButton} {$tr->tr("info.accept_dns")}{$tailscaleConInfo->AcceptDNS}{$acceptDNSButton} {$tr->tr("info.run_ssh")}{$tailscaleConInfo->RunSSH}{$sshButton} @@ -304,6 +309,16 @@ $utils->logmsg("Expiring node key"); $localAPI->expireKey(); break; + case 'set-auto-update': + if ( ! isset($_POST['enable'])) { + throw new \Exception("Missing enable parameter"); + } + + $enable = filter_var($_POST['enable'], FILTER_VALIDATE_BOOLEAN); + $utils->logmsg("Setting auto update to " . ($enable ? "true" : "false")); + + $localAPI->setAutoUpdate($enable); + break; case 'exit-node': if ( ! isset($_POST['node'])) { throw new \Exception("Missing node parameter"); diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/en_US.json b/src/usr/local/emhttp/plugins/tailscale/locales/en_US.json index 282c111..354201b 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/en_US.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/en_US.json @@ -89,6 +89,7 @@ "funnel_port": "Funnel Port for WebGUI", "port_in_use": "Port in Use", "peer_relay": "Peer Relay Port", + "auto_update": "Auto Update", "lock": { "node_key": "Node Key", "public_key": "Public Key", diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/ConnectionInfo.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/ConnectionInfo.php index ae2b7ad..b34f79b 100644 --- a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/ConnectionInfo.php +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/ConnectionInfo.php @@ -32,4 +32,5 @@ class ConnectionInfo public string $ExitNodeLocal = ""; public string $AdvertiseExitNode = ""; public string $UseExitNode = ""; + public string $AutoUpdate = ""; } diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Info.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Info.php index ac22b95..d97a561 100644 --- a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Info.php +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/Info.php @@ -116,6 +116,7 @@ public function getConnectionInfo(): ConnectionInfo $info->RunSSH = isset($prefs->RunSSH) ? ($prefs->RunSSH ? $this->tr("yes") : $this->tr("no")) : $this->tr("unknown"); $info->ExitNodeLocal = isset($prefs->ExitNodeAllowLANAccess) ? ($prefs->ExitNodeAllowLANAccess ? $this->tr("yes") : $this->tr("no")) : $this->tr("unknown"); $info->UseExitNode = $this->usesExitNode() ? $this->tr("yes") : $this->tr("no"); + $info->AutoUpdate = $this->autoUpdateEnabled() ? $this->tr("yes") : $this->tr("no"); if ($this->advertisesExitNode()) { if ($this->status->Self->ExitNodeOption) { @@ -498,4 +499,9 @@ public function getRelayServerPort(): int|false } return false; } + + public function autoUpdateEnabled(): bool + { + return $this->prefs->AutoUpdate->Apply ?? false; + } } diff --git a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LocalAPI.php b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LocalAPI.php index e1ddc3c..905b932 100644 --- a/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LocalAPI.php +++ b/src/usr/local/php/unraid-tailscale-utils/unraid-tailscale-utils/LocalAPI.php @@ -140,4 +140,13 @@ public function expireKey(): void { $this->tailscaleLocalAPI('v0/set-expiry-sooner?expiry=0', APIMethods::POST); } + + public function setAutoUpdate(bool $enabled): void + { + $body = []; + $body["AutoUpdate"] = ["Apply" => $enabled, "Check" => $enabled]; + $body["AutoUpdateSet"] = ["ApplySet" => true, "CheckSet" => true]; + + $this->tailscaleLocalAPI("v0/prefs", APIMethods::PATCH, (object) $body); + } } From 9b98ee8f938327cf13e481ee9c09f5d4a008d89b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 01:14:45 -0500 Subject: [PATCH 071/128] chore: update translations from Crowdin (#65) Co-authored-by: Crowdin Bot --- src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json | 1 + src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json | 1 + src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json | 1 + src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json | 1 + src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json | 1 + src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json | 1 + src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json | 1 + src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json | 1 + src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json | 1 + src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json | 1 + src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json | 1 + src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json | 1 + src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json | 1 + src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json | 3 ++- 14 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json b/src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json index 40a9233..261708d 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/de_DE.json @@ -89,6 +89,7 @@ "funnel_port": "Funnel-Port für WebGUI", "port_in_use": "Verwendeter Port", "peer_relay": "Peer-Relay-Port", + "auto_update": "Automatisches Aktualisieren", "lock": { "node_key": "Node Schlüssel", "public_key": "Öffentlicher Schlüssel", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json b/src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json index 8c05cba..00eba9f 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/es_ES.json @@ -89,6 +89,7 @@ "funnel_port": "Puerto del Funnel para WebGUI", "port_in_use": "Puerto en uso", "peer_relay": "Puerto de retransmisión entre pares", + "auto_update": "Actualización automática", "lock": { "node_key": "Clave de nodo", "public_key": "Clave pública", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json b/src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json index 6ea0fe9..447685b 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/fr_FR.json @@ -89,6 +89,7 @@ "funnel_port": "Port Funnel pour le WebGUI", "port_in_use": "Port utilisé", "peer_relay": "Port de relais pair-à-pair", + "auto_update": "Mise à jour automatique", "lock": { "node_key": "Clé du nœud", "public_key": "Clé publique", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json b/src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json index 82750d9..02e2d43 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/it_IT.json @@ -89,6 +89,7 @@ "funnel_port": "Porta Funnel per WebGUI", "port_in_use": "Porta in uso", "peer_relay": "Porta di inoltro peer", + "auto_update": "Aggiornamento automatico", "lock": { "node_key": "Chiave nodo", "public_key": "Chiave pubblica", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json b/src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json index 59dff92..111fb6e 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/ja_JP.json @@ -89,6 +89,7 @@ "funnel_port": "WebGUI 用 Funnel ポート", "port_in_use": "使用中のポート", "peer_relay": "ピアリレイポート", + "auto_update": "自動更新", "lock": { "node_key": "ノードキー", "public_key": "公開鍵", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json b/src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json index 8123b6f..c8f9ce5 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/ko_KR.json @@ -89,6 +89,7 @@ "funnel_port": "WebGUI용 Funnel 포트", "port_in_use": "사용 중인 포트", "peer_relay": "피어 릴레이 포트", + "auto_update": "자동 업데이트", "lock": { "node_key": "노드 키", "public_key": "공개 키", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json b/src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json index f70a8a2..88090c4 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/nl_NL.json @@ -89,6 +89,7 @@ "funnel_port": "Funnel-poort voor WebGUI", "port_in_use": "Poort in gebruik", "peer_relay": "Peer Relay-poort", + "auto_update": "Automatisch bijwerken", "lock": { "node_key": "Node sleutel", "public_key": "Publieke sleutel", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json b/src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json index 480b289..04221ed 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/no_NO.json @@ -89,6 +89,7 @@ "funnel_port": "Funnel-port for WebGUI", "port_in_use": "Port i bruk", "peer_relay": "Peer-reléport", + "auto_update": "Automatisk oppdatering", "lock": { "node_key": "Node-nøkkel", "public_key": "Offentlig nøkkel", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json b/src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json index 4b36629..bb86339 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/pl_PL.json @@ -89,6 +89,7 @@ "funnel_port": "Port Tailscale Funnel dla WebGUI", "port_in_use": "Port w użyciu", "peer_relay": "Port przekaźnika peer", + "auto_update": "Automatyczna aktualizacja", "lock": { "node_key": "Klucz węzła", "public_key": "Klucz publiczny", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json b/src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json index a5e1bc7..fef9c8a 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/pt_BR.json @@ -89,6 +89,7 @@ "funnel_port": "Porta do Funnel para WebGUI", "port_in_use": "Porta em uso", "peer_relay": "Porta de retransmissão de peer", + "auto_update": "Atualização Automática", "lock": { "node_key": "Chave do Nó", "public_key": "Chave Pública", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json b/src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json index 21e6875..55fdf52 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/pt_PT.json @@ -89,6 +89,7 @@ "funnel_port": "Porta do Funnel para WebGUI", "port_in_use": "Porta em Uso", "peer_relay": "Porta de retransmissão do peer", + "auto_update": "Atualização automática", "lock": { "node_key": "Chave do Nó", "public_key": "Chave Pública", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json b/src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json index 51e386e..9f88961 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/sv_SE.json @@ -89,6 +89,7 @@ "funnel_port": "Funnel-port för WebGUI", "port_in_use": "Port används", "peer_relay": "Peer-reläport", + "auto_update": "Automatisk uppdatering", "lock": { "node_key": "Nodnyckel", "public_key": "Publik nyckel", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json b/src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json index b5ec2ac..b15a84f 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/uk_UA.json @@ -89,6 +89,7 @@ "funnel_port": "Порт Funnel для WebGUI", "port_in_use": "Порт використовується", "peer_relay": "Порт ретрансляції пірів", + "auto_update": "Автоматичне оновлення", "lock": { "node_key": "Ключ вузла", "public_key": "Публічний ключ", diff --git a/src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json b/src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json index 5fab93d..58942de 100644 --- a/src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json +++ b/src/usr/local/emhttp/plugins/tailscale/locales/zh_CN.json @@ -88,7 +88,8 @@ "connected_via": "通过 Tailscale 连接", "funnel_port": "WebGUI 的 Funnel 端口", "port_in_use": "端口正在使用", - "peer_relay": "同行中继端口", + "peer_relay": "对等中继端口", + "auto_update": "自动更新", "lock": { "node_key": "节点密钥", "public_key": "公开密钥", From 9d230e55c7a468a80480373abc09317c11ec87ef Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 06:21:04 +0000 Subject: [PATCH 072/128] release: update plugin files for 2025.11.05.0614 --- plugin/tailscale-preview.plg | 71 ++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/plugin/tailscale-preview.plg b/plugin/tailscale-preview.plg index d681982..c4ac0c7 100644 --- a/plugin/tailscale-preview.plg +++ b/plugin/tailscale-preview.plg @@ -4,7 +4,7 @@ - + - - -exit 1 - - - https://pkgs.tailscale.com/stable/tailscale_1.90.6_amd64.tgz cc51b5b6fe86a25e66b2ef037334e799a61d06dc64fabcadfe6e2a7588a2b2c7 - -https://github.com/unraid/unraid-tailscale/releases/download/2025.11.03.0444/unraid-tailscale-utils-2025.11.03.0444-noarch-1.txz -24f6f93a2497ca4c6888555846378a0b7c60d5562244fded4f0370ac82dbae5f + +https://github.com/unraid/unraid-tailscale/releases/download/2025.11.05.0614/unraid-tailscale-utils-2025.11.05.0614-noarch-1.txz +ca6b2c8ad468464fb7b5dd27cc3f482d77065d0af25a844c928e203ca46b5296 ## Summary by CodeRabbit * **Bug Fixes** * Fixed the reauthenticate button so it reliably triggers the intended reauthentication action and removed the now-unused legacy behavior. * **Documentation** * Shortened and clarified the reauthenticate button description to a concise "Force a Tailscale reauthentication." --------- Signed-off-by: Derek Kaser <11674153+dkaser@users.noreply.github.com> --- .../emhttp/plugins/tailscale/include/Pages/Settings.php | 8 +------- src/usr/local/emhttp/plugins/tailscale/locales/en_US.json | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/usr/local/emhttp/plugins/tailscale/include/Pages/Settings.php b/src/usr/local/emhttp/plugins/tailscale/include/Pages/Settings.php index aca6b1c..0b9d878 100644 --- a/src/usr/local/emhttp/plugins/tailscale/include/Pages/Settings.php +++ b/src/usr/local/emhttp/plugins/tailscale/include/Pages/Settings.php @@ -215,7 +215,7 @@
tr("settings.context.reauthenticate"); ?>
- > + >
@@ -240,12 +240,6 @@ function requestErase(e) { var confirmButton = document.getElementById('tailscale_erase_confirm'); confirmButton.style.display = "inline"; } - - async function expireTailscaleKeyNow() { - $('div.spinner.fixed').show('fast'); - var res = await $.post('/plugins/tailscale/include/data/Config.php',{action: 'expire-key'}); - location.reload(); - } + +