mirror of
https://github.com/syncthing/syncthing.git
synced 2026-01-05 04:19:10 -05:00
Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
445e8cc532 | ||
|
|
e041877488 | ||
|
|
36e08f8eee | ||
|
|
8b321387c0 | ||
|
|
8edd67a569 | ||
|
|
e829a63295 | ||
|
|
6881a6897a | ||
|
|
9387e107b3 | ||
|
|
aa901790b9 | ||
|
|
17df4b8634 | ||
|
|
34ef30dd5c | ||
|
|
fc1c7a3c49 | ||
|
|
2abfefc18c | ||
|
|
86a08eb87d | ||
|
|
d330d65859 | ||
|
|
4e1831fa3a | ||
|
|
2f3eacdb6c | ||
|
|
1ce2af1238 | ||
|
|
683b48182c | ||
|
|
795aed306c | ||
|
|
cdefa535ed | ||
|
|
91084b83b4 | ||
|
|
5360e7153b | ||
|
|
5d0ca19350 | ||
|
|
e8d3529fed | ||
|
|
935a28c961 | ||
|
|
d21a2de055 | ||
|
|
6f1023665c | ||
|
|
c53a1f210c | ||
|
|
b71a930bfc | ||
|
|
a2cbc62521 | ||
|
|
768fd6bff8 | ||
|
|
48883e0e32 | ||
|
|
30fe2cf514 | ||
|
|
3850a08252 | ||
|
|
d0e407f3c3 | ||
|
|
16db6fcf3d | ||
|
|
4c5528bd0e | ||
|
|
d42fff1016 | ||
|
|
a28de73031 | ||
|
|
75310b58a0 | ||
|
|
8064957270 | ||
|
|
c1ec9a8826 | ||
|
|
1625b44892 | ||
|
|
d51760f410 | ||
|
|
7b1932d64e | ||
|
|
5bfc540c88 | ||
|
|
4cba99fcd4 | ||
|
|
2ae15aa454 | ||
|
|
47bcf4f8f4 | ||
|
|
a8b9096353 | ||
|
|
5328380691 | ||
|
|
6069cf39e5 | ||
|
|
1f7d236742 | ||
|
|
8e9ee3fbe8 | ||
|
|
35b0afc131 | ||
|
|
958ff67ccc | ||
|
|
13d9317a38 | ||
|
|
b184d46d8a | ||
|
|
3f32c5cb4b | ||
|
|
5c65a1bc83 | ||
|
|
d0a6dc5b13 | ||
|
|
439c6c5b7c | ||
|
|
aaee0c126b | ||
|
|
f3bd4d71de | ||
|
|
876d056705 | ||
|
|
e988978fa1 | ||
|
|
d5deede7a1 | ||
|
|
4f70f5c280 | ||
|
|
a1ad020b63 | ||
|
|
8f1b0df74b | ||
|
|
0f8dc6c1d3 | ||
|
|
8ae9db3b2d | ||
|
|
e477777f49 | ||
|
|
7a132bdf24 | ||
|
|
5e2b7825dc | ||
|
|
6d30c109e4 | ||
|
|
58bd931d90 | ||
|
|
854499382e | ||
|
|
cb4c1f9ad2 | ||
|
|
b452fb3ad2 | ||
|
|
c17a1fea77 | ||
|
|
d50511c5c6 | ||
|
|
bae6d5f375 | ||
|
|
b5082f6af8 |
42
.github/workflows/build-infra-dockers.yaml
vendored
42
.github/workflows/build-infra-dockers.yaml
vendored
@@ -4,30 +4,37 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- infrastructure
|
||||
- infra-*
|
||||
|
||||
env:
|
||||
GO_VERSION: "~1.21.1"
|
||||
CGO_ENABLED: "0"
|
||||
BUILD_USER: docker
|
||||
BUILD_HOST: github.syncthing.net
|
||||
GO_VERSION: "~1.21.5"
|
||||
CGO_ENABLED: "0"
|
||||
BUILD_USER: docker
|
||||
BUILD_HOST: github.syncthing.net
|
||||
|
||||
jobs:
|
||||
|
||||
docker-syncthing:
|
||||
name: Build and push Docker images
|
||||
if: github.repository == 'syncthing/syncthing'
|
||||
runs-on: ubuntu-latest
|
||||
environment: docker
|
||||
strategy:
|
||||
matrix:
|
||||
pkg:
|
||||
- stcrashreceiver
|
||||
- strelaypoolsrv
|
||||
- stupgrades
|
||||
matrix:
|
||||
pkg:
|
||||
- stcrashreceiver
|
||||
- strelaypoolsrv
|
||||
- stupgrades
|
||||
- ursrv
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -44,6 +51,17 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Set Docker tags (all branches)
|
||||
run: |
|
||||
tags=syncthing/${{ matrix.pkg }}:${{ github.sha }}
|
||||
echo "TAGS=$tags" >> $GITHUB_ENV
|
||||
|
||||
- name: Set Docker tags (latest)
|
||||
if: github.ref == 'refs/heads/infrastructure'
|
||||
run: |
|
||||
tags=syncthing/${{ matrix.pkg }}:latest,${{ env.TAGS }}
|
||||
echo "TAGS=$tags" >> $GITHUB_ENV
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
@@ -51,4 +69,6 @@ jobs:
|
||||
file: ./Dockerfile.${{ matrix.pkg }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: syncthing/${{ matrix.pkg }}:latest,syncthing/${{ matrix.pkg }}:${{ github.sha }}
|
||||
tags: ${{ env.TAGS }}
|
||||
labels: |
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
|
||||
85
.github/workflows/build-syncthing.yaml
vendored
85
.github/workflows/build-syncthing.yaml
vendored
@@ -12,7 +12,7 @@ env:
|
||||
# The go version to use for builds. We set check-latest to true when
|
||||
# installing, so we get the latest patch version that matches the
|
||||
# expression.
|
||||
GO_VERSION: "~1.21.1"
|
||||
GO_VERSION: "~1.21.5"
|
||||
|
||||
# Optimize compatibility on the slow archictures.
|
||||
GO386: softfloat
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
runner: ["windows-latest", "ubuntu-latest", "macos-latest"]
|
||||
# The oldest version in this list should match what we have in our go.mod.
|
||||
# Variables don't seem to be supported here, or we could have done something nice.
|
||||
go: ["1.20", "1.21"]
|
||||
go: ["~1.20.12", "~1.21.5"]
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- name: Set git to use LF
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
cache: true
|
||||
@@ -79,6 +79,7 @@ jobs:
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
go version
|
||||
go run build.go test | go-test-json-to-loki
|
||||
env:
|
||||
GOFLAGS: "-json"
|
||||
@@ -97,7 +98,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false
|
||||
@@ -153,12 +154,17 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false
|
||||
check-latest: true
|
||||
|
||||
- name: Get actual Go version
|
||||
run: |
|
||||
go version
|
||||
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
@@ -184,7 +190,7 @@ jobs:
|
||||
CODESIGN_TIMESTAMP_SERVER: ${{ secrets.CODESIGN_TIMESTAMP_SERVER }}
|
||||
|
||||
- name: Archive artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: packages-windows
|
||||
path: syncthing-windows-*.zip
|
||||
@@ -201,12 +207,17 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false
|
||||
check-latest: true
|
||||
|
||||
- name: Get actual Go version
|
||||
run: |
|
||||
go version
|
||||
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
@@ -224,7 +235,7 @@ jobs:
|
||||
CGO_ENABLED: "0"
|
||||
|
||||
- name: Archive artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: packages-linux
|
||||
path: syncthing-linux-*.tar.gz
|
||||
@@ -243,12 +254,17 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false
|
||||
check-latest: true
|
||||
|
||||
- name: Get actual Go version
|
||||
run: |
|
||||
go version
|
||||
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
@@ -318,7 +334,7 @@ jobs:
|
||||
zip -r "../$universal.zip" "$universal"
|
||||
|
||||
- name: Archive artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: packages-macos
|
||||
path: syncthing-*.zip
|
||||
@@ -333,7 +349,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: packages-macos
|
||||
|
||||
@@ -365,12 +381,17 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false
|
||||
check-latest: true
|
||||
|
||||
- name: Get actual Go version
|
||||
run: |
|
||||
go version
|
||||
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
@@ -410,7 +431,7 @@ jobs:
|
||||
CGO_ENABLED: "0"
|
||||
|
||||
- name: Archive artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: packages-other
|
||||
path: syncthing-*.tar.gz
|
||||
@@ -427,7 +448,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false
|
||||
@@ -450,7 +471,7 @@ jobs:
|
||||
mv "syncthing-source-$version.tar.gz" syncthing
|
||||
|
||||
- name: Archive artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: packages-source
|
||||
path: syncthing-source-*.tar.gz
|
||||
@@ -483,7 +504,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Install signing tool
|
||||
run: |
|
||||
@@ -522,7 +543,7 @@ jobs:
|
||||
GNUPG_SIGNING_KEY_BASE64: ${{ secrets.GNUPG_SIGNING_KEY_BASE64 }}
|
||||
|
||||
- name: Archive artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: packages-signed
|
||||
path: packages/*
|
||||
@@ -539,12 +560,17 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false
|
||||
check-latest: true
|
||||
|
||||
- name: Get actual Go version
|
||||
run: |
|
||||
go version
|
||||
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
|
||||
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: '3.0'
|
||||
@@ -569,7 +595,7 @@ jobs:
|
||||
BUILD_USER: debian
|
||||
|
||||
- name: Archive artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debian-packages
|
||||
path: "*.deb"
|
||||
@@ -594,7 +620,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: packages-signed
|
||||
path: packages
|
||||
@@ -636,13 +662,13 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download signed packages
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: packages-signed
|
||||
path: packages
|
||||
|
||||
- name: Download debian packages
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: debian-packages
|
||||
path: packages
|
||||
@@ -683,7 +709,7 @@ jobs:
|
||||
docker-syncthing:
|
||||
name: Build and push Docker images
|
||||
runs-on: ubuntu-latest
|
||||
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release-'))
|
||||
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/infrastructure' || startsWith(github.ref, 'refs/heads/release-'))
|
||||
environment: docker
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -706,12 +732,17 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false
|
||||
check-latest: true
|
||||
|
||||
- name: Get actual Go version
|
||||
run: |
|
||||
go version
|
||||
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
@@ -762,6 +793,7 @@ jobs:
|
||||
tags=${{ matrix.image }}:edge
|
||||
fi
|
||||
echo "DOCKER_TAGS=$tags" >> $GITHUB_ENV
|
||||
echo "VERSION=$version" >> $GITHUB_ENV
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
@@ -771,6 +803,9 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/7
|
||||
push: ${{ env.DOCKER_PUSH == 'true' }}
|
||||
tags: ${{ env.DOCKER_TAGS }}
|
||||
labels: |
|
||||
org.opencontainers.image.version=${{ env.VERSION }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
|
||||
#
|
||||
# Check for known vulnerabilities in Go dependencies
|
||||
@@ -782,7 +817,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.19.6
|
||||
- run: |
|
||||
|
||||
14
AUTHORS
14
AUTHORS
@@ -27,6 +27,7 @@ Alexander Seiler <seileralex@gmail.com>
|
||||
Alexandre Alves <alexandrealvesdb.contact@gmail.com>
|
||||
Alexandre Viau (aviau) <alexandre@alexandreviau.net> <aviau@debian.org>
|
||||
Aman Gupta <aman@tmm1.net>
|
||||
Anatoli Babenia <anatoli@rainforce.org>
|
||||
Anderson Mesquita (andersonvom) <andersonvom@gmail.com>
|
||||
Andreas Sommer <andreas.sommer87@googlemail.com>
|
||||
andresvia <andres.via@gmail.com>
|
||||
@@ -68,6 +69,7 @@ Brian R. Becker (brbecker) <brbecker@gmail.com>
|
||||
bt90 <btom1990@googlemail.com>
|
||||
Caleb Callaway (cqcallaw) <enlightened.despot@gmail.com>
|
||||
Carsten Hagemann (carstenhag) <moter8@gmail.com> <carsten@chagemann.de>
|
||||
Catfriend1 <16361913+Catfriend1@users.noreply.github.com>
|
||||
Cathryne Linenweaver (Cathryne) <cathryne.linenweaver@gmail.com> <Cathryne@users.noreply.github.com> <katrinleinweber@MAC.local>
|
||||
Cedric Staniewski (xduugu) <cedric@gmx.ca>
|
||||
chenrui <rui@meetup.com>
|
||||
@@ -79,6 +81,7 @@ Chris Tonkinson <chris@masterbran.ch>
|
||||
Christian Kujau <ckujau@users.noreply.github.com>
|
||||
Christian Prescott <me@christianprescott.com>
|
||||
chucic <chucic@seznam.cz>
|
||||
cjc7373 <niuchangcun@gmail.com>
|
||||
Colin Kennedy (moshen) <moshen.colin@gmail.com>
|
||||
Cromefire_ <tim.l@nghorst.net> <26320625+cromefire@users.noreply.github.com>
|
||||
cui fliter <imcusg@gmail.com>
|
||||
@@ -90,6 +93,7 @@ Daniel Barczyk <46358936+DanielBarczyk@users.noreply.github.com>
|
||||
Daniel Bergmann (brgmnn) <dan.arne.bergmann@gmail.com> <brgmnn@users.noreply.github.com>
|
||||
Daniel Harte (norgeous) <daniel@harte.me> <daniel@danielharte.co.uk> <norgeous@users.noreply.github.com>
|
||||
Daniel Martí (mvdan) <mvdan@mvdan.cc>
|
||||
Daniel Padrta <64928366+danpadcz@users.noreply.github.com>
|
||||
Darshil Chanpura (dtchanpura) <dtchanpura@gmail.com> <dcprime314@gmail.com>
|
||||
David Rimmer (dinosore) <dinosore@dbrsoftware.co.uk>
|
||||
deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
|
||||
@@ -99,8 +103,10 @@ Dennis Wilson (snnd) <dw@risu.io>
|
||||
dependabot-preview[bot] <dependabot-preview[bot]@users.noreply.github.com> <27856297+dependabot-preview[bot]@users.noreply.github.com>
|
||||
dependabot[bot] <dependabot[bot]@users.noreply.github.com> <49699333+dependabot[bot]@users.noreply.github.com>
|
||||
derekriemer <derek.riemer@colorado.edu>
|
||||
DerRockWolf <50499906+DerRockWolf@users.noreply.github.com>
|
||||
desbma <desbma@users.noreply.github.com>
|
||||
Devon G. Redekopp <devon@redekopp.com>
|
||||
diemade <spamkill@posteo.ch>
|
||||
digital <didev@dinid.net>
|
||||
Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com>
|
||||
Dmitry Saveliev (dsaveliev) <d.e.saveliev@gmail.com>
|
||||
@@ -133,6 +139,7 @@ Graham Miln (grahammiln) <graham.miln@dssw.co.uk> <graham.miln@miln.eu>
|
||||
greatroar <61184462+greatroar@users.noreply.github.com>
|
||||
Greg <gco@jazzhaiku.com>
|
||||
guangwu <guoguangwu@magic-shield.com>
|
||||
gudvinr <gudvinr@gmail.com>
|
||||
Han Boetes <han@boetes.org>
|
||||
HansK-p <42314815+HansK-p@users.noreply.github.com>
|
||||
Harrison Jones (harrisonhjones) <harrisonhjones@users.noreply.github.com>
|
||||
@@ -174,6 +181,7 @@ Jonathan Cross <jcross@gmail.com>
|
||||
Jonta <359397+Jonta@users.noreply.github.com>
|
||||
Jose Manuel Delicado (jmdaweb) <jmdaweb@hotmail.com> <jmdaweb@users.noreply.github.com>
|
||||
jtagcat <git-514635f7@jtag.cat> <git-12dbd862@jtag.cat>
|
||||
Julian Lehrhuber <jul13579@users.noreply.github.com>
|
||||
Jörg Thalheim <Mic92@users.noreply.github.com>
|
||||
Jędrzej Kula <kula.jedrek@gmail.com>
|
||||
K.B.Dharun Krishna <kbdharunkrishna@gmail.com>
|
||||
@@ -221,7 +229,7 @@ Max <github@germancoding.com>
|
||||
Max Schulze (kralo) <max.schulze@online.de> <kralo@users.noreply.github.com>
|
||||
MaximAL <almaximal@ya.ru>
|
||||
Maxime Thirouin <m@moox.io>
|
||||
Maximilian <maxi.rostock@outlook.de>
|
||||
Maximilian <maxi.rostock@outlook.de> <public@complexvector.space>
|
||||
mclang <1721600+mclang@users.noreply.github.com>
|
||||
Michael Jephcote (Rewt0r) <rewt0r@gmx.com> <Rewt0r@users.noreply.github.com>
|
||||
Michael Ploujnikov (plouj) <ploujj@gmail.com>
|
||||
@@ -236,6 +244,7 @@ Mingxuan Lin <gdlmx@users.noreply.github.com>
|
||||
mv1005 <49659413+mv1005@users.noreply.github.com>
|
||||
Nate Morrison (nrm21) <natemorrison@gmail.com>
|
||||
Naveen <172697+naveensrinivasan@users.noreply.github.com>
|
||||
nf <nf@wh3rd.net>
|
||||
Nicholas Rishel (PrototypeNM1) <rishel.nick@gmail.com> <PrototypeNM1@users.noreply.github.com>
|
||||
Nick Busey <NickBusey@users.noreply.github.com>
|
||||
Nico Stapelbroek <3368018+nstapelbroek@users.noreply.github.com>
|
||||
@@ -286,6 +295,7 @@ Sacheendra Talluri (sacheendra) <sacheendra.t@gmail.com>
|
||||
Scott Klupfel (kluppy) <kluppy@going2blue.com>
|
||||
sec65 <106604020+sec65@users.noreply.github.com>
|
||||
Sergey Mishin (ralder) <ralder@yandex.ru>
|
||||
Sertonix <83883937+Sertonix@users.noreply.github.com>
|
||||
Shaarad Dalvi <60266155+shaaraddalvi@users.noreply.github.com> <shdalv@microsoft.com>
|
||||
Simon Frei (imsodin) <freisim93@gmail.com>
|
||||
Simon Mwepu <simonmwepu@gmail.com>
|
||||
@@ -294,6 +304,7 @@ Stefan Kuntz (Stefan-Code) <stefan.github@gmail.com> <Stefan.github@gmail.com>
|
||||
Stefan Tatschner (rumpelsepp) <stefan@sevenbyte.org> <rumpelsepp@sevenbyte.org> <stefan@rumpelsepp.org>
|
||||
Steven Eckhoff <steven.eckhoff.opensource@gmail.com>
|
||||
Suhas Gundimeda (snugghash) <suhas.gundimeda@gmail.com> <snugghash@gmail.com>
|
||||
Sven Bachmann <dev@mcbachmann.de>
|
||||
Syncthing Automation <automation@syncthing.net>
|
||||
Syncthing Release Automation <release@syncthing.net>
|
||||
Taylor Khan (nelsonkhan) <nelsonkhan@gmail.com>
|
||||
@@ -310,6 +321,7 @@ Tully Robinson (tojrobinson) <tully@tojr.org>
|
||||
Tyler Brazier (tylerbrazier) <tyler@tylerbrazier.com>
|
||||
Tyler Kropp <kropptyler@gmail.com>
|
||||
Unrud (Unrud) <unrud@openaliasbox.org> <Unrud@users.noreply.github.com>
|
||||
vapatel2 <149737089+vapatel2@users.noreply.github.com>
|
||||
Veeti Paananen (veeti) <veeti.paananen@rojekti.fi>
|
||||
Victor Buinsky (buinsky) <vix_booja@tut.by>
|
||||
Vik <63919734+ViktorOn@users.noreply.github.com>
|
||||
|
||||
@@ -29,6 +29,14 @@ RUN if [ ! -f syncthing-linux-$TARGETARCH ] ; then \
|
||||
FROM alpine
|
||||
ARG TARGETARCH
|
||||
|
||||
LABEL org.opencontainers.image.authors="The Syncthing Project" \
|
||||
org.opencontainers.image.url="https://syncthing.net" \
|
||||
org.opencontainers.image.documentation="https://docs.syncthing.net" \
|
||||
org.opencontainers.image.source="https://github.com/syncthing/syncthing" \
|
||||
org.opencontainers.image.vendor="The Syncthing Project" \
|
||||
org.opencontainers.image.licenses="MPL-2.0" \
|
||||
org.opencontainers.image.title="Syncthing"
|
||||
|
||||
EXPOSE 8384 22000/tcp 22000/udp 21027/udp
|
||||
|
||||
VOLUME ["/var/syncthing"]
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
ARG GOVERSION=latest
|
||||
FROM golang:$GOVERSION
|
||||
|
||||
LABEL org.opencontainers.image.authors="The Syncthing Project" \
|
||||
org.opencontainers.image.url="https://syncthing.net" \
|
||||
org.opencontainers.image.documentation="https://docs.syncthing.net" \
|
||||
org.opencontainers.image.source="https://github.com/syncthing/syncthing" \
|
||||
org.opencontainers.image.vendor="The Syncthing Project" \
|
||||
org.opencontainers.image.licenses="MPL-2.0" \
|
||||
org.opencontainers.image.title="Syncthing Builder"
|
||||
|
||||
# FPM to build Debian packages
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
locales rubygems ruby-dev build-essential git \
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
FROM alpine
|
||||
ARG TARGETARCH
|
||||
|
||||
LABEL org.opencontainers.image.authors="The Syncthing Project" \
|
||||
org.opencontainers.image.url="https://syncthing.net" \
|
||||
org.opencontainers.image.documentation="https://docs.syncthing.net" \
|
||||
org.opencontainers.image.source="https://github.com/syncthing/syncthing" \
|
||||
org.opencontainers.image.vendor="The Syncthing Project" \
|
||||
org.opencontainers.image.licenses="MPL-2.0" \
|
||||
org.opencontainers.image.title="Syncthing Crash Receiver"
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
COPY stcrashreceiver-linux-${TARGETARCH} /bin/stcrashreceiver
|
||||
|
||||
@@ -16,6 +16,14 @@ RUN if [ ! -f stdiscosrv-linux-$TARGETARCH ] ; then \
|
||||
FROM alpine
|
||||
ARG TARGETARCH
|
||||
|
||||
LABEL org.opencontainers.image.authors="The Syncthing Project" \
|
||||
org.opencontainers.image.url="https://syncthing.net" \
|
||||
org.opencontainers.image.documentation="https://docs.syncthing.net" \
|
||||
org.opencontainers.image.source="https://github.com/syncthing/syncthing" \
|
||||
org.opencontainers.image.vendor="The Syncthing Project" \
|
||||
org.opencontainers.image.licenses="MPL-2.0" \
|
||||
org.opencontainers.image.title="Syncthing Discovery Server"
|
||||
|
||||
EXPOSE 19200 8443
|
||||
|
||||
VOLUME ["/var/stdiscosrv"]
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
FROM alpine
|
||||
ARG TARGETARCH
|
||||
|
||||
LABEL org.opencontainers.image.authors="The Syncthing Project" \
|
||||
org.opencontainers.image.url="https://syncthing.net" \
|
||||
org.opencontainers.image.documentation="https://docs.syncthing.net" \
|
||||
org.opencontainers.image.source="https://github.com/syncthing/syncthing" \
|
||||
org.opencontainers.image.vendor="The Syncthing Project" \
|
||||
org.opencontainers.image.licenses="MPL-2.0" \
|
||||
org.opencontainers.image.title="Syncthing Relay Pool Server"
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
RUN apk add --no-cache ca-certificates su-exec curl
|
||||
|
||||
@@ -16,6 +16,14 @@ RUN if [ ! -f strelaysrv-linux-$TARGETARCH ] ; then \
|
||||
FROM alpine
|
||||
ARG TARGETARCH
|
||||
|
||||
LABEL org.opencontainers.image.authors="The Syncthing Project" \
|
||||
org.opencontainers.image.url="https://syncthing.net" \
|
||||
org.opencontainers.image.documentation="https://docs.syncthing.net" \
|
||||
org.opencontainers.image.source="https://github.com/syncthing/syncthing" \
|
||||
org.opencontainers.image.vendor="The Syncthing Project" \
|
||||
org.opencontainers.image.licenses="MPL-2.0" \
|
||||
org.opencontainers.image.title="Syncthing Relay Server"
|
||||
|
||||
EXPOSE 22067 22070
|
||||
|
||||
VOLUME ["/var/strelaysrv"]
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
FROM alpine
|
||||
ARG TARGETARCH
|
||||
|
||||
LABEL org.opencontainers.image.authors="The Syncthing Project" \
|
||||
org.opencontainers.image.url="https://syncthing.net" \
|
||||
org.opencontainers.image.documentation="https://docs.syncthing.net" \
|
||||
org.opencontainers.image.source="https://github.com/syncthing/syncthing" \
|
||||
org.opencontainers.image.vendor="The Syncthing Project" \
|
||||
org.opencontainers.image.licenses="MPL-2.0" \
|
||||
org.opencontainers.image.title="Syncthing Upgrades"
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
COPY stupgrades-linux-${TARGETARCH} /bin/stupgrades
|
||||
|
||||
16
Dockerfile.ursrv
Normal file
16
Dockerfile.ursrv
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM alpine
|
||||
ARG TARGETARCH
|
||||
|
||||
LABEL org.opencontainers.image.authors="The Syncthing Project" \
|
||||
org.opencontainers.image.url="https://syncthing.net" \
|
||||
org.opencontainers.image.documentation="https://docs.syncthing.net" \
|
||||
org.opencontainers.image.source="https://github.com/syncthing/syncthing" \
|
||||
org.opencontainers.image.vendor="The Syncthing Project" \
|
||||
org.opencontainers.image.licenses="MPL-2.0" \
|
||||
org.opencontainers.image.title="Syncthing Usage Reporting Server"
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
COPY ursrv-linux-${TARGETARCH} /bin/ursrv
|
||||
|
||||
ENTRYPOINT [ "/bin/ursrv" ]
|
||||
@@ -89,7 +89,7 @@ build process.
|
||||
## Signed Releases
|
||||
|
||||
As of v0.10.15 and onwards, release binaries are GPG signed with the key
|
||||
D26E6ED000654A3E, available from https://syncthing.net/security.html and
|
||||
D26E6ED000654A3E, available from https://syncthing.net/security/ and
|
||||
most key servers.
|
||||
|
||||
There is also a built-in automatic upgrade mechanism (disabled in some
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -40,7 +41,7 @@ type currentFile struct {
|
||||
}
|
||||
|
||||
func (d *diskStore) Serve(ctx context.Context) {
|
||||
if err := os.MkdirAll(d.dir, 0750); err != nil {
|
||||
if err := os.MkdirAll(d.dir, 0o700); err != nil {
|
||||
log.Println("Creating directory:", err)
|
||||
return
|
||||
}
|
||||
@@ -60,7 +61,7 @@ func (d *diskStore) Serve(ctx context.Context) {
|
||||
case entry := <-d.inbox:
|
||||
path := d.fullPath(entry.path)
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
|
||||
log.Println("Creating directory:", err)
|
||||
continue
|
||||
}
|
||||
@@ -75,7 +76,7 @@ func (d *diskStore) Serve(ctx context.Context) {
|
||||
log.Println("Failed to compress crash report:", err)
|
||||
continue
|
||||
}
|
||||
if err := os.WriteFile(path, buf.Bytes(), 0644); err != nil {
|
||||
if err := os.WriteFile(path, buf.Bytes(), 0o600); err != nil {
|
||||
log.Printf("Failed to write %s: %v", entry.path, err)
|
||||
_ = os.Remove(path)
|
||||
continue
|
||||
@@ -147,6 +148,11 @@ func (d *diskStore) clean() {
|
||||
if len(d.currentFiles) > 0 {
|
||||
oldest = time.Since(time.Unix(d.currentFiles[0].mtime, 0)).Truncate(time.Minute)
|
||||
}
|
||||
|
||||
metricDiskstoreFilesTotal.Set(float64(len(d.currentFiles)))
|
||||
metricDiskstoreBytesTotal.Set(float64(d.currentSize))
|
||||
metricDiskstoreOldestAgeSeconds.Set(math.Round(oldest.Seconds()))
|
||||
|
||||
log.Printf("Clean complete: %d files, %d MB, oldest is %v ago", len(d.currentFiles), d.currentSize>>20, oldest)
|
||||
}
|
||||
|
||||
@@ -178,6 +184,11 @@ func (d *diskStore) inventory() error {
|
||||
if len(d.currentFiles) > 0 {
|
||||
oldest = time.Since(time.Unix(d.currentFiles[0].mtime, 0)).Truncate(time.Minute)
|
||||
}
|
||||
|
||||
metricDiskstoreFilesTotal.Set(float64(len(d.currentFiles)))
|
||||
metricDiskstoreBytesTotal.Set(float64(d.currentSize))
|
||||
metricDiskstoreOldestAgeSeconds.Set(math.Round(oldest.Seconds()))
|
||||
|
||||
log.Printf("Inventory complete: %d files, %d MB, oldest is %v ago", len(d.currentFiles), d.currentSize>>20, oldest)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/syncthing/syncthing/lib/sha256"
|
||||
"github.com/syncthing/syncthing/lib/ur"
|
||||
|
||||
@@ -33,14 +33,13 @@ import (
|
||||
const maxRequestSize = 1 << 20 // 1 MiB
|
||||
|
||||
type cli struct {
|
||||
Dir string `help:"Parent directory to store crash and failure reports in" env:"REPORTS_DIR" default:"."`
|
||||
DSN string `help:"Sentry DSN" env:"SENTRY_DSN"`
|
||||
Listen string `help:"HTTP listen address" default:":8080" env:"LISTEN_ADDRESS"`
|
||||
MaxDiskFiles int `help:"Maximum number of reports on disk" default:"100000" env:"MAX_DISK_FILES"`
|
||||
MaxDiskSizeMB int64 `help:"Maximum disk space to use for reports" default:"1024" env:"MAX_DISK_SIZE_MB"`
|
||||
CleanInterval time.Duration `help:"Interval between cleaning up old reports" default:"12h" env:"CLEAN_INTERVAL"`
|
||||
SentryQueue int `help:"Maximum number of reports to queue for sending to Sentry" default:"64" env:"SENTRY_QUEUE"`
|
||||
DiskQueue int `help:"Maximum number of reports to queue for writing to disk" default:"64" env:"DISK_QUEUE"`
|
||||
Dir string `help:"Parent directory to store crash and failure reports in" env:"REPORTS_DIR" default:"."`
|
||||
DSN string `help:"Sentry DSN" env:"SENTRY_DSN"`
|
||||
Listen string `help:"HTTP listen address" default:":8080" env:"LISTEN_ADDRESS"`
|
||||
MaxDiskFiles int `help:"Maximum number of reports on disk" default:"100000" env:"MAX_DISK_FILES"`
|
||||
MaxDiskSizeMB int64 `help:"Maximum disk space to use for reports" default:"1024" env:"MAX_DISK_SIZE_MB"`
|
||||
SentryQueue int `help:"Maximum number of reports to queue for sending to Sentry" default:"64" env:"SENTRY_QUEUE"`
|
||||
DiskQueue int `help:"Maximum number of reports to queue for writing to disk" default:"64" env:"DISK_QUEUE"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -72,6 +71,7 @@ func main() {
|
||||
mux.HandleFunc("/ping", func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write([]byte("OK"))
|
||||
})
|
||||
mux.Handle("/metrics", promhttp.Handler())
|
||||
|
||||
if params.DSN != "" {
|
||||
mux.HandleFunc("/newcrash/failure", handleFailureFn(params.DSN, filepath.Join(params.Dir, "failure_reports")))
|
||||
@@ -85,6 +85,11 @@ func main() {
|
||||
|
||||
func handleFailureFn(dsn, failureDir string) func(w http.ResponseWriter, req *http.Request) {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
result := "failure"
|
||||
defer func() {
|
||||
metricFailureReportsTotal.WithLabelValues(result).Inc()
|
||||
}()
|
||||
|
||||
lr := io.LimitReader(req.Body, maxRequestSize)
|
||||
bs, err := io.ReadAll(lr)
|
||||
req.Body.Close()
|
||||
@@ -135,6 +140,7 @@ func handleFailureFn(dsn, failureDir string) func(w http.ResponseWriter, req *ht
|
||||
log.Println("Failed to send failure report:", err)
|
||||
} else {
|
||||
log.Println("Sent failure report:", r.Description)
|
||||
result = "success"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
40
cmd/stcrashreceiver/metrics.go
Normal file
40
cmd/stcrashreceiver/metrics.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2023 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var (
|
||||
metricCrashReportsTotal = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "syncthing",
|
||||
Subsystem: "crashreceiver",
|
||||
Name: "crash_reports_total",
|
||||
}, []string{"result"})
|
||||
metricFailureReportsTotal = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "syncthing",
|
||||
Subsystem: "crashreceiver",
|
||||
Name: "failure_reports_total",
|
||||
}, []string{"result"})
|
||||
metricDiskstoreFilesTotal = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: "syncthing",
|
||||
Subsystem: "crashreceiver",
|
||||
Name: "diskstore_files_total",
|
||||
})
|
||||
metricDiskstoreBytesTotal = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: "syncthing",
|
||||
Subsystem: "crashreceiver",
|
||||
Name: "diskstore_bytes_total",
|
||||
})
|
||||
metricDiskstoreOldestAgeSeconds = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: "syncthing",
|
||||
Subsystem: "crashreceiver",
|
||||
Name: "diskstore_oldest_age_seconds",
|
||||
})
|
||||
)
|
||||
@@ -71,6 +71,11 @@ func (r *crashReceiver) serveHead(reportID string, w http.ResponseWriter, _ *htt
|
||||
|
||||
// servePut accepts and stores the given report.
|
||||
func (r *crashReceiver) servePut(reportID string, w http.ResponseWriter, req *http.Request) {
|
||||
result := "receive_failure"
|
||||
defer func() {
|
||||
metricCrashReportsTotal.WithLabelValues(result).Inc()
|
||||
}()
|
||||
|
||||
// Read at most maxRequestSize of report data.
|
||||
log.Println("Receiving report", reportID)
|
||||
lr := io.LimitReader(req.Body, maxRequestSize)
|
||||
@@ -81,13 +86,17 @@ func (r *crashReceiver) servePut(reportID string, w http.ResponseWriter, req *ht
|
||||
return
|
||||
}
|
||||
|
||||
result = "success"
|
||||
|
||||
// Store the report
|
||||
if !r.store.Put(reportID, bs) {
|
||||
log.Println("Failed to store report (queue full):", reportID)
|
||||
result = "queue_failure"
|
||||
}
|
||||
|
||||
// Send the report to Sentry
|
||||
if !r.sentry.Send(reportID, userIDFor(req), bs) {
|
||||
log.Println("Failed to send report to sentry (queue full):", reportID)
|
||||
result = "sentry_failure"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,12 @@ func (s *apiSrv) handler(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
if s.useHTTP {
|
||||
remoteAddr.IP = net.ParseIP(req.Header.Get("X-Forwarded-For"))
|
||||
// X-Forwarded-For can have multiple client IPs; split using the comma separator
|
||||
forwardIP, _, _ := strings.Cut(req.Header.Get("X-Forwarded-For"), ",")
|
||||
|
||||
// net.ParseIP will return nil if leading/trailing whitespace exists; use strings.TrimSpace()
|
||||
remoteAddr.IP = net.ParseIP(strings.TrimSpace(forwardIP))
|
||||
|
||||
if parsedPort, err := strconv.ParseInt(req.Header.Get("X-Client-Port"), 10, 0); err == nil {
|
||||
remoteAddr.Port = int(parsedPort)
|
||||
}
|
||||
@@ -207,7 +212,9 @@ func (s *apiSrv) handleGET(w http.ResponseWriter, req *http.Request) {
|
||||
s.db.put(key, rec)
|
||||
}
|
||||
|
||||
w.Header().Set("Retry-After", notFoundRetryAfterString(int(misses)))
|
||||
afterS := notFoundRetryAfterSeconds(int(misses))
|
||||
retryAfterHistogram.Observe(float64(afterS))
|
||||
w.Header().Set("Retry-After", strconv.Itoa(afterS))
|
||||
http.Error(w, "Not Found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
@@ -410,13 +417,13 @@ func fixupAddresses(remote *net.TCPAddr, addresses []string) []string {
|
||||
continue
|
||||
}
|
||||
|
||||
if remote != nil {
|
||||
if host == "" || ip.IsUnspecified() {
|
||||
if host == "" || ip.IsUnspecified() {
|
||||
if remote != nil {
|
||||
// Replace the unspecified IP with the request source.
|
||||
|
||||
// ... unless the request source is the loopback address or
|
||||
// multicast/unspecified (can't happen, really).
|
||||
if remote.IP.IsLoopback() || remote.IP.IsMulticast() || remote.IP.IsUnspecified() {
|
||||
if remote.IP == nil || remote.IP.IsLoopback() || remote.IP.IsMulticast() || remote.IP.IsUnspecified() {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -432,11 +439,22 @@ func fixupAddresses(remote *net.TCPAddr, addresses []string) []string {
|
||||
}
|
||||
|
||||
host = remote.IP.String()
|
||||
|
||||
} else {
|
||||
// remote is nil, unable to determine host IP
|
||||
continue
|
||||
}
|
||||
|
||||
// If zero port was specified, use remote port.
|
||||
if port == "0" && remote.Port > 0 {
|
||||
}
|
||||
|
||||
// If zero port was specified, use remote port.
|
||||
if port == "0" {
|
||||
if remote != nil && remote.Port > 0 {
|
||||
// use remote port
|
||||
port = strconv.Itoa(remote.Port)
|
||||
} else {
|
||||
// unable to determine remote port
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,13 +494,13 @@ func errorRetryAfterString() string {
|
||||
return strconv.Itoa(errorRetryAfterSeconds + rand.Intn(errorRetryFuzzSeconds))
|
||||
}
|
||||
|
||||
func notFoundRetryAfterString(misses int) string {
|
||||
func notFoundRetryAfterSeconds(misses int) int {
|
||||
retryAfterS := notFoundRetryMinSeconds + notFoundRetryIncSeconds*misses
|
||||
if retryAfterS > notFoundRetryMaxSeconds {
|
||||
retryAfterS = notFoundRetryMaxSeconds
|
||||
}
|
||||
retryAfterS += rand.Intn(notFoundRetryFuzzSeconds)
|
||||
return strconv.Itoa(retryAfterS)
|
||||
return retryAfterS
|
||||
}
|
||||
|
||||
func reannounceAfterString() string {
|
||||
|
||||
@@ -69,6 +69,14 @@ func TestFixupAddresses(t *testing.T) {
|
||||
remote: addr("123.123.123.123", 9000),
|
||||
in: []string{"tcp://44.44.44.44:0"},
|
||||
out: []string{"tcp://44.44.44.44:9000"},
|
||||
}, { // remote ip nil
|
||||
remote: addr("", 9000),
|
||||
in: []string{"tcp://:22000", "tcp://44.44.44.44:9000"},
|
||||
out: []string{"tcp://44.44.44.44:9000"},
|
||||
}, { // remote port 0
|
||||
remote: addr("123.123.123.123", 0),
|
||||
in: []string{"tcp://:22000", "tcp://44.44.44.44"},
|
||||
out: []string{"tcp://123.123.123.123:22000"},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net"
|
||||
"net/url"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
@@ -218,7 +220,7 @@ func (s *levelDBStore) statisticsServe(trigger <-chan struct{}, done chan<- stru
|
||||
cutoff24h := t0.Add(-24 * time.Hour).UnixNano()
|
||||
cutoff1w := t0.Add(-7 * 24 * time.Hour).UnixNano()
|
||||
cutoff2Mon := t0.Add(-60 * 24 * time.Hour).UnixNano()
|
||||
current, last24h, last1w, inactive, errors := 0, 0, 0, 0, 0
|
||||
current, currentIPv4, currentIPv6, last24h, last1w, inactive, errors := 0, 0, 0, 0, 0, 0, 0
|
||||
|
||||
iter := s.db.NewIterator(&util.Range{}, nil)
|
||||
for iter.Next() {
|
||||
@@ -233,9 +235,35 @@ func (s *levelDBStore) statisticsServe(trigger <-chan struct{}, done chan<- stru
|
||||
// If there are addresses that have not expired it's a current
|
||||
// record, otherwise account it based on when it was last seen
|
||||
// (last 24 hours or last week) or finally as inactice.
|
||||
addrs := expire(rec.Addresses, nowNanos)
|
||||
switch {
|
||||
case len(expire(rec.Addresses, nowNanos)) > 0:
|
||||
case len(addrs) > 0:
|
||||
current++
|
||||
seenIPv4, seenIPv6 := false, false
|
||||
for _, addr := range addrs {
|
||||
uri, err := url.Parse(addr.Address)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
host, _, err := net.SplitHostPort(uri.Host)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil && ip.To4() != nil {
|
||||
seenIPv4 = true
|
||||
} else if ip != nil {
|
||||
seenIPv6 = true
|
||||
}
|
||||
if seenIPv4 && seenIPv6 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if seenIPv4 {
|
||||
currentIPv4++
|
||||
}
|
||||
if seenIPv6 {
|
||||
currentIPv6++
|
||||
}
|
||||
case rec.Seen > cutoff24h:
|
||||
last24h++
|
||||
case rec.Seen > cutoff1w:
|
||||
@@ -259,6 +287,8 @@ func (s *levelDBStore) statisticsServe(trigger <-chan struct{}, done chan<- stru
|
||||
iter.Release()
|
||||
|
||||
databaseKeys.WithLabelValues("current").Set(float64(current))
|
||||
databaseKeys.WithLabelValues("currentIPv4").Set(float64(currentIPv4))
|
||||
databaseKeys.WithLabelValues("currentIPv6").Set(float64(currentIPv6))
|
||||
databaseKeys.WithLabelValues("last24h").Set(float64(last24h))
|
||||
databaseKeys.WithLabelValues("last1w").Set(float64(last1w))
|
||||
databaseKeys.WithLabelValues("inactive").Set(float64(inactive))
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -102,6 +103,8 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
buildInfo.WithLabelValues(build.Version, runtime.Version(), build.User, build.Date.UTC().Format("2006-01-02T15:04:05Z")).Set(1)
|
||||
|
||||
if largeDB {
|
||||
levelDBOptions.BlockCacheCapacity = 64 << 20
|
||||
levelDBOptions.BlockSize = 64 << 10
|
||||
|
||||
@@ -14,6 +14,14 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
buildInfo = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "syncthing",
|
||||
Subsystem: "discovery",
|
||||
Name: "build_info",
|
||||
Help: "A metric with a constant '1' value labeled by version, goversion, builduser and builddate from which stdiscosrv was built.",
|
||||
}, []string{"version", "goversion", "builduser", "builddate"})
|
||||
|
||||
apiRequestsTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: "syncthing",
|
||||
@@ -90,6 +98,14 @@ var (
|
||||
Help: "Latency of database operations.",
|
||||
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
|
||||
}, []string{"operation"})
|
||||
|
||||
retryAfterHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
|
||||
Namespace: "syncthing",
|
||||
Subsystem: "discovery",
|
||||
Name: "retry_after_seconds",
|
||||
Help: "Retry-After header value in seconds.",
|
||||
Buckets: prometheus.ExponentialBuckets(60, 2, 7), // 60, 120, 240, 480, 960, 1920, 3840
|
||||
})
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -104,11 +120,13 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
prometheus.MustRegister(apiRequestsTotal, apiRequestsSeconds,
|
||||
prometheus.MustRegister(buildInfo,
|
||||
apiRequestsTotal, apiRequestsSeconds,
|
||||
lookupRequestsTotal, announceRequestsTotal,
|
||||
replicationSendsTotal, replicationRecvsTotal,
|
||||
databaseKeys, databaseStatisticsSeconds,
|
||||
databaseOperations, databaseOperationSeconds)
|
||||
databaseOperations, databaseOperationSeconds,
|
||||
retryAfterHistogram)
|
||||
|
||||
processCollectorOpts := collectors.ProcessCollectorOpts{
|
||||
Namespace: "syncthing_discovery",
|
||||
@@ -120,5 +138,4 @@ func init() {
|
||||
prometheus.MustRegister(
|
||||
collectors.NewProcessCollector(processCollectorOpts),
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@@ -194,7 +194,15 @@ func main() {
|
||||
cfg.Options.NATTimeoutS = natTimeout
|
||||
})
|
||||
natSvc := nat.NewService(id, wrapper)
|
||||
mapping := mapping{natSvc.NewMapping(nat.TCP, addr.IP, addr.Port)}
|
||||
var ipVersion nat.IPVersion
|
||||
if strings.HasSuffix(proto, "4") {
|
||||
ipVersion = nat.IPv4Only
|
||||
} else if strings.HasSuffix(proto, "6") {
|
||||
ipVersion = nat.IPv6Only
|
||||
} else {
|
||||
ipVersion = nat.IPvAny
|
||||
}
|
||||
mapping := mapping{natSvc.NewMapping(nat.TCP, ipVersion, addr.IP, addr.Port)}
|
||||
|
||||
if natEnabled {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"reflect"
|
||||
|
||||
"github.com/AudriusButkevicius/recli"
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@@ -23,9 +24,20 @@ type configHandler struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func getConfigCommand(f *apiClientFactory) (cli.Command, error) {
|
||||
type configCommand struct {
|
||||
Args []string `arg:"" default:"-h"`
|
||||
}
|
||||
|
||||
func (c *configCommand) Run(ctx Context, _ *kong.Context) error {
|
||||
app := cli.NewApp()
|
||||
app.Name = "syncthing"
|
||||
app.Author = "The Syncthing Authors"
|
||||
app.Metadata = map[string]interface{}{
|
||||
"clientFactory": ctx.clientFactory,
|
||||
}
|
||||
|
||||
h := new(configHandler)
|
||||
h.client, h.err = f.getClient()
|
||||
h.client, h.err = ctx.clientFactory.getClient()
|
||||
if h.err == nil {
|
||||
h.cfg, h.err = getConfig(h.client)
|
||||
}
|
||||
@@ -38,17 +50,15 @@ func getConfigCommand(f *apiClientFactory) (cli.Command, error) {
|
||||
|
||||
commands, err := recli.New(recliCfg).Construct(&h.cfg)
|
||||
if err != nil {
|
||||
return cli.Command{}, fmt.Errorf("config reflect: %w", err)
|
||||
return fmt.Errorf("config reflect: %w", err)
|
||||
}
|
||||
|
||||
return cli.Command{
|
||||
Name: "config",
|
||||
HideHelp: true,
|
||||
Usage: "Configuration modification command group",
|
||||
Subcommands: commands,
|
||||
Before: h.configBefore,
|
||||
After: h.configAfter,
|
||||
}, nil
|
||||
app.Commands = commands
|
||||
app.HideHelp = true
|
||||
app.Before = h.configBefore
|
||||
app.After = h.configAfter
|
||||
|
||||
return app.Run(append([]string{app.Name}, c.Args...))
|
||||
}
|
||||
|
||||
func (h *configHandler) configBefore(c *cli.Context) error {
|
||||
|
||||
@@ -9,47 +9,37 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var debugCommand = cli.Command{
|
||||
Name: "debug",
|
||||
HideHelp: true,
|
||||
Usage: "Debug command group",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "file",
|
||||
Usage: "Show information about a file (or directory/symlink)",
|
||||
ArgsUsage: "FOLDER-ID PATH",
|
||||
Action: expects(2, debugFile()),
|
||||
},
|
||||
indexCommand,
|
||||
{
|
||||
Name: "profile",
|
||||
Usage: "Save a profile to help figuring out what Syncthing does.",
|
||||
ArgsUsage: "cpu | heap",
|
||||
Action: expects(1, profile()),
|
||||
},
|
||||
},
|
||||
type fileCommand struct {
|
||||
FolderID string `arg:""`
|
||||
Path string `arg:""`
|
||||
}
|
||||
|
||||
func debugFile() cli.ActionFunc {
|
||||
return func(c *cli.Context) error {
|
||||
query := make(url.Values)
|
||||
query.Set("folder", c.Args()[0])
|
||||
query.Set("file", normalizePath(c.Args()[1]))
|
||||
return indexDumpOutput("debug/file?" + query.Encode())(c)
|
||||
func (f *fileCommand) Run(ctx Context) error {
|
||||
indexDumpOutput := indexDumpOutputWrapper(ctx.clientFactory)
|
||||
|
||||
query := make(url.Values)
|
||||
query.Set("folder", f.FolderID)
|
||||
query.Set("file", normalizePath(f.Path))
|
||||
return indexDumpOutput("debug/file?" + query.Encode())
|
||||
}
|
||||
|
||||
type profileCommand struct {
|
||||
Type string `arg:"" help:"cpu | heap"`
|
||||
}
|
||||
|
||||
func (p *profileCommand) Run(ctx Context) error {
|
||||
switch t := p.Type; t {
|
||||
case "cpu", "heap":
|
||||
return saveToFile(fmt.Sprintf("debug/%vprof", p.Type), ctx.clientFactory)
|
||||
default:
|
||||
return fmt.Errorf("expected cpu or heap as argument, got %v", t)
|
||||
}
|
||||
}
|
||||
|
||||
func profile() cli.ActionFunc {
|
||||
return func(c *cli.Context) error {
|
||||
switch t := c.Args()[0]; t {
|
||||
case "cpu", "heap":
|
||||
return saveToFile(fmt.Sprintf("debug/%vprof", c.Args()[0]))(c)
|
||||
default:
|
||||
return fmt.Errorf("expected cpu or heap as argument, got %v", t)
|
||||
}
|
||||
}
|
||||
type debugCommand struct {
|
||||
File fileCommand `cmd:"" help:"Show information about a file (or directory/symlink)"`
|
||||
Profile profileCommand `cmd:"" help:"Save a profile to help figuring out what Syncthing does"`
|
||||
Index indexCommand `cmd:"" help:"Show information about the index (database)"`
|
||||
}
|
||||
|
||||
@@ -11,36 +11,25 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"github.com/alecthomas/kong"
|
||||
)
|
||||
|
||||
var errorsCommand = cli.Command{
|
||||
Name: "errors",
|
||||
HideHelp: true,
|
||||
Usage: "Error command group",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "show",
|
||||
Usage: "Show pending errors",
|
||||
Action: expects(0, indexDumpOutput("system/error")),
|
||||
},
|
||||
{
|
||||
Name: "push",
|
||||
Usage: "Push an error to active clients",
|
||||
ArgsUsage: "ERROR-MESSAGE",
|
||||
Action: expects(1, errorsPush),
|
||||
},
|
||||
{
|
||||
Name: "clear",
|
||||
Usage: "Clear pending errors",
|
||||
Action: expects(0, emptyPost("system/error/clear")),
|
||||
},
|
||||
},
|
||||
type errorsCommand struct {
|
||||
Show struct{} `cmd:"" help:"Show pending errors"`
|
||||
Push errorsPushCommand `cmd:"" help:"Push an error to active clients"`
|
||||
Clear struct{} `cmd:"" help:"Clear pending errors"`
|
||||
}
|
||||
|
||||
func errorsPush(c *cli.Context) error {
|
||||
client := c.App.Metadata["client"].(APIClient)
|
||||
errStr := strings.Join(c.Args(), " ")
|
||||
type errorsPushCommand struct {
|
||||
ErrorMessage string `arg:""`
|
||||
}
|
||||
|
||||
func (e *errorsPushCommand) Run(ctx Context) error {
|
||||
client, err := ctx.clientFactory.getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errStr := e.ErrorMessage
|
||||
response, err := client.Post("system/error", strings.TrimSpace(errStr))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -59,3 +48,13 @@ func errorsPush(c *cli.Context) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*errorsCommand) Run(ctx Context, kongCtx *kong.Context) error {
|
||||
switch kongCtx.Selected().Name {
|
||||
case "show":
|
||||
return indexDumpOutput("system/error", ctx.clientFactory)
|
||||
case "clear":
|
||||
return emptyPost("system/error/clear", ctx.clientFactory)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,32 +7,26 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli"
|
||||
"github.com/alecthomas/kong"
|
||||
)
|
||||
|
||||
var indexCommand = cli.Command{
|
||||
Name: "index",
|
||||
Usage: "Show information about the index (database)",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "dump",
|
||||
Usage: "Print the entire db",
|
||||
Action: expects(0, indexDump),
|
||||
},
|
||||
{
|
||||
Name: "dump-size",
|
||||
Usage: "Print the db size of different categories of information",
|
||||
Action: expects(0, indexDumpSize),
|
||||
},
|
||||
{
|
||||
Name: "check",
|
||||
Usage: "Check the database for inconsistencies",
|
||||
Action: expects(0, indexCheck),
|
||||
},
|
||||
{
|
||||
Name: "account",
|
||||
Usage: "Print key and value size statistics per key type",
|
||||
Action: expects(0, indexAccount),
|
||||
},
|
||||
},
|
||||
type indexCommand struct {
|
||||
Dump struct{} `cmd:"" help:"Print the entire db"`
|
||||
DumpSize struct{} `cmd:"" help:"Print the db size of different categories of information"`
|
||||
Check struct{} `cmd:"" help:"Check the database for inconsistencies"`
|
||||
Account struct{} `cmd:"" help:"Print key and value size statistics per key type"`
|
||||
}
|
||||
|
||||
func (*indexCommand) Run(kongCtx *kong.Context) error {
|
||||
switch kongCtx.Selected().Name {
|
||||
case "dump":
|
||||
return indexDump()
|
||||
case "dump-size":
|
||||
return indexDumpSize()
|
||||
case "check":
|
||||
return indexCheck()
|
||||
case "account":
|
||||
return indexAccount()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,12 +10,10 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// indexAccount prints key and data size statistics per class
|
||||
func indexAccount(*cli.Context) error {
|
||||
func indexAccount() error {
|
||||
ldb, err := getDB()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -11,13 +11,11 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/db"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
)
|
||||
|
||||
func indexDump(*cli.Context) error {
|
||||
func indexDump() error {
|
||||
ldb, err := getDB()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -11,12 +11,10 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/db"
|
||||
)
|
||||
|
||||
func indexDumpSize(*cli.Context) error {
|
||||
func indexDumpSize() error {
|
||||
type sizedElement struct {
|
||||
key string
|
||||
size int
|
||||
|
||||
@@ -13,8 +13,6 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/db"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
)
|
||||
@@ -35,7 +33,7 @@ type sequenceKey struct {
|
||||
sequence uint64
|
||||
}
|
||||
|
||||
func indexCheck(*cli.Context) (err error) {
|
||||
func indexCheck() (err error) {
|
||||
ldb, err := getDB()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -8,165 +8,88 @@ package cli
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/flynn-archive/go-shlex"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/syncthing/syncthing/cmd/syncthing/cmdutil"
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
)
|
||||
|
||||
type preCli struct {
|
||||
type CLI struct {
|
||||
cmdutil.CommonOptions
|
||||
DataDir string `name:"data" placeholder:"PATH" env:"STDATADIR" help:"Set data directory (database and logs)"`
|
||||
GUIAddress string `name:"gui-address"`
|
||||
GUIAPIKey string `name:"gui-apikey"`
|
||||
HomeDir string `name:"home"`
|
||||
ConfDir string `name:"config"`
|
||||
DataDir string `name:"data"`
|
||||
|
||||
Show showCommand `cmd:"" help:"Show command group"`
|
||||
Debug debugCommand `cmd:"" help:"Debug command group"`
|
||||
Operations operationCommand `cmd:"" help:"Operation command group"`
|
||||
Errors errorsCommand `cmd:"" help:"Error command group"`
|
||||
Config configCommand `cmd:"" help:"Configuration modification command group" passthrough:""`
|
||||
Stdin stdinCommand `cmd:"" name:"-" help:"Read commands from stdin"`
|
||||
}
|
||||
|
||||
func Run() error {
|
||||
// This is somewhat a hack around a chicken and egg problem. We need to set
|
||||
// the home directory and potentially other flags to know where the
|
||||
// syncthing instance is running in order to get it's config ... which we
|
||||
// then use to construct the actual CLI ... at which point it's too late to
|
||||
// add flags there...
|
||||
c := preCli{}
|
||||
parseFlags(&c)
|
||||
return runInternal(c, os.Args)
|
||||
type Context struct {
|
||||
clientFactory *apiClientFactory
|
||||
}
|
||||
|
||||
func RunWithArgs(cliArgs []string) error {
|
||||
c := preCli{}
|
||||
parseFlagsWithArgs(cliArgs, &c)
|
||||
return runInternal(c, cliArgs)
|
||||
}
|
||||
|
||||
func runInternal(c preCli, cliArgs []string) error {
|
||||
// Not set as default above because the strings can be really long.
|
||||
err := cmdutil.SetConfigDataLocationsFromFlags(c.HomeDir, c.ConfDir, c.DataDir)
|
||||
func (cli CLI) AfterApply(kongCtx *kong.Context) error {
|
||||
err := cmdutil.SetConfigDataLocationsFromFlags(cli.HomeDir, cli.ConfDir, cli.DataDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Command line options: %w", err)
|
||||
return fmt.Errorf("command line options: %w", err)
|
||||
}
|
||||
|
||||
clientFactory := &apiClientFactory{
|
||||
cfg: config.GUIConfiguration{
|
||||
RawAddress: c.GUIAddress,
|
||||
APIKey: c.GUIAPIKey,
|
||||
RawAddress: cli.GUIAddress,
|
||||
APIKey: cli.GUIAPIKey,
|
||||
},
|
||||
}
|
||||
|
||||
configCommand, err := getConfigCommand(clientFactory)
|
||||
if err != nil {
|
||||
return err
|
||||
context := Context{
|
||||
clientFactory: clientFactory,
|
||||
}
|
||||
|
||||
// Implement the same flags at the upper CLI, but do nothing with them.
|
||||
// This is so that the usage text is the same
|
||||
fakeFlags := []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "gui-address",
|
||||
Usage: "Override GUI address to `URL` (e.g. \"192.0.2.42:8443\")",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "gui-apikey",
|
||||
Usage: "Override GUI API key to `API-KEY`",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "home",
|
||||
Usage: "Set configuration and data directory to `PATH`",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "config",
|
||||
Usage: "Set configuration directory (config and keys) to `PATH`",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "data",
|
||||
Usage: "Set data directory (database and logs) to `PATH`",
|
||||
},
|
||||
}
|
||||
|
||||
// Construct the actual CLI
|
||||
app := cli.NewApp()
|
||||
app.Author = "The Syncthing Authors"
|
||||
app.Metadata = map[string]interface{}{
|
||||
"clientFactory": clientFactory,
|
||||
}
|
||||
app.Commands = []cli.Command{{
|
||||
Name: "cli",
|
||||
Usage: "Syncthing command line interface",
|
||||
Flags: fakeFlags,
|
||||
Subcommands: []cli.Command{
|
||||
configCommand,
|
||||
showCommand,
|
||||
operationCommand,
|
||||
errorsCommand,
|
||||
debugCommand,
|
||||
{
|
||||
Name: "-",
|
||||
HideHelp: true,
|
||||
Usage: "Read commands from stdin",
|
||||
Action: func(ctx *cli.Context) error {
|
||||
if ctx.NArg() > 0 {
|
||||
return errors.New("command does not expect any arguments")
|
||||
}
|
||||
|
||||
// Drop the `-` not to recurse into self.
|
||||
args := make([]string, len(cliArgs)-1)
|
||||
copy(args, cliArgs)
|
||||
|
||||
fmt.Println("Reading commands from stdin...", args)
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
input, err := shlex.Split(scanner.Text())
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing input: %w", err)
|
||||
}
|
||||
if len(input) == 0 {
|
||||
continue
|
||||
}
|
||||
err = app.Run(append(args, input...))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return scanner.Err()
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
return app.Run(cliArgs)
|
||||
kongCtx.Bind(context)
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseFlags(c *preCli) error {
|
||||
// kong only needs to parse the global arguments after "cli" and before the
|
||||
// subcommand (if any).
|
||||
if len(os.Args) <= 2 {
|
||||
return nil
|
||||
}
|
||||
return parseFlagsWithArgs(os.Args[2:], c)
|
||||
}
|
||||
type stdinCommand struct{}
|
||||
|
||||
func parseFlagsWithArgs(args []string, c *preCli) error {
|
||||
for i := 0; i < len(args); i++ {
|
||||
if !strings.HasPrefix(args[i], "--") {
|
||||
args = args[:i]
|
||||
break
|
||||
func (*stdinCommand) Run() error {
|
||||
// Drop the `-` not to recurse into self.
|
||||
args := make([]string, len(os.Args)-1)
|
||||
copy(args, os.Args)
|
||||
|
||||
fmt.Println("Reading commands from stdin...", args)
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
input, err := shlex.Split(scanner.Text())
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing input: %w", err)
|
||||
}
|
||||
if !strings.Contains(args[i], "=") {
|
||||
i++
|
||||
if len(input) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var cli CLI
|
||||
p, err := kong.New(&cli)
|
||||
if err != nil {
|
||||
// can't happen, really
|
||||
return fmt.Errorf("creating parser: %w", err)
|
||||
}
|
||||
ctx, err := p.Parse(input)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
continue
|
||||
}
|
||||
if err := ctx.Run(); err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
// We don't want kong to print anything nor os.Exit (e.g. on -h)
|
||||
parser, err := kong.New(c, kong.Writers(io.Discard, io.Discard), kong.Exit(func(int) {}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = parser.Parse(args)
|
||||
return err
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
@@ -12,48 +12,43 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var operationCommand = cli.Command{
|
||||
Name: "operations",
|
||||
HideHelp: true,
|
||||
Usage: "Operation command group",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "restart",
|
||||
Usage: "Restart syncthing",
|
||||
Action: expects(0, emptyPost("system/restart")),
|
||||
},
|
||||
{
|
||||
Name: "shutdown",
|
||||
Usage: "Shutdown syncthing",
|
||||
Action: expects(0, emptyPost("system/shutdown")),
|
||||
},
|
||||
{
|
||||
Name: "upgrade",
|
||||
Usage: "Upgrade syncthing (if a newer version is available)",
|
||||
Action: expects(0, emptyPost("system/upgrade")),
|
||||
},
|
||||
{
|
||||
Name: "folder-override",
|
||||
Usage: "Override changes on folder (remote for sendonly, local for receiveonly). WARNING: Destructive - deletes/changes your data.",
|
||||
ArgsUsage: "FOLDER-ID",
|
||||
Action: expects(1, foldersOverride),
|
||||
},
|
||||
{
|
||||
Name: "default-ignores",
|
||||
Usage: "Set the default ignores (config) from a file",
|
||||
ArgsUsage: "PATH",
|
||||
Action: expects(1, setDefaultIgnores),
|
||||
},
|
||||
},
|
||||
type folderOverrideCommand struct {
|
||||
FolderID string `arg:""`
|
||||
}
|
||||
|
||||
func foldersOverride(c *cli.Context) error {
|
||||
client, err := getClientFactory(c).getClient()
|
||||
type defaultIgnoresCommand struct {
|
||||
Path string `arg:""`
|
||||
}
|
||||
|
||||
type operationCommand struct {
|
||||
Restart struct{} `cmd:"" help:"Restart syncthing"`
|
||||
Shutdown struct{} `cmd:"" help:"Shutdown syncthing"`
|
||||
Upgrade struct{} `cmd:"" help:"Upgrade syncthing (if a newer version is available)"`
|
||||
FolderOverride folderOverrideCommand `cmd:"" help:"Override changes on folder (remote for sendonly, local for receiveonly). WARNING: Destructive - deletes/changes your data"`
|
||||
DefaultIgnores defaultIgnoresCommand `cmd:"" help:"Set the default ignores (config) from a file"`
|
||||
}
|
||||
|
||||
func (*operationCommand) Run(ctx Context, kongCtx *kong.Context) error {
|
||||
f := ctx.clientFactory
|
||||
|
||||
switch kongCtx.Selected().Name {
|
||||
case "restart":
|
||||
return emptyPost("system/restart", f)
|
||||
case "shutdown":
|
||||
return emptyPost("system/shutdown", f)
|
||||
case "upgrade":
|
||||
return emptyPost("system/upgrade", f)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *folderOverrideCommand) Run(ctx Context) error {
|
||||
client, err := ctx.clientFactory.getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -61,7 +56,7 @@ func foldersOverride(c *cli.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rid := c.Args()[0]
|
||||
rid := f.FolderID
|
||||
for _, folder := range cfg.Folders {
|
||||
if folder.ID == rid {
|
||||
response, err := client.Post("db/override", "")
|
||||
@@ -86,12 +81,12 @@ func foldersOverride(c *cli.Context) error {
|
||||
return fmt.Errorf("Folder %q not found", rid)
|
||||
}
|
||||
|
||||
func setDefaultIgnores(c *cli.Context) error {
|
||||
client, err := getClientFactory(c).getClient()
|
||||
func (d *defaultIgnoresCommand) Run(ctx Context) error {
|
||||
client, err := ctx.clientFactory.getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dir, file := filepath.Split(c.Args()[0])
|
||||
dir, file := filepath.Split(d.Path)
|
||||
filesystem := fs.NewFilesystem(fs.FilesystemTypeBasic, dir)
|
||||
|
||||
fd, err := filesystem.Open(file)
|
||||
|
||||
@@ -9,37 +9,30 @@ package cli
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"github.com/alecthomas/kong"
|
||||
)
|
||||
|
||||
var pendingCommand = cli.Command{
|
||||
Name: "pending",
|
||||
HideHelp: true,
|
||||
Usage: "Pending subcommand group",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "devices",
|
||||
Usage: "Show pending devices",
|
||||
Action: expects(0, indexDumpOutput("cluster/pending/devices")),
|
||||
},
|
||||
{
|
||||
Name: "folders",
|
||||
Usage: "Show pending folders",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "device", Usage: "Show pending folders offered by given device"},
|
||||
},
|
||||
Action: expects(0, folders()),
|
||||
},
|
||||
},
|
||||
type pendingCommand struct {
|
||||
Devices struct{} `cmd:"" help:"Show pending devices"`
|
||||
Folders struct {
|
||||
Device string `help:"Show pending folders offered by given device"`
|
||||
} `cmd:"" help:"Show pending folders"`
|
||||
}
|
||||
|
||||
func folders() cli.ActionFunc {
|
||||
return func(c *cli.Context) error {
|
||||
if c.String("device") != "" {
|
||||
func (p *pendingCommand) Run(ctx Context, kongCtx *kong.Context) error {
|
||||
indexDumpOutput := indexDumpOutputWrapper(ctx.clientFactory)
|
||||
|
||||
switch kongCtx.Selected().Name {
|
||||
case "devices":
|
||||
return indexDumpOutput("cluster/pending/devices")
|
||||
case "folders":
|
||||
if p.Folders.Device != "" {
|
||||
query := make(url.Values)
|
||||
query.Set("device", c.String("device"))
|
||||
return indexDumpOutput("cluster/pending/folders?" + query.Encode())(c)
|
||||
query.Set("device", p.Folders.Device)
|
||||
return indexDumpOutput("cluster/pending/folders?" + query.Encode())
|
||||
}
|
||||
return indexDumpOutput("cluster/pending/folders")(c)
|
||||
return indexDumpOutput("cluster/pending/folders")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,44 +7,36 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli"
|
||||
"github.com/alecthomas/kong"
|
||||
)
|
||||
|
||||
var showCommand = cli.Command{
|
||||
Name: "show",
|
||||
HideHelp: true,
|
||||
Usage: "Show command group",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "version",
|
||||
Usage: "Show syncthing client version",
|
||||
Action: expects(0, indexDumpOutput("system/version")),
|
||||
},
|
||||
{
|
||||
Name: "config-status",
|
||||
Usage: "Show configuration status, whether or not a restart is required for changes to take effect",
|
||||
Action: expects(0, indexDumpOutput("config/restart-required")),
|
||||
},
|
||||
{
|
||||
Name: "system",
|
||||
Usage: "Show system status",
|
||||
Action: expects(0, indexDumpOutput("system/status")),
|
||||
},
|
||||
{
|
||||
Name: "connections",
|
||||
Usage: "Report about connections to other devices",
|
||||
Action: expects(0, indexDumpOutput("system/connections")),
|
||||
},
|
||||
{
|
||||
Name: "discovery",
|
||||
Usage: "Show the discovered addresses of remote devices (from cache of the running syncthing instance)",
|
||||
Action: expects(0, indexDumpOutput("system/discovery")),
|
||||
},
|
||||
pendingCommand,
|
||||
{
|
||||
Name: "usage",
|
||||
Usage: "Show usage report",
|
||||
Action: expects(0, indexDumpOutput("svc/report")),
|
||||
},
|
||||
},
|
||||
type showCommand struct {
|
||||
Version struct{} `cmd:"" help:"Show syncthing client version"`
|
||||
ConfigStatus struct{} `cmd:"" help:"Show configuration status, whether or not a restart is required for changes to take effect"`
|
||||
System struct{} `cmd:"" help:"Show system status"`
|
||||
Connections struct{} `cmd:"" help:"Report about connections to other devices"`
|
||||
Discovery struct{} `cmd:"" help:"Show the discovered addresses of remote devices (from cache of the running syncthing instance)"`
|
||||
Usage struct{} `cmd:"" help:"Show usage report"`
|
||||
Pending pendingCommand `cmd:"" help:"Pending subcommand group"`
|
||||
}
|
||||
|
||||
func (*showCommand) Run(ctx Context, kongCtx *kong.Context) error {
|
||||
indexDumpOutput := indexDumpOutputWrapper(ctx.clientFactory)
|
||||
|
||||
switch kongCtx.Selected().Name {
|
||||
case "version":
|
||||
return indexDumpOutput("system/version")
|
||||
case "config-status":
|
||||
return indexDumpOutput("config/restart-required")
|
||||
case "system":
|
||||
return indexDumpOutput("system/status")
|
||||
case "connections":
|
||||
return indexDumpOutput("system/connections")
|
||||
case "discovery":
|
||||
return indexDumpOutput("system/discovery")
|
||||
case "usage":
|
||||
return indexDumpOutput("svc/report")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/db/backend"
|
||||
"github.com/syncthing/syncthing/lib/locations"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func responseToBArray(response *http.Response) ([]byte, error) {
|
||||
@@ -30,68 +29,72 @@ func responseToBArray(response *http.Response) ([]byte, error) {
|
||||
return bytes, response.Body.Close()
|
||||
}
|
||||
|
||||
func emptyPost(url string) cli.ActionFunc {
|
||||
return func(c *cli.Context) error {
|
||||
client, err := getClientFactory(c).getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = client.Post(url, "")
|
||||
func emptyPost(url string, apiClientFactory *apiClientFactory) error {
|
||||
client, err := apiClientFactory.getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = client.Post(url, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func indexDumpOutputWrapper(apiClientFactory *apiClientFactory) func(url string) error {
|
||||
return func(url string) error {
|
||||
return indexDumpOutput(url, apiClientFactory)
|
||||
}
|
||||
}
|
||||
|
||||
func indexDumpOutput(url string) cli.ActionFunc {
|
||||
return func(c *cli.Context) error {
|
||||
client, err := getClientFactory(c).getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response, err := client.Get(url)
|
||||
if errors.Is(err, errNotFound) {
|
||||
return errors.New("not found (folder/file not in database)")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return prettyPrintResponse(response)
|
||||
}
|
||||
}
|
||||
|
||||
func saveToFile(url string) cli.ActionFunc {
|
||||
return func(c *cli.Context) error {
|
||||
client, err := getClientFactory(c).getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response, err := client.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, params, err := mime.ParseMediaType(response.Header.Get("Content-Disposition"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filename := params["filename"]
|
||||
if filename == "" {
|
||||
return errors.New("Missing filename in response")
|
||||
}
|
||||
bs, err := responseToBArray(response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.Write(bs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Wrote results to", filename)
|
||||
func indexDumpOutput(url string, apiClientFactory *apiClientFactory) error {
|
||||
client, err := apiClientFactory.getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response, err := client.Get(url)
|
||||
if errors.Is(err, errNotFound) {
|
||||
return errors.New("not found (folder/file not in database)")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return prettyPrintResponse(response)
|
||||
}
|
||||
|
||||
func saveToFile(url string, apiClientFactory *apiClientFactory) error {
|
||||
client, err := apiClientFactory.getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response, err := client.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, params, err := mime.ParseMediaType(response.Header.Get("Content-Disposition"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filename := params["filename"]
|
||||
if filename == "" {
|
||||
return errors.New("Missing filename in response")
|
||||
}
|
||||
bs, err := responseToBArray(response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Write(bs)
|
||||
if err != nil {
|
||||
_ = f.Close()
|
||||
return err
|
||||
}
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Wrote results to", filename)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getConfig(c APIClient) (config.Configuration, error) {
|
||||
@@ -111,19 +114,6 @@ func getConfig(c APIClient) (config.Configuration, error) {
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func expects(n int, actionFunc cli.ActionFunc) cli.ActionFunc {
|
||||
return func(ctx *cli.Context) error {
|
||||
if ctx.NArg() != n {
|
||||
plural := ""
|
||||
if n != 1 {
|
||||
plural = "s"
|
||||
}
|
||||
return fmt.Errorf("expected %d argument%s, got %d", n, plural, ctx.NArg())
|
||||
}
|
||||
return actionFunc(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func prettyPrintJSON(data interface{}) error {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
@@ -159,7 +149,3 @@ func nulString(bs []byte) string {
|
||||
func normalizePath(path string) string {
|
||||
return filepath.ToSlash(filepath.Clean(path))
|
||||
}
|
||||
|
||||
func getClientFactory(c *cli.Context) *apiClientFactory {
|
||||
return c.App.Metadata["clientFactory"].(*apiClientFactory)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/thejerf/suture/v4"
|
||||
"github.com/willabides/kongplete"
|
||||
|
||||
"github.com/syncthing/syncthing/cmd/syncthing/cli"
|
||||
"github.com/syncthing/syncthing/cmd/syncthing/cmdutil"
|
||||
@@ -88,9 +89,6 @@ above.
|
||||
STTRACE A comma separated string of facilities to trace. The valid
|
||||
facility strings are listed below.
|
||||
|
||||
STDEADLOCKTIMEOUT Used for debugging internal deadlocks; sets debug
|
||||
sensitivity. Use only under direction of a developer.
|
||||
|
||||
STLOCKTHRESHOLD Used for debugging internal deadlocks; sets debug
|
||||
sensitivity. Use only under direction of a developer.
|
||||
|
||||
@@ -136,10 +134,11 @@ var (
|
||||
// commands and options here are top level commands to syncthing.
|
||||
// Cli is just a placeholder for the help text (see main).
|
||||
var entrypoint struct {
|
||||
Serve serveOptions `cmd:"" help:"Run Syncthing"`
|
||||
Generate generate.CLI `cmd:"" help:"Generate key and config, then exit"`
|
||||
Decrypt decrypt.CLI `cmd:"" help:"Decrypt or verify an encrypted folder"`
|
||||
Cli struct{} `cmd:"" help:"Command line interface for Syncthing"`
|
||||
Serve serveOptions `cmd:"" help:"Run Syncthing"`
|
||||
Generate generate.CLI `cmd:"" help:"Generate key and config, then exit"`
|
||||
Decrypt decrypt.CLI `cmd:"" help:"Decrypt or verify an encrypted folder"`
|
||||
Cli cli.CLI `cmd:"" help:"Command line interface for Syncthing"`
|
||||
InstallCompletions kongplete.InstallCompletions `cmd:"" help:"Print commands to install shell completions"`
|
||||
}
|
||||
|
||||
// serveOptions are the options for the `syncthing serve` command.
|
||||
@@ -173,7 +172,6 @@ type serveOptions struct {
|
||||
// Debug options below
|
||||
DebugDBIndirectGCInterval time.Duration `env:"STGCINDIRECTEVERY" help:"Database indirection GC interval"`
|
||||
DebugDBRecheckInterval time.Duration `env:"STRECHECKDBEVERY" help:"Database metadata recalculation interval"`
|
||||
DebugDeadlockTimeout int `placeholder:"SECONDS" env:"STDEADLOCKTIMEOUT" help:"Used for debugging internal deadlocks"`
|
||||
DebugGUIAssetsDir string `placeholder:"PATH" help:"Directory to load GUI assets from" env:"STGUIASSETS"`
|
||||
DebugPerfStats bool `env:"STPERFSTATS" help:"Write running performance statistics to perf-$pid.csv (Unix only)"`
|
||||
DebugProfileBlock bool `env:"STBLOCKPROFILE" help:"Write block profiles to block-$pid-$timestamp.pprof every 20 seconds"`
|
||||
@@ -213,17 +211,6 @@ func defaultVars() kong.Vars {
|
||||
}
|
||||
|
||||
func main() {
|
||||
// The "cli" subcommand uses a different command line parser, and e.g. help
|
||||
// gets mangled when integrating it as a subcommand -> detect it here at the
|
||||
// beginning.
|
||||
if len(os.Args) > 1 && os.Args[1] == "cli" {
|
||||
if err := cli.Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// First some massaging of the raw command line to fit the new model.
|
||||
// Basically this means adding the default command at the front, and
|
||||
// converting -options to --options.
|
||||
@@ -249,11 +236,20 @@ func main() {
|
||||
|
||||
// Create a parser with an overridden help function to print our extra
|
||||
// help info.
|
||||
parser, err := kong.New(&entrypoint, kong.Help(helpHandler), defaultVars())
|
||||
parser, err := kong.New(
|
||||
&entrypoint,
|
||||
kong.ConfigureHelp(kong.HelpOptions{
|
||||
NoExpandSubcommands: true,
|
||||
Compact: true,
|
||||
}),
|
||||
kong.Help(helpHandler),
|
||||
defaultVars(),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
kongplete.Complete(parser)
|
||||
ctx, err := parser.Parse(args)
|
||||
parser.FatalIfErrorf(err)
|
||||
ctx.BindTo(l, (*logger.Logger)(nil)) // main logger available to subcommands
|
||||
@@ -626,7 +622,6 @@ func syncthingMain(options serveOptions) {
|
||||
}
|
||||
|
||||
appOpts := syncthing.Options{
|
||||
DeadlockTimeoutS: options.DebugDeadlockTimeout,
|
||||
NoUpgrade: options.NoUpgrade,
|
||||
ProfilerAddr: options.DebugProfilerListen,
|
||||
ResetDeltaIdxs: options.DebugResetDeltaIdxs,
|
||||
@@ -637,10 +632,6 @@ func syncthingMain(options serveOptions) {
|
||||
if options.Audit {
|
||||
appOpts.AuditWriter = auditWriter(options.AuditFile)
|
||||
}
|
||||
if t := os.Getenv("STDEADLOCKTIMEOUT"); t != "" {
|
||||
secs, _ := strconv.Atoi(t)
|
||||
appOpts.DeadlockTimeoutS = secs
|
||||
}
|
||||
if dur, err := time.ParseDuration(os.Getenv("STRECHECKDBEVERY")); err == nil {
|
||||
appOpts.DBRecheckInterval = dur
|
||||
}
|
||||
@@ -874,6 +865,7 @@ func cleanConfigDirectory() {
|
||||
"backup-of-v0.8": 30 * 24 * time.Hour, // these neither
|
||||
"tmp-index-sorter.*": time.Minute, // these should never exist on startup
|
||||
"support-bundle-*": 30 * 24 * time.Hour, // keep old support bundle zip or folder for a month
|
||||
"csrftokens.txt": 0, // deprecated, remove immediately
|
||||
}
|
||||
|
||||
for pat, dur := range patterns {
|
||||
|
||||
26
cmd/ursrv/serve/metrics.go
Normal file
26
cmd/ursrv/serve/metrics.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (C) 2023 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package serve
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
var metricReportsTotal = promauto.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "syncthing",
|
||||
Subsystem: "ursrv",
|
||||
Name: "reports_total",
|
||||
}, []string{"version"})
|
||||
|
||||
func init() {
|
||||
metricReportsTotal.WithLabelValues("fail")
|
||||
metricReportsTotal.WithLabelValues("duplicate")
|
||||
metricReportsTotal.WithLabelValues("v1")
|
||||
metricReportsTotal.WithLabelValues("v2")
|
||||
metricReportsTotal.WithLabelValues("v3")
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"database/sql"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
@@ -26,6 +27,7 @@ import (
|
||||
|
||||
_ "github.com/lib/pq" // PostgreSQL driver
|
||||
"github.com/oschwald/geoip2-golang"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
@@ -53,26 +55,26 @@ var (
|
||||
// Maps well known builders to the official distribution method that
|
||||
// they represent
|
||||
|
||||
{regexp.MustCompile(`teamcity@build\.syncthing\.net`), "GitHub"},
|
||||
{regexp.MustCompile(`jenkins@build\.syncthing\.net`), "GitHub"},
|
||||
{regexp.MustCompile(`builder@github\.syncthing\.net`), "GitHub"},
|
||||
{regexp.MustCompile(`\steamcity@build\.syncthing\.net`), "GitHub"},
|
||||
{regexp.MustCompile(`\sjenkins@build\.syncthing\.net`), "GitHub"},
|
||||
{regexp.MustCompile(`\sbuilder@github\.syncthing\.net`), "GitHub"},
|
||||
|
||||
{regexp.MustCompile(`deb@build\.syncthing\.net`), "APT"},
|
||||
{regexp.MustCompile(`debian@github\.syncthing\.net`), "APT"},
|
||||
{regexp.MustCompile(`\sdeb@build\.syncthing\.net`), "APT"},
|
||||
{regexp.MustCompile(`\sdebian@github\.syncthing\.net`), "APT"},
|
||||
|
||||
{regexp.MustCompile(`docker@syncthing\.net`), "Docker Hub"},
|
||||
{regexp.MustCompile(`docker@build.syncthing\.net`), "Docker Hub"},
|
||||
{regexp.MustCompile(`docker@github.syncthing\.net`), "Docker Hub"},
|
||||
{regexp.MustCompile(`\sdocker@syncthing\.net`), "Docker Hub"},
|
||||
{regexp.MustCompile(`\sdocker@build.syncthing\.net`), "Docker Hub"},
|
||||
{regexp.MustCompile(`\sdocker@github.syncthing\.net`), "Docker Hub"},
|
||||
|
||||
{regexp.MustCompile(`android-builder@github\.syncthing\.net`), "Google Play"},
|
||||
{regexp.MustCompile(`android-.*teamcity@build\.syncthing\.net`), "Google Play"},
|
||||
{regexp.MustCompile(`android-.*vagrant@basebox-stretch64`), "F-Droid"},
|
||||
{regexp.MustCompile(`vagrant@bullseye`), "F-Droid"},
|
||||
{regexp.MustCompile(`builduser@(archlinux|svetlemodry)`), "Arch (3rd party)"},
|
||||
{regexp.MustCompile(`\sandroid-builder@github\.syncthing\.net`), "Google Play"},
|
||||
{regexp.MustCompile(`\sandroid-.*teamcity@build\.syncthing\.net`), "Google Play"},
|
||||
{regexp.MustCompile(`\sandroid-.*vagrant@basebox-stretch64`), "F-Droid"},
|
||||
{regexp.MustCompile(`\svagrant@bullseye`), "F-Droid"},
|
||||
{regexp.MustCompile(`\sbuilduser@(archlinux|svetlemodry)`), "Arch (3rd party)"},
|
||||
{regexp.MustCompile(`@debian`), "Debian (3rd party)"},
|
||||
{regexp.MustCompile(`@fedora`), "Fedora (3rd party)"},
|
||||
{regexp.MustCompile(`\bbrew@`), "Homebrew (3rd party)"},
|
||||
{regexp.MustCompile(`root@buildkitsandbox`), "LinuxServer.io (3rd party)"},
|
||||
{regexp.MustCompile(`\sbrew@`), "Homebrew (3rd party)"},
|
||||
{regexp.MustCompile(`\sroot@buildkitsandbox`), "LinuxServer.io (3rd party)"},
|
||||
{regexp.MustCompile(`.`), "Others"},
|
||||
}
|
||||
)
|
||||
@@ -196,6 +198,7 @@ func (cli *CLI) Run() error {
|
||||
http.HandleFunc("/performance.json", srv.performanceHandler)
|
||||
http.HandleFunc("/blockstats.json", srv.blockStatsHandler)
|
||||
http.HandleFunc("/locations.json", srv.locationsHandler)
|
||||
http.Handle("/metrics", promhttp.Handler())
|
||||
http.Handle("/static/", http.FileServer(http.FS(statics)))
|
||||
|
||||
go srv.cacheRefresher()
|
||||
@@ -289,6 +292,12 @@ func (s *server) locationsHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
}
|
||||
|
||||
func (s *server) newDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
version := "fail"
|
||||
defer func() {
|
||||
// Version is "fail", "duplicate", "v2", "v3", ...
|
||||
metricReportsTotal.WithLabelValues(version).Inc()
|
||||
}()
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
addr := r.Header.Get("X-Forwarded-For")
|
||||
@@ -334,6 +343,7 @@ func (s *server) newDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if err.Error() == `pq: duplicate key value violates unique constraint "uniqueidjsonindex"` {
|
||||
// We already have a report today for the same unique ID; drop
|
||||
// this one without complaining.
|
||||
version = "duplicate"
|
||||
return
|
||||
}
|
||||
log.Println("insert:", err)
|
||||
@@ -343,6 +353,8 @@ func (s *server) newDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Database Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
version = fmt.Sprintf("v%d", rep.URVersion)
|
||||
}
|
||||
|
||||
func (s *server) summaryHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -722,6 +734,10 @@ func getReport(db *sql.DB, geoIPPath string) map[string]interface{} {
|
||||
add(featureGroups["Folder"]["v3"], "Pull Order", prettyCase(key), value)
|
||||
}
|
||||
|
||||
for key, value := range rep.FolderUsesV3.CopyRangeMethod {
|
||||
add(featureGroups["Folder"]["v3"], "Copy Range Method", prettyCase(key), value)
|
||||
}
|
||||
|
||||
inc(features["Device"]["v3"], "Untrusted", rep.DeviceUsesV3.Untrusted)
|
||||
|
||||
totals["GUI"] += rep.GUIStats.Enabled
|
||||
|
||||
@@ -611,6 +611,7 @@ found in the LICENSE file.
|
||||
</div>
|
||||
<hr>
|
||||
<p>
|
||||
<a href="https://github.com/syncthing/syncthing/tree/main/cmd/ursrv">Source code</a>.
|
||||
This product includes GeoLite2 data created by MaxMind, available from
|
||||
<a href="http://www.maxmind.com">http://www.maxmind.com</a>.
|
||||
</p>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Name=Syncthing Web UI
|
||||
GenericName=File synchronization UI
|
||||
Comment=Opens Syncthing's Web UI in the default browser (Syncthing must already be started).
|
||||
Exec=/usr/bin/syncthing -browser-only
|
||||
Exec=/usr/bin/syncthing --browser-only
|
||||
Icon=syncthing
|
||||
Terminal=false
|
||||
Type=Application
|
||||
|
||||
73
go.mod
73
go.mod
@@ -8,74 +8,83 @@ require (
|
||||
github.com/calmh/incontainer v0.0.0-20221224152218-b3e71b103d7a
|
||||
github.com/calmh/xdr v1.1.0
|
||||
github.com/ccding/go-stun v0.1.4
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chmduquesne/rollinghash v4.0.0+incompatible
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/d4l3k/messagediff v1.2.1
|
||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568
|
||||
github.com/getsentry/raven-go v0.2.0
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/greatroar/blobloom v0.7.2
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/jackpal/gateway v1.0.10
|
||||
github.com/jackpal/go-nat-pmp v1.0.2
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/maruel/panicparse/v2 v2.3.1
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.7.0
|
||||
github.com/minio/sha256-simd v1.0.1
|
||||
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75
|
||||
github.com/oschwald/geoip2-golang v1.9.0
|
||||
github.com/pierrec/lz4/v4 v4.1.18
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.17.0
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/quic-go/quic-go v0.39.1
|
||||
github.com/pierrec/lz4/v4 v4.1.19
|
||||
github.com/prometheus/client_golang v1.18.0
|
||||
github.com/quic-go/quic-go v0.40.1
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
|
||||
github.com/sasha-s/go-deadlock v0.3.1
|
||||
github.com/shirou/gopsutil/v3 v3.23.9
|
||||
github.com/shirou/gopsutil/v3 v3.23.11
|
||||
github.com/syncthing/notify v0.0.0-20210616190510-c6b7342338d2
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
|
||||
github.com/thejerf/suture/v4 v4.0.2
|
||||
github.com/urfave/cli v1.22.14
|
||||
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/sys v0.13.0
|
||||
golang.org/x/text v0.13.0
|
||||
golang.org/x/time v0.3.0
|
||||
golang.org/x/tools v0.14.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
github.com/willabides/kongplete v0.3.0
|
||||
golang.org/x/crypto v0.17.0
|
||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc
|
||||
golang.org/x/net v0.19.0
|
||||
golang.org/x/sys v0.15.0
|
||||
golang.org/x/text v0.14.0
|
||||
golang.org/x/time v0.5.0
|
||||
golang.org/x/tools v0.16.1
|
||||
google.golang.org/protobuf v1.32.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/alecthomas/assert/v2 v2.4.1 // indirect
|
||||
github.com/alecthomas/repr v0.3.0 // indirect
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect
|
||||
github.com/google/uuid v1.3.1 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.13.0 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.0.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||
github.com/nxadm/tail v1.4.11 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.13.2 // indirect
|
||||
github.com/onsi/gomega v1.30.0 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/posener/complete v1.2.3 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
|
||||
github.com/prometheus/common v0.45.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.uber.org/mock v0.3.0 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
)
|
||||
|
||||
// https://github.com/gobwas/glob/pull/55
|
||||
|
||||
132
go.sum
132
go.sum
@@ -3,12 +3,15 @@ github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f/go.mod
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0=
|
||||
github.com/alecthomas/assert/v2 v2.4.1 h1:mwPZod/d35nlaCppr6sFP0rbCL05WH9fIo7lvsf47zo=
|
||||
github.com/alecthomas/assert/v2 v2.4.1/go.mod h1:fw5suVxB+wfYJ3291t0hRTqtGzFYdSwstnRQdaQx2DM=
|
||||
github.com/alecthomas/kong v0.8.1 h1:acZdn3m4lLRobeh3Zi2S2EpnXTd1mOL6U7xVml+vfkY=
|
||||
github.com/alecthomas/kong v0.8.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
|
||||
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
|
||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA=
|
||||
github.com/alecthomas/repr v0.3.0 h1:NeYzUPfjjlqHY4KtzgKJiWd6sVq2eNUPTi34PiFGjY8=
|
||||
github.com/alecthomas/repr v0.3.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b h1:Fjm4GuJ+TGMgqfGHN42IQArJb77CfD/mAwLbDUoJe6g=
|
||||
@@ -40,15 +43,18 @@ github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWT
|
||||
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
|
||||
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
@@ -67,7 +73,6 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@@ -76,15 +81,21 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ=
|
||||
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8=
|
||||
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/greatroar/blobloom v0.7.2 h1:F30MGLHOcb4zr0pwCPTcKdlTM70rEgkf+LzdUPc5ss8=
|
||||
github.com/greatroar/blobloom v0.7.2/go.mod h1:mjMJ1hh1wjGVfr93QIHJ6FfDNVrA0IELv8OvMHJxHKs=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
@@ -100,8 +111,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
@@ -109,74 +120,76 @@ github.com/maruel/panicparse/v2 v2.3.1 h1:NtJavmbMn0DyzmmSStE8yUsmPZrZmudPH7kplx
|
||||
github.com/maruel/panicparse/v2 v2.3.1/go.mod h1:s3UmQB9Fm/n7n/prcD2xBGDkwXD6y2LeZnhbEXvs9Dg=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0 h1:rBhB9Rls+yb8kA4x5a/cWxOufWfXt24E+kq4YlbGj3g=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0/go.mod h1:fJ0UAZc1fx3xZhU4eSHQDJ1ApFmTVhp5VTpV9tm2ogg=
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.7.0 h1:z0CfPybq3CxaJvrrpf7Gme1psZTqHhJxf83q6apkSpI=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.7.0/go.mod h1:RVP6/F85JyxTrbJxWIdKU2vlSvK48iCMnMXRkSz7xtg=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 h1:cUVxyR+UfmdEAZGJ8IiKld1O0dbGotEnkMolG5hfMSY=
|
||||
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75/go.mod h1:pBbZyGwC5i16IBkjVKoy/sznA8jPD/K9iedwe1ESE6w=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
|
||||
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
||||
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc=
|
||||
github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y=
|
||||
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
|
||||
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
|
||||
github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc h1:8bQZVK1X6BJR/6nYUPxQEP+ReTsceJTKizeuwjWOPUA=
|
||||
github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.19 h1:tYLzDnjDXh9qIxSTKHwXwOYmm9d887Y7Y1ZkyXYHAN4=
|
||||
github.com/pierrec/lz4/v4 v4.1.19/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
|
||||
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
|
||||
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
|
||||
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
|
||||
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/quic-go v0.39.1 h1:d/m3oaN/SD2c+f7/yEjZxe2zEVotXprnrCCJ2y/ZZFE=
|
||||
github.com/quic-go/quic-go v0.39.1/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q=
|
||||
github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab h1:ZjX6I48eZSFetPb41dHudEyVr5v953N15TsNZXlkcWY=
|
||||
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab/go.mod h1:/PfPXh0EntGc3QAAyUaviy4S9tzy4Zp0e2ilq4voC6E=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
|
||||
github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
|
||||
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
|
||||
github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E=
|
||||
github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA=
|
||||
github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ=
|
||||
github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@@ -197,28 +210,30 @@ github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
|
||||
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
|
||||
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0 h1:okhMind4q9H1OxF44gNegWkiP4H/gsTFLalHFa4OOUI=
|
||||
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0/go.mod h1:TTbGUfE+cXXceWtbTHq6lqcTvYPBKLNejBEbnUsQJtU=
|
||||
github.com/willabides/kongplete v0.3.0 h1:8dJZ0r2a2YnSdYCQk9TjQDKzLrj1zUvIOPIG3bOV75c=
|
||||
github.com/willabides/kongplete v0.3.0/go.mod h1:VPdrG6LY+tP0LMkSBuTgIQ8c6+P8wvIDHVJzDdDh9Fw=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
|
||||
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM=
|
||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -232,16 +247,15 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -265,13 +279,14 @@ golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@@ -283,10 +298,11 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
@@ -294,8 +310,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
||||
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -309,8 +325,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
|
||||
@@ -143,7 +143,8 @@ table.table-auto td {
|
||||
max-width: 0px;
|
||||
}
|
||||
|
||||
td input[type="checkbox"] {
|
||||
/* Tag name is needed for selector to be specific enough to override Bootstrap style */
|
||||
input[type="checkbox"].extended-attributes-filter-rule-checkbox {
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
@@ -179,7 +180,7 @@ td input[type="checkbox"] {
|
||||
margin-right: .14285715em;
|
||||
}
|
||||
|
||||
.remote-devices-panel {
|
||||
.inline-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -459,15 +460,17 @@ ul.three-columns li, ul.two-columns li {
|
||||
}
|
||||
|
||||
@media (max-width: 419px) {
|
||||
/* the selectors are build to target only the content of folder and device
|
||||
panels as it would "destroy" e.g. out of sync or recent changes listings */
|
||||
/* The selectors are build to target only the content of folder and device
|
||||
panels as it would "destroy" e.g. out of sync or recent changes listings.
|
||||
The !important is needed to override .visible-xs that sets display to a
|
||||
specific table element instead of block. */
|
||||
div[id^='device-'].panel-collapse table,
|
||||
div[id^='folder-'].panel-collapse table,
|
||||
div[id^='device-'].panel-collapse tbody,
|
||||
div[id^='folder-'].panel-collapse tbody,
|
||||
div[id^='device-'].panel-collapse tr,
|
||||
div[id^='folder-'].panel-collapse tr {
|
||||
display: block;
|
||||
display: block !important;
|
||||
}
|
||||
div[id^='device-'].panel-collapse th,
|
||||
div[id^='folder-'].panel-collapse th,
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"A device with that ID is already added.": "تم أضافه عنوان هذا الجهاز من قبل.",
|
||||
"A device with that ID is already added.": "أضيف هذا الجهاز بالفعل.",
|
||||
"A negative number of days doesn't make sense.": "لا يمكن استخدام قيمة سالبة لعدد الأيام.",
|
||||
"A new major version may not be compatible with previous versions.": "الإصدار الجديد قد لا يتوافق مع الإصدارات السابقة.",
|
||||
"API Key": "مفتاح API",
|
||||
"About": "حول",
|
||||
"Action": "اجراء",
|
||||
"Action": "إجراء",
|
||||
"Actions": "الإجراءات",
|
||||
"Active filter rules": "قواعد التصفية النشطة",
|
||||
"Add": "إضافة",
|
||||
"Add Device": "إضافة جهاز",
|
||||
"Add Folder": "إضافة مجلد",
|
||||
"Add Remote Device": "أضافه جهاز بعيد",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "اضف أجهزة من المعرف/المقدم إلى قائمة الأجهزة الخاصة بنا، للمجلدات المشتركة بشكل متبادل.",
|
||||
"Add Remote Device": "إضافة جهاز بعيد",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "أضف أجهزة من الوسيط إلى قائمة الأجهزة الخاصة بنا، للمجلدات المشتركة بشكل متبادل.",
|
||||
"Add filter entry": "إضافة عامل التصفية",
|
||||
"Add ignore patterns": "أضف أنماط التجاهل",
|
||||
"Add new folder?": "إضافة مجلد جديد؟",
|
||||
@@ -20,7 +20,7 @@
|
||||
"Addresses": "العناوين",
|
||||
"Advanced": "متقدم",
|
||||
"Advanced Configuration": "ضبط متقدم",
|
||||
"All Data": "كل المعلومات",
|
||||
"All Data": "كل البيانات",
|
||||
"All Time": "كل الوقت",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "يجب حماية جميع المجلدات التي تمت مشاركتها مع هذا الجهاز بكلمة مرور ، بحيث تكون جميع البيانات المرسلة غير قابلة للقراءة بدون كلمة المرور المقدمة.",
|
||||
"Allow Anonymous Usage Reporting?": "السماح بإرسال تقارير الإستخدام المجهولة؟",
|
||||
@@ -30,21 +30,23 @@
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "الإصدار يتم معالجته بواسطة أمر خارجي. يجب إزالة الملف من المجلدات المشتركة. إذا كان المسار للتطبيق يحتوي على مسافات، يجب وضعها بين علامتي تنصيص دلالة على الاقتباس.",
|
||||
"Anonymous Usage Reporting": "تقارير الإستخدام المجهولة",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "هل تريد الانتقال الى التصميم الجديد لتقرير الاستخدام المجهول ؟",
|
||||
"Applied to LAN": "الشبكة المحلية",
|
||||
"Apply": "تقدم",
|
||||
"Are you sure you want to override all remote changes?": "هل أنت متأكد أنك تريد تجاوز كافة التغييرات عن بُعد؟",
|
||||
"Are you sure you want to permanently delete all these files?": "هل أنت متأكد أنك تريد حذف كل هذه الملفات بشكل دائم؟",
|
||||
"Are you sure you want to remove device {%name%}?": " هل انت متاكد من حذف الجهاز {{name}}? ",
|
||||
"Are you sure you want to remove device {%name%}?": "هل أنت متيقِّن من حذف هذا الجهاز {{name}}؟",
|
||||
"Are you sure you want to remove folder {%label%}?": "هل انت متاكد من حذف المجلد {{label}}؟",
|
||||
"Are you sure you want to restore {%count%} files?": "هل انت متاكد من استعادة {{count}} ملف؟",
|
||||
"Are you sure you want to revert all local changes?": "هل أنت متأكد أنك تريد التراجع عن كافة التغييرات المحلية؟",
|
||||
"Are you sure you want to upgrade?": "هل أنت متأكد أنك تريد الترقية؟",
|
||||
"Authentication Required": "يلزم الاستيثاق",
|
||||
"Authors": "المؤلفون",
|
||||
"Auto Accept": "القبول تلقائيا",
|
||||
"Automatic Crash Reporting": "التبليغ التلقائي للاخطاء",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "توفر الترقية التلقائية الاختيار بين النسخ الثابتة أو النسخ المرشحة.",
|
||||
"Automatic upgrades": "تحديث تلقائي",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "الترقية التلقائية مفعلة دائمًا للنسخ المرشحة. ",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "تلقائيا أنشئ وشارك المجلدات الموجودة في المسار الافتراضي.",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "الترقية التلقائية مفعلة دائمًا للنسخ المرشحة.",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "أنشئ وشارك المجلدات الموجودة في المسار الافتراضي تلقائيا.",
|
||||
"Available debug logging facilities:": "خدمات سجلات تدقيق البرمجيات المتوفرة:",
|
||||
"Be careful!": "احذر!",
|
||||
"Body:": "جسم:",
|
||||
@@ -64,13 +66,14 @@
|
||||
"Configured": "تكوين",
|
||||
"Connected (Unused)": "متصل (غير مستخدم)",
|
||||
"Connection Error": "خطأ في الإتصال",
|
||||
"Connection Management": "إعدادات الاتصال",
|
||||
"Connection Type": "نوع الاتصال",
|
||||
"Connections": "اتصالات",
|
||||
"Connections via relays might be rate limited by the relay": "قد يكون معدل التوصيلات عبر المرحلات محدودًا بواسطة المرحل",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "مراقبة الملفات بشكل مستمر متوفر في Syncthing. يتم فحص الملفات التي تم تغييرها في المسار فقط. هذا يساعد على تجنب فحص كامل المسار لأداء اسرع. ",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "مراقبة الملفات بشكل مستمر متوفر في Syncthing. يتم فحص الملفات التي تم تغييرها في المسار فقط. هذا يساعد على تجنب فحص كامل المسار لأداء اسرع.",
|
||||
"Copied from elsewhere": "منسوخ من مكان أخر",
|
||||
"Copied from original": "منسوخ من الأصل",
|
||||
"Copied!": "تم النسخ",
|
||||
"Copied!": "تم النسخ!",
|
||||
"Copy": "نسخ",
|
||||
"Copy failed! Try to select and copy manually.": "فشل النسخ! حاول التحديد والنسخ يدويًا.",
|
||||
"Currently Shared With Devices": "حاليًا تم مشاركته مع الأجهزة",
|
||||
@@ -91,14 +94,15 @@
|
||||
"Deselect devices to stop sharing this folder with.": "قم بإلغاء تحديد الأجهزة لإيقاف مشاركة هذا المجلد معها.",
|
||||
"Deselect folders to stop sharing with this device.": "قم بإلغاء تحديد المجلدات لإيقاف المشاركة مع هذا الجهاز.",
|
||||
"Device": "جهاز",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "الجهاز \"{{الاسم}}\" ({{الجهاز}} في {{العنوان}}) يرغب في الاتصال، إضافة جهاز جديد؟",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "الجهاز \"{{name}}\" ({{device}} في {{address}}) يرغب في الاتصال، إضافة جهاز جديد؟",
|
||||
"Device Certificate": "شهادة الجهاز",
|
||||
"Device ID": "هوية الجهاز",
|
||||
"Device Identification": "هوية الجهاز",
|
||||
"Device Name": "أسم الجهاز",
|
||||
"Device ID": "معرف الجهاز",
|
||||
"Device Identification": "معرف الجهاز",
|
||||
"Device Name": "اسم الجهاز",
|
||||
"Device Status": "حالة الجهاز",
|
||||
"Device is untrusted, enter encryption password": "الجهاز غير موثوق به، أدخل كلمة مرور التشفير",
|
||||
"Device rate limits": "حدود معدل نقل البيانات",
|
||||
"Device that last modified the item": "اخر جهاز جهاز عدل على العنصر",
|
||||
"Device that last modified the item": "آخر من عدل على العنصر",
|
||||
"Devices": "الأجهزة",
|
||||
"Disable Crash Reporting": "تعطيل ميزة التبليغ عن الأخطاء",
|
||||
"Disabled": "معطل",
|
||||
@@ -127,233 +131,425 @@
|
||||
"Edit Device": "تعديل الجهاز",
|
||||
"Edit Device Defaults": "تحرير الإعدادات الافتراضية للجهاز",
|
||||
"Edit Folder": "تعديل المجلد",
|
||||
"Edit Folder Defaults": "تعديل الإعدادت الافتراضية للمجلد",
|
||||
"Editing {%path%}.": "تعديل {{path}}.",
|
||||
"Enable Crash Reporting": "تفعيل التبليغ عن الاخطاء",
|
||||
"Enable NAT traversal": "تفعيل اجتياز النات",
|
||||
"Enable Relaying": "تفعيل الترحيل",
|
||||
"Enabled": "مفعل",
|
||||
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "يفعل إرسال البيانات الثانوية، وتطبيق البيانات الثانوية المستوردة. قد يطلب صلاحيات أكثر.",
|
||||
"Enables sending extended attributes to other devices, but not applying incoming extended attributes. This can have a significant performance impact. Always enabled when \"Sync Extended Attributes\" is enabled.": "تصدير بيانات ثانوية، ولا يطبق البيانات الثانوية المستوردة. قد يؤثر سلبا على الأداء. يفعل تلقائيا عند تفعيل \"مزامنة البيانات الثانوية\".",
|
||||
"Enables sending ownership information to other devices, and applying incoming ownership information. Typically requires running with elevated privileges.": "يفعل إرسال معلومات الملكية للأجهزة الأخرى، ويفعل معلومات الملكية المستوردة. غالبا ما يطلب صلاحيات أكثر.",
|
||||
"Enables sending ownership information to other devices, but not applying incoming ownership information. This can have a significant performance impact. Always enabled when \"Sync Ownership\" is enabled.": "يفعل إرسال معلومات الملكية للأجهزة الأخرى، ولكن لا يفعل معلومات الملكية المستوردة. يمكن أن يؤثر سلبا على الأداء. يفعل تلقائيا عند تفعيل \"مزامنة الملكية\".",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "أدخل رقمًا غير سالب (مثلًا، \"2.35\") واختر وحدة. النسب المئوية هي جزء من إجمالي حجم القرص.",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "ادخل رقم منفذ غير مقيد (1024 - 65535).",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "أدخل فواصل لكي تفصل بين العناوين (\"tcp://ip:port\", \"tcp://host:port\") أو \"العناوين الديناميكية\" للاكتشاف التلقائي للعنوان.",
|
||||
"Enter ignore patterns, one per line.": "ادخل نمط التجاهل، كل نمط في سطر.",
|
||||
"Enter up to three octal digits.": "أدخل ثلاثة أرقام ثُمَانِيَّةٍ أو أقل .",
|
||||
"Error": "خطأ",
|
||||
"Extended Attributes": "البيانات الثانوية",
|
||||
"Extended Attributes Filter": "مُنقِّح البيانات الثانوية",
|
||||
"External": "خارجي",
|
||||
"External File Versioning": "إصدار الملف الخارجي",
|
||||
"Failed Items": "العناصر الفاشلة",
|
||||
"Failed to setup, retrying": "فشل الأعداد، جاري المحاولة مره اخرى",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "من المتوقع فشل الاتصال بخوادم IPv6 إذا لم يكن هناك اتصال IPv6.",
|
||||
"File Pull Order": "ترتيب ملف السحب",
|
||||
"File Versioning": "ملف الإصدارات",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "الملفات يتم نقلها إلى دليل .stversions عند الاستبدال أو الحذف بواسطة البرنامج.",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "يتم نقل الملفات إلى الإصدارات المؤرخة المختومة في دليل .vversions عند استبدالها أو حذفها بواسطة Syncthing.",
|
||||
"Failed to load file versions.": "لم يُتَوَصَّل لنسخة الملف.",
|
||||
"Failed to load ignore patterns.": "فشل التَّوَصُّل إلى مُرَشِّحات التجاهل.",
|
||||
"Failed to setup, retrying": "فشل الإعداد، تجري المحاولة مرة أخرى",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "يُتوقع فشل الاتصال بخوادم IPv6، إذا لم يكن IPv6 متاحا.",
|
||||
"File Pull Order": "ترتيب استيراد الملفات",
|
||||
"File Versioning": "إصدارات الملف",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "الملفات يتم نقلها إلى مجلد `.stversions` عند الاستبدال أو الحذف بواسطة البرنامج.",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "يتم نقل الملفات إلى الإصدارات المؤرخة المختومة في مجلد `.stversions` عند استبدالها أو حذفها بواسطة Syncthing.",
|
||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "الملفات محمية من التغييرات التي تم إجراؤها على الأجهزة الأخرى ، ولكن سيتم إرسال التغييرات التي تم إجراؤها على هذا الجهاز إلى بقية الأجهزة.",
|
||||
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "تُزامَنُ الملفات من العنقود، لكن التغيرات المحلية على هذا الجهاز لاتُطَبَّقُ على غيره من الأجهزة.",
|
||||
"Filesystem Watcher Errors": "أخطاء مراقب نظام الملفات",
|
||||
"Filter by date": "فلتره بالتاريخ ",
|
||||
"Filter by date": "فلترة بالتاريخ",
|
||||
"Filter by name": "فلتر باستخدام الاسم",
|
||||
"Folder": "مجلد",
|
||||
"Folder ID": "هوية المجلد",
|
||||
"Folder ID": "مُعرِّف المجلد",
|
||||
"Folder Label": "تسمية المجلد",
|
||||
"Folder Path": "مسار المجلد",
|
||||
"Folder Status": "حالة المجلد",
|
||||
"Folder Type": "نوع المجلد",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "نوع المجلد \"{{receiveEncrypted}}\" لا يمكن إعداده إلا أثناء إنشاء الملف.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "نوع المجلد \"{{receiveEncrypted}}\" لا يمكن إعداده بعد إضافة المجلد. يجب أن تحذف المجلد وأن تفك تشفير البيانات على قرص التخزين أو تحذفها، بعدها تنشئ المجلد مجددا.",
|
||||
"Folders": "المجلدات",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "للمجلدات التالية، حدث خطأ قبل بدء مشاهدة التغييرات. ستتم إعادة المحاولة كل دقيقة، نظرًا لذلك قد تختفي الأخطاء قريبًا. لكن إذا استمرت، فحاول حل المشكلة واطلب المساعدة إذا لم تستطع حل المشكلة.",
|
||||
"Full Rescan Interval (s)": "مدة أعاده الفحص الكامل (ثانية)",
|
||||
"Forever": "للأبد",
|
||||
"Full Rescan Interval (s)": "مدة إعادة الفحص الكامل (ثانية)",
|
||||
"GUI": "واجهة المستخدم الرسومية",
|
||||
"GUI Authentication Password": "كلمة الس",
|
||||
"GUI Authentication User": "أسم المستخدم لدخول واجهة الرسومية",
|
||||
"GUI / API HTTPS Certificate": "الواجهة / API وثيقة HTTPS",
|
||||
"GUI Authentication Password": "كلمة السر لتوثيق الواجهة",
|
||||
"GUI Authentication User": "اسم المستخدم لدخول واجهة الرسومية",
|
||||
"GUI Authentication: Set User and Password": "توثيق الواجهة: أنشئ كلمة مرور للمستخدم",
|
||||
"GUI Listen Address": "واجهة الرسومية الاستماع الى العنوان",
|
||||
"GUI Theme": "شكل الواجه",
|
||||
"GUI Override Directory": "مجلد إحلال الواجهة",
|
||||
"GUI Theme": "شكل الواجهة",
|
||||
"General": "عام",
|
||||
"Generate": "توليد",
|
||||
"Global Discovery": "الاكتشاف العالمي",
|
||||
"Global Discovery Servers": "الاكتشاف العالمي",
|
||||
"Global State": "الحالة العامة ",
|
||||
"Global State": "الحالة العامة",
|
||||
"Help": "مساعدة",
|
||||
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "ملحوظة: إذا كان الإعداد الافتراضي هو الرفض، وحدها قواعد الرفض تُرصد. جرب إضافة \"السماح للكل\" كخيار أخير.",
|
||||
"Home page": "الصفحة الرئيسية",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "ومع ذلك، تشير إعداداتك الحالية إلى أنك قد لا ترغب في تمكينه. لذلك تم تعطيل الإبلاغ التلقائي عن الأعطال.",
|
||||
"Identification": "المُعرِّف",
|
||||
"If untrusted, enter encryption password": "في حالة الرِّيبة، أدخل كلمة سر التشفير",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "إذا أردت منع المستخدمين الآخرين على هذا الحاسب من الوصول لملفاتك من خلال Syncthing، يُنصَح بإعداد وثائق الملكية.",
|
||||
"Ignore": "تجاهل",
|
||||
"Ignore Patterns": "تجاهل الأنماط",
|
||||
"Ignore Permissions": "تجاهل الصلاحيات",
|
||||
"Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.": "يمكنك إعداد أنماط التجاهل بعد إنشاء المجلد فقط. إذا فُعِّلَتْ، سيظهر حقل لإعداد هذه الأنماط بعد حفظ المجلد.",
|
||||
"Ignored Devices": "الأجهزة المتجاهلة",
|
||||
"Ignored Folders": "المجلدات المتجاهلة",
|
||||
"Ignored at": "تجاهل عند",
|
||||
"Included Software": "البرامج المُضمَّنة",
|
||||
"Incoming Rate Limit (KiB/s)": "الحد الأقصى البيانات الواردة (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "الإعدادات الغير صحيحه قد تدمر بيانات المجلد وتجعل المزامنة غير صالحه للعمل",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "الإعدادات الغير صحيحة قد تدمر بيانات المجلد وتُفْشِلُ المزامنة.",
|
||||
"Incorrect user name or password.": "رُصِدَ خطأ في اسم المستخدم أو كلمة المرور.",
|
||||
"Internally used paths:": "المسار المستخدم محليّا:",
|
||||
"Introduced By": "عرف بواسطة",
|
||||
"Introducer": "المعرف",
|
||||
"Introducer": "الوسيط",
|
||||
"Introduction": "تقديم",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "عكس الحالة المذكورة (مثلا: لا تستثنِ)",
|
||||
"Keep Versions": "احتفظ بالاصدارات",
|
||||
"LDAP": "LDAP",
|
||||
"LDAP": "تعليمات الوصول البسيطة للمجلدات (LDAP)",
|
||||
"Largest First": "الأكبر أولا",
|
||||
"Last Scan": "اخر فحص",
|
||||
"Last 30 Days": "الثلاثون يومًا السابقة",
|
||||
"Last 7 Days": "الأيام السبعة السابقة",
|
||||
"Last Month": "الشهر الماضي",
|
||||
"Last Scan": "آخر فحص",
|
||||
"Last seen": "اخر ظهور",
|
||||
"Latest Change": "اخر التغييرات",
|
||||
"Learn more": "اعرف اكثر ",
|
||||
"Learn more": "اعرف أكثر",
|
||||
"Learn more at {%url%}": "اطلع على المزيد في {{url}}",
|
||||
"Limit": "الحد",
|
||||
"Listener Failures": "فشل المستمع",
|
||||
"Listener Status": "حالة المستمع",
|
||||
"Listeners": "المستمعين",
|
||||
"Loading data...": "تحميل بيانات...",
|
||||
"Loading...": "تحميل...",
|
||||
"Local Additions": "الإضافات المحلِّيَّة",
|
||||
"Local Discovery": "الاكتشاف المحلي",
|
||||
"Local State": "الحالة المحلية",
|
||||
"Local State (Total)": "الحالة المحلية (مجموع)",
|
||||
"Locally Changed Items": "العناصر المتغيرة محليا",
|
||||
"Log": "سجل",
|
||||
"Log File": "سِجِلُّ الأحداث",
|
||||
"Log In": "تسجيل الدخول",
|
||||
"Log Out": "تسجيل الخروج",
|
||||
"Log in to see paths information.": "سجل دخولك لتطلع على معلومات المسار.",
|
||||
"Log in to see version information.": "سجل دخولك لتطلع على معلومات الإصدار.",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "تتبع السجل متوقف، مَرِّر للأسفل للاستئناف.",
|
||||
"Login failed, see Syncthing logs for details.": "فشل تسجيل الدخول، اطَّلِع على سِجِلِّ Syncthing للتفاصيل.",
|
||||
"Logs": "سجلات",
|
||||
"Major Upgrade": "ترقية أساسية",
|
||||
"Mass actions": "التأثيرات العامة",
|
||||
"Maximum Age": "أقصى مدة",
|
||||
"Maximum single entry size": "الحد الأقصى للمدخلات",
|
||||
"Maximum total size": "السعة القصوى",
|
||||
"Metadata Only": "البيانات الوصفية فقط",
|
||||
"Minimum Free Disk Space": "أدنى حد لمساحة التخزين الحرة",
|
||||
"Mod. Device": "وضع الجهاز",
|
||||
"Mod. Time": "وضع الوقت",
|
||||
"More than a month ago": "منذ أكثر من شهر",
|
||||
"More than a week ago": "منذ أكثر من أسبوع",
|
||||
"More than a year ago": "منذ أكثر من عام",
|
||||
"Move to top of queue": "الانتقال لأعلى قائمة الانتظار",
|
||||
"Multi level wildcard (matches multiple directory levels)": "المطابقة على مستويات عدة",
|
||||
"Never": "أبدا",
|
||||
"New Device": "جهاز جديد",
|
||||
"New Folder": "مجلد جديد",
|
||||
"Newest First": "الأحدث أولا",
|
||||
"No": "لا",
|
||||
"No files will be deleted as a result of this operation.": "لن يتم حذف اي ملفات بسبب هذا العملية",
|
||||
"No File Versioning": "لا تقسيم لإصدارات الملفات",
|
||||
"No files will be deleted as a result of this operation.": "لن يتم حذف أي ملفات بسبب هذا العملية.",
|
||||
"No rules set": "لم تحدد قواعد",
|
||||
"No upgrades": "لا يوجد ترقيات",
|
||||
"Not shared": "لم يُشارَك",
|
||||
"Notice": "ملاحظة",
|
||||
"Number of Connections": "عدد الاتصالات",
|
||||
"OK": "موافق",
|
||||
"Off": "اطفئ",
|
||||
"Oldest First": "الأقدم أولا",
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "تسمية وصفية اختيارية للمجلد . يمكن أن تكون مختلفة على كل جهاز. ",
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "تسمية وصفية اختيارية للمجلد. يمكن أن تكون مختلفة على كل جهاز.",
|
||||
"Options": "خيارات",
|
||||
"Out of Sync": "خارج التزامن",
|
||||
"Out of Sync Items": "عناصر خارج التزامن",
|
||||
"Outgoing Rate Limit (KiB/s)": "الحد من سرعة التصدير (كيلوبايت/ث)",
|
||||
"Override": "أَحِلَّ",
|
||||
"Override Changes": "تخطي التغييرات",
|
||||
"Ownership": "الملكية",
|
||||
"Password": "كلمة المرور",
|
||||
"Path": "مسار",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "مسار المجلد على هذا الحاسب. سيُنْشَأ إن لم يوجد مسبقا. علامة المد (~) يمكن استخدامها اختصارا ل",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "المسار حيث تخزن الإصدارات (يترك فارغًا لدليل .vversions الافتراضي في المجلد المشترك).",
|
||||
"Paths": "المسارات",
|
||||
"Pause": "إيقاف",
|
||||
"Pause All": "أيقاف الكل ",
|
||||
"Pause All": "إيقاف الكل",
|
||||
"Paused": "توقف",
|
||||
"Paused (Unused)": "متوقف (مهمل)",
|
||||
"Pending changes": "التغييرات المعلقة",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "المسح الدوري خلال فترة زمنية معينة وتعطيل مشاهدة التغييرات.",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "المسح الدوري خلال فترة زمنية معينة وتفعيل مشاهدة التغييرات.",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "المسح الدوري خلال فترة زمنية معينة وفشل اعداد مشاهدة التغييرات، اعادة المحاولة كل 1 دقيقة.",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "الفحص الدوري خلال فترة زمنية معينة وتعطيل تَرقُّب التغييرات",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "المسح الدوري خلال فترة زمنية معينة وتفعيل تَرقُّب التغييرات",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "المسح الدوري خلال فترة زمنية معينة وفشل إعداد تَرقُّب التغييرات، إعادة المحاولة كل 1 دقيقة:",
|
||||
"Permanently add it to the ignore list, suppressing further notifications.": "أدرجها أبداً على قائمة التجاهل، اكتم الإشعارات مستقبلا.",
|
||||
"Please consult the release notes before performing a major upgrade.": "يرجى العودة إلى ملاحظات الإصدار قبل تنفيذ ترقية رئيسية.",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "تفضل بإنشاء مستخدما موثقا للواجهة وكلمة مرور من خلال قائمة الإعدادات.",
|
||||
"Please wait": "يرجى الانتظار",
|
||||
"Prefix indicating that the file can be deleted if preventing directory removal": "سابقة تشير بإمكانية حذف الملف إذا منع إزالة المجلد",
|
||||
"Prefix indicating that the pattern should be matched without case sensitivity": "سابقة تعني عدم لزوم حالة الحرف في البحث (غير مهمة للعربية)",
|
||||
"Preparing to Sync": "يُجَهَّزُ للمزامنة",
|
||||
"Preview": "معاينة",
|
||||
"Preview Usage Report": "معاينة تقرير الاستخدام",
|
||||
"Quick guide to supported patterns": "الدليل مختصر للأنماط المدعومة ",
|
||||
"QR code": "الصورة المشفرة (QR)",
|
||||
"QUIC LAN": "اتصال QUIC للشبكة المحلية (LAN)",
|
||||
"QUIC WAN": "QUIC الشبكة العامة",
|
||||
"Quick guide to supported patterns": "دليل مختصر للأنماط المدعومة",
|
||||
"Random": "عشوائي",
|
||||
"Receive Encrypted": "استلام المشفَّر",
|
||||
"Receive Only": "استقبال فقط",
|
||||
"Received data is already encrypted": "البيانات المستوردة مشفرة بالفعل",
|
||||
"Recent Changes": "اخر التغييرات",
|
||||
"Reduced by ignore patterns": "تقليص بواسطة تجاهل الأنماط. ",
|
||||
"Reduced by ignore patterns": "تقليص بواسطة تجاهل الأنماط",
|
||||
"Relay LAN": "ترحيل الشبكة المحلية (LAN)",
|
||||
"Relay WAN": "ترحيل الشبكة العامة (WAN)",
|
||||
"Release Notes": "ملاحظات الإصدار",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "مُمَهِّدات الإصدار تحتوي على آخر الخصائص والإصلاحات. وهي مماثلة للإصدارات النصف شهرية التقليدية لـ Syncthing .",
|
||||
"Remote Devices": "جهاز بعيد",
|
||||
"Remote GUI": "الواجهة النائية",
|
||||
"Remove": "إزالة",
|
||||
"Remove Device": "حذف جهاز",
|
||||
"Remove Folder": "حذف مجلد",
|
||||
"Required identifier for the folder. Must be the same on all cluster devices.": "يتطلب معرفًا للمجلد. يجب أن يستخدم نفس المعرف لبقية الأجهزة. ",
|
||||
"Required identifier for the folder. Must be the same on all cluster devices.": "يتطلب معرفًا للمجلد. يجب أن يستخدم نفس المعرف لبقية الأجهزة.",
|
||||
"Rescan": "إعادة فحص",
|
||||
"Rescan All": "أعادة فحص الكل",
|
||||
"Rescan All": "إعادة فحص الكل",
|
||||
"Rescans": "يعيد الفحص",
|
||||
"Restart": "إعادة تشغيل",
|
||||
"Restart Needed": "مطلوب أعادة تشغيل",
|
||||
"Restarting": "يتم إعادة التشغيل",
|
||||
"Restore": "استعادة",
|
||||
"Restore Versions": "استعادة أصدارات ",
|
||||
"Restore Versions": "استعادة إصدارات",
|
||||
"Resume": "استرد",
|
||||
"Resume All": "استعادة الكل ",
|
||||
"Reused": "إعادة الاستخدام",
|
||||
"Resume All": "استئناف الجميع",
|
||||
"Reused": "مُعادة الاستخدام",
|
||||
"Revert": "الرجوع عن التغيير",
|
||||
"Revert Local Changes": "التراجع عن التغييرات",
|
||||
"Save": "حفظ",
|
||||
"Saving changes": "تُحفَظ التعديلات",
|
||||
"Scan Time Remaining": "فحص الوقت المتبقي",
|
||||
"Scanning": "يتم الفحص",
|
||||
"See external versioning help for supported templated command line parameters.": "راجع تعليمات الإصدارات الخارجية لمعرفة القيم المدعومة في سطر الأوامر. ",
|
||||
"See external versioning help for supported templated command line parameters.": "راجع تعليمات الإصدارات الخارجية لمعرفة القيم المدعومة في سطر الأوامر.",
|
||||
"Select All": "تحديد الكل",
|
||||
"Select a version": "اختار أصدار ",
|
||||
"Select latest version": "اختار اخر أصدار ",
|
||||
"Select a version": "اختر إصداراً",
|
||||
"Select additional devices to share this folder with.": "اختيار المزيد من الأجهزة التي ترغب في مشاركة هذا المجلد معها.",
|
||||
"Select additional folders to share with this device.": "اختيار المزيد من المجلدات لمشاركتها مع هذا الجهاز.",
|
||||
"Select latest version": "اختر آخر إصدار",
|
||||
"Select oldest version": "اختيار أقدم إصدار",
|
||||
"Send & Receive": "إرسال واستقبال ",
|
||||
"Send & Receive": "إرسال واستقبال",
|
||||
"Send Extended Attributes": "أرسل البيانات الثانوية",
|
||||
"Send Only": "إرسال فقط",
|
||||
"Send Ownership": "أرسل الملكية",
|
||||
"Set Ignores on Added Folder": "طبِّق التجاهلات على المجلدات المضافة",
|
||||
"Settings": "إعدادات",
|
||||
"Share": "مشاركة",
|
||||
"Share Folder": "مشاركة مجلد",
|
||||
"Share by Email": "شارك بالبريد الإلكتروني",
|
||||
"Share by SMS": "شارك برسائل الـ SMS",
|
||||
"Share this folder?": "مشاركة هذا المجلد؟",
|
||||
"Shared Folders": "المجلدات المُشارَكة",
|
||||
"Shared With": "مشاركة مع",
|
||||
"Sharing": "مشاركه",
|
||||
"Show ID": "عرض الهوية",
|
||||
"Show ID": "عرض المُعرِّف",
|
||||
"Show QR": "اظهار QR",
|
||||
"Show diff with previous version": "اظهر الفرق مع النسخة السابقة ",
|
||||
"Show detailed discovery status": "اعرض حالة الاكتشاف تفصيليا",
|
||||
"Show detailed listener status": "اعرض حالة الاستماع تفصيليا",
|
||||
"Show diff with previous version": "أظهر الفرق مقارنةً بالنسخة السابقة",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "يُعرَض بدلا من المُعرِّف ضمن العناقيد. سيُروَّج للأجهزة الأخرى على أنه اسم أساسي محتمل.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "يُعرَض بدلا من المُعرِّف ضمن العناقيد. إذا تُرك فارغا، سيُحدَّث إلى الاسم المختار من قِبَل الجهاز.",
|
||||
"Shutdown": "إغلاق",
|
||||
"Shutdown Complete": "تم الإغلاق",
|
||||
"Simple": "بسيط",
|
||||
"Simple File Versioning": "التقسيم البسيط لإصدارات الملفات",
|
||||
"Single level wildcard (matches within a directory only)": "المقارنة على مستوى واحد (المقارنة مع الملفات في المجلد الحالي فقط)",
|
||||
"Size": "حجم",
|
||||
"Smallest First": "الأصغر أولا",
|
||||
"Some discovery methods could not be established for finding other devices or announcing this device:": "بعض أساليب الاستكشاف يمكن استخدامها للبحث عن أجهزة أخرى أو الإعلان عن هذا الجهاز:",
|
||||
"Some items could not be restored:": "بعض العناصر لا يمكن استرجاعها:",
|
||||
"Some listening addresses could not be enabled to accept connections:": "بعض عناوين الاستماع لا يمكن تفعيلها لقبول الاتصالات:",
|
||||
"Source Code": "مصدر الشفرة",
|
||||
"Stable releases and release candidates": "الإصدارات المستقرة والإصدارات المرشحة.",
|
||||
"Stable releases and release candidates": "الإصدارات المستقرة والإصدارات المرشحة",
|
||||
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "الإصدارات المستقرة تأخرت بنحو أسبوعين. خلال هذه الفترة يتم إجراء الاختبارات كإصدارات مرشحة.",
|
||||
"Stable releases only": "الإصدارات المستقرة فقط",
|
||||
"Staggered": "مترنِّح",
|
||||
"Staggered File Versioning": "تقسمات إصدارات الملف مهترئة",
|
||||
"Start Browser": "تشغيل المتصفح",
|
||||
"Statistics": "إحصائيات",
|
||||
"Stay logged in": "ابقِ مُسجل الدخول",
|
||||
"Stopped": "متوقف",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "يُزامن ويخزن البيانات المشفرة فقط. يجب أن تكون المجلدات على جميع الأجهزة مُجهزَّة بكلمة المرور نفسها، أو أن تكون من نوع \"{{receiveEncrypted}}\".",
|
||||
"Subject:": "الموضوع:",
|
||||
"Support": "الدعم",
|
||||
"Support Bundle": "حزمه مدعومه",
|
||||
"Sync Extended Attributes": "زامن الخصائص الثانوية",
|
||||
"Sync Ownership": "زامن الملكية",
|
||||
"Sync Protocol Listen Addresses": "عناوين بروتوكول استقبال المزامنة",
|
||||
"Sync Status": "وضع المزامنة",
|
||||
"Syncing": "يتم التزامن",
|
||||
"Syncthing device ID for \"{%devicename%}\"": "مُعرِّف Syncthing للجهاز {{devicename}}",
|
||||
"Syncthing has been shut down.": "تم إيقاف Syncthing.",
|
||||
"Syncthing includes the following software or portions thereof:": "المزامنة تتضمن البرامج التالية أو أجزائها:",
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing هو برنامج حر مفتوح المصدر تحت ترخيص MPL v2.0 (ترخيص موزيلا العام النسخة الثانية).",
|
||||
"Syncthing is a continuous file synchronization program. It synchronizes files between two or more computers in real time, safely protected from prying eyes. Your data is your data alone and you deserve to choose where it is stored, whether it is shared with some third party, and how it's transmitted over the internet.": "Syncthing هو تطبيق للمزامنة المستمرة للملفات. يزامن الملفات بين جهازين أو أكثر بشكل لحظي، آمن من الأعين المتربصة. بياناتك ملك لك وحدك، من حقك أن تختار أين تُخَزَّن، وهل يطلع عليها طرف ثالث أم لا، وكيف تتنقل عبر الشبكة.",
|
||||
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing يترقب محاولات الاتصال على العنوان التالي:",
|
||||
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing لا يترقب أي محاولة للاتصال على أي من عناوين الشبكة. الاتصالات الصادرة فقط هي التي يمكن أن تعمل.",
|
||||
"Syncthing is restarting.": "يتم إعادة تشغيل Syncthing.",
|
||||
"Syncthing is saving changes.": "Syncthing يحفظ التعديلات.",
|
||||
"Syncthing is upgrading.": "يتم تطوير Syncthing.",
|
||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing يدعم إعادة التشغيل التلقائي للمطورين. هذه الخاصية مفعلة بشكل افتراضي.",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing معطل على ما يبدو، ربما يكون العطل في شبكتك. إعادة المحاولة…",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing يواجه مشكلة في معالجة طلبك. إذا استعصت المشكلة، أعد تحميل الصفحة رجاء.",
|
||||
"TCP LAN": "TCP الشبكة المحلية",
|
||||
"TCP WAN": "TCP الشبكة واسعة النطاق",
|
||||
"Take me back": "رجوع",
|
||||
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "خيارات البدء حلَّت محل عنوان الواجهة. هذه التعديلات لن تدخل حيز التنفيذ ما بقي هذا الإحلال.",
|
||||
"The Syncthing Authors": "مُلَّاكُ Syncthing",
|
||||
"The Syncthing admin interface is configured to allow remote access without a password.": "واجهة مدير Syncthing معدة للسماح بالوصول بغير كلمة مرور.",
|
||||
"The aggregated statistics are publicly available at the URL below.": "الإحصاءات المجمعة متاحة للجميع على العنوان التالي.",
|
||||
"The cleanup interval cannot be blank.": "المدة بين عمليات التنظيف لا يمكن تركها فارغة.",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "تم حفظ الإعدادات ولكن لم يتم تفعيلها بعد. يجب أعادة تشغيل Syncthing حتى تم تفعيل الإعدادات.",
|
||||
"The device ID cannot be blank.": "هوية الجهاز لا يمكن أن تكون فارغة.",
|
||||
"The folder ID cannot be blank.": "هوية المجلد لا يمكن أن تكون فارغة.",
|
||||
"The folder ID must be unique.": "يجب أن يكون عنوان المجلد فريد ",
|
||||
"The folder path cannot be blank.": "مسار المجلد لا يمكن أن يكون فارغ",
|
||||
"The following items could not be synchronized.": "فشل مزامنة العناصر التالية",
|
||||
"The following items were changed locally.": "تم تغيير العناصر التالية محليا",
|
||||
"The device ID cannot be blank.": "مُعرِّف الجهاز لا يمكن أن يكون فارغاً.",
|
||||
"The device ID to enter here can be found in the \"Actions > Show ID\" dialog on the other device. Spaces and dashes are optional (ignored).": "يمكنك أن تجد مُعرِّف الجهاز الذي ينبغي استخدامه هنا في قائمة \"الإجراءات > عرض المُعرِّف\" على الجهاز الآخر. المسافات والخطوط الاعتراضية تُتجاهَل.",
|
||||
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes, and app versions. If the reported data set is changed you will be prompted with this dialog again.": "تقارير الاستخدام المشفرة ترسل يوميا. تُستخدم هذه التقارير لتتبع المنصات الشائعة، أحجام المجلدات، إصدارات التطبيق. إذا تغيرت بنود هذا التقرير، ستواجَهُ بهذه النافذة مرة أخرى.",
|
||||
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "المُعرِّف المُقَدَّم ناقص على ما يبدو. المُعرِّف مكوَّن من 52 أو 56 رمزاً بين حروف وأرقام، مع تجاهل المسافات الفارغة والخطوط الفاصلة.",
|
||||
"The folder ID cannot be blank.": "معرف المجلد لا يمكن أن يكون فارغاً.",
|
||||
"The folder ID must be unique.": "يجب أن يكون عنوان المجلد فريداً.",
|
||||
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "ستحل محتويات هذا المجلد محل محتويات مجلدات الأجهزة الأخرى. الملفات التي ليس لها وجود هنا ستحذف عن الأجهزة الأخرى.",
|
||||
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "ستحل البيانات في الأجهزة الأخرى محل البيانات في هذا المجلد. ستحذف الملفات التي ستُنشأ هنا.",
|
||||
"The folder path cannot be blank.": "مسار المجلد لا يمكن أن يكون فارغاً.",
|
||||
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "المُدَد التالية مُستخدَمة: تُحفظ نسخة كل 30 ثانية في الساعة الأولى، ونسخة كل ساعة لبقية اليوم الأول، ونسخة يومية لأول ثلاثين يوما، ونسخة أسبوعية للأمد.",
|
||||
"The following items could not be synchronized.": "فشلت مزامنة العناصر التالية.",
|
||||
"The following items were changed locally.": "غُيِّرت العناصر التالية محليا.",
|
||||
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "الطرق التالية مستخدمة للإعلان عن هذا الجهاز، وإيجاد الأجهزة الأخرى أيضا:",
|
||||
"The following text will automatically be inserted into a new message.": "النص التالي سيُضمَّن تلقائيا في رسالة جديدة.",
|
||||
"The following unexpected items were found.": "عُثِر على المحتوى غير المتوقع التالي.",
|
||||
"The interval must be a positive number of seconds.": "المُدة يجب أن تكون رقما موجبا من الثواني.",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "المدة البينية بالثواني لإجراء عمليات التنظيف الدورية في مجلد الإصدارات. اجعلها صِفرا لتعطيل التنظيف الدوري.",
|
||||
"The maximum age must be a number and cannot be blank.": "الحد الأقصى للسن يجب أن يكون رقمًا وألا يكون فارغًا.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "الحد الأقصى للاحتفاظ بإصدار ما (بالأيام ، اضبط على 0 للاحتفاظ بالإصدارات إلى الأبد).",
|
||||
"The number of connections must be a non-negative number.": "عدد الاتصالات يجب ألا يكون سالبا.",
|
||||
"The number of days must be a number and cannot be blank.": "حقل عدد الأيام يجب أن يكون رقم ولا يمكن تركه فارغ.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "عدد أيام حفظ الملفات في سلة المهملات. الصفر يعني إلى الأبد.",
|
||||
"The number of old versions to keep, per file.": "عدد النسخ القديمة المحفوظة، لكل ملف. ",
|
||||
"The number of old versions to keep, per file.": "عدد النسخ القديمة المحفوظة، لكل ملف.",
|
||||
"The number of versions must be a number and cannot be blank.": "حقل عدد النسخ يجب أن يكون رقم ولا يمكن أن تركة فارغا.",
|
||||
"The path cannot be blank.": "المسار لا يمكن أن يكون فارغ.",
|
||||
"The rate limit is applied to the accumulated traffic of all connections to this device.": "السرعة القصوى المختارة لنقل البيانات المتراكمة من جميع الاتصالات على هذا الجهاز.",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "يجب أن يكون الحد عددًا غير سالب (0: تعني بلا حد)",
|
||||
"The remote device has not accepted sharing this folder.": "الجهاز الآخر رفض مشاركة هذا المجلد.",
|
||||
"The remote device has paused this folder.": "الجهاز الآخر جمَّد هذا المجلد.",
|
||||
"The rescan interval must be a non-negative number of seconds.": "يجب أن يكون الفاصل الزمني لإعادة الفحص عددًا غير سالب من الثواني.",
|
||||
"There are no devices to share this folder with.": "لا توجد أجهزة أخرى لتشاركها هذا المجلد.",
|
||||
"There are no file versions to restore.": "لا توجد إصدارات يمكن استعادتها لهذا الملف.",
|
||||
"There are no folders to share with this device.": "لا توجد مجلدات لمشاركتها مع هذا الجهاز.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "تتم إعادة المحاولة تلقائيًا وسيتم مزامنتها عند إصلاح الخطأ.",
|
||||
"This Device": "هذا الجهاز",
|
||||
"This Month": "هذا الشهر",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "هذا قد يسبب في اختراق جهازك.",
|
||||
"This is a major version upgrade.": "ترقية أساسية ",
|
||||
"This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.": "لا يمكن لهذا الجهاز أن يرصد الأجهزة الأخرى تلقائيا، ولا أن يعلن عنوانه ليمكن الأجهزة الأخرى من إيجاده. الأجهزة ثابتة العناوين فقط يمكن أن تتصل.",
|
||||
"This is a major version upgrade.": "ترقية أساسية.",
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "هذا الخيار يتحكم في المساحة الفارغة المطلوبة من القرص الرئيسي.",
|
||||
"Time": "الوقت",
|
||||
"Time the item was last modified": "توقيت اخر تعديل للعنصر",
|
||||
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "للاتصال بالجهاز المسمى {{devicename}}، أضف جهازًا مغايرًا جديدًا تحت هذا العنوان:",
|
||||
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "لتفعيل القاعدة، ظلل المربع. لتعطيلها اترك المربع فارغاً.",
|
||||
"Today": "اليوم",
|
||||
"Trash Can": "المنفى",
|
||||
"Trash Can File Versioning": "إصدارات الملفات المنفية",
|
||||
"Type": "نوع",
|
||||
"UNIX Permissions": "صلاحيات UNIX",
|
||||
"Unavailable": "غير متوفر",
|
||||
"Unavailable/Disabled by administrator or maintainer": "غير متوفر/معطل من قبل المسؤول أو الصيانة",
|
||||
"Undecided (will prompt)": "غير محدد ( ستظهر نافذة للتحديد لاحقًا )",
|
||||
"Unexpected Items": "المحتويات المفاجِئة",
|
||||
"Unexpected items have been found in this folder.": "عُثِر على محتويات غير متوقعة في هذا المجلد.",
|
||||
"Unignore": "لا يتم التجاهل",
|
||||
"Unknown": "غير معرف",
|
||||
"Unshared": "غير مشترك",
|
||||
"Up to Date": "اخز أصدار ",
|
||||
"Unshared Devices": "الأجهزة غير المُشَارَكة",
|
||||
"Unshared Folders": "المجلدات غير المُشارَكة",
|
||||
"Untrusted": "غير موثوق",
|
||||
"Up to Date": "مُزَامَن",
|
||||
"Updated {%file%}": "مُحَدَّث {{file}}",
|
||||
"Upgrade": "ترقية",
|
||||
"Upgrade To {%version%}": "ترقية الى {{version}} ",
|
||||
"Upgrade To {%version%}": "ترقية إلى النسخة {{version}}",
|
||||
"Upgrading": "جاري الترقية",
|
||||
"Upload Rate": "معدل الرفع",
|
||||
"Uptime": "وقت التشغيل",
|
||||
"Usage reporting is always enabled for candidate releases.": "تقارير الاستخدام مفعلة دائمًا للنسخ المرشحة.",
|
||||
"Use HTTPS for GUI": "استخدام HTTPS مع الواجه الرسومية ",
|
||||
"Use notifications from the filesystem to detect changed items.": "استخدم أشغارات نظام الملفات لمعرفة الملفات المتغيرة",
|
||||
"Use HTTPS for GUI": "استخدم HTTPS لتأمين واجهة المستخدم",
|
||||
"Use notifications from the filesystem to detect changed items.": "استخدم إشعارات نظام الملفات لمعرفة الملفات المتغيرة.",
|
||||
"User": "مستخدِم",
|
||||
"User Home": "منزل المستخدم",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "اسم المستخدم/كلمة المرور لم يُنشَآ لتوثيق الواجهة. يُرجى إنشاؤهما من فضلك.",
|
||||
"Using a QUIC connection over LAN": "استخدام اتصال QUIC بدلا من LAN",
|
||||
"Using a QUIC connection over WAN": "استخدام اتصال QUIC بدلا من WAN",
|
||||
"Using a direct TCP connection over LAN": "استخدام اتصال TCP مباشر بدلا من LAN",
|
||||
"Using a direct TCP connection over WAN": "استخدام اتصال TCP مباشر بدلا من WAN",
|
||||
"Version": "الإصدار",
|
||||
"Versions": "نسخ",
|
||||
"Versions Path": "مسار النسخ",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "يتم حذف الإصدارات تلقائيًا إذا تجاوزت العمر الأقصى أو تجاوزت عدد الملفات المسموح بها خلال فاصل زمني محدد.",
|
||||
"Waiting to Clean": "في انتظار التنظيف",
|
||||
"Waiting to Scan": "في انتظار الفحص",
|
||||
"Waiting to Sync": "في انتظار المزامنة",
|
||||
"Warning": "تحذير",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "تحذير، هذا المجلد يحتوي داخله على مجلد آخر أضيف مسبقا {{otherFolder}}.",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "تحذير، هذا المجلد يحتوي داخله على مجلد آخر أضيف مسبقا\"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "تحذير، هذا المجلد هو أحد محتويات مجلد مضاف مسبقا \"{{otherFolder}}\".",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "تحذير، هذا المجلد هو أحد محتويات مجلد مضاف مسبقا \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "تحذير: إذا كنت مستخدما لمراقب خارجي كـ {{syncthingInotify}}، تأكد من تعطيله.",
|
||||
"Watch for Changes": "راقب التغييرات",
|
||||
"Watching for Changes": "جاري مراقبة التغيرات",
|
||||
"Watching for changes discovers most changes without periodic scanning.": "مراقبة التغييرات تكشف معظم التغييرات دون إجراء المسح الدوري.",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "يجب أضافه الأجهزة الجديدة في الطرفين",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "يجب إضافة الأجهزة الجديدة في الطرفين.",
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "عند إضافة مجلد جديد ، ضع في الاعتبار أن معرف المجلد يُستخدم لربط المجلدات معًا بين الأجهزة المختلفة. وهي حساسة لحالة الأحرف لذا يجب أن تتطابق تمامًا بين جميع الأجهزة.",
|
||||
"When set to more than one on both devices, Syncthing will attempt to establish multiple concurrent connections. If the values differ, the highest will be used. Set to zero to let Syncthing decide.": "إذا عُرفَّ Syncthing بأنه أكثر من واحد على كلا الجهازين، فإنه سيحاول إقامة عدة اتصالات متوازية. إذا اختلفت القِيَم، أعلاها ستُستخدَم. صَفِّرها لتترك القرار لـ Syncthing.",
|
||||
"Yes": "نعم",
|
||||
"You can also select one of these nearby devices:": "يمكنك أيضا اختيار واحد من الأجهزة القريبة ",
|
||||
"Yesterday": "أمس",
|
||||
"You can also copy and paste the text into a new message manually.": "يكنك نسخ النص لتدرجه في رسالة جديدة بنفسك.",
|
||||
"You can also select one of these nearby devices:": "يمكنك أيضا اختيار واحدة من الأجهزة القريبة:",
|
||||
"You can change your choice at any time in the Settings dialog.": "يمكنك تغيير اختيارك في أي وقت بواسطة الاعدادات.",
|
||||
"You can read more about the two release channels at the link below.": "يمكنك قراءة المزيد عن إصداريّ القناتين عبر الرابط بالأسفل.",
|
||||
"You have no ignored devices.": "لا يوجد أجهزة في قائمة التجاهل ",
|
||||
"You have no ignored folders.": "لا يوجد مجلدات في قائمه التجاهل ",
|
||||
"You have unsaved changes. Do you really want to discard them?": "الإعدادات لم تحفظ. هل انت متأكد من الإلغاء؟ ",
|
||||
"You must keep at least one version.": "يجب الاحتفاظ بنسخة واحده على الاقل",
|
||||
"You have no ignored devices.": "لا أجهزة مُتجاهَلَةٌ.",
|
||||
"You have no ignored folders.": "لا مجلدات مُتجاهَلَةٌ.",
|
||||
"You have unsaved changes. Do you really want to discard them?": "الإعدادات لم تُحفظ. هل أنت متأكد من الإلغاء؟",
|
||||
"You must keep at least one version.": "يجب الاحتفاظ بنسخة واحدة على الأقل.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "ينبغي ألا تغير شيئا في المجلد المحلي في حالة كان \"{{receiveEncrypted}}\".",
|
||||
"Your SMS app should open to let you choose the recipient and send it from your own number.": "ينبغي أن يسمح تطبيق SMS لديك بأن تختار مستلما ويرسلها من رقمك.",
|
||||
"Your email app should open to let you choose the recipient and send it from your own address.": "ينبغي أن يسمح تطبيق البريد الإلكتروني الخاص بك باختيار مستلم و أن يرسلها من عنوانك.",
|
||||
"days": "أيام",
|
||||
"deleted": "مُسِحَ",
|
||||
"deny": "امنع",
|
||||
"directories": "مجلدات",
|
||||
"file": "ملف",
|
||||
"files": "ملفات",
|
||||
"folder": "مجلد",
|
||||
"full documentation": "الوثائق الكاملة",
|
||||
"items": "العناصر",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} يريد مشاركة مجلد \"{{folder}}\". ",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} يريد مشاركة مجلد \"{{folderlabel}}\" ({{folder}}). "
|
||||
"modified": "عُدِّل",
|
||||
"permit": "اسمح",
|
||||
"seconds": "ثواني",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "أسوَد",
|
||||
"dark": "داكن",
|
||||
"default": "افتراضي",
|
||||
"light": "أبيض"
|
||||
}
|
||||
},
|
||||
"unknown device": "جهاز مجهول",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} يريد مشاركة هذا المجلد \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} يريد مشاركة هذا المجلد \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} يمكن أن يعيد تقديم هذا الجهاز."
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
"Device ID": "Идентификатор на устройство",
|
||||
"Device Identification": "Идентификация на устройство",
|
||||
"Device Name": "Име на устройството",
|
||||
"Device Status": "Състояние на устройството",
|
||||
"Device is untrusted, enter encryption password": "Устройството е недоверено, въведете парола за шифроване",
|
||||
"Device rate limits": "Ограничаване на скоростта",
|
||||
"Device that last modified the item": "Устройство, което последно промени обекта",
|
||||
@@ -168,6 +169,7 @@
|
||||
"Folder ID": "Идентификатор на папката",
|
||||
"Folder Label": "Име на папката",
|
||||
"Folder Path": "Път до папката",
|
||||
"Folder Status": "Състояние на папката",
|
||||
"Folder Type": "Вид на папката",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Вида „{{receiveEncrypted}}“ може да бъде избран само при добавяне на папка.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Видът папката „{{receiveEncrypted}}“ не може да бъде променян след нейното създаване. Трябва да я премахнете, изтриете или разшифровате съдържанието и да добавите папката отново.",
|
||||
@@ -304,7 +306,6 @@
|
||||
"QR code": "Код за QR",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "В повечето случаи връзките през протокола QUIC се считат за неоптимални",
|
||||
"Quick guide to supported patterns": "Кратък наръчник на поддържаните шаблони",
|
||||
"Random": "Произволен",
|
||||
"Receive Encrypted": "Приема шифровани данни",
|
||||
@@ -538,11 +539,16 @@
|
||||
"modified": "променено",
|
||||
"permit": "разрешаване",
|
||||
"seconds": "секунди",
|
||||
"theme-name-black": "Черна",
|
||||
"theme-name-dark": "Тъмна",
|
||||
"theme-name-default": "По подразбиране",
|
||||
"theme-name-light": "Светла",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} споделя папката „{{folder}}“.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} споделя папката „{{folderlabel}}“ ({{folder}}).",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Черна",
|
||||
"dark": "Тъмна",
|
||||
"default": "По подразбиране",
|
||||
"light": "Светла"
|
||||
}
|
||||
},
|
||||
"unknown device": "непознато устройство",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} иска папката „{{folder}}“ да бъде споделена.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} иска папката „{{folderlabel}}“ ({{folder}}) да бъде споделена.",
|
||||
"{%reintroducer%} might reintroduce this device.": "Поръчителят {{reintroducer}} може отново да предложи това устройство."
|
||||
}
|
||||
|
||||
@@ -292,7 +292,6 @@
|
||||
"QR code": "Codi QR",
|
||||
"QUIC LAN": "Connexió QUIC LAN",
|
||||
"QUIC WAN": "Connexió QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "En la majoria dels casos, les connexions QUIC es consideren subòptimes",
|
||||
"Quick guide to supported patterns": "Guia ràpida per als possibles patrons",
|
||||
"Random": "Aleatori",
|
||||
"Receive Encrypted": "Rebre xifrat",
|
||||
@@ -518,10 +517,14 @@
|
||||
"modified": "modificat",
|
||||
"permit": "permís",
|
||||
"seconds": "segons",
|
||||
"theme-name-black": "Negre",
|
||||
"theme-name-dark": "Fosc",
|
||||
"theme-name-default": "Per defecte",
|
||||
"theme-name-light": "Clar",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Negre",
|
||||
"dark": "Fosc",
|
||||
"default": "Per defecte",
|
||||
"light": "Clar"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vol compartir la carpeta \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vol compartir la carpeta \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} podria tornar a introduir aquest dispositiu."
|
||||
|
||||
@@ -292,7 +292,6 @@
|
||||
"QR code": "Codi QR",
|
||||
"QUIC LAN": "Xarxa QUIC LAN",
|
||||
"QUIC WAN": "Xarxa QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "En la majoria dels casos, les connexions QUIC es consideren subòptimes",
|
||||
"Quick guide to supported patterns": "Guía ràpida de patrons suportats",
|
||||
"Random": "Aleatori",
|
||||
"Receive Encrypted": "Rebre xifrat",
|
||||
@@ -518,10 +517,14 @@
|
||||
"modified": "modificat",
|
||||
"permit": "permís",
|
||||
"seconds": "segons",
|
||||
"theme-name-black": "Negre",
|
||||
"theme-name-dark": "Fosc",
|
||||
"theme-name-default": "Per defecte",
|
||||
"theme-name-light": "Clar",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Negre",
|
||||
"dark": "Fosc",
|
||||
"default": "Per defecte",
|
||||
"light": "Clar"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vol compartit la carpeta \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vol compartir la carpeta \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} podria tornar a introduir aquest dispositiu."
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
"About": "O aplikaci",
|
||||
"Action": "Akce",
|
||||
"Actions": "Akce",
|
||||
"Active filter rules": "Aktivní pravidla filtrování",
|
||||
"Add": "Přidat",
|
||||
"Add Device": "Přidat zařízení",
|
||||
"Add Folder": "Přidat složku",
|
||||
"Add Remote Device": "Přidat vzdálené zařízení",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "Přidat zařízení z uvaděče do místního seznamu zařízení a získat tak vzájemně sdílené složky.",
|
||||
"Add filter entry": "Přidej filtr",
|
||||
"Add ignore patterns": "Přidat vzory ignorovaného",
|
||||
"Add new folder?": "Přidat novou složku?",
|
||||
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Dále bude prodloužen interval mezi plnými skeny (60krát, t.j. nová výchozí hodnota 1h). V případě, že nyní zvolíte Ne, stále ještě toto později můžete u každé složky jednotlivě ručně upravit.",
|
||||
@@ -24,10 +26,11 @@
|
||||
"Allow Anonymous Usage Reporting?": "Povolit anonymní hlášení o používání?",
|
||||
"Allowed Networks": "Sítě, ze kterých je umožněn přístup",
|
||||
"Alphabetic": "Abecední",
|
||||
"Altered by ignoring deletes.": "Změněno pomocí vzorů ignorovaného",
|
||||
"Altered by ignoring deletes.": "Změněno pomocí vzorů ignorovaného.",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Správu verzí obstarává externí příkaz. U toho je třeba, aby neaktuální soubory jím byly odsouvány pryč ze sdílené složky. Pokud popis umístění tohoto příkazu obsahuje mezeru, je třeba popis umístění uzavřít do uvozovek.",
|
||||
"Anonymous Usage Reporting": "Anonymní hlášení o používání",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Formát anonymního hlášení o používání byl změněn. Chcete přejít na nový formát?",
|
||||
"Applied to LAN": "Použité pro místní síť",
|
||||
"Apply": "Aplikovat",
|
||||
"Are you sure you want to override all remote changes?": "Skutečně si přejete přebít všechny vzdálené změny?",
|
||||
"Are you sure you want to permanently delete all these files?": "Skutečně chcete smazat všechny tyto soubory?",
|
||||
@@ -36,6 +39,7 @@
|
||||
"Are you sure you want to restore {%count%} files?": "Opravdu chcete obnovit {{count}} souborů?",
|
||||
"Are you sure you want to revert all local changes?": "Skutečně si přejete vrátit všechny lokální změny?",
|
||||
"Are you sure you want to upgrade?": "Skutečně chcete provést aktualizaci?",
|
||||
"Authentication Required": "Autentizace vyžadována",
|
||||
"Authors": "Autoři",
|
||||
"Auto Accept": "Přijmout automaticky",
|
||||
"Automatic Crash Reporting": "Automatické hlášení pádů",
|
||||
@@ -61,16 +65,22 @@
|
||||
"Configured": "Nastaveno",
|
||||
"Connected (Unused)": "Připojeno (nepoužité)",
|
||||
"Connection Error": "Chyba připojení",
|
||||
"Connection Management": "Správa připojení",
|
||||
"Connection Type": "Typ připojení",
|
||||
"Connections": "Spojení",
|
||||
"Connections via relays might be rate limited by the relay": "Připojení přes přenašeč může být přenašečem omezena rychlost",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Syncthing nyní umožňuje nepřetržité sledování změn. To zachytí změny na úložišti a spustí sken pouze pro umístění, ve kterých se něco změnilo. Výhodami jsou rychlejší propagace změn a méně plných skenů.",
|
||||
"Copied from elsewhere": "Zkopírováno odjinud",
|
||||
"Copied from original": "Zkopírováno z originálu",
|
||||
"Copied!": "Zkopírováno!",
|
||||
"Copy": "Kopírovat",
|
||||
"Copy failed! Try to select and copy manually.": "Kopírování selhalo! Zkuste vybrat a zkopírovat manuálně.",
|
||||
"Currently Shared With Devices": "Aktuálně sdíleno se zařízeními",
|
||||
"Custom Range": "Přesný rozsah",
|
||||
"Danger!": "Nebezpečí!",
|
||||
"Database Location": "Umístění databáze",
|
||||
"Debugging Facilities": "Nástroje pro ladění",
|
||||
"Default": "Výchozí",
|
||||
"Default Configuration": "Výchozí nastavení",
|
||||
"Default Device": "Výchozí zařízení",
|
||||
"Default Folder": "Výchozí složka",
|
||||
@@ -100,6 +110,7 @@
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Zakazuje porovnávání a synchronizaci souborových oprávnění. To je užitečné na systémech, kde oprávnění souborů chybí, nebo jsou nestandardní (např. FAT, exFAT, Synology, Android).",
|
||||
"Discard": "Zahodit",
|
||||
"Disconnected": "Odpojeno",
|
||||
"Disconnected (Inactive)": "Odpojeno (Neaktivní)",
|
||||
"Disconnected (Unused)": "Odpojeno (nepoužité)",
|
||||
"Discovered": "Objeveno",
|
||||
"Discovery": "Objevování",
|
||||
@@ -135,6 +146,7 @@
|
||||
"Enter up to three octal digits.": "Zadejte nanejvýš tři osmičkové číslice.",
|
||||
"Error": "Chyba",
|
||||
"Extended Attributes": "Rozšířené atributy",
|
||||
"Extended Attributes Filter": "Rozšířený filtr atributů",
|
||||
"External": "Externí",
|
||||
"External File Versioning": "Externí správa verzí souborů",
|
||||
"Failed Items": "Nezdařené položky",
|
||||
@@ -191,9 +203,11 @@
|
||||
"Included Software": "Použitý software",
|
||||
"Incoming Rate Limit (KiB/s)": "Omezení příchozí rychlosti (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Nesprávné nastavení může poškodit obsah Vašich složek a znefunkčnít Syncthing.",
|
||||
"Incorrect user name or password.": "Nesprávné uživatelské jméno nebo heslo.",
|
||||
"Internally used paths:": "Interně používané cesty:",
|
||||
"Introduced By": "Zavedeno od",
|
||||
"Introducer": "Zavaděč",
|
||||
"Introduction": "Úvod",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Převrácení dané podmínky (např. nevynechat)",
|
||||
"Keep Versions": "Kolik verzí ponechávat",
|
||||
"LDAP": "LDAP",
|
||||
@@ -218,6 +232,8 @@
|
||||
"Locally Changed Items": "Lokálně změněné položky",
|
||||
"Log": "Záznam událostí",
|
||||
"Log File": "Soubor logů",
|
||||
"Log In": "Přihlásit se",
|
||||
"Log Out": "Odhlásit se",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Zaznamenávání událostí pozastaveno. Sjeďte dolů pro pokračování.",
|
||||
"Logs": "Záznamy událostí",
|
||||
"Major Upgrade": "Aktualizace hlavní verze",
|
||||
@@ -227,6 +243,9 @@
|
||||
"Minimum Free Disk Space": "Minimální velikost volného místa na úložišti",
|
||||
"Mod. Device": "Zařízení, které provedlo změnu",
|
||||
"Mod. Time": "Okamžik změny",
|
||||
"More than a month ago": "Více než před měsícem",
|
||||
"More than a week ago": "Více než před týdnem",
|
||||
"More than a year ago": "Více než před rokem",
|
||||
"Move to top of queue": "Přesunout na začátek fronty",
|
||||
"Multi level wildcard (matches multiple directory levels)": "Víceúrovňový zástupný znak (shody i skrz více úrovní složek)",
|
||||
"Never": "Nikdy",
|
||||
@@ -239,6 +258,7 @@
|
||||
"No upgrades": "Žádné aktualizace",
|
||||
"Not shared": "Nesdílené",
|
||||
"Notice": "Oznámení",
|
||||
"Number of Connections": "Počet připojení",
|
||||
"OK": "OK",
|
||||
"Off": "Vypnuta",
|
||||
"Oldest First": "Od nejstarších",
|
||||
@@ -250,6 +270,7 @@
|
||||
"Override": "Přebít",
|
||||
"Override Changes": "Přebít změny na ostatních",
|
||||
"Ownership": "Vlastnictví",
|
||||
"Password": "Heslo",
|
||||
"Path": "Popis umístění",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Popis umístění složky na tomto počítači. Pokud neexistuje, bude vytvořeno. Znak vlnovky (~) může být použit jako zkratka pro",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Popis umístění, ve kterém ukládat verze (ponechte prázdné pro výchozí podsložku .stversions ve sdílené složce).",
|
||||
@@ -261,7 +282,7 @@
|
||||
"Pending changes": "Čekající změny",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Periodické skenování podle zadaného intervalu; sledování změn vypnuto",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Periodické skenování podle zadaného intervalu; sledování změn zapnuto",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodické skenování podle zadaného intervalu; nastavení sledování změn se nezdařilo, opětovný pokus každou 1 min: ",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodické skenování podle zadaného intervalu; nastavení sledování změn se nezdařilo, opětovný pokus každou 1 min:",
|
||||
"Permanently add it to the ignore list, suppressing further notifications.": "Natrvalo ignorovat, takže oznámení již nebudou přicházet.",
|
||||
"Please consult the release notes before performing a major upgrade.": "Před přechodem na novější hlavní verzi si nejdříve přečtěte poznámky k vydání nové verze.",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "V dialogu Nastavení zadejte uživatelské jméno a heslo pro ověření se v GUI.",
|
||||
@@ -318,6 +339,8 @@
|
||||
"Settings": "Nastavení",
|
||||
"Share": "Sdílet",
|
||||
"Share Folder": "Sdílet složku",
|
||||
"Share by Email": "Sdílet přes E-Mail",
|
||||
"Share by SMS": "Sdílet přes SMS",
|
||||
"Share this folder?": "Sdílet tuto složku?",
|
||||
"Shared Folders": "Sdílené složky",
|
||||
"Shared With": "Sdíleno s",
|
||||
@@ -349,6 +372,7 @@
|
||||
"Statistics": "Statistiky",
|
||||
"Stopped": "Zastaveno",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Ukládá a synchronizuje pouze zašifrovaná data. Složky na všech připojených zařízeních musí mít nastavené stejné heslo a nebo být také typu „{{receiveEncrypted}}“.",
|
||||
"Subject:": "Předmět:",
|
||||
"Support": "Podpora",
|
||||
"Support Bundle": "Balík podpory",
|
||||
"Sync Extended Attributes": "Synchronizovat rozšířené atributy",
|
||||
@@ -474,10 +498,14 @@
|
||||
"full documentation": "úplná dokumentace",
|
||||
"items": "položky",
|
||||
"seconds": "sekund",
|
||||
"theme-name-black": "Černý",
|
||||
"theme-name-dark": "Tmavý",
|
||||
"theme-name-default": "Výchozí",
|
||||
"theme-name-light": "Světlý",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Černý",
|
||||
"dark": "Tmavý",
|
||||
"default": "Výchozí",
|
||||
"light": "Světlý"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce sdílet složku „{{folder}}“.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} chce sdílet složku „{{folderlabel}}“ ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} může toto zařízení znovu uvést."
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
"Device ID": "Enheds-ID",
|
||||
"Device Identification": "Enhedsidentifikation",
|
||||
"Device Name": "Enhedsnavn",
|
||||
"Device Status": "Enhedsstatus",
|
||||
"Device is untrusted, enter encryption password": "Enhed er ikke-troværdig, indtast krypteringsadgangskode",
|
||||
"Device rate limits": "Enhedens hastighedsbegrænsning",
|
||||
"Device that last modified the item": "Enhed, som sidst ændrede filen",
|
||||
@@ -168,6 +169,7 @@
|
||||
"Folder ID": "Mappe-ID",
|
||||
"Folder Label": "Mappeetiket",
|
||||
"Folder Path": "Mappesti",
|
||||
"Folder Status": "Mappestatus",
|
||||
"Folder Type": "Mappetype",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Mappe type \"{{receiveEncrypted}}\" kan kun indstilles når en ny mappe tilføjes.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Mappetype \"{{receiveEncrypted}}\" kan ikke ændres, efter at mappen er tilføjet. Du skal fjerne mappen, slette eller dekryptere dataene på disken og tilføje mappen igen.",
|
||||
@@ -304,7 +306,6 @@
|
||||
"QR code": "QR-kode",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "QUIC-forbindelser anses i de fleste tilfælde for at være mindre optimale",
|
||||
"Quick guide to supported patterns": "Kvikguide til understøttede mønstre",
|
||||
"Random": "Tilfældig",
|
||||
"Receive Encrypted": "Modtag krypteret",
|
||||
@@ -538,10 +539,15 @@
|
||||
"modified": "ændret",
|
||||
"permit": "tillad",
|
||||
"seconds": "sekunder",
|
||||
"theme-name-black": "Sort",
|
||||
"theme-name-dark": "Mørk",
|
||||
"theme-name-default": "Standard",
|
||||
"theme-name-light": "Lys",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Sort",
|
||||
"dark": "Mørk",
|
||||
"default": "Standard",
|
||||
"light": "Lys"
|
||||
}
|
||||
},
|
||||
"unknown device": "ukendt enhed",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} ønsker at dele mappen “{{folder}}”.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} ønsker at dele mappen “{{folderlabel}}” ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} vil muligvis genindføre denne enhed."
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"Allowed Networks": "Erlaubte Netzwerke",
|
||||
"Alphabetic": "Alphabetisch",
|
||||
"Altered by ignoring deletes.": "Weicht ab, weil Löschungen ignoriert werden.",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Die Versionskontrolle erfolgt über einen externen Befehl. Die Datei aus dem freigegebenen Ordner muss entfernen werden. Wenn der Pfad der Anwendung Leerzeichen enthält, sollte dieser in Anführungszeichen stehen.",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Die Versionierung erfolgt über einen externen Befehl. Er muss die Datei aus dem geteilten Ordner entfernen. Wenn der Pfad zur Anwendung Leerzeichen enthält, sollte er in Anführungszeichen gesetzt werden.",
|
||||
"Anonymous Usage Reporting": "Anonymer Nutzungsbericht",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Das Format des anonymen Nutzungsberichts hat sich geändert. Möchten Sie auf das neue Format umsteigen?",
|
||||
"Applied to LAN": "Angewendet auf LAN",
|
||||
@@ -46,7 +46,7 @@
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Die automatische Aktualisierung bietet jetzt die Wahl zwischen stabilen Veröffentlichungen und Veröffentlichungskandidaten.",
|
||||
"Automatic upgrades": "Automatische Aktualisierungen aktivieren",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "Automatische Upgrades sind für Veröffentlichungskandidaten immer aktiviert.",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "Automatisch Ordner erstellen oder freigeben, die dieses Gerät im Standardpfad ankündigt.",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "Automatisch Ordner im Standardpfad erstellen oder freigeben, die dieses Gerät ankündigt.",
|
||||
"Available debug logging facilities:": "Verfügbare Debugging-Möglichkeiten:",
|
||||
"Be careful!": "Vorsicht!",
|
||||
"Body:": "Nachrichtentext:",
|
||||
@@ -70,7 +70,7 @@
|
||||
"Connection Type": "Verbindungstyp",
|
||||
"Connections": "Verbindungen",
|
||||
"Connections via relays might be rate limited by the relay": "Verbindungen über Weiterleitungsserver können von diesen in der Geschwindigkeit begrenzt werden",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Kontinuierliche Änderungssuche ist jetzt in Syncthing verfügbar. Dadurch werden Änderungen auf der Festplatte erkannt und durch einen Scan nur die geänderten Pfade überprüft. Die Vorteile bestehen darin, dass Änderungen schneller festgestellt werden und weniger vollständige Scans erforderlich sind.",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Kontinuierliche Überwachung von Änderungen ist jetzt in Syncthing verfügbar. Dadurch werden Änderungen auf dem Datenträger erkannt und durch einen Scan nur die geänderten Pfade überprüft. Die Vorteile bestehen darin, dass Änderungen schneller festgestellt werden und weniger vollständige Scans erforderlich sind.",
|
||||
"Copied from elsewhere": "Von anderer Quelle kopiert",
|
||||
"Copied from original": "Vom Original kopiert",
|
||||
"Copied!": "Kopiert!",
|
||||
@@ -99,15 +99,16 @@
|
||||
"Device ID": "Gerätekennung",
|
||||
"Device Identification": "Geräteidentifikation",
|
||||
"Device Name": "Gerätename",
|
||||
"Device Status": "Gerätestatus",
|
||||
"Device is untrusted, enter encryption password": "Gerät wird nicht vertraut, Verschlüsselungspasswort eingeben",
|
||||
"Device rate limits": "Gerät Datenratelimit",
|
||||
"Device rate limits": "Datenratenbegrenzungen fürs Gerät",
|
||||
"Device that last modified the item": "Gerät, das das Element zuletzt geändert hat",
|
||||
"Devices": "Geräte",
|
||||
"Disable Crash Reporting": "Absturzmeldung deaktivieren",
|
||||
"Disabled": "Deaktiviert",
|
||||
"Disabled periodic scanning and disabled watching for changes": "Deaktivierter periodischer Scann und deaktivierter Überwachung von Änderungen",
|
||||
"Disabled periodic scanning and enabled watching for changes": "Deaktivierter periodischer Scann und aktivierter Überwachung von Änderungen",
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Deaktivierter periodischer Scann, fehlgeschlagene überprüfen auf Änderungen und erneuter versuch in 1 Min:",
|
||||
"Disabled periodic scanning and disabled watching for changes": "Periodischer Scan deaktiviert und Überwachung von Änderungen deaktiviert",
|
||||
"Disabled periodic scanning and enabled watching for changes": "Periodischer Scan deaktiviert und Überwachung von Änderungen aktiviert",
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Periodischer Scan deaktiviert und Überwachung von Änderungen fehlgeschlagen, erneuter Versuch jede Minute:",
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Deaktiviert Vergleich und Synchronisierung der Dateiberechtigungen. Dies ist hilfreich für Dateisysteme ohne konfigurierbare Berechtigungsparameter (z. B. FAT, exFAT, Synology, Android).",
|
||||
"Discard": "Verwerfen",
|
||||
"Disconnected": "Getrennt",
|
||||
@@ -121,7 +122,7 @@
|
||||
"Do not add it to the ignore list, so this notification may recur.": "Nicht zur Ignorierliste hinzufügen, diese Benachrichtigung kann erneut auftauchen.",
|
||||
"Do not restore": "Nicht wiederherstellen",
|
||||
"Do not restore all": "Nicht alle wiederherstellen",
|
||||
"Do you want to enable watching for changes for all your folders?": "Möchten Sie das nach Änderungen für alle Ihre Ordner gesucht wird aktivieren?",
|
||||
"Do you want to enable watching for changes for all your folders?": "Möchten Sie die Überwachung von Änderungen für all Ihre Ordner aktivieren?",
|
||||
"Documentation": "Dokumentation",
|
||||
"Download Rate": "Downloadrate",
|
||||
"Downloaded": "Heruntergeladen",
|
||||
@@ -153,7 +154,7 @@
|
||||
"Failed Items": "Fehlgeschlagene Elemente",
|
||||
"Failed to load file versions.": "Fehler beim Laden der Dateiversionen.",
|
||||
"Failed to load ignore patterns.": "Fehler beim Laden der Ignoriermuster.",
|
||||
"Failed to setup, retrying": "Fehler beim Installieren, versuche erneut",
|
||||
"Failed to setup, retrying": "Fehler beim Einrichten, erneuter Versuch",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Ein Verbindungsfehler zu IPv6-Servern ist zu erwarten, wenn es keine IPv6-Konnektivität gibt.",
|
||||
"File Pull Order": "Dateiübertragungsreihenfolge",
|
||||
"File Versioning": "Dateiversionierung",
|
||||
@@ -168,11 +169,12 @@
|
||||
"Folder ID": "Ordnerkennung",
|
||||
"Folder Label": "Ordnerbezeichnung",
|
||||
"Folder Path": "Ordnerpfad",
|
||||
"Folder Status": "Ordnerstatus",
|
||||
"Folder Type": "Ordnertyp",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Ordnertyp „{{receiveEncrypted}}“ kann nur beim Hinzufügen eines neuen Ordners festgelegt werden.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Der Ordnertyp „{{receiveEncrypted}}“ kann nach dem Hinzufügen nicht geändert werden. Sie müssen den Ordner entfernen, die Daten auf dem Speichermedium löschen oder entschlüsseln und anschließend den Ordner wieder neu hinzufügen.",
|
||||
"Folders": "Ordner",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "Bei den folgenden Ordnern ist ein Fehler aufgetreten, während Sie nach Änderungen suchten. Es wird jede Minute erneut gesucht, damit die Fehler bald verschwinden. Falls die Fehler bestehen bleiben, versuchen Sie, das zugrunde liegende Problem zu beheben, und fragen Sie evtl. nach Hilfe.",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "Bei den folgenden Ordnern ist die Überwachung von Änderungen fehlgeschlagen. Diese wird jede Minute erneut versucht, wodurch die Fehler bald verschwinden könnten. Falls diese fortbestehen, versuchen Sie, das zugrunde liegende Problem zu beheben und fragen Sie evtl. nach Hilfe.",
|
||||
"Forever": "Für immer",
|
||||
"Full Rescan Interval (s)": "Vollständiges Scanintervall (s)",
|
||||
"GUI": "GUI",
|
||||
@@ -203,7 +205,7 @@
|
||||
"Ignored Folders": "Ignorierte Ordner",
|
||||
"Ignored at": "Ignoriert am",
|
||||
"Included Software": "Beinhaltete Software",
|
||||
"Incoming Rate Limit (KiB/s)": "Eingehendes Datenratelimit (KiB/s)",
|
||||
"Incoming Rate Limit (KiB/s)": "Eingehende Datenratenbegrenzung (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Eine falsche Konfiguration kann den Ordnerinhalt beschädigen und Syncthing in einen unausführbaren Zustand versetzen.",
|
||||
"Incorrect user name or password.": "Falscher Benutzername oder Passwort.",
|
||||
"Internally used paths:": "Intern verwendete Pfade:",
|
||||
@@ -275,7 +277,7 @@
|
||||
"Options": "Optionen",
|
||||
"Out of Sync": "Nicht synchronisiert",
|
||||
"Out of Sync Items": "Nicht synchronisierte Elemente",
|
||||
"Outgoing Rate Limit (KiB/s)": "Ausgehendes Datenratelimit (KiB/s)",
|
||||
"Outgoing Rate Limit (KiB/s)": "Ausgehende Datenratenbegrenzung (KiB/s)",
|
||||
"Override": "Überschreiben",
|
||||
"Override Changes": "Änderungen überschreiben",
|
||||
"Ownership": "Besitzinformation",
|
||||
@@ -289,9 +291,9 @@
|
||||
"Paused": "Pausiert",
|
||||
"Paused (Unused)": "Pausiert (Nicht genutzt)",
|
||||
"Pending changes": "Ausstehende Änderungen",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Periodisches Scannen bei angegebenen Intervall und deaktivierter Überwachung von Änderungen",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Periodisches Scannen bei angegebenen Intervall und aktivierter Überwachung von Änderungen",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodisches Scannen bei angegebenen Intervall, fehlgeschlagene überprüfen auf Änderungen und erneuter versuch in 1 Min:",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Periodischer Scan im angegebenen Intervall und Überwachung von Änderungen deaktiviert",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Periodischer Scan im angegebenen Intervall und Überwachung von Änderungen aktiviert",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodischer Scan im angegebenen Intervall, Überwachung von Änderungen fehlgeschlagen, erneuter Versuch jede Minute:",
|
||||
"Permanently add it to the ignore list, suppressing further notifications.": "Permanent zur Ignorierliste hinzufügen, um weitere Benachrichtigungen zu unterdrücken.",
|
||||
"Please consult the release notes before performing a major upgrade.": "Bitte lesen Sie die Veröffentlichungshinweise bevor Sie eine Hauptversionsaktualisierung installieren.",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "Bitte lege einen Benutzer und ein Passwort für die Benutzeroberfläche in den Einstellungen fest.",
|
||||
@@ -304,7 +306,6 @@
|
||||
"QR code": "QR-Code",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "QUIC-Verbindungen sind in den meisten Fällen suboptimal",
|
||||
"Quick guide to supported patterns": "Schnellanleitung zu den unterstützten Mustern",
|
||||
"Random": "Zufall",
|
||||
"Receive Encrypted": "Empfange verschlüsselt",
|
||||
@@ -315,7 +316,7 @@
|
||||
"Relay LAN": "Weiterleitung LAN",
|
||||
"Relay WAN": "Weiterleitung WAN",
|
||||
"Release Notes": "Veröffentlichungshinweise",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Veröffentlichungskandidaten enthalten die neuesten Funktionen und Verbesserungen. Sie gleichen den üblichen zweiwöchentlichen Syncthing-Veröffentlichungen.",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Veröffentlichungskandidaten enthalten die neuesten Funktionen und Verbesserungen. Diese gleichen den üblichen zweiwöchentlichen Syncthing-Veröffentlichungen.",
|
||||
"Remote Devices": "Externe Geräte",
|
||||
"Remote GUI": "Entfernte Oberfläche",
|
||||
"Remove": "Entfernen",
|
||||
@@ -385,6 +386,7 @@
|
||||
"Staggered File Versioning": "Stufenweise Dateiversionierung",
|
||||
"Start Browser": "Browser starten",
|
||||
"Statistics": "Statistiken",
|
||||
"Stay logged in": "Angemeldet bleiben",
|
||||
"Stopped": "Gestoppt",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Speichert und synchronisiert nur verschlüsselte Daten. Ordner auf allen verbundenen Geräten müssen mit dem selben Passwort eingerichtet werden oder vom Typ „{{receiveEncrypted}}“ sein.",
|
||||
"Subject:": "Betreff:",
|
||||
@@ -406,7 +408,7 @@
|
||||
"Syncthing is saving changes.": "Syncthing speichert Änderungen.",
|
||||
"Syncthing is upgrading.": "Syncthing wird aktualisiert.",
|
||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing unterstützt jetzt automatische Absturzberichte an die Entwickler. Diese Funktion ist standardmäßig aktiviert.",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing scheint nicht erreichbar zu sein oder es gibt ein Problem mit deiner Internetverbindung. Versuche erneut…",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing scheint nicht erreichbar zu sein oder es gibt ein Problem mit der Internetverbindung. Erneuter Versuch …",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing scheint ein Problem mit der Verarbeitung Deiner Eingabe zu haben. Bitte lade die Seite neu oder führe einen Neustart durch, falls das Problem weiterhin besteht.",
|
||||
"TCP LAN": "TCP LAN",
|
||||
"TCP WAN": "TCP WAN",
|
||||
@@ -443,13 +445,13 @@
|
||||
"The number of versions must be a number and cannot be blank.": "Die Anzahl von Versionen muss eine Ganzzahl und darf nicht leer sein.",
|
||||
"The path cannot be blank.": "Der Pfad darf nicht leer sein.",
|
||||
"The rate limit is applied to the accumulated traffic of all connections to this device.": "Die Datenratenbegrenzung wird auf den gesamten Datenverkehr aller Verbindungen zu diesem Gerät angewendet.",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "Die Datenratenbegrenzung muss eine nicht-negative Zahl sein (0 = keine Begrenzung).",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "Die Datenratenbegrenzung muss eine nicht-negative Zahl sein (0 = keine Begrenzung)",
|
||||
"The remote device has not accepted sharing this folder.": "Dieser geteilte Ordner wurde vom Gerät nicht angenommen.",
|
||||
"The remote device has paused this folder.": "Dieser geteilte Ordner ist auf dem Gerät pausiert.",
|
||||
"The rescan interval must be a non-negative number of seconds.": "Das Scanintervall muss eine nicht negative Anzahl (in Sekunden) sein.",
|
||||
"There are no devices to share this folder with.": "Es gibt keine Geräte, mit denen dieser Ordner geteilt werden kann.",
|
||||
"There are no file versions to restore.": "Es gibt keine Dateiversionen zum Wiederherstellen.",
|
||||
"There are no folders to share with this device.": "Es gibt keine Ordner, die mit diesem Gerät geteilt werden können.",
|
||||
"The rescan interval must be a non-negative number of seconds.": "Das Scanintervall muss eine nicht-negative Zahl (in Sekunden) sein.",
|
||||
"There are no devices to share this folder with.": "Es sind keine Geräte vorhanden, mit denen dieser Ordner geteilt werden kann.",
|
||||
"There are no file versions to restore.": "Es sind keine Dateiversionen zum Wiederherstellen vorhanden.",
|
||||
"There are no folders to share with this device.": "Es sind keine Ordner vorhanden, die mit diesem Gerät geteilt werden können.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Sie werden automatisch heruntergeladen und werden synchronisiert, wenn der Fehler behoben wurde.",
|
||||
"This Device": "Dieses Gerät",
|
||||
"This Month": "Dieser Monat",
|
||||
@@ -506,9 +508,9 @@
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warnung, dieser Pfad ist ein übergeordneter Ordner eines existierenden Ordners „{{otherFolderLabel}}“ ({{otherFolder}}).",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Warnung, dieser Pfad ist ein Unterordner des existierenden Ordners „{{otherFolder}}“.",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warnung, dieser Pfad ist ein Unterordner eines existierenden Ordners „{{otherFolderLabel}}“ ({{otherFolder}}).",
|
||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Achtung: Wenn Sie einen externen Beobachter wie {{syncthingInotify}} benutzen, sollten sie sicher sein das dieser deaktiviert ist.",
|
||||
"Watch for Changes": "Auf Änderungen achten",
|
||||
"Watching for Changes": "Auf Änderungen achten",
|
||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Warnung: Wenn Sie ein externes Überwachungsprogramm wie {{syncthingInotify}} verwenden, sollten Sie sicherstellen, dass dieses deaktiviert ist.",
|
||||
"Watch for Changes": "Änderungen überwachen",
|
||||
"Watching for Changes": "Überwachung von Änderungen",
|
||||
"Watching for changes discovers most changes without periodic scanning.": "Das Überwachen von Änderungen entdeckt die meisten Änderungen ohne regelmäßiges Scannen.",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "Beachte beim Hinzufügen eines neuen Gerätes, dass dieses Gerät auch auf den anderen Geräten hinzugefügt werden muss.",
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Beachte bitte beim Hinzufügen eines neuen Ordners, dass die Ordnerkennung dazu verwendet wird, Ordner zwischen Geräten zu verbinden. Die Kennung muss also auf allen Geräten gleich sein, die Groß- und Kleinschreibung muss dabei beachtet werden.",
|
||||
@@ -516,12 +518,12 @@
|
||||
"Yes": "Ja",
|
||||
"Yesterday": "Gestern",
|
||||
"You can also copy and paste the text into a new message manually.": "Sie können den Text auch kopieren und manuell in eine neue Nachricht einfügen.",
|
||||
"You can also select one of these nearby devices:": "Sie können auch ein in der Nähe befindliches Geräte auswählen:",
|
||||
"You can also select one of these nearby devices:": "Sie können auch eines dieser in der Nähe befindlichen Geräte auswählen:",
|
||||
"You can change your choice at any time in the Settings dialog.": "Sie können Ihre Wahl jederzeit in den Einstellungen ändern.",
|
||||
"You can read more about the two release channels at the link below.": "Über den folgenden Link können Sie mehr über die zwei Veröffentlichungskanäle erfahren.",
|
||||
"You have no ignored devices.": "Sie haben keine ignorierten Geräte.",
|
||||
"You have no ignored folders.": "Sie haben keine ignorierten Ordner.",
|
||||
"You have unsaved changes. Do you really want to discard them?": "Sie haben nicht gespeicherte Änderungen. Wollen sie diese wirklich verwerfen?",
|
||||
"You have unsaved changes. Do you really want to discard them?": "Sie haben nicht gespeicherte Änderungen. Wollen Sie diese wirklich verwerfen?",
|
||||
"You must keep at least one version.": "Du musst mindestens eine Version behalten.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Sie sollten nie etwas im „{{receiveEncrypted}}“ Ordner lokal ändern oder hinzufügen.",
|
||||
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Ihre SMS-App sollte sich öffnen, damit Sie den Empfänger auswählen und die Nachricht von Ihrer eigenen Nummer aus versenden können.",
|
||||
@@ -538,10 +540,15 @@
|
||||
"modified": "geändert",
|
||||
"permit": "erlauben",
|
||||
"seconds": "Sekunden",
|
||||
"theme-name-black": "Schwarz",
|
||||
"theme-name-dark": "Dunkel",
|
||||
"theme-name-default": "Standard",
|
||||
"theme-name-light": "Hell",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Schwarz",
|
||||
"dark": "Dunkel",
|
||||
"default": "Standard",
|
||||
"light": "Hell"
|
||||
}
|
||||
},
|
||||
"unknown device": "unbekanntes Gerät",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} möchte den Ordner „{{folder}}“ teilen.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} möchte den Ordner „{{folderlabel}}“ ({{folder}}) teilen.",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} könnte dieses Gerät wieder einführen."
|
||||
|
||||
@@ -292,7 +292,6 @@
|
||||
"QR code": "QR code",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "QUIC connections are in most cases considered suboptimal",
|
||||
"Quick guide to supported patterns": "Quick guide to supported patterns",
|
||||
"Random": "Random",
|
||||
"Receive Encrypted": "Receive Encrypted",
|
||||
@@ -518,10 +517,14 @@
|
||||
"modified": "modified",
|
||||
"permit": "permit",
|
||||
"seconds": "seconds",
|
||||
"theme-name-black": "Black",
|
||||
"theme-name-dark": "Dark",
|
||||
"theme-name-default": "Default",
|
||||
"theme-name-light": "Light",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Black",
|
||||
"dark": "Dark",
|
||||
"default": "Default",
|
||||
"light": "Light"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
|
||||
@@ -304,7 +304,6 @@
|
||||
"QR code": "QR code",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "QUIC connections are in most cases considered suboptimal",
|
||||
"Quick guide to supported patterns": "Quick guide to supported patterns",
|
||||
"Random": "Random",
|
||||
"Receive Encrypted": "Receive Encrypted",
|
||||
@@ -538,10 +537,15 @@
|
||||
"modified": "modified",
|
||||
"permit": "permit",
|
||||
"seconds": "seconds",
|
||||
"theme-name-black": "Black",
|
||||
"theme-name-dark": "Dark",
|
||||
"theme-name-default": "Default",
|
||||
"theme-name-light": "Light",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Black",
|
||||
"dark": "Dark",
|
||||
"default": "Default",
|
||||
"light": "Light"
|
||||
}
|
||||
},
|
||||
"unknown device": "unknown device",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
"Device ID": "Device ID",
|
||||
"Device Identification": "Device Identification",
|
||||
"Device Name": "Device Name",
|
||||
"Device Status": "Device Status",
|
||||
"Device is untrusted, enter encryption password": "Device is untrusted, enter encryption password",
|
||||
"Device rate limits": "Device rate limits",
|
||||
"Device that last modified the item": "Device that last modified the item",
|
||||
@@ -168,6 +169,7 @@
|
||||
"Folder ID": "Folder ID",
|
||||
"Folder Label": "Folder Label",
|
||||
"Folder Path": "Folder Path",
|
||||
"Folder Status": "Folder Status",
|
||||
"Folder Type": "Folder Type",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Folder type \"{{receiveEncrypted}}\" can only be set when adding a new folder.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Folder type \"{{receiveEncrypted}}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.",
|
||||
@@ -304,7 +306,6 @@
|
||||
"QR code": "QR code",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "QUIC connections are in most cases considered suboptimal",
|
||||
"Quick guide to supported patterns": "Quick guide to supported patterns",
|
||||
"Random": "Random",
|
||||
"Receive Encrypted": "Receive Encrypted",
|
||||
@@ -385,6 +386,7 @@
|
||||
"Staggered File Versioning": "Staggered File Versioning",
|
||||
"Start Browser": "Start Browser",
|
||||
"Statistics": "Statistics",
|
||||
"Stay logged in": "Stay logged in",
|
||||
"Stopped": "Stopped",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{{receiveEncrypted}}\" too.",
|
||||
"Subject:": "Subject:",
|
||||
@@ -538,10 +540,15 @@
|
||||
"modified": "modified",
|
||||
"permit": "permit",
|
||||
"seconds": "seconds",
|
||||
"theme-name-black": "Black",
|
||||
"theme-name-dark": "Dark",
|
||||
"theme-name-default": "Default",
|
||||
"theme-name-light": "Light",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Black",
|
||||
"dark": "Dark",
|
||||
"default": "Default",
|
||||
"light": "Light"
|
||||
}
|
||||
},
|
||||
"unknown device": "unknown device",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wants to share folder \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} might reintroduce this device."
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
"Options": "Opcioj",
|
||||
"Out of Sync": "Elsinkronigita",
|
||||
"Out of Sync Items": "Elsinkronigitaj Eroj",
|
||||
"Outgoing Rate Limit (KiB/s)": " Eliranta Rapideco Limo (KiB/s)",
|
||||
"Outgoing Rate Limit (KiB/s)": "Eliranta Rapideco Limo (KiB/s)",
|
||||
"Override Changes": "Transpasi Ŝanĝojn",
|
||||
"Path": "Vojo",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Vojo de la dosierujo en la loka komputilo. Kreiĝos se ne ekzistas. La tilda signo (~) povas esti uzata kiel mallongigilo por",
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
"Device ID": "ID del Dispositivo",
|
||||
"Device Identification": "Identificación del Dispositivo",
|
||||
"Device Name": "Nombre del Dispositivo",
|
||||
"Device Status": "Estado del dispositivo",
|
||||
"Device is untrusted, enter encryption password": "El dispositivo no es de confianza, introduzca la contraseña de cifrado",
|
||||
"Device rate limits": "Límites de velocidad del dispositivo",
|
||||
"Device that last modified the item": "Dispositivo que modificó por última vez el ítem",
|
||||
@@ -168,6 +169,7 @@
|
||||
"Folder ID": "ID de carpeta",
|
||||
"Folder Label": "Etiqueta de la Carpeta",
|
||||
"Folder Path": "Ruta de la carpeta",
|
||||
"Folder Status": "Estado de la carpeta",
|
||||
"Folder Type": "Tipo de carpeta",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "El tipo de carpeta \"{{receiveEncrypted}}\" solo puede ser establecido al agregar una nueva carpeta.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "El tipo de carpeta \"{{receiveEncrypted}}\" no se puede cambiar después de añadir la carpeta. Es necesario eliminar la carpeta, borrar o descifrar los datos en el disco y volver a añadir la carpeta.",
|
||||
@@ -304,7 +306,6 @@
|
||||
"QR code": "Código QR",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "Las conexiones QUIC se consideran en la mayoría de los casos subóptimas",
|
||||
"Quick guide to supported patterns": "Guía rápida de patrones soportados",
|
||||
"Random": "Aleatorio",
|
||||
"Receive Encrypted": "Recibir Encriptado",
|
||||
@@ -385,6 +386,7 @@
|
||||
"Staggered File Versioning": "Versionado escalonado de fichero",
|
||||
"Start Browser": "Iniciar el navegador",
|
||||
"Statistics": "Estadísticas",
|
||||
"Stay logged in": "Permanecer conectado",
|
||||
"Stopped": "Detenido",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Almacena y sincroniza sólo los datos cifrados. Las carpetas de todos los dispositivos conectados deben estar configuradas con la misma contraseña o ser también del tipo \"{{receiveEncrypted}}\".",
|
||||
"Subject:": "Asunto:",
|
||||
@@ -507,7 +509,7 @@
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Peligro! Esta ruta es un subdirectorio de una carpeta ya existente llamada \"{{otherFolder}}\".",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Peligro, esta ruta es un subdirectorio de una carpeta ya existente llamada \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Advertencia: Si estás utilizando un observador externo como {{syncthingInotify}}, debes asegurarte de que está desactivado.",
|
||||
"Watch for Changes": "Vigila los cambios",
|
||||
"Watch for Changes": "Monitorear los cambios",
|
||||
"Watching for Changes": "Vigilando los cambios",
|
||||
"Watching for changes discovers most changes without periodic scanning.": "El control de cambios descubre la mayoría de cambios sin el escaneo periódico.",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "Cuando añada un nuevo dispositivo, tenga en cuenta que este debe añadirse también en el otro lado.",
|
||||
@@ -538,10 +540,15 @@
|
||||
"modified": "modificado",
|
||||
"permit": "permiso",
|
||||
"seconds": "segundos",
|
||||
"theme-name-black": "Negro",
|
||||
"theme-name-dark": "Oscuro",
|
||||
"theme-name-default": "Por Defecto",
|
||||
"theme-name-light": "Claro",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Negro",
|
||||
"dark": "Oscuro",
|
||||
"default": "Por Defecto",
|
||||
"light": "Claro"
|
||||
}
|
||||
},
|
||||
"unknown device": "dispositivo desconocido",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quiere compartir la carpeta \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quiere compartir la carpeta \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} puede reintroducir este dispositivo."
|
||||
|
||||
@@ -289,7 +289,7 @@
|
||||
"See external versioning help for supported templated command line parameters.": "Ikusi kanpoko bertsioen kudeatzailearen laguntza txantiloi bat erabiltzen duten komandoen lerro-parametroetarako.",
|
||||
"Select All": "Hautatu guztia",
|
||||
"Select a version": "Bertsio bat aukeratu",
|
||||
"Select additional devices to share this folder with.": " Tresna gehigarriak hauta itzazu partekatze honekin sinkronizatzeko ",
|
||||
"Select additional devices to share this folder with.": "Tresna gehigarriak hauta itzazu partekatze honekin sinkronizatzeko",
|
||||
"Select additional folders to share with this device.": "Aukeratu karpeta osagarriak gailu honekin partekatzeko.",
|
||||
"Select latest version": "Aukeratu azken bertsioa",
|
||||
"Select oldest version": "Aukeratu bertsio zaharrena",
|
||||
@@ -316,7 +316,7 @@
|
||||
"Size": "Tamaina",
|
||||
"Smallest First": "Ttipienak lehenik",
|
||||
"Some discovery methods could not be established for finding other devices or announcing this device:": "Aurkikuntza-metodo batzuk ezin izan dira ezarri beste gailu batzuk aurkitzeko edo gailu honen berri emateko:",
|
||||
"Some items could not be restored:": "Zenbat fitxategi ezin izan dira berreskuratu",
|
||||
"Some items could not be restored:": "Zenbat fitxategi ezin izan dira berreskuratu:",
|
||||
"Some listening addresses could not be enabled to accept connections:": "Entzuteko helbide batzuek ezin dituzte konexioak onartu:",
|
||||
"Source Code": "Iturri kodea",
|
||||
"Stable releases and release candidates": "iraunkor eta aintzin-bertsioak",
|
||||
|
||||
@@ -376,10 +376,14 @@
|
||||
"files": "tiedostot",
|
||||
"full documentation": "täysi dokumentaatio",
|
||||
"items": "kohteet",
|
||||
"theme-name-black": "Musta",
|
||||
"theme-name-dark": "Tumma",
|
||||
"theme-name-default": "Oletus",
|
||||
"theme-name-light": "Vaalea",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Musta",
|
||||
"dark": "Tumma",
|
||||
"default": "Oletus",
|
||||
"light": "Vaalea"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} haluaa jakaa kansion \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} haluaa jakaa kansion \"{{folderlabel}}\" ({{folder}})."
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"Automatic upgrades": "Mises à jour automatiques",
|
||||
"Be careful!": "Faites attention !",
|
||||
"Changelog": "Historique des versions",
|
||||
"Clean out after": "Purger après :",
|
||||
"Clean out after": "Purger après",
|
||||
"Close": "Fermer",
|
||||
"Command": "Commande",
|
||||
"Comment, when used at the start of a line": "Commentaire lorsque utilisé en début de ligne",
|
||||
|
||||
@@ -225,7 +225,7 @@
|
||||
"Limit": "Limite",
|
||||
"Listener Failures": "Échecs de l'écouteur",
|
||||
"Listener Status": "État de l'écouteur",
|
||||
"Listeners": "Systèmes en écoute",
|
||||
"Listeners": "Écoute",
|
||||
"Loading data...": "Chargement des données...",
|
||||
"Loading...": "Chargement...",
|
||||
"Local Additions": "Ajouts locaux",
|
||||
@@ -304,7 +304,6 @@
|
||||
"QR code": "Code QR",
|
||||
"QUIC LAN": "LAN QUIC",
|
||||
"QUIC WAN": "WAN QUIC",
|
||||
"QUIC connections are in most cases considered suboptimal": "Les connexions QUIC sont généralement peu performantes",
|
||||
"Quick guide to supported patterns": "Guide rapide des masques compatibles ci-dessous",
|
||||
"Random": "Aléatoire",
|
||||
"Receive Encrypted": "Réception chiffrée",
|
||||
@@ -442,7 +441,7 @@
|
||||
"The number of old versions to keep, per file.": "Nombre maximal d'anciennes versions à conserver indéfiniment, par fichier.",
|
||||
"The number of versions must be a number and cannot be blank.": "Le nombre de versions doit être numérique, et ne peut pas être vide.",
|
||||
"The path cannot be blank.": "Le chemin ne peut pas être vide.",
|
||||
"The rate limit is applied to the accumulated traffic of all connections to this device.": "La limite de taux s'applique au trafic cumulé des connexions à notre appareil.",
|
||||
"The rate limit is applied to the accumulated traffic of all connections to this device.": "Les limites s'appliquent au trafic cumulé des connexions à notre appareil (0 = pas de limite).",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "La limite de débit ne doit pas être négative (0 = pas de limite)",
|
||||
"The remote device has not accepted sharing this folder.": "L'appareil distant n'a pas (encore ?) accepté de partager ce répertoire.",
|
||||
"The remote device has paused this folder.": "L'appareil distant a mis ce partage en pause.",
|
||||
@@ -538,10 +537,14 @@
|
||||
"modified": "modifié",
|
||||
"permit": "partager tous les attributs",
|
||||
"seconds": "secondes",
|
||||
"theme-name-black": "Noir",
|
||||
"theme-name-dark": "Sombre",
|
||||
"theme-name-default": "Par défaut (système)",
|
||||
"theme-name-light": "Clair",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Noir",
|
||||
"dark": "Sombre",
|
||||
"default": "Par défaut (système)",
|
||||
"light": "Clair"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vous invite au partage \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vous invite au partage \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} pourrait ré-enrôler cet appareil."
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"Default Ignore Patterns": "Standert Negearpatroanen",
|
||||
"Defaults": "Standertwearden",
|
||||
"Delete": "Fuortsmite",
|
||||
"Delete Unexpected Items": " Unferwachte items wiskje",
|
||||
"Delete Unexpected Items": "Unferwachte items wiskje",
|
||||
"Deleted {%file%}": "{{file}} is fuortsmiten",
|
||||
"Deselect All": "Alles Deselektearje",
|
||||
"Deselect devices to stop sharing this folder with.": "Kies de apparaten om dizze map net langer mei te dielen.",
|
||||
@@ -170,7 +170,7 @@
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Lykwols, jo aktuele ynstellings litte sjen dat jo it miskien net oan sette wol. Wy hawwe automatysk rapportearjen fan fêstrinnen foar jo útsetten.",
|
||||
"Identification": "Identifikaasje",
|
||||
"If untrusted, enter encryption password": "As net fertroud, fier dan fersiferingswachtwurd yn",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "As jo wolle foarkomme dat oare brûkers op dizze kompjûter tagong krije ta Syncthing en dêrtroch jo bestannen, oerweeg dan it ynstellen fan ferifikaasje.",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "As jo wolle foarkomme dat oare brûkers op dizze kompjûter tagong krije ta Syncthing en dêrtroch jo bestannen, oerweeg dan it ynstellen fan ferifikaasje.",
|
||||
"Ignore": "Negearje",
|
||||
"Ignore Patterns": "Negear-patroanen",
|
||||
"Ignore Permissions": "Negear-rjochten",
|
||||
@@ -305,7 +305,7 @@
|
||||
"Single level wildcard (matches within a directory only)": "Inkel-nivo jokerteken (wildcard) (fergeliket allinnich binnen in map)",
|
||||
"Size": "Grutte",
|
||||
"Smallest First": "Lytste earst",
|
||||
"Some items could not be restored:": "Guon uûnderdielen koenen net tebeksetten wurde.",
|
||||
"Some items could not be restored:": "Guon uûnderdielen koenen net tebeksetten wurde:",
|
||||
"Source Code": "Boarnekoade",
|
||||
"Stable releases and release candidates": "Stabyle ferzjes en ferzje kanditaten",
|
||||
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabyle ferzjes wurde likernôch twa wiken útstelt. Yn die tiid wurde se testen as ferzje kandidaten.",
|
||||
|
||||
@@ -193,7 +193,7 @@
|
||||
"Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.": "Mellőzési minták csak a mappa létrehozása után adhatók hozzá. Bejelölve egy beviteli mező fog megjelenni mentés után a mellőzési minták számára.",
|
||||
"Ignored Devices": "Mellőzött eszközök",
|
||||
"Ignored Folders": "Mellőzött mappák",
|
||||
"Ignored at": "Mellőzve:",
|
||||
"Ignored at": "Mellőzve",
|
||||
"Included Software": "Felhasznált szoftver",
|
||||
"Incoming Rate Limit (KiB/s)": "Bejövő sebességkorlát (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Helytelen konfiguráció esetén károsodhat a mappák tartalma és működésképtelenné válhat a Syncthing.",
|
||||
@@ -284,7 +284,6 @@
|
||||
"QR code": "QR-kód",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "A QUIC-kapcsolatok a legtöbb esetben nem tekinthetők optimálisnak",
|
||||
"Quick guide to supported patterns": "Rövid útmutató a használható mintákról",
|
||||
"Random": "Véletlenszerű",
|
||||
"Receive Encrypted": "Titkosított fogadás",
|
||||
@@ -507,10 +506,14 @@
|
||||
"items": "elem",
|
||||
"modified": "módosított",
|
||||
"seconds": "másodperc",
|
||||
"theme-name-black": "Fekete",
|
||||
"theme-name-dark": "Sötét",
|
||||
"theme-name-default": "Alapértelmezett",
|
||||
"theme-name-light": "Világos",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Fekete",
|
||||
"dark": "Sötét",
|
||||
"default": "Alapértelmezett",
|
||||
"light": "Világos"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} szeretné megosztani a mappát: „{{folder}}”.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} szeretné megosztani a mappát: „{{folderlabel}}” ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} újra bevezetheti ezt az eszközt."
|
||||
|
||||
@@ -452,10 +452,14 @@
|
||||
"full documentation": "dokumentasi penuh",
|
||||
"items": "berkas",
|
||||
"seconds": "detik",
|
||||
"theme-name-black": "Hitam",
|
||||
"theme-name-dark": "Gelap",
|
||||
"theme-name-default": "Bawaan",
|
||||
"theme-name-light": "Terang",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Hitam",
|
||||
"dark": "Gelap",
|
||||
"default": "Bawaan",
|
||||
"light": "Terang"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} ingin berbagi folder \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} ingin berbagi folder \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} mungkin memperkenalkan ulang perangkat ini."
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"Are you sure you want to restore {%count%} files?": "Sei sicuro di voler ripristinare {{count}} file?",
|
||||
"Are you sure you want to revert all local changes?": "Sei sicuro di voler ripristinare tutte le modifiche locali?",
|
||||
"Are you sure you want to upgrade?": "Sei sicuro di voler aggiornare?",
|
||||
"Authentication Required": "Autenticazione richiesta",
|
||||
"Authors": "Autori",
|
||||
"Auto Accept": "Accettazione Automatica",
|
||||
"Automatic Crash Reporting": "Segnalazione Automatica degli arresti anomali",
|
||||
@@ -98,6 +99,7 @@
|
||||
"Device ID": "ID Dispositivo",
|
||||
"Device Identification": "Identificazione Dispositivo",
|
||||
"Device Name": "Nome Dispositivo",
|
||||
"Device Status": "Stato Dispositivo",
|
||||
"Device is untrusted, enter encryption password": "Il dispositivo non è attendibile, inserisci la password di crittografia",
|
||||
"Device rate limits": "Limiti di velocità del dispositivo",
|
||||
"Device that last modified the item": "Dispositivo che ha modificato l'elemento per ultimo",
|
||||
@@ -167,6 +169,7 @@
|
||||
"Folder ID": "ID Cartella",
|
||||
"Folder Label": "Etichetta per la Cartella",
|
||||
"Folder Path": "Percorso Cartella",
|
||||
"Folder Status": "Stato Cartella",
|
||||
"Folder Type": "Tipo di Cartella",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Il tipo di cartella \"{{receiveEncrypted}}\" può essere impostata solo quando si aggiunge una nuova cartella.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Il tipo di cartella \"{{receiveEncrypted}}\" non può essere modificato dopo aver aggiunto la cartella. È necessario rimuovere la cartella, eliminare o decrittografare i dati sul disco e aggiungere nuovamente la cartella.",
|
||||
@@ -204,6 +207,7 @@
|
||||
"Included Software": "Software Incluso",
|
||||
"Incoming Rate Limit (KiB/s)": "Limite Velocità in Ingresso (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Una configurazione non corretta potrebbe danneggiare il contenuto delle cartelle e rendere Syncthing inoperativo.",
|
||||
"Incorrect user name or password.": "Nome utente o password errati.",
|
||||
"Internally used paths:": "Percorsi interni utilizzati:",
|
||||
"Introduced By": "Introdotto da",
|
||||
"Introducer": "Introduttore",
|
||||
@@ -233,7 +237,12 @@
|
||||
"Locally Changed Items": "Elementi modificati in locale",
|
||||
"Log": "Log",
|
||||
"Log File": "File Log",
|
||||
"Log In": "Login",
|
||||
"Log Out": "Disconnetti",
|
||||
"Log in to see paths information.": "Accedere per visualizzare le informazioni sui percorsi.",
|
||||
"Log in to see version information.": "Accedere per visualizzare le informazioni sulla versione.",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Visualizzazione log in pausa. Scorri fino in fondo per continuare.",
|
||||
"Login failed, see Syncthing logs for details.": "Accesso non riuscito, vedi i log di Syncthing per i dettagli.",
|
||||
"Logs": "Log",
|
||||
"Major Upgrade": "Aggiornamento Principale",
|
||||
"Mass actions": "Azioni di massa",
|
||||
@@ -272,6 +281,7 @@
|
||||
"Override": "Sovrascivere",
|
||||
"Override Changes": "Ignora le Modifiche",
|
||||
"Ownership": "Proprietà",
|
||||
"Password": "Password",
|
||||
"Path": "Percorso",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Percorso della cartella nel computer locale. Verrà creata se non esiste già. Il carattere tilde (~) può essere utilizzato come scorciatoia per",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Percorso di salvataggio delle versioni (lasciare vuoto per utilizzare la cartella predefinita .stversions in questa cartella).",
|
||||
@@ -296,7 +306,6 @@
|
||||
"QR code": "Codice QR",
|
||||
"QUIC LAN": "LAN QUIC",
|
||||
"QUIC WAN": "WAN QUIC",
|
||||
"QUIC connections are in most cases considered suboptimal": "Le connessioni QUIC sono nella maggior parte dei casi considerate non ottimali",
|
||||
"Quick guide to supported patterns": "Guida veloce agli schemi supportati",
|
||||
"Random": "Casuale",
|
||||
"Receive Encrypted": "Ricevi crittografato",
|
||||
@@ -479,6 +488,7 @@
|
||||
"Usage reporting is always enabled for candidate releases.": "Le segnalazioni di utilizzo sono sempre abilitate le versioni candidate al rilascio.",
|
||||
"Use HTTPS for GUI": "Utilizza HTTPS per l'interfaccia grafica",
|
||||
"Use notifications from the filesystem to detect changed items.": "Usa le notifiche dal filesystem per rilevare gli elementi modificati.",
|
||||
"User": "Utente",
|
||||
"User Home": "Home Utente",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Utente/password non sono stati impostati per autenticazione GUI. Considerane la configurazione.",
|
||||
"Using a QUIC connection over LAN": "Utilizza una connessione QUIC su LAN",
|
||||
@@ -529,10 +539,15 @@
|
||||
"modified": "modificato",
|
||||
"permit": "permettere",
|
||||
"seconds": "secondi",
|
||||
"theme-name-black": "Nero",
|
||||
"theme-name-dark": "Scuro",
|
||||
"theme-name-default": "Predefinito",
|
||||
"theme-name-light": "Chiaro",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Nero",
|
||||
"dark": "Scuro",
|
||||
"default": "Predefinito",
|
||||
"light": "Chiaro"
|
||||
}
|
||||
},
|
||||
"unknown device": "dispositivo sconosciuto",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vuole condividere la cartella \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vuole condividere la cartella \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} potrebbe reintrodurre questo dispositivo."
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"Are you sure you want to restore {%count%} files?": "{{count}} ファイルを復元してもよろしいですか?",
|
||||
"Are you sure you want to revert all local changes?": "ローカルでの変更をすべて取り消してもよろしいですか?",
|
||||
"Are you sure you want to upgrade?": "アップグレードしてよろしいですか?",
|
||||
"Authentication Required": "認証が必要です",
|
||||
"Authors": "作者",
|
||||
"Auto Accept": "自動承諾",
|
||||
"Automatic Crash Reporting": "自動クラッシュレポート",
|
||||
@@ -71,6 +72,7 @@
|
||||
"Default Configuration": "デフォルトの設定",
|
||||
"Default Device": "デフォルトのデバイス",
|
||||
"Default Folder": "デフォルトのフォルダー",
|
||||
"Default Ignore Patterns": "デフォルトの無視パターン",
|
||||
"Defaults": "デフォルト",
|
||||
"Delete": "削除",
|
||||
"Delete Unexpected Items": "予期しないアイテムを削除",
|
||||
@@ -128,6 +130,8 @@
|
||||
"External": "外部",
|
||||
"External File Versioning": "外部バージョン管理",
|
||||
"Failed Items": "失敗した項目",
|
||||
"Failed to load file versions.": "ファイルバージョンの読み込みに失敗しました。",
|
||||
"Failed to load ignore patterns.": "無視パターンの読み込みに失敗しました。",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "お使いのネットワークがIPv6を利用していない場合、IPv6のサーバーへの接続に失敗しても異常ではありません。",
|
||||
"File Pull Order": "ファイルを取得する順序",
|
||||
"File Versioning": "ファイルのバージョン管理",
|
||||
@@ -369,6 +373,8 @@
|
||||
"The remote device has paused this folder.": "接続先デバイスはこのフォルダーを一時停止中です。",
|
||||
"The rescan interval must be a non-negative number of seconds.": "再スキャン間隔は0秒以上で指定してください。",
|
||||
"There are no devices to share this folder with.": "どのデバイスともフォルダーを共有していません。",
|
||||
"There are no file versions to restore.": "復元するファイルバージョンはありません。",
|
||||
"There are no folders to share with this device.": "このデバイスと共有するフォルダはありません。",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "エラーが解決すると、自動的に再試行され同期されます。",
|
||||
"This Device": "このデバイス",
|
||||
"This Month": "今月",
|
||||
@@ -432,6 +438,7 @@
|
||||
"You must keep at least one version.": "少なくとも一つのバージョンを保持する必要があります。",
|
||||
"days": "日",
|
||||
"deleted": "削除",
|
||||
"deny": "拒否",
|
||||
"directories": "個のディレクトリ",
|
||||
"file": "個のファイル",
|
||||
"files": "個のファイル",
|
||||
@@ -439,11 +446,17 @@
|
||||
"full documentation": "詳細なマニュアル",
|
||||
"items": "項目",
|
||||
"modified": "更新",
|
||||
"permit": "許可",
|
||||
"seconds": "秒",
|
||||
"theme-name-black": "ブラック",
|
||||
"theme-name-dark": "ダーク",
|
||||
"theme-name-default": "デフォルト",
|
||||
"theme-name-light": "ライト",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "ブラック",
|
||||
"dark": "ダーク",
|
||||
"default": "デフォルト",
|
||||
"light": "ライト"
|
||||
}
|
||||
},
|
||||
"unknown device": "不明なデバイス",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} がフォルダー \"{{folder}}\" を共有するよう求めています。",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} がフォルダー「{{folderlabel}}」 ({{folder}}) を共有するよう求めています。"
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
"Device ID": "기기 식별자",
|
||||
"Device Identification": "기기 식별자",
|
||||
"Device Name": "기기명",
|
||||
"Device Status": "기기 상태",
|
||||
"Device is untrusted, enter encryption password": "신뢰하지 않는 기기입니다; 암호화 비밀번호를 입력하십시오",
|
||||
"Device rate limits": "기기 속도 제한",
|
||||
"Device that last modified the item": "항목을 가장 최근에 수정한 기기",
|
||||
@@ -168,6 +169,7 @@
|
||||
"Folder ID": "폴더 식별자",
|
||||
"Folder Label": "폴더명",
|
||||
"Folder Path": "폴더 경로",
|
||||
"Folder Status": "폴더 상태",
|
||||
"Folder Type": "폴더 유형",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "\"{{receiveEncrypted}}\" 폴더 유형은 새 폴더를 추가할 때만 설정할 수 있습니다.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "\"{{receiveEncrypted}}\" 폴더 유형은 폴더를 추가한 후에 변경할 수 없습니다. 폴더를 먼저 삭제하고, 저장 장치에 있는 데이터를 삭제 또는 해독한 다음에 폴더를 다시 추가하십시오.",
|
||||
@@ -304,7 +306,6 @@
|
||||
"QR code": "QR 코드",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "QUIC을 통한 연결은 대부분의 경우에 최적이지 않답니다.",
|
||||
"Quick guide to supported patterns": "지원하는 양식에 대한 빠른 도움말",
|
||||
"Random": "무작위",
|
||||
"Receive Encrypted": "암호화 수신",
|
||||
@@ -538,10 +539,15 @@
|
||||
"modified": "수정됨",
|
||||
"permit": "허용",
|
||||
"seconds": "초",
|
||||
"theme-name-black": "검은색",
|
||||
"theme-name-dark": "어두운 색",
|
||||
"theme-name-default": "기본 색",
|
||||
"theme-name-light": "밝은 색",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "검은색",
|
||||
"dark": "어두운 색",
|
||||
"default": "기본 색",
|
||||
"light": "밝은 색"
|
||||
}
|
||||
},
|
||||
"unknown device": "알 수 없는 기기",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 기기가 \"{{folder}}\" 폴더를 공유하길 원합니다.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} 기기가 \"{{folderlabel}}\" ({{folder}}) 폴더를 공유하길 원합니다.",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} 기기에서 이 기기를 다시 소개할 수 있습니다."
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"Add filter entry": "Filter-item toevoegen",
|
||||
"Add ignore patterns": "Negeerpatronen toevoegen",
|
||||
"Add new folder?": "Nieuwe map toevoegen?",
|
||||
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Het interval van volledige scan zal daarbij ook vergroot worden (maal 60, dit is een nieuwe standaardwaarde van 1 uur). U kunt het later voor elke map manueel configureren nadat u nee kiest.",
|
||||
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Het interval van volledige scan zal daarbij ook vergroot worden (maal 60, dit is een nieuwe standaardwaarde van 1 uur). Je kunt het later voor elke map manueel configureren nadat je nee kiest.",
|
||||
"Address": "Adres",
|
||||
"Addresses": "Adressen",
|
||||
"Advanced": "Geavanceerd",
|
||||
@@ -29,21 +29,21 @@
|
||||
"Altered by ignoring deletes.": "Veranderd door het negeren van verwijderingen.",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Een externe opdracht regelt het versiebeheer. Hij moet het bestand verwijderen uit de gedeelde map. Als het pad naar de toepassing spaties bevat, moet dit tussen aanhalingstekens geplaatst worden.",
|
||||
"Anonymous Usage Reporting": "Anonieme gebruikersstatistieken",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Het formaat voor anonieme gebruikersrapporten is gewijzigd. Wilt u naar het nieuwe formaat overschakelen?",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Het formaat voor anonieme gebruikersrapporten is gewijzigd. Wil je naar het nieuwe formaat overschakelen?",
|
||||
"Applied to LAN": "Toegepast op LAN",
|
||||
"Apply": "Toepassen",
|
||||
"Are you sure you want to override all remote changes?": "Weet u zeker dat u alle externe wijzigingen wilt overschrijven?",
|
||||
"Are you sure you want to permanently delete all these files?": "Weet u zeker dat u al deze bestanden permanent wilt verwijderen?",
|
||||
"Are you sure you want to remove device {%name%}?": "Weet u zeker dat u apparaat {{name}} wilt verwijderen?",
|
||||
"Are you sure you want to remove folder {%label%}?": "Weet u zeker dat u map {{label}} wilt verwijderen?",
|
||||
"Are you sure you want to restore {%count%} files?": "Weet u zeker dat u {{count}} bestanden wilt herstellen?",
|
||||
"Are you sure you want to revert all local changes?": "Weet u zeker dat u alle lokale wijzigingen wilt terugdraaien?",
|
||||
"Are you sure you want to upgrade?": "Weet u zeker dat u wilt bijwerken?",
|
||||
"Are you sure you want to override all remote changes?": "Weet je zeker dat je alle externe wijzigingen wil overschrijven?",
|
||||
"Are you sure you want to permanently delete all these files?": "Weet je zeker dat je al deze bestanden permanent wil verwijderen?",
|
||||
"Are you sure you want to remove device {%name%}?": "Weet je zeker dat je apparaat {{name}} wilt verwijderen?",
|
||||
"Are you sure you want to remove folder {%label%}?": "Weet je zeker dat je map {{label}} wil verwijderen?",
|
||||
"Are you sure you want to restore {%count%} files?": "Weet je zeker dat je {{count}} bestanden wil herstellen?",
|
||||
"Are you sure you want to revert all local changes?": "Weet je zeker dat je alle lokale wijzigingen wil terugdraaien?",
|
||||
"Are you sure you want to upgrade?": "Weet je zeker dat je wil bijwerken?",
|
||||
"Authentication Required": "Authenticatie vereist",
|
||||
"Authors": "Auteurs",
|
||||
"Auto Accept": "Automatisch aanvaarden",
|
||||
"Automatic Crash Reporting": "Automatische crashrapportage",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatisch bijwerken biedt nu de keuze tussen stabiele releases en release canditates.",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automatisch bijwerken biedt nu de keuze tussen stabiele releases en release kandidaten.",
|
||||
"Automatic upgrades": "Automatische upgrades",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "Automatische upgrades zijn altijd ingeschakeld voor kandidaat-releases.",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "Automatisch mappen die dit apparaat aankondigt aanmaken of delen op de standaardlocatie.",
|
||||
@@ -99,6 +99,7 @@
|
||||
"Device ID": "Apparaat-ID",
|
||||
"Device Identification": "Apparaat-identificatie",
|
||||
"Device Name": "Naam apparaat",
|
||||
"Device Status": "Apparaatstatus",
|
||||
"Device is untrusted, enter encryption password": "Apparaat wordt niet vertrouwd. Versleutelingswachtwoord opgeven",
|
||||
"Device rate limits": "Snelheidsbegrenzingen apparaat",
|
||||
"Device that last modified the item": "Apparaat dat het item laatst gewijzigd heeft",
|
||||
@@ -121,7 +122,7 @@
|
||||
"Do not add it to the ignore list, so this notification may recur.": "Niet toevoegen aan de negeerlijst zodat deze melding kan terugkomen.",
|
||||
"Do not restore": "Niet herstellen",
|
||||
"Do not restore all": "Niet alles herstellen",
|
||||
"Do you want to enable watching for changes for all your folders?": "Wilt u het opvolgen van wijzigingen voor al uw mappen inschakelen?",
|
||||
"Do you want to enable watching for changes for all your folders?": "Wil je het opvolgen van wijzigingen voor al je mappen inschakelen?",
|
||||
"Documentation": "Documentatie",
|
||||
"Download Rate": "Downloadsnelheid",
|
||||
"Downloaded": "Gedownload",
|
||||
@@ -168,11 +169,12 @@
|
||||
"Folder ID": "Map-ID",
|
||||
"Folder Label": "Maplabel",
|
||||
"Folder Path": "Maplocatie",
|
||||
"Folder Status": "Mapstatus",
|
||||
"Folder Type": "Soort map",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Maptype \"{{receiveEncrypted}}\" kan alleen ingesteld worden bij het toevoegen van een nieuwe map.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Maptype \"{{receiveEncrypted}}\" kan niet gewijzigd worden na het toevoegen van de map. U moet de map verwijderen, de gegevens op schijf verwijderen of ontsleutelen en daarna de map opnieuw toevoegen.",
|
||||
"Folders": "Mappen",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "Voor de volgende mappen trad een fout op tijdens het beginnen opvolgen van wijzigingen. Er zal elke minuut opnieuw geprobeerd worden, dus de fouten kunnen snel verdwijnen. Als ze blijven voorkomen, probeer dan het onderliggende probleem op te lossen en vraag om hulp als u dat niet kunt.",
|
||||
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "Voor de volgende mappen trad een fout op tijdens het beginnen opvolgen van wijzigingen. Er zal elke minuut opnieuw geprobeerd worden, dus de fouten kunnen snel verdwijnen. Als ze blijven voorkomen, probeer dan het onderliggende probleem op te lossen en vraag om hulp als je dat niet kunt.",
|
||||
"Forever": "Voor altijd",
|
||||
"Full Rescan Interval (s)": "Interval voor volledig opnieuw scannen (s)",
|
||||
"GUI": "GUI",
|
||||
@@ -191,10 +193,10 @@
|
||||
"Help": "Help",
|
||||
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Tip: er zijn alleen weigeren-regels gedetecteerd terwijl de standaardwaarde weigeren is. Overweeg \"om het even welke toestaan\" toe te voegen als laatste regel.",
|
||||
"Home page": "Startpagina",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Uw huidige instellingen geven echter aan dat u het misschien niet wilt inschakelen. We hebben de automatische crashrapportage voor u uitgeschakeld.",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Jouw huidige instellingen geven echter aan dat je het misschien niet wil inschakelen. We hebben de automatische crashrapportage voor je uitgeschakeld.",
|
||||
"Identification": "Identificatie",
|
||||
"If untrusted, enter encryption password": "Indien niet vertrouwd, versleutelingswachtwoord opgeven",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Als u wilt voorkomen dat andere gebruikers op deze computer toegang krijgen tot Syncthing en daardoor uw bestanden, kunt u overwegen om authenticatie in te stellen.",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Als je wil voorkomen dat andere gebruikers op deze computer toegang krijgen tot Syncthing en daardoor jouw bestanden, kun je overwegen om authenticatie in te stellen.",
|
||||
"Ignore": "Negeren",
|
||||
"Ignore Patterns": "Negeerpatronen",
|
||||
"Ignore Permissions": "Machtigingen negeren",
|
||||
@@ -204,7 +206,7 @@
|
||||
"Ignored at": "Genegeerd op",
|
||||
"Included Software": "Bijgevoegde software",
|
||||
"Incoming Rate Limit (KiB/s)": "Begrenzing downloadsnelheid (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Verkeerde configuratie kan de inhoud van uw map beschadigen en Syncthing onbruikbaar maken.",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Verkeerde configuratie kan de inhoud van je map beschadigen en Syncthing onbruikbaar maken.",
|
||||
"Incorrect user name or password.": "Onjuiste gebruikersnaam of wachtwoord.",
|
||||
"Internally used paths:": "Intern gebruikte paden:",
|
||||
"Introduced By": "Geïntroduceerd door",
|
||||
@@ -304,7 +306,6 @@
|
||||
"QR code": "QR-code",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "QUIC-verbindingen worden in de meeste gevallen als suboptimaal beschouwd",
|
||||
"Quick guide to supported patterns": "Snelgids voor ondersteunde patronen",
|
||||
"Random": "Willekeurig",
|
||||
"Receive Encrypted": "Versleuteld ontvangen",
|
||||
@@ -399,15 +400,15 @@
|
||||
"Syncthing has been shut down.": "Syncthing werd afgesloten.",
|
||||
"Syncthing includes the following software or portions thereof:": "Syncthing bevat de volgende software of delen daarvan:",
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is gratis en opensource software onder licentie van MPL v2.0.",
|
||||
"Syncthing is a continuous file synchronization program. It synchronizes files between two or more computers in real time, safely protected from prying eyes. Your data is your data alone and you deserve to choose where it is stored, whether it is shared with some third party, and how it's transmitted over the internet.": "Syncthing is een doorlopend bestandssynchronisatieprogramma. Het synchroniseert bestanden tussen twee of meer computers in realtime, veilig beschermd tegen nieuwsgierige ogen. Uw gegevens zijn uw gegevens alleen en u verdient het om te kiezen waar ze worden opgeslagen, of ze worden gedeeld met een derde partij, en hoe ze worden verzonden via het internet.",
|
||||
"Syncthing is a continuous file synchronization program. It synchronizes files between two or more computers in real time, safely protected from prying eyes. Your data is your data alone and you deserve to choose where it is stored, whether it is shared with some third party, and how it's transmitted over the internet.": "Syncthing is een doorlopend bestandssynchronisatieprogramma. Het synchroniseert bestanden tussen twee of meer computers in realtime, veilig beschermd tegen nieuwsgierige ogen. Jouw gegevens zijn jouw gegevens alleen en je verdient het om te kiezen waar ze worden opgeslagen, of ze worden gedeeld met een derde partij, en hoe ze worden verzonden via het internet.",
|
||||
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing luistert op de volgende netwerkadressen naar verbindingspogingen van andere apparaten:",
|
||||
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing luistert op geen enkel adres naar verbindingspogingen van andere apparaten. Alleen uitgaande verbindingen vanaf dit apparaat zouden kunnen werken.",
|
||||
"Syncthing is restarting.": "Syncthing wordt opnieuw gestart.",
|
||||
"Syncthing is saving changes.": "Syncthing slaat de wijzigingen op.",
|
||||
"Syncthing is upgrading.": "Syncthing is aan het bijwerken.",
|
||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ondersteunt nu automatisch rapporteren van crashes naar de ontwikkelaars. De functie is standaard ingeschakeld.",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing lijkt gestopt te zijn, of er is een probleem met uw internetverbinding. Opnieuw proberen…",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing lijkt een probleem te ondervinden met het verwerken van uw verzoek. Vernieuw de pagina of start Syncthing opnieuw als het probleem zich blijft voordoen.",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing lijkt gestopt te zijn, of er is een probleem met de internetverbinding. Opnieuw proberen…",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing lijkt een probleem te ondervinden met het verwerken van je verzoek. Vernieuw de pagina of start Syncthing opnieuw als het probleem zich blijft voordoen.",
|
||||
"TCP LAN": "TCP LAN",
|
||||
"TCP WAN": "TCP WAN",
|
||||
"Take me back": "Terugkeren",
|
||||
@@ -453,13 +454,13 @@
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Ze worden automatisch opnieuw geprobeerd en zullen gesynchroniseerd worden wanneer de fout opgelost is.",
|
||||
"This Device": "Dit apparaat",
|
||||
"This Month": "Deze maand",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Dit kan hackers eenvoudig toegang geven om bestanden op uw computer te lezen en te wijzigen.",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Dit kan hackers eenvoudig toegang geven om bestanden op je computer te lezen en te wijzigen.",
|
||||
"This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.": "Dit apparaat kan andere apparaten niet automatisch detecteren of zijn eigen adres aankondigen om door anderen gevonden te worden. Alleen apparaten met statisch geconfigureerde adressen kunnen verbinding maken.",
|
||||
"This is a major version upgrade.": "Dit is een grote versie-upgrade.",
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "Deze instelling bepaalt de benodigde vrije ruimte op de home-schijf (d.w.z. de indexdatabase).",
|
||||
"Time": "Tijd",
|
||||
"Time the item was last modified": "Tijdstip waarop het item laatst gewijzigd is",
|
||||
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Om verbinding te maken met het Syncthing-apparaat \"{{devicename}}\", voegt u aan uw kant een nieuw extern apparaat toe met deze ID:",
|
||||
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Om verbinding te maken met het Syncthing-apparaat \"{{devicename}}\", voeg je aan jouw kant een nieuw extern apparaat toe met deze ID:",
|
||||
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Vink het vakje aan om een regel toe te staan. Laat het vakje uitgevinkt om een regel te weigeren.",
|
||||
"Today": "Vandaag",
|
||||
"Trash Can": "Prullenbak",
|
||||
@@ -506,7 +507,7 @@
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Waarschuwing, dit pad is een bovenliggende map van een bestaande map \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Waarschuwing, dit ppad is een onderliggende map van een bestaande map \"{{otherFolder}}\".",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Waarschuwing, dit pad is een onderliggende map van een bestaande map \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Waarschuwing: als u een externe opvolger gebruikt zoals {{syncthingInotify}}, moet u zich ervan verzekeren dat die uitgeschakeld is.",
|
||||
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Waarschuwing: als je een externe opvolger gebruikt zoals {{syncthingInotify}}, moet je zeker weten dat die uitgeschakeld is.",
|
||||
"Watch for Changes": "Wijzigingen opvolgen",
|
||||
"Watching for Changes": "Wijzigingen opvolgen",
|
||||
"Watching for changes discovers most changes without periodic scanning.": "Opvolgen van wijzigingen ontdekt de meeste wijzigingen zonder regelmatig scannen.",
|
||||
@@ -517,15 +518,15 @@
|
||||
"Yesterday": "Gisteren",
|
||||
"You can also copy and paste the text into a new message manually.": "U kunt de tekst ook handmatig kopiëren en in een nieuw bericht plakken.",
|
||||
"You can also select one of these nearby devices:": "U kunt ook een van deze apparaten in de buurt selecteren:",
|
||||
"You can change your choice at any time in the Settings dialog.": "U kunt uw keuze op elk moment aanpassen in het instellingen-venster.",
|
||||
"You can change your choice at any time in the Settings dialog.": "Je kunt je keuze op elk moment aanpassen in het instellingen-venster.",
|
||||
"You can read more about the two release channels at the link below.": "U kunt meer te weten komen over de twee release-kanalen via onderstaande link.",
|
||||
"You have no ignored devices.": "U hebt geen genegeerde apparaten.",
|
||||
"You have no ignored folders.": "U hebt geen genegeerde mappen.",
|
||||
"You have unsaved changes. Do you really want to discard them?": "U hebt niet-opgeslagen wijzigingen. Wilt u ze echt verwerpen?",
|
||||
"You have unsaved changes. Do you really want to discard them?": "Je hebt niet-opgeslagen wijzigingen. Wil je ze echt verwerpen?",
|
||||
"You must keep at least one version.": "U moet minstens één versie bewaren.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "U mag lokaal nooit iets toevoegen of wijzigen in een \"{{receiveEncrypted}}\"-map.",
|
||||
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Uw sms-app zou moeten openen om u de ontvanger te laten kiezen en het te versturen vanaf uw eigen nummer.",
|
||||
"Your email app should open to let you choose the recipient and send it from your own address.": "Uw e-mail-app zou moeten openen om u de ontvanger te laten kiezen en het te versturen vanaf uw eigen adres.",
|
||||
"Your SMS app should open to let you choose the recipient and send it from your own number.": "De sms-app zou moeten openen zodat je de ontvanger kan kiezen en het te versturen vanaf jouw eigen nummer.",
|
||||
"Your email app should open to let you choose the recipient and send it from your own address.": "Uw e-mail-app zou moeten openen zodat je de ontvanger kan kiezen en het kan versturen vanaf uw eigen adres.",
|
||||
"days": "dagen",
|
||||
"deleted": "verwijderd",
|
||||
"deny": "weigeren",
|
||||
@@ -538,10 +539,15 @@
|
||||
"modified": "gewijzigd",
|
||||
"permit": "toestaan",
|
||||
"seconds": "seconden",
|
||||
"theme-name-black": "Zwart",
|
||||
"theme-name-dark": "Donker",
|
||||
"theme-name-default": "Standaard",
|
||||
"theme-name-light": "Licht",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Zwart",
|
||||
"dark": "Donker",
|
||||
"default": "Standaard",
|
||||
"light": "Licht"
|
||||
}
|
||||
},
|
||||
"unknown device": "onbekend apparaat",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wil map \"{{folder}}\" delen.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} wil map \"{{folderlabel}}\" ({{folder}}) delen.",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} kan dit apparaat mogelijk opnieuw introduceren."
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"About": "Om",
|
||||
"Action": "Handling",
|
||||
"Actions": "Handlingar",
|
||||
"Active filter rules": "Filterreglar i bruk",
|
||||
"Add": "Legg til",
|
||||
"Add Device": "Legg til eining",
|
||||
"Add Folder": "Legg til mappe",
|
||||
@@ -229,7 +230,7 @@
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Desse vil bli prøvd på nytt automatisk og vil bli synkronisert når feilen har blitt utbetra.",
|
||||
"This Device": "Denne eininga",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Dette kan lett gje datasnokar tilgang til å lesa og endra vilkårlege filer på denne maskina.",
|
||||
"This is a major version upgrade.": "Dette er ei hovudoppgradering",
|
||||
"This is a major version upgrade.": "Dette er ei hovudoppgradering.",
|
||||
"Trash Can File Versioning": "Papirkorg-filutgåvehandtering",
|
||||
"Unknown": "Ukjent",
|
||||
"Unshared": "Ikkje delt",
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
"Device ID": "Identyfikator urządzenia",
|
||||
"Device Identification": "Identyfikator urządzenia",
|
||||
"Device Name": "Nazwa urządzenia",
|
||||
"Device Status": "Stan urządzenia",
|
||||
"Device is untrusted, enter encryption password": "Urządzenie jest niezaufane; wprowadź szyfrujące hasło",
|
||||
"Device rate limits": "Ograniczenia prędkości urządzenia",
|
||||
"Device that last modified the item": "Urządzenie, które jako ostatnie zmodyfikowało ten element",
|
||||
@@ -168,6 +169,7 @@
|
||||
"Folder ID": "Identyfikator folderu",
|
||||
"Folder Label": "Etykieta folderu",
|
||||
"Folder Path": "Ścieżka folderu",
|
||||
"Folder Status": "Stan folderu",
|
||||
"Folder Type": "Rodzaj folderu",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Rodzaj folderu \"{{receiveEncrypted}}\" może być ustawiony tylko przy dodawaniu nowego folderu.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Rodzaj folderu \"{{receiveEncrypted}}\" nie może być zmieniony po dodaniu folderu. Musisz najpierw usunąć folder, skasować bądź też odszyfrować dane na dysku, a następnie dodać folder ponownie.",
|
||||
@@ -304,7 +306,6 @@
|
||||
"QR code": "Kod QR",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "Połączenia przez QUIC w większości przypadków są uważane za nieoptymalne",
|
||||
"Quick guide to supported patterns": "Krótki przewodnik po obsługiwanych wzorcach",
|
||||
"Random": "Losowo",
|
||||
"Receive Encrypted": "Odbierz zaszyfrowane",
|
||||
@@ -385,6 +386,7 @@
|
||||
"Staggered File Versioning": "Rozbudowane wersjonowanie plików",
|
||||
"Start Browser": "Uruchom przeglądarkę",
|
||||
"Statistics": "Statystyki",
|
||||
"Stay logged in": "Pozostań zalogowany",
|
||||
"Stopped": "Zatrzymany",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Przechowuje i synchronizuje tylko zaszyfrowane dane. Foldery na wszystkich połączonych urządzeniach muszą używać tego samego hasła bądź też być rodzaju \"{{receiveEncrypted}}\".",
|
||||
"Subject:": "Tytuł:",
|
||||
@@ -395,7 +397,7 @@
|
||||
"Sync Protocol Listen Addresses": "Adres nasłuchu protokołu synchronizacji",
|
||||
"Sync Status": "Stan synchronizacji",
|
||||
"Syncing": "Synchronizowanie",
|
||||
"Syncthing device ID for \"{%devicename%}\"": "Identyfikator Syncthinga dla urządzenia \"{{devicename}}\"",
|
||||
"Syncthing device ID for \"{%devicename%}\"": "Identyfikator Syncthing dla urządzenia \"{{devicename}}\"",
|
||||
"Syncthing has been shut down.": "Syncthing został wyłączony.",
|
||||
"Syncthing includes the following software or portions thereof:": "Syncthing zawiera następujące oprogramowanie lub ich części:",
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing to wolne i otwarte oprogramowanie na licencji MPL 2.0.",
|
||||
@@ -412,7 +414,7 @@
|
||||
"TCP WAN": "TCP WAN",
|
||||
"Take me back": "Powrót",
|
||||
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Adres GUI jest nadpisywany przez opcje uruchamiania. Zmiany dokonane tutaj nie będą obowiązywać, dopóki nadpisywanie jest w użyciu.",
|
||||
"The Syncthing Authors": "The Syncthing Authors",
|
||||
"The Syncthing Authors": "Twórcy Syncthing",
|
||||
"The Syncthing admin interface is configured to allow remote access without a password.": "Ustawienia interfejsu administracyjnego aplikacji Syncthing zezwalają na zdalny dostęp bez hasła.",
|
||||
"The aggregated statistics are publicly available at the URL below.": "Zebrane statystyki są publicznie dostępne pod poniższym adresem.",
|
||||
"The cleanup interval cannot be blank.": "Przedział czasowy czyszczenia nie może być pusty.",
|
||||
@@ -439,7 +441,7 @@
|
||||
"The number of connections must be a non-negative number.": "Liczba połączeń musi być nieujemną wartością liczbową.",
|
||||
"The number of days must be a number and cannot be blank.": "Liczba dni musi być wartością liczbową oraz nie może być pusta.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "Liczba dni, przez które pliki trzymane będą w koszu. Zero oznacza nieskończoność.",
|
||||
"The number of old versions to keep, per file.": "Liczba starszych wersji do zachowania, dla pojedynczego pliku.",
|
||||
"The number of old versions to keep, per file.": "Liczba starszych wersji każdego pliku, które należy zachować.",
|
||||
"The number of versions must be a number and cannot be blank.": "Liczba wersji musi być wartością liczbową oraz nie może być pusta.",
|
||||
"The path cannot be blank.": "Ścieżka nie może być pusta.",
|
||||
"The rate limit is applied to the accumulated traffic of all connections to this device.": "Ograniczenie prędkości dotyczy skumulowanego ruchu na wszystkich połączeniach z tym urządzeniem.",
|
||||
@@ -538,10 +540,15 @@
|
||||
"modified": "modyfikacja",
|
||||
"permit": "zezwól",
|
||||
"seconds": "sekundy",
|
||||
"theme-name-black": "Czarny",
|
||||
"theme-name-dark": "Ciemny",
|
||||
"theme-name-default": "Domyślny",
|
||||
"theme-name-light": "Jasny",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Czarny",
|
||||
"dark": "Ciemny",
|
||||
"default": "Domyślny",
|
||||
"light": "Jasny"
|
||||
}
|
||||
},
|
||||
"unknown device": "nieznane urządzenie",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "Urządzenie {{device}} chce współdzielić folder \"{{folder}}\"",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "Urządzenie {{device}} chce współdzielić folder \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "Urządzenie {{reintroducer}} może ponownie wprowadzić to urządzenie."
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"Are you sure you want to restore {%count%} files?": "Deseja mesmo restaurar {{count}} arquivo(s)?",
|
||||
"Are you sure you want to revert all local changes?": "Tem a certeza que quer reverter todas as alterações locais?",
|
||||
"Are you sure you want to upgrade?": "Deseja mesmo fazer a atualização?",
|
||||
"Authentication Required": "Autenticação Necessária",
|
||||
"Authors": "Autores",
|
||||
"Auto Accept": "Aceitar automaticamente",
|
||||
"Automatic Crash Reporting": "Relatório automático de falhas",
|
||||
@@ -204,6 +205,7 @@
|
||||
"Included Software": "Software incluído",
|
||||
"Incoming Rate Limit (KiB/s)": "Limite de velocidade de recepção (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "A configuração incorreta poderá causar danos aos seus dados e tornar o Syncthing inoperante.",
|
||||
"Incorrect user name or password.": "Nome de usuário ou senha incorretos.",
|
||||
"Internally used paths:": "Caminhos usados internamente:",
|
||||
"Introduced By": "Introduzido por",
|
||||
"Introducer": "Apresentador",
|
||||
@@ -231,10 +233,13 @@
|
||||
"Local State": "Estado local",
|
||||
"Local State (Total)": "Estado local (total)",
|
||||
"Locally Changed Items": "Itens modificados localmente",
|
||||
"Log": "Log",
|
||||
"Log": "Registro",
|
||||
"Log File": "Arquivo de registro",
|
||||
"Log In": "Entrar",
|
||||
"Log Out": "Sair",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Log tailing pausado. Role até o final para continuar.",
|
||||
"Logs": "Logs",
|
||||
"Login failed, see Syncthing logs for details.": "Falha no login. Veja os logs do Syncthing para mais detalhes.",
|
||||
"Logs": "Registros",
|
||||
"Major Upgrade": "Atualização \"major\"",
|
||||
"Mass actions": "Ações em massa",
|
||||
"Maximum Age": "Idade máxima",
|
||||
@@ -272,6 +277,7 @@
|
||||
"Override": "Substituir",
|
||||
"Override Changes": "Sobrescrever alterações",
|
||||
"Ownership": "Informação de propriedade",
|
||||
"Password": "Senha",
|
||||
"Path": "Caminho",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Caminho para a pasta na máquina local. Será criado caso não exista. O caractere til (~) pode ser usado como um atalho para",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Caminho do diretório onde as versões são salvas (deixe em branco para que seja o diretório padrão .stversions dentro da pasta compartilhada).",
|
||||
@@ -296,7 +302,6 @@
|
||||
"QR code": "Código QR",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "Conexões QUIC são, na maioria dos casos, consideradas abaixo do ideal",
|
||||
"Quick guide to supported patterns": "Guia rápido dos padrões suportados",
|
||||
"Random": "Aleatória",
|
||||
"Receive Encrypted": "Receber Criptografado",
|
||||
@@ -479,6 +484,7 @@
|
||||
"Usage reporting is always enabled for candidate releases.": "O relatório de uso está sempre habilitado em versões candidatas ao lançamento.",
|
||||
"Use HTTPS for GUI": "Usar HTTPS para a interface web",
|
||||
"Use notifications from the filesystem to detect changed items.": "Usar notificações do sistema de ficheiros para detectar itens alterados.",
|
||||
"User": "Usuário",
|
||||
"User Home": "Pasta do usuário",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "O Usuário/Senha não foi definido para a autenticação da GUI. Por favor, considere defini-los.",
|
||||
"Using a QUIC connection over LAN": "Usando conexão QUIC sobre LAN",
|
||||
@@ -529,10 +535,14 @@
|
||||
"modified": "modificado",
|
||||
"permit": "permitir",
|
||||
"seconds": "segundos",
|
||||
"theme-name-black": "Preto",
|
||||
"theme-name-dark": "Escuro",
|
||||
"theme-name-default": "Padrão",
|
||||
"theme-name-light": "Claro",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Preto",
|
||||
"dark": "Escuro",
|
||||
"default": "Padrão",
|
||||
"light": "Claro"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quer compartilhar a pasta \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quer compartilhar a pasta \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} pode reintroduzir este dispositivo."
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
"Device ID": "ID do dispositivo",
|
||||
"Device Identification": "Identificação do dispositivo",
|
||||
"Device Name": "Nome do dispositivo",
|
||||
"Device Status": "Estado do dispositivo",
|
||||
"Device is untrusted, enter encryption password": "Dispositivo não fiável, insira senha de encriptação",
|
||||
"Device rate limits": "Limites de velocidade do dispositivo",
|
||||
"Device that last modified the item": "Último dispositivo a modificar o item",
|
||||
@@ -168,6 +169,7 @@
|
||||
"Folder ID": "ID da pasta",
|
||||
"Folder Label": "Etiqueta da pasta",
|
||||
"Folder Path": "Caminho da pasta",
|
||||
"Folder Status": "Estado da pasta",
|
||||
"Folder Type": "Tipo de pasta",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "O tipo de pasta \"{{receiveEncrypted}}\" apenas pode ser definido aquando da adição de uma nova pasta.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "O tipo de pasta \"{{receiveEncrypted}}\" não pode ser modificado depois de adicionar a pasta. Tem de remover a pasta, eliminar ou desencriptar os dados no disco e adicionar a pasta novamente.",
|
||||
@@ -304,7 +306,6 @@
|
||||
"QR code": "Código QR",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "Ligações QUIC são, na maioria dos casos, consideradas inferiores ao ideal",
|
||||
"Quick guide to supported patterns": "Guia rápido dos padrões suportados",
|
||||
"Random": "Aleatória",
|
||||
"Receive Encrypted": "recebe dados encriptados",
|
||||
@@ -538,10 +539,15 @@
|
||||
"modified": "modificado",
|
||||
"permit": "permitir",
|
||||
"seconds": "segundos",
|
||||
"theme-name-black": "Preto",
|
||||
"theme-name-dark": "Escuro",
|
||||
"theme-name-default": "Predefinido",
|
||||
"theme-name-light": "Claro",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Preto",
|
||||
"dark": "Escuro",
|
||||
"default": "Predefinido",
|
||||
"light": "Claro"
|
||||
}
|
||||
},
|
||||
"unknown device": "dispositivo desconhecido",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quer partilhar a pasta \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} quer partilhar a pasta \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} poderá reintroduzir este dispositivo."
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"Add filter entry": "Добавить правило фильтрации",
|
||||
"Add ignore patterns": "Добавить шаблоны для игнорирования",
|
||||
"Add new folder?": "Добавить новую папку?",
|
||||
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Также будет увеличен интервал полного сканирования (в 60 раз, т. е. новое значение — 1 час). Вы можете вручную настроить интервал для каждой папки, выбрав \"Нет\".",
|
||||
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Также будет увеличен интервал полного сканирования (в 60 раз, т. е. новое значение — 1 час). Вы можете вручную настроить интервал для каждой папки, выбрав «Нет».",
|
||||
"Address": "Адрес",
|
||||
"Addresses": "Адреса",
|
||||
"Advanced": "Дополнительно",
|
||||
@@ -26,9 +26,11 @@
|
||||
"Allow Anonymous Usage Reporting?": "Разрешить анонимный отчет об использовании?",
|
||||
"Allowed Networks": "Разрешённые сети",
|
||||
"Alphabetic": "По алфавиту",
|
||||
"Altered by ignoring deletes.": "Изменено, игнорируя удаления.",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Для версионирования используется внешняя программа. Ей нужно удалить файл из общей папки. Если путь к приложению содержит пробелы, его нужно взять в кавычки.",
|
||||
"Anonymous Usage Reporting": "Анонимный отчет об использовании",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Формат анонимных отчётов изменился. Хотите переключиться на новый формат?",
|
||||
"Applied to LAN": "Примененоо к LAN",
|
||||
"Apply": "Применить",
|
||||
"Are you sure you want to override all remote changes?": "Уверены, что хотите перезаписать все удалённые изменения?",
|
||||
"Are you sure you want to permanently delete all these files?": "Уверены, что хотите навсегда удалить эти файлы?",
|
||||
@@ -36,7 +38,8 @@
|
||||
"Are you sure you want to remove folder {%label%}?": "Уверены, что хотите удалить папку {{label}}?",
|
||||
"Are you sure you want to restore {%count%} files?": "Уверены, что хотите восстановить {{count}} файлов?",
|
||||
"Are you sure you want to revert all local changes?": "Уверены, что хотите отменить все локальные изменения?",
|
||||
"Are you sure you want to upgrade?": "Уверены, что хотите обновить?",
|
||||
"Are you sure you want to upgrade?": "Уверены, что хотите обновить версию приложения?",
|
||||
"Authentication Required": "Требуется Аутентификация",
|
||||
"Authors": "Авторы",
|
||||
"Auto Accept": "Автопринятие",
|
||||
"Automatic Crash Reporting": "Автоматическая отправка отчётов о сбоях",
|
||||
@@ -63,6 +66,7 @@
|
||||
"Configured": "Сконфигурировано",
|
||||
"Connected (Unused)": "Подключено (не используется)",
|
||||
"Connection Error": "Ошибка подключения",
|
||||
"Connection Management": "Управление соединениями",
|
||||
"Connection Type": "Тип соединения",
|
||||
"Connections": "Подключения",
|
||||
"Connections via relays might be rate limited by the relay": "Соединения через промежуточные узлы могут быть ограничены по количеству запросов единицу времени",
|
||||
@@ -77,14 +81,14 @@
|
||||
"Danger!": "Опасно!",
|
||||
"Database Location": "Расположение базы данных",
|
||||
"Debugging Facilities": "Средства отладки",
|
||||
"Default": "По-умолчанию",
|
||||
"Default": "По умолчанию",
|
||||
"Default Configuration": "Настройки по умолчанию",
|
||||
"Default Device": "Стандартное Устройство",
|
||||
"Default Device": "Стандартное устройство",
|
||||
"Default Folder": "Стандартная папка",
|
||||
"Default Ignore Patterns": "Шаблон игнорирования по умолчанию",
|
||||
"Defaults": "Стандартные параметры",
|
||||
"Delete": "Удалить",
|
||||
"Delete Unexpected Items": "Удалить неожиданные элементы",
|
||||
"Delete Unexpected Items": "Удалить неожиданные объекты",
|
||||
"Deleted {%file%}": "{{file}} удалён",
|
||||
"Deselect All": "Снять выделение",
|
||||
"Deselect devices to stop sharing this folder with.": "Отмените выбор устройств, чтобы прекратить совместное использование этой папки.",
|
||||
@@ -95,6 +99,7 @@
|
||||
"Device ID": "ID устройства",
|
||||
"Device Identification": "Идентификация устройства",
|
||||
"Device Name": "Имя устройства",
|
||||
"Device Status": "Статус устройства",
|
||||
"Device is untrusted, enter encryption password": "Устройство ненадёжно, укажите пароль шифрования",
|
||||
"Device rate limits": "Ограничения скорости для устройства",
|
||||
"Device that last modified the item": "Устройство, последним изменившее объект",
|
||||
@@ -103,7 +108,7 @@
|
||||
"Disabled": "Отключено",
|
||||
"Disabled periodic scanning and disabled watching for changes": "Периодическое сканирование и отслеживание изменений отключено",
|
||||
"Disabled periodic scanning and enabled watching for changes": "Периодическое сканирование и отслеживание изменений отключено",
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Периодическое сканирование отключено, не удалось включить отслеживание изменений, повторная попытка каждую минуту.",
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Периодическое сканирование отключено, не удалось включить отслеживание изменений, повторная попытка каждую минуту:",
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Отключает сравнение и синхронизацию разрешений файлов. Полезно для систем с несуществующими или настраиваемыми разрешениями (например, FAT, exFAT, Synology, Android).",
|
||||
"Discard": "Отменить",
|
||||
"Disconnected": "Нет соединения",
|
||||
@@ -129,8 +134,8 @@
|
||||
"Edit Folder Defaults": "Изменить умолчания папки",
|
||||
"Editing {%path%}.": "Правка {{path}}.",
|
||||
"Enable Crash Reporting": "Включить отчёты о сбоях",
|
||||
"Enable NAT traversal": "Включить NAT traversal",
|
||||
"Enable Relaying": "Включить релеи",
|
||||
"Enable NAT traversal": "Использовать обход NAT",
|
||||
"Enable Relaying": "Использовать ретрансляторы",
|
||||
"Enabled": "Включено",
|
||||
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Включает отправку расширенных атрибутов на другие устройства и применение расширенных атрибутов локально. Может потребовать запуска с повышенными правами.",
|
||||
"Enables sending extended attributes to other devices, but not applying incoming extended attributes. This can have a significant performance impact. Always enabled when \"Sync Extended Attributes\" is enabled.": "Включает отправку расширенных атрибутов на другие устройства, но не применение расширенных атрибутов с других устройств. Это может значительно повлиять на производительность. Всегда активно, если включено «Синхронизировать расширенные атрибуты».",
|
||||
@@ -146,15 +151,15 @@
|
||||
"Extended Attributes Filter": "Фильтр расширенных атрибутов",
|
||||
"External": "Внешний",
|
||||
"External File Versioning": "Внешний контроль версий файлов",
|
||||
"Failed Items": "Сбои",
|
||||
"Failed Items": "Сбойные объекты",
|
||||
"Failed to load file versions.": "Не удалось загрузить версии файлов.",
|
||||
"Failed to load ignore patterns.": "Не удалось загрузить шаблоны игнорирования.",
|
||||
"Failed to setup, retrying": "Не удалось настроить, пробуем ещё",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Если нет IPv6-соединений, при подключении к IPv6-серверам произойдёт ошибка.",
|
||||
"File Pull Order": "Порядок получения файлов",
|
||||
"File Versioning": "Управление версиями",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Когда Syncthing изменяет или удаляет файлы, они помещаются в папку .stversions",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Когда Syncthing изменяет или удаляет файлы, их версии с отметками времени помещаются в папку .stversions",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Когда Syncthing изменяет или удаляет файлы, они помещаются в папку .stversions.",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Когда Syncthing изменяет или удаляет файлы, их версии с отметками времени помещаются в папку .stversions.",
|
||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Файлы защищены от изменений сделанных на других устройствах, но изменения сделанные на этом устройстве будут отправлены всему кластеру.",
|
||||
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Файлы синхронизируются из группы, но изменения, сделанные на этом устройстве, не будут отправлены на другие устройства группы.",
|
||||
"Filesystem Watcher Errors": "Ошибки отслеживания файловой системы",
|
||||
@@ -164,6 +169,7 @@
|
||||
"Folder ID": "ID папки",
|
||||
"Folder Label": "Ярлык папки",
|
||||
"Folder Path": "Путь к папке",
|
||||
"Folder Status": "Статус папки",
|
||||
"Folder Type": "Тип папки",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Тип папки «{{receiveEncrypted}}» может быть указан только при создании новой папки.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Тип папки «{{receiveEncrypted}}» не может быть изменён после добавления. Вам необходимо убрать папку, удалить или дешифровать данные на диске, а затем снова добавить папку.",
|
||||
@@ -172,10 +178,12 @@
|
||||
"Forever": "Вечно",
|
||||
"Full Rescan Interval (s)": "Интервал полного сканирования (в секундах)",
|
||||
"GUI": "Интерфейс",
|
||||
"GUI / API HTTPS Certificate": "HTTPS-сертификат GUI/API",
|
||||
"GUI Authentication Password": "Пароль для доступа к панели управления",
|
||||
"GUI Authentication User": "Имя пользователя для доступа к панели управления",
|
||||
"GUI Authentication: Set User and Password": "GUI аутентификация: Установите имя пользователя и пароль",
|
||||
"GUI Listen Address": "Адрес GUI",
|
||||
"GUI Override Directory": "Каталог переопределения графического интерфейса",
|
||||
"GUI Theme": "Тема оформления",
|
||||
"General": "Общие",
|
||||
"Generate": "Сгенерировать",
|
||||
@@ -183,6 +191,7 @@
|
||||
"Global Discovery Servers": "Серверы глобального обнаружения",
|
||||
"Global State": "Глобальное состояние",
|
||||
"Help": "Помощь",
|
||||
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Подсказка: обнаруживаются только запрещающие правила, тогда как по умолчанию они отклоняются. Рассмотрите возможность добавления «разрешить любое» в качестве последнего правила.",
|
||||
"Home page": "Сайт",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Ваши настройки указывают что вы не хотите, чтобы эта функция была включена. Мы отключили отправку отчетов о сбоях.",
|
||||
"Identification": "Идентификация",
|
||||
@@ -198,9 +207,11 @@
|
||||
"Included Software": "Включенное программное обеспечение",
|
||||
"Incoming Rate Limit (KiB/s)": "Ограничение входящей скорости (КиБ/с)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Неправильные настройки могут повредить содержимое папок и сделать Syncthing неработоспособным.",
|
||||
"Incorrect user name or password.": "Неверное имя пользователя или пароль.",
|
||||
"Internally used paths:": "Внутренние используемые пути:",
|
||||
"Introduced By": "Рекомендовано",
|
||||
"Introducer": "Рекомендатель",
|
||||
"Introduction": "Вступление",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Инвертировать текущее условие (например, исключить)",
|
||||
"Keep Versions": "Количество хранимых версий",
|
||||
"LDAP": "LDAP",
|
||||
@@ -217,8 +228,8 @@
|
||||
"Listener Failures": "Ошибки прослушивателя",
|
||||
"Listener Status": "Статус прослушивателя",
|
||||
"Listeners": "Прослушиватель",
|
||||
"Loading data...": "Загрузка данных...",
|
||||
"Loading...": "Загрузка...",
|
||||
"Loading data...": "Загрузка данных…",
|
||||
"Loading...": "Загрузка…",
|
||||
"Local Additions": "Локальные дополнения",
|
||||
"Local Discovery": "Локальное обнаружение",
|
||||
"Local State": "Локальное состояние",
|
||||
@@ -226,11 +237,18 @@
|
||||
"Locally Changed Items": "Объекты, изменённые на этом компьютере",
|
||||
"Log": "Журнал",
|
||||
"Log File": "Файл журнала",
|
||||
"Log In": "Вход",
|
||||
"Log Out": "Выход",
|
||||
"Log in to see paths information.": "Войдите, чтобы увидеть информацию о путях.",
|
||||
"Log in to see version information.": "Войдите, чтобы просмотреть информацию о версии.",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Вывод журнала приостановлен. Прокрутите вниз, чтобы продолжить.",
|
||||
"Login failed, see Syncthing logs for details.": "Не удалось войти в систему. Посмотреть подробности можно в журнале Syncthing.",
|
||||
"Logs": "Журналы",
|
||||
"Major Upgrade": "Обновление основной версии",
|
||||
"Mass actions": "Массовые действия",
|
||||
"Maximum Age": "Максимальный срок",
|
||||
"Maximum single entry size": "Максимальный размер одной записи",
|
||||
"Maximum total size": "Общий максимальный размер",
|
||||
"Metadata Only": "Только метаданные",
|
||||
"Minimum Free Disk Space": "Минимальное свободное место на диске",
|
||||
"Mod. Device": "Изм. устройство",
|
||||
@@ -246,10 +264,12 @@
|
||||
"Newest First": "Сначала новые",
|
||||
"No": "Нет",
|
||||
"No File Versioning": "Без управления версиями файлов",
|
||||
"No files will be deleted as a result of this operation.": "В результате этой операции никакие файлы не будут удалены",
|
||||
"No files will be deleted as a result of this operation.": "В результате этой операции никакие файлы не будут удалены.",
|
||||
"No rules set": "Правила не заданы",
|
||||
"No upgrades": "Нет обновлений",
|
||||
"Not shared": "Не зашаренный",
|
||||
"Not shared": "Нет общего доступа",
|
||||
"Notice": "Внимание",
|
||||
"Number of Connections": "Количество подключений",
|
||||
"OK": "ОК",
|
||||
"Off": "Отключить",
|
||||
"Oldest First": "Сначала старые",
|
||||
@@ -260,6 +280,8 @@
|
||||
"Outgoing Rate Limit (KiB/s)": "Ограничение исходящей скорости (КиБ/с)",
|
||||
"Override": "Перезаписать",
|
||||
"Override Changes": "Перезаписать изменения",
|
||||
"Ownership": "Владелец",
|
||||
"Password": "Пароль",
|
||||
"Path": "Путь",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Путь к папке на локальном компьютере. Если её не существует, то она будет создана. Тильда (~) может использоваться как сокращение для",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Путь, в котором нужно хранить версии (оставьте пустым для папки по умолчанию .stversions внутри общей папки).",
|
||||
@@ -271,10 +293,10 @@
|
||||
"Pending changes": "Несохранённые изменения",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Периодическое сканирование с заданным интервалом, отслеживание изменений отключено",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Периодическое сканирование с заданным интервалом и включено отслеживание изменений",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Периодическое сканирование с заданным интервалом, неудачная настройка отслеживания изменений, повторная попытка каждую минуту.",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Периодическое сканирование с заданным интервалом, неудачная настройка отслеживания изменений, повторная попытка каждую минуту:",
|
||||
"Permanently add it to the ignore list, suppressing further notifications.": "Добавление в список игнорирования навсегда с подавлением будущих уведомлений.",
|
||||
"Please consult the release notes before performing a major upgrade.": "Перед проведением обновления основной версии ознакомьтесь, пожалуйста, с замечаниями к версии",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "Установите имя пользователя и пароль для интерфейса в настройках",
|
||||
"Please consult the release notes before performing a major upgrade.": "Перед проведением обновления основной версии ознакомьтесь, пожалуйста, с замечаниями к версии.",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "Установите имя пользователя и пароль для интерфейса в настройках.",
|
||||
"Please wait": "Пожалуйста, подождите",
|
||||
"Prefix indicating that the file can be deleted if preventing directory removal": "Префикс, указывающий что файл может быть удалён, если он мешает удалить папку",
|
||||
"Prefix indicating that the pattern should be matched without case sensitivity": "Префикс, указывающий что шаблон должен сопоставляться без учёта регистра",
|
||||
@@ -282,7 +304,8 @@
|
||||
"Preview": "Предварительный просмотр",
|
||||
"Preview Usage Report": "Посмотреть отчёт об использовании",
|
||||
"QR code": "QR-код",
|
||||
"QUIC connections are in most cases considered suboptimal": "Соединения QUIC в большинстве случаев считаются неоптимальными",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"Quick guide to supported patterns": "Краткое руководство по поддерживаемым шаблонам",
|
||||
"Random": "Случайно",
|
||||
"Receive Encrypted": "Принять шифрованный",
|
||||
@@ -314,9 +337,10 @@
|
||||
"Revert": "Обратить",
|
||||
"Revert Local Changes": "Отменить изменения на этом компьютере",
|
||||
"Save": "Сохранить",
|
||||
"Saving changes": "Изменения сохраняются",
|
||||
"Scan Time Remaining": "Оставшееся время сканирования",
|
||||
"Scanning": "Сканирование",
|
||||
"See external versioning help for supported templated command line parameters.": "Поддерживаемые шаблонные параметры командной строки см. в документации сторонней программы контроля версий",
|
||||
"See external versioning help for supported templated command line parameters.": "Поддерживаемые шаблонные параметры командной строки см. в документации сторонней программы контроля версий.",
|
||||
"Select All": "Выбрать все",
|
||||
"Select a version": "Выберите версию",
|
||||
"Select additional devices to share this folder with.": "Выберите дополнительные устройства, для которых будет доступна эта папка.",
|
||||
@@ -327,6 +351,7 @@
|
||||
"Send Extended Attributes": "Отправлять расширенные атрибуты",
|
||||
"Send Only": "Только отправить",
|
||||
"Send Ownership": "Отправлять информацию о владельце",
|
||||
"Set Ignores on Added Folder": "Установить игнорирования для добавленной папки",
|
||||
"Settings": "Настройки",
|
||||
"Share": "Предоставить доступ",
|
||||
"Share Folder": "Предоставить доступ к папке",
|
||||
@@ -361,11 +386,14 @@
|
||||
"Staggered File Versioning": "Ступенчатое управление версиями файлов",
|
||||
"Start Browser": "Запускать браузер",
|
||||
"Statistics": "Статистика",
|
||||
"Stay logged in": "Оставаться в системе",
|
||||
"Stopped": "Остановлено",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Хранит и синхронизирует только зашифрованные данные. Папки на всех подключённых устройствах должны быть настроены под один и тот же пароль или иметь тип «{{receiveEncrypted}}».",
|
||||
"Subject:": "Субьект:",
|
||||
"Support": "Поддержка",
|
||||
"Support Bundle": "Данные для поддержки",
|
||||
"Sync Extended Attributes": "Синхронизировать расширенные атрибуты",
|
||||
"Sync Ownership": "Синхронизация владений",
|
||||
"Sync Protocol Listen Addresses": "Адрес протокола синхронизации",
|
||||
"Sync Status": "Состояние синхронизации",
|
||||
"Syncing": "Синхронизация",
|
||||
@@ -377,10 +405,13 @@
|
||||
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing ожидает подключения от других устройств на следующих сетевых адресах:",
|
||||
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing не ожидает попыток подключения ни на каких адресах. Только исходящие подключения могут работать на этом устройстве.",
|
||||
"Syncthing is restarting.": "Перезапуск Syncthing.",
|
||||
"Syncthing is saving changes.": "Синхронизация это сохранение изменений.",
|
||||
"Syncthing is upgrading.": "Обновление Syncthing.",
|
||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing теперь поддерживает автоматическую отправку отчетов о сбоях разработчикам. Эта функция включена по умолчанию.",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Кажется, Syncthing не запущен или есть проблемы с подключением к Интернету. Переподключаюсь...",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Кажется, Syncthing не запущен или есть проблемы с подключением к Интернету. Переподключаюсь…",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing столкнулся с проблемой при обработке Вашего запроса. Пожалуйста, обновите страницу или перезапустите Syncthing если проблема повторится.",
|
||||
"TCP LAN": "TCP LAN",
|
||||
"TCP WAN": "TCP WAN",
|
||||
"Take me back": "Вернуться к редактированию",
|
||||
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Эти изменения не вступят в силу, пока адрес панели управления переопределён в настройках запуска.",
|
||||
"The Syncthing Authors": "Авторы Syncthing",
|
||||
@@ -397,22 +428,25 @@
|
||||
"The folder content on other devices will be overwritten to become identical with this device. Files not present here will be deleted on other devices.": "Содержание папки на других устройствах будет перезаписано и станет идентично этому устройству. Файлы, отсутствующие здесь, будут удалены на других устройствах.",
|
||||
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Содержание папки на этом устройстве будет перезаписано и станет идентично другим устройствам. Новые файлы на этом устройстве будут удалены.",
|
||||
"The folder path cannot be blank.": "Путь к папке не должен быть пустым.",
|
||||
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "Используются следующие интервалы: в первый час версия меняется каждые 30 секунд, в первый день - каждый час, первые 30 дней - каждый день, после, до максимального срока - каждую неделю.",
|
||||
"The following items could not be synchronized.": "Невозможно синхронизировать следующие объекты",
|
||||
"The following items were changed locally.": "Следующие объекты были изменены локально",
|
||||
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "Используются следующие интервалы: в первый час версия меняется каждые 30 секунд, в первый день — каждый час, первые 30 дней — каждый день, после, до максимального срока — каждую неделю.",
|
||||
"The following items could not be synchronized.": "Невозможно синхронизировать следующие объекты.",
|
||||
"The following items were changed locally.": "Следующие объекты были изменены локально.",
|
||||
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Для обнаружения других устройств в сети и анонсирования этого устройства используются следующие методы:",
|
||||
"The following text will automatically be inserted into a new message.": "Следующий текст будет автоматически вставлен в новое сообщение.",
|
||||
"The following unexpected items were found.": "Были найдены следующие объекты.",
|
||||
"The interval must be a positive number of seconds.": "Интервал секунд должен быть положительным.",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Интервал в секундах для запуска очистки в каталоге версий. Ноль, чтобы отключить периодическую очистку.",
|
||||
"The maximum age must be a number and cannot be blank.": "Максимальный срок должен быть числом и не может быть пустым.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Максимальный срок хранения версии (в днях, 0 значит вечное хранение).",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Максимальный срок хранения версии в днях (0 — бесконечно).",
|
||||
"The number of connections must be a non-negative number.": "Количество соединений должно быть неотрицательным.",
|
||||
"The number of days must be a number and cannot be blank.": "Количество дней должно быть числом и не может быть пустым.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "Количество дней хранения файлов в корзине. Ноль значит навсегда.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "Количество дней хранения файлов в корзине (0 — бесконечно).",
|
||||
"The number of old versions to keep, per file.": "Количество хранимых версий файла.",
|
||||
"The number of versions must be a number and cannot be blank.": "Количество версий должно быть числом и не может быть пустым.",
|
||||
"The path cannot be blank.": "Путь не может быть пустым.",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "Скорость должна быть неотрицательным числом (0: нет ограничения)",
|
||||
"The rate limit is applied to the accumulated traffic of all connections to this device.": "Ограничение скорости применяется к накопленному трафику всех подключений к этому устройству.",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "Скорость должна быть неотрицательным числом (0 — без ограничений)",
|
||||
"The remote device has not accepted sharing this folder.": "Удаленное устройство не разрешило общий доступ к этой папке.",
|
||||
"The remote device has paused this folder.": "Удаленное устройство приостановило эту папку.",
|
||||
"The rescan interval must be a non-negative number of seconds.": "Интервал пересканирования должен быть неотрицательным количеством секунд.",
|
||||
"There are no devices to share this folder with.": "Нет устройств, для которых будет доступна эта папка.",
|
||||
@@ -437,8 +471,8 @@
|
||||
"Unavailable": "Недоступно",
|
||||
"Unavailable/Disabled by administrator or maintainer": "Недоступно или отключено администратором",
|
||||
"Undecided (will prompt)": "Не определено (запрашивать каждый раз)",
|
||||
"Unexpected Items": "Неожиданные элементы",
|
||||
"Unexpected items have been found in this folder.": "В папке найдены неожиданные элементы.",
|
||||
"Unexpected Items": "Неожиданные объекты",
|
||||
"Unexpected items have been found in this folder.": "В папке найдены неожиданные объекты.",
|
||||
"Unignore": "Не игнорировать",
|
||||
"Unknown": "Неизвестно",
|
||||
"Unshared": "Не общедоступно",
|
||||
@@ -455,9 +489,13 @@
|
||||
"Usage reporting is always enabled for candidate releases.": "Отправка отчётов об использовании всегда включена для кандидатов в релизы.",
|
||||
"Use HTTPS for GUI": "Использовать HTTPS для панели управления",
|
||||
"Use notifications from the filesystem to detect changed items.": "Использовать уведомления от файловой системы для обнаружения изменённых объектов.",
|
||||
"User": "Пользователь",
|
||||
"User Home": "Папка пользователя",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Имя пользователя/пароль не был установлен для GUI-аутентификации. Настройте его.",
|
||||
"Using a direct TCP connection over LAN": "Использование прямого TCP-соединения через LAN",
|
||||
"Using a direct TCP connection over WAN": "Использование прямого TCP-соединения через WAN",
|
||||
"Using a QUIC connection over LAN": "Использовать QUIC-соединения в LAN",
|
||||
"Using a QUIC connection over WAN": "Использовать QUIC-соединения в WAN",
|
||||
"Using a direct TCP connection over LAN": "Использовать прямые TCP-соединения в LAN",
|
||||
"Using a direct TCP connection over WAN": "Использовать прямые TCP-соединения в WAN",
|
||||
"Version": "Версия",
|
||||
"Versions": "Версии",
|
||||
"Versions Path": "Путь к версиям",
|
||||
@@ -476,6 +514,7 @@
|
||||
"Watching for changes discovers most changes without periodic scanning.": "Отслеживание обнаруживает изменения без периодического сканирования.",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "Когда добавляете устройство, помните о том, что это же устройство должно быть добавлено и другой стороной.",
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Когда добавляете новую папку, помните, что ID папок используются для того, чтобы связывать папки между всеми устройствами. Они чувствительны к регистру и должны совпадать на всех используемых устройствах.",
|
||||
"When set to more than one on both devices, Syncthing will attempt to establish multiple concurrent connections. If the values differ, the highest will be used. Set to zero to let Syncthing decide.": "Когда установлено значение больше еденицы, Syncthing попытается установить несколько одновременных подключений. Если значения различаются, будет использоваться наибольшее. Установите значение 0, чтобы позволить Syncthing решать самостоятельно.",
|
||||
"Yes": "Да",
|
||||
"Yesterday": "Вчера",
|
||||
"You can also copy and paste the text into a new message manually.": "Вы также можете скопировать и вставить текст в новое сообщение вручную.",
|
||||
@@ -487,20 +526,29 @@
|
||||
"You have unsaved changes. Do you really want to discard them?": "Есть несохранённые изменения. Вы действительно хотите отменить их?",
|
||||
"You must keep at least one version.": "Вы должны хранить как минимум одну версию.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Не добавляйте и не изменяйте ничего локально в папке «{{receiveEncrypted}}».",
|
||||
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Должно открыться приложение SMS, где вы сможете выбрать получателя и отправителя со своего номера.",
|
||||
"Your email app should open to let you choose the recipient and send it from your own address.": "Ваше почтовое приложение должно открыться, чтобы вы могли выбрать получателя и отправителя со своего адреса.",
|
||||
"days": "дней",
|
||||
"deleted": "удалено",
|
||||
"deny": "отклонить",
|
||||
"directories": "папок",
|
||||
"file": "файл",
|
||||
"files": "файлов",
|
||||
"folder": "папка",
|
||||
"full documentation": "полная документация",
|
||||
"items": "элементы",
|
||||
"items": "объекты",
|
||||
"modified": "изменено",
|
||||
"permit": "разрешить",
|
||||
"seconds": "сек.",
|
||||
"theme-name-black": "Чёрная",
|
||||
"theme-name-dark": "Тёмная",
|
||||
"theme-name-default": "По умолчанию",
|
||||
"theme-name-light": "Светлая",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Чёрная",
|
||||
"dark": "Тёмная",
|
||||
"default": "По умолчанию",
|
||||
"light": "Светлая"
|
||||
}
|
||||
},
|
||||
"unknown device": "неизвестное устройство",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} хочет поделиться папкой «{{folder}}».",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} хочет поделиться папкой «{{folderlabel}}» ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} может повторно рекомендовать это устройство."
|
||||
|
||||
@@ -502,10 +502,14 @@
|
||||
"modified": "සංශෝධිතයි",
|
||||
"permit": "අවසරය",
|
||||
"seconds": "තත්පර",
|
||||
"theme-name-black": "කළු",
|
||||
"theme-name-dark": "අඳුරු",
|
||||
"theme-name-default": "පෙරනිමි",
|
||||
"theme-name-light": "දීප්ත",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "කළු",
|
||||
"dark": "අඳුරු",
|
||||
"default": "පෙරනිමි",
|
||||
"light": "දීප්ත"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} ට \"{{folder}}\" ෆෝල්ඩරය බෙදා ගැනීමට අවශ්යයි.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} ට \"{{folderlabel}}\" ({{folder}}) ෆෝල්ඩරය බෙදා ගැනීමට අවශ්යයි.",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} මෙම උපාංගය නැවත හඳුන්වා දිය හැක."
|
||||
|
||||
@@ -304,7 +304,6 @@
|
||||
"QR code": "QR kód",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "Pripojenia QUIC sa vo väčšine prípadov považujú za suboptimálne",
|
||||
"Quick guide to supported patterns": "Rýchly sprievodca podporovanými vzormi",
|
||||
"Random": "Náhodne",
|
||||
"Receive Encrypted": "Prijať šifrované",
|
||||
@@ -538,10 +537,14 @@
|
||||
"modified": "zmenené",
|
||||
"permit": "povolenie",
|
||||
"seconds": "sekúnd",
|
||||
"theme-name-black": "Čierna",
|
||||
"theme-name-dark": "Tmavé",
|
||||
"theme-name-default": "Predvolené",
|
||||
"theme-name-light": "Svetlá",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Čierna",
|
||||
"dark": "Tmavé",
|
||||
"default": "Predvolené",
|
||||
"light": "Svetlá"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce zdieľať adresár \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} chce zdieľať adresár \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} môže znova uviesť toto zariadenie."
|
||||
|
||||
@@ -261,7 +261,6 @@
|
||||
"Preview": "Predogled",
|
||||
"Preview Usage Report": "Predogled poročila uporabe",
|
||||
"QR code": "QR koda",
|
||||
"QUIC connections are in most cases considered suboptimal": "QUIC povezave običajno niso optimalne",
|
||||
"Quick guide to supported patterns": "Hitri vodnik za podprte vzorce",
|
||||
"Random": "Naključno",
|
||||
"Receive Encrypted": "Prejmi šifrirano",
|
||||
@@ -456,10 +455,14 @@
|
||||
"items": "predmeti",
|
||||
"modified": "spremenjeno",
|
||||
"seconds": "sekunde",
|
||||
"theme-name-black": "Črna",
|
||||
"theme-name-dark": "Temno",
|
||||
"theme-name-default": "Privzeto",
|
||||
"theme-name-light": "Svetlo",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Črna",
|
||||
"dark": "Temno",
|
||||
"default": "Privzeto",
|
||||
"light": "Svetlo"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} želi deliti mapo \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} želi deliti mapo \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} bo morda znova predstavil to napravo."
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"A device with that ID is already added.": "En enhet med detta ID har redan lagts tillagt.",
|
||||
"A device with that ID is already added.": "En enhet med detta ID har redan lagts till.",
|
||||
"A negative number of days doesn't make sense.": "Ett negativt antal dagar är inte rimligt.",
|
||||
"A new major version may not be compatible with previous versions.": "En ny huvudversion kan eventuellt vara inkompatibel med tidigare versioner.",
|
||||
"API Key": "API-nyckel",
|
||||
@@ -99,6 +99,7 @@
|
||||
"Device ID": "Enhets-ID",
|
||||
"Device Identification": "Enhetens identifikation",
|
||||
"Device Name": "Enhetsnamn",
|
||||
"Device Status": "Enhetsstatus",
|
||||
"Device is untrusted, enter encryption password": "Enheten är otillförlitlig, ange krypteringslösenord",
|
||||
"Device rate limits": "Enhetshastighetsgränser",
|
||||
"Device that last modified the item": "Enhet som senast ändrade objektet",
|
||||
@@ -168,6 +169,7 @@
|
||||
"Folder ID": "Mapp-ID",
|
||||
"Folder Label": "Mappetikett",
|
||||
"Folder Path": "Mappsökväg",
|
||||
"Folder Status": "Mappstatus",
|
||||
"Folder Type": "Mapptyp",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Mapptypen \"{{receiveEncrypted}}\" kan bara ställas in vid tilläggning av en ny mapp.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Mapptypen \"{{receiveEncrypted}}\" kan inte ändras efter att mappen har lagts till. Du måste ta bort mappen, ta bort eller dekryptera data på disken och lägga till mappen igen.",
|
||||
@@ -304,7 +306,6 @@
|
||||
"QR code": "QR-kod",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "Quic-anslutningar anses i de flesta fall suboptimala",
|
||||
"Quick guide to supported patterns": "Snabb handledning till mönster som stöds",
|
||||
"Random": "Slumpmässig",
|
||||
"Receive Encrypted": "Ta emot krypterade",
|
||||
@@ -385,6 +386,7 @@
|
||||
"Staggered File Versioning": "Filversionshantering i intervall",
|
||||
"Start Browser": "Starta webbläsaren",
|
||||
"Statistics": "Statistik",
|
||||
"Stay logged in": "Förbli inloggad",
|
||||
"Stopped": "Stoppad",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Lagrar och synkroniserar endast krypterade data. Mappar på alla anslutna enheter måste ställas in med samma lösenord eller också vara av typen \"{{receiveEncrypted}}\".",
|
||||
"Subject:": "Ämne:",
|
||||
@@ -538,10 +540,15 @@
|
||||
"modified": "ändrad",
|
||||
"permit": "tillåt",
|
||||
"seconds": "sekunder",
|
||||
"theme-name-black": "Svart",
|
||||
"theme-name-dark": "Mörkt",
|
||||
"theme-name-default": "Standard",
|
||||
"theme-name-light": "Ljust",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Svart",
|
||||
"dark": "Mörkt",
|
||||
"default": "Standard",
|
||||
"light": "Ljust"
|
||||
}
|
||||
},
|
||||
"unknown device": "okänd enhet",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} vill dela mapp \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} vill dela mapp \"{{folderlabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} kan återinföra denna enhet."
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"Are you sure you want to upgrade?": "Yükseltmek istediğinize emin misiniz?",
|
||||
"Authentication Required": "Kimlik Doğrulaması Gerekli",
|
||||
"Authors": "Hazırlayan",
|
||||
"Auto Accept": "Otomatik kabul et",
|
||||
"Auto Accept": "Otomatik Kabul Et",
|
||||
"Automatic Crash Reporting": "Otomatik Çökme Bildirme",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Otomatik yükseltme artık kararlı yayımlar ve yayım adayları arasında seçim yapmayı sunar.",
|
||||
"Automatic upgrades": "Otomatik yükseltmeler",
|
||||
@@ -99,6 +99,7 @@
|
||||
"Device ID": "Cihaz Kimliği",
|
||||
"Device Identification": "Cihaz Kimliği",
|
||||
"Device Name": "Cihaz Adı",
|
||||
"Device Status": "Cihaz Durumu",
|
||||
"Device is untrusted, enter encryption password": "Cihaz güvenilmez, şifreleme parolasını girin",
|
||||
"Device rate limits": "Cihaz hız sınırları",
|
||||
"Device that last modified the item": "Öğeyi son değiştiren cihaz",
|
||||
@@ -127,9 +128,9 @@
|
||||
"Downloaded": "İndirildi",
|
||||
"Downloading": "İndiriliyor",
|
||||
"Edit": "Düzenle",
|
||||
"Edit Device": "Düzenlenen Cihaz",
|
||||
"Edit Device": "Cihazı Düzenle",
|
||||
"Edit Device Defaults": "Cihaz Varsayılanlarını Düzenle",
|
||||
"Edit Folder": "Düzenlenen Klasör",
|
||||
"Edit Folder": "Klasörü Düzenle",
|
||||
"Edit Folder Defaults": "Klasör Varsayılanlarını Düzenle",
|
||||
"Editing {%path%}.": "Düzenlenen {{path}}.",
|
||||
"Enable Crash Reporting": "Çökme Bildirmeyi etkinleştir",
|
||||
@@ -168,6 +169,7 @@
|
||||
"Folder ID": "Klasör Kimliği",
|
||||
"Folder Label": "Klasör Etiketi",
|
||||
"Folder Path": "Klasör Yolu",
|
||||
"Folder Status": "Klasör Durumu",
|
||||
"Folder Type": "Klasör Türü",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Klasör türü \"{{receiveEncrypted}}\" yalnızca yeni bir klasör eklerken ayarlanabilir.",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Klasör türü \"{{receiveEncrypted}}\", klasör eklendikten sonra değiştirilemez. Klasörü kaldırmanız, diskteki verileri silmeniz veya şifresini çözmeniz ve klasörü tekrar eklemeniz gerekir.",
|
||||
@@ -300,11 +302,10 @@
|
||||
"Prefix indicating that the pattern should be matched without case sensitivity": "Büyük/küçük harf duyarlılığı olmadan şeklin eşleştirilmesi gerektiğini gösteren önek",
|
||||
"Preparing to Sync": "Eşitlemeye hazırlanıyor",
|
||||
"Preview": "Önizle",
|
||||
"Preview Usage Report": "Kullanım Raporunu önizle",
|
||||
"Preview Usage Report": "Kullanım Raporunu Önizle",
|
||||
"QR code": "QR kod",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "QUIC bağlantıları çoğu durumda yetersiz olarak kabul edilir",
|
||||
"Quick guide to supported patterns": "Desteklenen şekiller için hızlı rehber",
|
||||
"Random": "Rastgele",
|
||||
"Receive Encrypted": "Şifrelenmiş Al",
|
||||
@@ -334,7 +335,7 @@
|
||||
"Resume All": "Tümüne Devam",
|
||||
"Reused": "Yeniden Kullanılan",
|
||||
"Revert": "Geri al",
|
||||
"Revert Local Changes": "Yerel Değişiklikleri Geri Döndür",
|
||||
"Revert Local Changes": "Yerel Değişiklikleri Geri Al",
|
||||
"Save": "Kaydet",
|
||||
"Saving changes": "Değişiklikler kaydediliyor",
|
||||
"Scan Time Remaining": "Kalan Tarama Süresi",
|
||||
@@ -385,6 +386,7 @@
|
||||
"Staggered File Versioning": "Aşamalı Dosya Sürümlendirme",
|
||||
"Start Browser": "Tarayıcıyı başlat",
|
||||
"Statistics": "İstatistikler",
|
||||
"Stay logged in": "Oturum açık kal",
|
||||
"Stopped": "Durduruldu",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Yalnızca şifrelenmiş verileri depolar ve eşitler. Tüm bağlı cihazlardaki klasörlerin de aynı parola ile ayarlanması veya \"{{receiveEncrypted}}\" türünde olması gerekir.",
|
||||
"Subject:": "Konu:",
|
||||
@@ -538,10 +540,15 @@
|
||||
"modified": "değiştirildi",
|
||||
"permit": "izin ver",
|
||||
"seconds": "saniye",
|
||||
"theme-name-black": "Siyah",
|
||||
"theme-name-dark": "Koyu",
|
||||
"theme-name-default": "Varsayılan",
|
||||
"theme-name-light": "Açık",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Siyah",
|
||||
"dark": "Koyu",
|
||||
"default": "Varsayılan",
|
||||
"light": "Açık"
|
||||
}
|
||||
},
|
||||
"unknown device": "bilinmeyen cihaz",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}}, \"{{folder}}\" klasörünü paylaşmak istiyor.",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}}, \"{{folderlabel}}\" ({{folder}}) klasörünü paylaşmak istiyor.",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} bu cihazı yeniden tanıtabilir."
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"Are you sure you want to revert all local changes?": "Ви впевнені, що бажаєте відкинути всі локальні зміни?",
|
||||
"Are you sure you want to upgrade?": "Впевнені, що хочете оновитися?",
|
||||
"Authors": "Автори",
|
||||
"Auto Accept": "Затверджувати автоматично пропоновані віддаленим пристроєм каталоги",
|
||||
"Auto Accept": "Автоприймання",
|
||||
"Automatic Crash Reporting": "Автоматичне звітування про збої",
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Автоматиче оновлення зараз дозволяє обирати між стабільними випусками та реліз-кандидатами.",
|
||||
"Automatic upgrades": "Автоматичні оновлення",
|
||||
@@ -284,7 +284,6 @@
|
||||
"QR code": "QR-код",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"QUIC connections are in most cases considered suboptimal": "QUIC-з'єднання вважаються неоптимальними у більшості випадків",
|
||||
"Quick guide to supported patterns": "Короткий посібник по шаблонам, що підтримуються",
|
||||
"Random": "Випадково",
|
||||
"Receive Encrypted": "Отримувати зашифровано",
|
||||
@@ -507,10 +506,14 @@
|
||||
"items": "елементи",
|
||||
"modified": "змінено",
|
||||
"seconds": "секунд",
|
||||
"theme-name-black": "Чорна",
|
||||
"theme-name-dark": "Темна",
|
||||
"theme-name-default": "Стандартна",
|
||||
"theme-name-light": "Світла",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Чорна",
|
||||
"dark": "Темна",
|
||||
"default": "Стандартна",
|
||||
"light": "Світла"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} хоче поділитися папкою \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} хоче поділитися папкою \"{{folderLabel}}\" ({{folder}}).",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} може повторно порекомендувати цей пристрій."
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
"Device ID": "设备 ID",
|
||||
"Device Identification": "设备标识",
|
||||
"Device Name": "设备名",
|
||||
"Device Status": "设备状态",
|
||||
"Device is untrusted, enter encryption password": "设备不可信,请输入加密密码",
|
||||
"Device rate limits": "设备速率限制",
|
||||
"Device that last modified the item": "最近修改该项的设备",
|
||||
@@ -168,6 +169,7 @@
|
||||
"Folder ID": "文件夹 ID",
|
||||
"Folder Label": "文件夹标签",
|
||||
"Folder Path": "文件夹路径",
|
||||
"Folder Status": "文件夹状态",
|
||||
"Folder Type": "文件夹类型",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "只能在添加新文件夹时设置文件夹类型“{{receiveEncrypted}}”。",
|
||||
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "添加文件夹后,无法更改文件夹类型“ {{receiveEncrypted}}”。您需要删除该文件夹,删除或解密磁盘上的数据,然后再次添加该文件夹。",
|
||||
@@ -296,7 +298,7 @@
|
||||
"Please consult the release notes before performing a major upgrade.": "请在进行重大更新前查看发布说明。",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "请在设置对话框中设置 GUI 验证用户及其密码。",
|
||||
"Please wait": "请稍候",
|
||||
"Prefix indicating that the file can be deleted if preventing directory removal": "可以删除文件的前缀来防止目录删除",
|
||||
"Prefix indicating that the file can be deleted if preventing directory removal": "此前缀表示,如果文件阻止删除目录则文件可被删除",
|
||||
"Prefix indicating that the pattern should be matched without case sensitivity": "此前缀表示,后面的模式在匹配时不区分大小写",
|
||||
"Preparing to Sync": "准备同步",
|
||||
"Preview": "预览",
|
||||
@@ -304,7 +306,6 @@
|
||||
"QR code": "二维码",
|
||||
"QUIC LAN": "QUIC 局域网",
|
||||
"QUIC WAN": "QUIC 广域网",
|
||||
"QUIC connections are in most cases considered suboptimal": "QUIC 连接在多数情况下是次优的选择",
|
||||
"Quick guide to supported patterns": "支持的通配符的简单教程",
|
||||
"Random": "随机顺序",
|
||||
"Receive Encrypted": "加密接收",
|
||||
@@ -385,6 +386,7 @@
|
||||
"Staggered File Versioning": "阶段版本控制",
|
||||
"Start Browser": "启动浏览器",
|
||||
"Statistics": "统计",
|
||||
"Stay logged in": "保持登录",
|
||||
"Stopped": "已停止",
|
||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "仅存储和同步加密的数据。所有连接的设备上的文件夹也需要使用相同的密码设置,或者也必须设置为“ {{receiveEncrypted}}”类型。",
|
||||
"Subject:": "主题:",
|
||||
@@ -538,10 +540,15 @@
|
||||
"modified": "已修改",
|
||||
"permit": "允许",
|
||||
"seconds": "秒",
|
||||
"theme-name-black": "黑色",
|
||||
"theme-name-dark": "深色",
|
||||
"theme-name-default": "默认",
|
||||
"theme-name-light": "浅色",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "黑色",
|
||||
"dark": "深色",
|
||||
"default": "默认",
|
||||
"light": "浅色"
|
||||
}
|
||||
},
|
||||
"unknown device": "未知设备",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 想将 “{{folder}}” 文件夹共享给您。",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} 想要共享 \"{{folderlabel}}\" ({{folder}}) 文件夹给您。",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}}可能会重新引入此设备。"
|
||||
|
||||
@@ -281,7 +281,6 @@
|
||||
"Preview": "預覽",
|
||||
"Preview Usage Report": "預覽使用報告",
|
||||
"QR code": "二維碼",
|
||||
"QUIC connections are in most cases considered suboptimal": "QUIC 連接在大多數情況下被認為是次優的",
|
||||
"Quick guide to supported patterns": "支持的通配符的簡單教程:",
|
||||
"Random": "隨機順序",
|
||||
"Receive Encrypted": "接收加密",
|
||||
@@ -498,10 +497,6 @@
|
||||
"full documentation": "完整文檔",
|
||||
"items": "條目",
|
||||
"seconds": "秒",
|
||||
"theme-name-black": "theme-name-black",
|
||||
"theme-name-dark": "theme-name-dark",
|
||||
"theme-name-default": "theme-name-default",
|
||||
"theme-name-light": "theme-name-light",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{%device%} 想將 「{%folder%}」 文件夾共享給您。",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{%device%} 想要共享 \"{%folderlabel%}\" ({%folder%}) 文件夾給您。",
|
||||
"{%reintroducer%} might reintroduce this device.": "{%reintroducer%} 可能會重新引入此設備。"
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
{
|
||||
"A device with that ID is already added.": "該裝置識別碼已被新增。",
|
||||
"A negative number of days doesn't make sense.": "一個負的天數並不合理。",
|
||||
"A negative number of days doesn't make sense.": "無效的負數天數。",
|
||||
"A new major version may not be compatible with previous versions.": "新的主要版本可能與先前的版本不相容。",
|
||||
"API Key": "API 金鑰",
|
||||
"About": "關於",
|
||||
"Action": "操作",
|
||||
"Actions": "操作",
|
||||
"Active filter rules": "主動過濾規則",
|
||||
"Add": "新增",
|
||||
"Add Device": "新增裝置",
|
||||
"Add Folder": "添加資料夾",
|
||||
"Add Folder": "新增資料夾",
|
||||
"Add Remote Device": "新增遠端裝置",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "對於共享的資料夾,匯入引入者的裝置清單。",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "將引入者的裝置新增到我們的裝置列表,以互相分享資料夾。",
|
||||
"Add filter entry": "新增過濾器",
|
||||
"Add ignore patterns": "新增忽略模式",
|
||||
"Add new folder?": "新增資料夾?",
|
||||
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "另外,完整地重新掃瞄的間隔將增大(時間 60,例如:新的預設值為 1 小時)。您也可以在選擇「否」後,手動配置每個資料夾的時間間隔。",
|
||||
@@ -55,13 +57,16 @@
|
||||
"Configured": "已設定",
|
||||
"Connected (Unused)": "已連線(未使用)",
|
||||
"Connection Error": "連線錯誤",
|
||||
"Connection Management": "連線管理",
|
||||
"Connection Type": "連線類型",
|
||||
"Connections": "連線",
|
||||
"Connections via relays might be rate limited by the relay": "通過中繼的連線可能會受到中繼的速率限制",
|
||||
"Continuously watching for changes is now available within Syncthing. This will detect changes on disk and issue a scan on only the modified paths. The benefits are that changes are propagated quicker and that less full scans are required.": "Syncthing 現在能持續地監視變動了。此機制將偵測到磁碟上的變動並僅對修改過的項目發起掃描。優點是檔案的變動將更快地傳播,並且減少完整掃描的需求。",
|
||||
"Copied from elsewhere": "從別處複製",
|
||||
"Copied from original": "從原處複製",
|
||||
"Copied!": "已複製!",
|
||||
"Copy": "複製",
|
||||
"Copy failed! Try to select and copy manually.": "複製失敗!嘗試手動選擇並複製。",
|
||||
"Currently Shared With Devices": "目前與裝置共享",
|
||||
"Danger!": "危險!",
|
||||
"Debugging Facilities": "除錯工具",
|
||||
@@ -172,6 +177,7 @@
|
||||
"Ignore": "忽略",
|
||||
"Ignore Patterns": "忽略樣式",
|
||||
"Ignore Permissions": "忽略權限",
|
||||
"Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.": "忽略模式只能在建立資料夾之後新增。如果選取這個項目,會在儲存後顯示一個設定忽略模式的欄位。",
|
||||
"Ignored Devices": "已忽略的裝置",
|
||||
"Ignored Folders": "已忽略的資料夾",
|
||||
"Ignored at": "忽略時間",
|
||||
@@ -258,7 +264,6 @@
|
||||
"Preview Usage Report": "預覽數據報告",
|
||||
"QUIC LAN": "QUIC 區域網路",
|
||||
"QUIC WAN": "QUIC 廣域網路",
|
||||
"QUIC connections are in most cases considered suboptimal": "QUIC 連線在大多數情況下被認為是次優的",
|
||||
"Quick guide to supported patterns": "可支援樣式的快速指南",
|
||||
"Random": "隨機",
|
||||
"Receive Encrypted": "接收已加密",
|
||||
@@ -381,6 +386,7 @@
|
||||
"The number of old versions to keep, per file.": "每個檔案要保留的舊版本數量。",
|
||||
"The number of versions must be a number and cannot be blank.": "每個檔案要保留的舊版本數量必須是數字且不能為空白。",
|
||||
"The path cannot be blank.": "路徑不能空白。",
|
||||
"The rate limit is applied to the accumulated traffic of all connections to this device.": "速率限制會套用到這台裝置所有連線的流量總和。",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "限制速率必須為非負的數字 (0: 不設限制)",
|
||||
"The remote device has not accepted sharing this folder.": "遠端裝置尚未接受分享這個資料夾。",
|
||||
"The remote device has paused this folder.": "遠端裝置已暫停同步此資料夾。",
|
||||
@@ -441,6 +447,7 @@
|
||||
"Watching for changes discovers most changes without periodic scanning.": "監視變動會發現大多數變更,而無需定期掃描。",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "當新增一個裝置時,務必記住,當前的這個裝置也同樣必須被添加至另一邊。",
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "當新增一個資料夾時,請記住,資料夾識別碼是用來將裝置之間的資料夾綁定在一起的。它們有區分大小寫,且必須在所有裝置之間完全相同。",
|
||||
"When set to more than one on both devices, Syncthing will attempt to establish multiple concurrent connections. If the values differ, the highest will be used. Set to zero to let Syncthing decide.": "當兩台裝置都設定為大於 1 時,Syncthing 會嘗試建立多個並行的連線。如果兩台裝置數值不同,最高的數值會被使用。設定為 0 以便讓 Syncthing 自行決定。",
|
||||
"Yes": "是",
|
||||
"You can also select one of these nearby devices:": "您亦可從這些附近裝置中擇一:",
|
||||
"You can change your choice at any time in the Settings dialog.": "您可以在設定對話框中隨時更改您的選擇。",
|
||||
@@ -456,10 +463,14 @@
|
||||
"full documentation": "完整說明文件",
|
||||
"items": "個項目",
|
||||
"seconds": "秒",
|
||||
"theme-name-black": "黑色",
|
||||
"theme-name-dark": "深色",
|
||||
"theme-name-default": "預設",
|
||||
"theme-name-light": "淺色",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "黑色",
|
||||
"dark": "深色",
|
||||
"default": "預設",
|
||||
"light": "淺色"
|
||||
}
|
||||
},
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} 想要共享資料夾 \"{{folder}}\"。",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} 想要共享資料夾 \"{{folderlabel}}\" ({{folder}})。",
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} 可能會重新引入此裝置。"
|
||||
|
||||
@@ -1 +1 @@
|
||||
var langPrettyprint = {"bg":"Bulgarian","ca":"Catalan","ca@valencia":"Valencian","cs":"Czech","da":"Danish","de":"German","en":"English","en-GB":"English (United Kingdom)","es":"Spanish","eu":"Basque","fr":"French","fy":"Frisian","hu":"Hungarian","id":"Indonesian","it":"Italian","ja":"Japanese","ko-KR":"Korean","lt":"Lithuanian","nl":"Dutch","pl":"Polish","pt-BR":"Portuguese (Brazil)","pt-PT":"Portuguese (Portugal)","ro-RO":"Romanian","ru":"Russian","sk":"Slovak","sl":"Slovenian","sv":"Swedish","tr":"Turkish","uk":"Ukrainian","zh-CN":"Chinese (Simplified)","zh-HK":"Chinese (Traditional, Hong Kong)","zh-TW":"Chinese (Traditional)"}
|
||||
var langPrettyprint = {"ar":"Arabic","bg":"Bulgarian","ca":"Catalan","ca@valencia":"Valencian","cs":"Czech","da":"Danish","de":"German","en":"English","en-GB":"English (United Kingdom)","es":"Spanish","eu":"Basque","fr":"French","fy":"Frisian","hu":"Hungarian","id":"Indonesian","it":"Italian","ja":"Japanese","ko-KR":"Korean","lt":"Lithuanian","nl":"Dutch","pl":"Polish","pt-BR":"Portuguese (Brazil)","pt-PT":"Portuguese (Portugal)","ro-RO":"Romanian","ru":"Russian","sk":"Slovak","sl":"Slovenian","sv":"Swedish","tr":"Turkish","uk":"Ukrainian","zh-CN":"Chinese (Simplified)","zh-HK":"Chinese (Traditional, Hong Kong)","zh-TW":"Chinese (Traditional)"}
|
||||
|
||||
@@ -1 +1 @@
|
||||
var validLangs = ["bg","ca","ca@valencia","cs","da","de","en","en-GB","es","eu","fr","fy","hu","id","it","ja","ko-KR","lt","nl","pl","pt-BR","pt-PT","ro-RO","ru","sk","sl","sv","tr","uk","zh-CN","zh-HK","zh-TW"]
|
||||
var validLangs = ["ar","bg","ca","ca@valencia","cs","da","de","en","en-GB","es","eu","fr","fy","hu","id","it","ja","ko-KR","lt","nl","pl","pt-BR","pt-PT","ro-RO","ru","sk","sl","sv","tr","uk","zh-CN","zh-HK","zh-TW"]
|
||||
|
||||
@@ -359,6 +359,12 @@
|
||||
<input id="password" class="form-control" type="password" name="password" ng-model="login.password" ng-trim="false" autocomplete="current-password" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="login.stayLoggedIn" > <span translate>Stay logged in</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-9 login-form-messages">
|
||||
<p ng-if="login.errors.badLogin" class="text-danger" translate>
|
||||
@@ -369,7 +375,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-3 text-right">
|
||||
<button type="submit" class="btn btn-default" ng-disabled="login.inProgress" translate>Log In</button>
|
||||
<button type="submit" id="submit" class="btn btn-default" ng-disabled="login.inProgress" translate>Log In</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -395,37 +401,12 @@
|
||||
<span ng-if="folder.type == 'receiveencrypted'" class="fas fa-fw fa-lock"></span>
|
||||
</div>
|
||||
<div class="panel-status pull-right text-{{folderClass(folder)}}" ng-switch="folderStatus(folder)">
|
||||
<span ng-switch-when="paused"><span class="hidden-xs" translate>Paused</span><span class="visible-xs" aria-label="{{'Paused' | translate}}"><i class="fas fa-fw fa-pause"></i></span></span>
|
||||
<span ng-switch-when="unknown"><span class="hidden-xs" translate>Unknown</span><span class="visible-xs" aria-label="{{'Unknown' | translate}}"><i class="fas fa-fw fa-question-circle"></i></span></span>
|
||||
<span ng-switch-when="unshared"><span class="hidden-xs" translate>Unshared</span><span class="visible-xs" aria-label="{{'Unshared' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
|
||||
<span ng-switch-when="scan-waiting"><span class="hidden-xs" translate>Waiting to Scan</span><span class="visible-xs" aria-label="{{'Waiting to Scan' | translate}}"><i class="fas fa-fw fa-hourglass-half"></i></span></span>
|
||||
<span ng-switch-when="cleaning"><span class="hidden-xs" translate>Cleaning Versions</span><span class="visible-xs" aria-label="{{'Cleaning Versions' | translate}}"><i class="fas fa-fw fa-recycle"></i></span></span>
|
||||
<span ng-switch-when="clean-waiting"><span class="hidden-xs" translate>Waiting to Clean</span><span class="visible-xs" aria-label="{{'Waiting to Clean' | translate}}"><i class="fas fa-fw fa-hourglass-half"></i></span></span>
|
||||
<span ng-switch-when="stopped"><span class="hidden-xs" translate>Stopped</span><span class="visible-xs" aria-label="{{'Stopped' | translate}}"><i class="fas fa-fw fa-stop"></i></span></span>
|
||||
<span ng-switch-when="scanning">
|
||||
<span class="hidden-xs" translate>Scanning</span>
|
||||
<span class="hidden-xs" ng-if="scanPercentage(folder.id) != undefined">
|
||||
({{scanPercentage(folder.id) | percent}})
|
||||
</span>
|
||||
<span class="visible-xs" aria-label="{{'Scanning' | translate}}"><i class="fas fa-fw fa-search"></i></span>
|
||||
</span>
|
||||
<span ng-switch-when="idle"><span class="hidden-xs" translate>Up to Date</span><span class="visible-xs" aria-label="{{'Up to Date' | translate}}"><i class="fas fa-fw fa-check"></i></span></span>
|
||||
<span ng-switch-when="localadditions"><span class="hidden-xs" translate>Local Additions</span><span class="visible-xs" aria-label="{{'Local Additions' | translate}}"><i class="fas fa-fw fa-check"></i></span></span>
|
||||
<span ng-switch-when="sync-waiting">
|
||||
<span class="hidden-xs" translate>Waiting to Sync</span>
|
||||
<span class="visible-xs" aria-label="{{'Waiting to Sync' | translate}}"><i class="fas fa-fw fa-hourglass-half"></i></span>
|
||||
</span>
|
||||
<span ng-switch-when="sync-preparing">
|
||||
<span class="hidden-xs" translate>Preparing to Sync</span>
|
||||
<span class="visible-xs" aria-label="{{'Preparing to Sync' | translate}}"><i class="fas fa-fw fa-hourglass-half"></i></span>
|
||||
</span>
|
||||
<span ng-switch-when="syncing">
|
||||
<span class="hidden-xs" translate>Syncing</span>
|
||||
<span>({{syncPercentage(folder.id) | percent}}, {{model[folder.id].needBytes | binary}}B)</span>
|
||||
</span>
|
||||
<span ng-switch-when="outofsync"><span class="hidden-xs" translate>Out of Sync</span><span class="visible-xs" aria-label="{{'Out of Sync' | translate}}"><i class="fas fa-fw fa-exclamation-circle"></i></span></span>
|
||||
<span ng-switch-when="faileditems"><span class="hidden-xs" translate>Failed Items</span><span class="visible-xs" aria-label="{{'Failed Items' | translate}}"><i class="fas fa-fw fa-exclamation-circle"></i></span></span>
|
||||
<span ng-switch-when="localunencrypted"><span class="hidden-xs">{{'Unexpected Items' | translate}}</span><span class="visible-xs" aria-label="{{'Unexpected Items' | translate}}"><i class="fas fa-fw fa-exclamation-circle"></i></span></span>
|
||||
<span class="hidden-xs">{{folderStatusText(folder)}}</span>
|
||||
<span ng-switch-when="scanning" ng-if="scanPercentage(folder.id) != undefined">({{scanPercentage(folder.id) | percent}})</span>
|
||||
<span ng-switch-when="syncing">({{syncPercentage(folder.id) | percent}}, {{model[folder.id].needBytes | binary}}B)</span>
|
||||
<span class="inline-icon">
|
||||
<span class="visible-xs fa fa-fw {{folderStatusIcon(folder)}}" aria-label="{{folderStatusText(folder)}}"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="panel-title-text">
|
||||
<span tooltip data-original-title="{{folder.label.length != 0 ? folder.id : ''}}">{{folder.label.length != 0 ? folder.label : folder.id}}</span>
|
||||
@@ -436,6 +417,10 @@
|
||||
<div class="panel-body">
|
||||
<table class="table table-condensed table-striped table-auto">
|
||||
<tbody>
|
||||
<tr class="visible-xs">
|
||||
<th><span class="fa fa-fw {{folderStatusIcon(folder)}}"></span> <span translate>Folder Status</span></th>
|
||||
<td class="text-right">{{folderStatusText(folder)}}</td>
|
||||
</tr>
|
||||
<tr ng-show="folder.label != undefined && folder.label.length > 0">
|
||||
<th><span class="fas fa-fw fa-info-circle"></span> <span translate>Folder ID</span></th>
|
||||
<td class="text-right no-overflow-ellipse">{{folder.id}}</td>
|
||||
@@ -794,23 +779,16 @@
|
||||
<div class="panel-progress" ng-show="deviceStatus(deviceCfg) == 'syncing'" ng-attr-style="width: {{completion[deviceCfg.deviceID]._total | percent}}"></div>
|
||||
<h4 class="panel-title">
|
||||
<identicon class="panel-icon" data-value="deviceCfg.deviceID"></identicon>
|
||||
<span class="pull-right text-{{deviceClass(deviceCfg)}}">
|
||||
<span ng-switch="deviceStatus(deviceCfg)" class="remote-devices-panel">
|
||||
<span ng-switch-when="insync"><span class="hidden-xs" translate>Up to Date</span><span class="visible-xs" aria-label="{{'Up to Date' | translate}}"><i class="fas fa-fw fa-check"></i></span></span>
|
||||
<span ng-switch-when="unused-insync"><span class="hidden-xs" translate>Connected (Unused)</span><span class="visible-xs" aria-label="{{'Connected (Unused)' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
|
||||
<span ng-switch-when="syncing">
|
||||
<span class="hidden-xs" translate>Syncing</span> ({{completion[deviceCfg.deviceID]._total | percent}}, {{completion[deviceCfg.deviceID]._needBytes | binary}}B)
|
||||
</span>
|
||||
<span ng-switch-when="paused"><span class="hidden-xs" translate>Paused</span><span class="visible-xs" aria-label="{{'Paused' | translate}}"><i class="fas fa-fw fa-pause"></i></span></span>
|
||||
<span ng-switch-when="unused-paused"><span class="hidden-xs" translate>Paused (Unused)</span><span class="visible-xs" aria-label="{{'Paused (Unused)' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
|
||||
<span ng-switch-when="disconnected"><span class="hidden-xs" translate>Disconnected</span><span class="visible-xs" aria-label="{{'Disconnected' | translate}}"><i class="fas fa-fw fa-power-off"></i></span></span>
|
||||
<span ng-switch-when="disconnected-inactive"><span class="hidden-xs" translate>Disconnected (Inactive)</span><span class="visible-xs" aria-label="{{'Disconnected (Inactive)' | translate}}"><i class="fas fa-fw fa-power-off"></i></span></span>
|
||||
<span ng-switch-when="unused-disconnected"><span class="hidden-xs" translate>Disconnected (Unused)</span><span class="visible-xs" aria-label="{{'Disconnected (Unused)' | translate}}"><i class="fas fa-fw fa-unlink"></i></span></span>
|
||||
<div class="panel-status pull-right text-{{deviceClass(deviceCfg)}}" ng-switch="deviceStatus(deviceCfg)">
|
||||
<span class="hidden-xs">{{deviceStatusText(deviceCfg)}}</span>
|
||||
<span ng-switch-when="syncing">({{completion[deviceCfg.deviceID]._total | percent}}, {{completion[deviceCfg.deviceID]._needBytes | binary}}B)</span>
|
||||
<span class="inline-icon">
|
||||
<span class="visible-xs fa fa-fw {{deviceStatusIcon(deviceCfg)}}" aria-label="{{deviceStatusText(deviceCfg)}}"></span>
|
||||
</span>
|
||||
<span class="remote-devices-panel">
|
||||
<span class="inline-icon">
|
||||
<span ng-class="rdConnTypeIcon(rdConnType(deviceCfg.deviceID))" class="reception reception-theme"></span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="panel-title-text">{{deviceName(deviceCfg)}}</div>
|
||||
</h4>
|
||||
</button>
|
||||
@@ -818,6 +796,10 @@
|
||||
<div class="panel-body">
|
||||
<table class="table table-condensed table-striped table-auto">
|
||||
<tbody>
|
||||
<tr class="visible-xs">
|
||||
<th><span class="fa fa-fw {{deviceStatusIcon(deviceCfg)}}"></span> <span translate>Device Status</span></th>
|
||||
<td class="text-right">{{deviceStatusText(deviceCfg)}}</td>
|
||||
</tr>
|
||||
<tr ng-if="!connections[deviceCfg.deviceID].connected">
|
||||
<th><span class="fas fa-fw fa-eye"></span> <span translate>Last seen</span></th>
|
||||
<td class="text-right">
|
||||
@@ -934,7 +916,7 @@
|
||||
</tr>
|
||||
<tr ng-if="deviceCfg.introducedBy">
|
||||
<th><span class="far fa-fw fa-handshake-o"></span> <span translate>Introduced By</span></th>
|
||||
<td class="text-right">{{ deviceName(devices[deviceCfg.introducedBy]) || deviceCfg.introducedBy.substring(0, 5) }}</td>
|
||||
<td class="text-right">{{ deviceName(devices[deviceCfg.introducedBy]) || deviceShortID(deviceCfg.introducedBy) }}</td>
|
||||
</tr>
|
||||
<tr ng-if="deviceCfg.autoAcceptFolders">
|
||||
<th><span class="fa fa-fw fa-level-down"></span> <span translate>Auto Accept</span></th>
|
||||
|
||||
@@ -18,6 +18,9 @@ var syncthing = angular.module('syncthing', [
|
||||
var urlbase = 'rest';
|
||||
var authUrlbase = urlbase + '/noauth/auth';
|
||||
|
||||
// keep consistent with ShortIDStringLength in lib/protocol/deviceid.go
|
||||
var shortIDStringLength = 7;
|
||||
|
||||
syncthing.config(function ($httpProvider, $translateProvider, LocaleServiceProvider) {
|
||||
// language and localisation
|
||||
|
||||
@@ -26,6 +29,7 @@ syncthing.config(function ($httpProvider, $translateProvider, LocaleServiceProvi
|
||||
prefix: 'assets/lang/lang-',
|
||||
suffix: '.json'
|
||||
});
|
||||
$translateProvider.fallbackLanguage('en');
|
||||
|
||||
LocaleServiceProvider.setAvailableLocales(validLangs);
|
||||
LocaleServiceProvider.setDefaultLocale('en');
|
||||
@@ -38,9 +42,8 @@ syncthing.config(function ($httpProvider, $translateProvider, LocaleServiceProvi
|
||||
return;
|
||||
}
|
||||
|
||||
var deviceIDShort = metadata.deviceID.substr(0, 5);
|
||||
$httpProvider.defaults.xsrfHeaderName = 'X-CSRF-Token-' + deviceIDShort;
|
||||
$httpProvider.defaults.xsrfCookieName = 'CSRF-Token-' + deviceIDShort;
|
||||
$httpProvider.defaults.xsrfHeaderName = 'X-CSRF-Token-' + metadata.deviceIDShort;
|
||||
$httpProvider.defaults.xsrfCookieName = 'CSRF-Token-' + metadata.deviceIDShort;
|
||||
});
|
||||
|
||||
// @TODO: extract global level functions into separate service(s)
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<h4 class="text-center" translate>The Syncthing Authors</h4>
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="contributor-list">
|
||||
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, bt90, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Aleksey Vasenev, Alessandro G., Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Kujau, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, Devon G. Redekopp, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Emil Lundberg, Eng Zer Jun, Eric Lesiuta, Eric P, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jauder Ho, Jaya Chithra, Jaya Kumar, Jeffery To, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Jörg Thalheim, Jędrzej Kula, K.B.Dharun Krishna, Kalle Laine, Karol Różycki, Kebin Liu, Keith Harrison, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, LSmithx2, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Martin Polehla, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, Maximilian, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Naveen, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Shaarad Dalvi, Simon Mwepu, Sly_tom_cat, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, Vladimir Rusinov, Will Rouesnel, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, chenrui, chucic, cui fliter, d-volution, derekriemer, desbma, digital, entity0xfe, georgespatton, ghjklw, guangwu, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, luzpaz, marco-m, mclang, mv1005, orangekame3, otbutz, overkill, perewa, red_led, rubenbe, sec65, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙, 落心
|
||||
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Eric P, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, bt90, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Aleksey Vasenev, Alessandro G., Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Anatoli Babenia, Andreas Sommer, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Kujau, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Darshil Chanpura, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, DerRockWolf, Devon G. Redekopp, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Emil Lundberg, Eng Zer Jun, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jauder Ho, Jaya Chithra, Jaya Kumar, Jeffery To, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, K.B.Dharun Krishna, Kalle Laine, Karol Różycki, Kebin Liu, Keith Harrison, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, LSmithx2, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Martin Polehla, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, Maximilian, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Naveen, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Sertonix, Shaarad Dalvi, Simon Mwepu, Sly_tom_cat, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, Vladimir Rusinov, Will Rouesnel, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, chenrui, chucic, cjc7373, cui fliter, d-volution, derekriemer, desbma, diemade, digital, entity0xfe, georgespatton, ghjklw, guangwu, gudvinr, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, luzpaz, marco-m, mclang, mv1005, nf, orangekame3, otbutz, overkill, perewa, red_led, rubenbe, sec65, vapatel2, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙, 落心
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -103,6 +103,7 @@ angular.module('syncthing.core')
|
||||
$http.post(authUrlbase + '/password', {
|
||||
username: $scope.login.username,
|
||||
password: $scope.login.password,
|
||||
stayLoggedIn: $scope.login.stayLoggedIn,
|
||||
}).then(function () {
|
||||
location.reload();
|
||||
}).catch(function (response) {
|
||||
@@ -1039,18 +1040,29 @@ angular.module('syncthing.core')
|
||||
// Do the same thing in case we only have zero byte files to sync.
|
||||
return 95;
|
||||
}
|
||||
var pct = 100 * $scope.model[folder].inSyncBytes / $scope.model[folder].globalBytes;
|
||||
return Math.floor(pct);
|
||||
return progressIntegerPercentage($scope.model[folder].inSyncBytes, $scope.model[folder].globalBytes);
|
||||
};
|
||||
|
||||
$scope.scanPercentage = function (folder) {
|
||||
if (!$scope.scanProgress[folder]) {
|
||||
return undefined;
|
||||
}
|
||||
var pct = 100 * $scope.scanProgress[folder].current / $scope.scanProgress[folder].total;
|
||||
return Math.floor(pct);
|
||||
return progressIntegerPercentage($scope.scanProgress[folder].current, $scope.scanProgress[folder].total);
|
||||
};
|
||||
|
||||
function progressIntegerPercentage(current, total) {
|
||||
// Even after whatever is being tracked (e.g. hashed or synced
|
||||
// bytes) is completed, there's likely some more work to be done to
|
||||
// fully finish the process (db updates, ...). Users apparently
|
||||
// don't like seeing 100%, so give them 99% to indicate "about to be
|
||||
// finished".
|
||||
if (current === total) {
|
||||
return 99;
|
||||
}
|
||||
var pct = 100 * current / total;
|
||||
return Math.floor(pct);
|
||||
}
|
||||
|
||||
$scope.scanRate = function (folder) {
|
||||
if (!$scope.scanProgress[folder]) {
|
||||
return 0;
|
||||
@@ -1151,6 +1163,113 @@ angular.module('syncthing.core')
|
||||
}
|
||||
};
|
||||
|
||||
$scope.deviceStatusIcon = function(cfg) {
|
||||
switch ($scope.deviceStatus(cfg)) {
|
||||
case 'disconnected':
|
||||
case 'disconnected-inactive':
|
||||
return 'fa-power-off';
|
||||
case 'insync':
|
||||
return 'fa-check';
|
||||
case 'paused':
|
||||
return 'fa-pause';
|
||||
case 'syncing':
|
||||
return 'fa-sync';
|
||||
case 'unused-disconnected':
|
||||
case 'unused-insync':
|
||||
case 'unused-paused':
|
||||
return 'fa-unlink';
|
||||
}
|
||||
};
|
||||
|
||||
$scope.deviceStatusText = function(device) {
|
||||
switch ($scope.deviceStatus(device)) {
|
||||
case 'disconnected':
|
||||
return $translate.instant('Disconnected');
|
||||
case 'disconnected-inactive':
|
||||
return $translate.instant('Disconnected (Inactive)');
|
||||
case 'insync':
|
||||
return $translate.instant('Up to Date');
|
||||
case 'paused':
|
||||
return $translate.instant('Paused');
|
||||
case 'syncing':
|
||||
return $translate.instant('Syncing');
|
||||
case 'unused-disconnected':
|
||||
return $translate.instant('Disconnected (Unused)');
|
||||
case 'unused-insync':
|
||||
return $translate.instant('Connected (Unused)');
|
||||
case 'unused-paused':
|
||||
return $translate.instant('Paused (Unused)');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.folderStatusIcon = function(cfg) {
|
||||
switch ($scope.folderStatus(cfg)) {
|
||||
case 'clean-waiting':
|
||||
case 'scan-waiting':
|
||||
case 'sync-preparing':
|
||||
case 'sync-waiting':
|
||||
return 'fa-hourglass-half';
|
||||
case 'cleaning':
|
||||
return 'fa-recycle';
|
||||
case 'faileditems':
|
||||
case 'localunencrypted':
|
||||
case 'outofsync':
|
||||
return 'fa-exclamation-circle';
|
||||
case 'idle':
|
||||
case 'localadditions':
|
||||
return 'fa-check';
|
||||
case 'paused':
|
||||
return 'fa-pause';
|
||||
case 'scanning':
|
||||
return 'fa-search';
|
||||
case 'stopped':
|
||||
return 'fa-stop';
|
||||
case 'syncing':
|
||||
return 'fa-sync';
|
||||
case 'unknown':
|
||||
return 'fa-question-circle';
|
||||
case 'unshared':
|
||||
return 'fa-unlink';
|
||||
}
|
||||
};
|
||||
|
||||
$scope.folderStatusText = function(folder) {
|
||||
switch ($scope.folderStatus(folder)) {
|
||||
case 'clean-waiting':
|
||||
return $translate.instant('Waiting to Clean');
|
||||
case 'cleaning':
|
||||
return $translate.instant('Cleaning Versions');
|
||||
case 'faileditems':
|
||||
return $translate.instant('Failed Items');
|
||||
case 'idle':
|
||||
return $translate.instant('Up to Date');
|
||||
case 'localadditions':
|
||||
return $translate.instant('Local Additions');
|
||||
case 'localunencrypted':
|
||||
return $translate.instant('Unexpected Items');
|
||||
case 'outofsync':
|
||||
return $translate.instant('Out of Sync');
|
||||
case 'paused':
|
||||
return $translate.instant('Paused');
|
||||
case 'scan-waiting':
|
||||
return $translate.instant('Waiting to Scan');
|
||||
case 'scanning':
|
||||
return $translate.instant('Scanning');
|
||||
case 'stopped':
|
||||
return $translate.instant('Stopped');
|
||||
case 'sync-preparing':
|
||||
return $translate.instant('Preparing to Sync');
|
||||
case 'sync-waiting':
|
||||
return $translate.instant('Waiting to Sync');
|
||||
case 'syncing':
|
||||
return $translate.instant('Syncing');
|
||||
case 'unknown':
|
||||
return $translate.instant('Unknown');
|
||||
case 'unshared':
|
||||
return $translate.instant('Unshared');
|
||||
}
|
||||
};
|
||||
|
||||
$scope.deviceClass = function (deviceCfg) {
|
||||
if (typeof $scope.connections[deviceCfg.deviceID] === 'undefined') {
|
||||
return 'info';
|
||||
@@ -1342,7 +1461,7 @@ angular.module('syncthing.core')
|
||||
|
||||
$scope.friendlyNameFromShort = function (shortID) {
|
||||
var matches = Object.keys($scope.devices).filter(function (id) {
|
||||
return id.substr(0, 7) === shortID;
|
||||
return id.substr(0, shortIDStringLength) === shortID;
|
||||
});
|
||||
if (matches.length !== 1) {
|
||||
return shortID;
|
||||
@@ -1355,7 +1474,7 @@ angular.module('syncthing.core')
|
||||
if (match) {
|
||||
return $scope.deviceName(match);
|
||||
}
|
||||
return deviceID.substr(0, 6);
|
||||
return deviceID.substr(0, shortIDStringLength);
|
||||
};
|
||||
|
||||
$scope.deviceName = function (deviceCfg) {
|
||||
@@ -1372,18 +1491,18 @@ angular.module('syncthing.core')
|
||||
if (typeof deviceID === 'undefined') {
|
||||
return "";
|
||||
}
|
||||
return deviceID.substr(0, 6);
|
||||
return deviceID.substr(0, shortIDStringLength);
|
||||
};
|
||||
|
||||
$scope.thisDeviceName = function () {
|
||||
var device = $scope.thisDevice();
|
||||
if (typeof device === 'undefined') {
|
||||
return "(unknown device)";
|
||||
return '(' + $translate.instant("unknown device") + ')';
|
||||
}
|
||||
if (device.name) {
|
||||
return device.name;
|
||||
}
|
||||
return device.deviceID.substr(0, 6);
|
||||
return device.deviceID.substr(0, shortIDStringLength);
|
||||
};
|
||||
|
||||
$scope.showDeviceIdentification = function (deviceCfg) {
|
||||
@@ -3215,8 +3334,8 @@ angular.module('syncthing.core')
|
||||
};
|
||||
|
||||
$scope.themeName = function (theme) {
|
||||
var translation = $translate.instant("theme-name-" + theme);
|
||||
if (translation.indexOf("theme-name-") == 0) {
|
||||
var translation = $translate.instant("theme.name." + theme);
|
||||
if (translation.indexOf("theme.name.") == 0) {
|
||||
// Fall back to simple Title Casing on missing translation
|
||||
translation = theme.toLowerCase().replace(/(?:^|\s)\S/g, function (a) {
|
||||
return a.toUpperCase();
|
||||
@@ -3484,7 +3603,7 @@ angular.module('syncthing.core')
|
||||
return n.match !== "";
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// The showModal and hideModal functions are a bandaid for a Bootstrap
|
||||
// bug (see https://github.com/twbs/bootstrap/issues/3902) that causes
|
||||
// multiple consecutively shown or hidden modals to overlap which leads
|
||||
|
||||
@@ -344,7 +344,7 @@
|
||||
</colgroup>
|
||||
<tr ng-repeat="entry in currentFolder.xattrFilter.entries">
|
||||
<td>
|
||||
<input type="checkbox" ng-model="entry.permit">
|
||||
<input type="checkbox" ng-model="entry.permit" class="extended-attributes-filter-rule-checkbox"/>
|
||||
</td>
|
||||
<td><input class="form-control text-left" aria-required="true" ng-model="entry.match"/></td>
|
||||
<td>
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<modal id="restoreVersions" status="default" icon="fas fa-undo" heading="{{'Restore Versions' | translate}} ({{folderLabel(restoreVersions.folder)}})" large="yes" closeable="yes">
|
||||
<div class="modal-body">
|
||||
<!--
|
||||
Inform the user about loading data even before the tree is initialised
|
||||
to avoid confusion where nothing is displayed on the screen for a while.
|
||||
Ref: https://forum.syncthing.net/t/reversed-date-in-search-filter/21369/3
|
||||
-->
|
||||
<div ng-if="!restoreVersions.versions" translate>Loading data...</div>
|
||||
<div ng-if="restoreVersions.versions && !restoreVersions.errors">
|
||||
<div id="restoreTree-container">
|
||||
<table id="restoreTree">
|
||||
|
||||
@@ -47,7 +47,6 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/discover"
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/ignore"
|
||||
"github.com/syncthing/syncthing/lib/locations"
|
||||
"github.com/syncthing/syncthing/lib/logger"
|
||||
"github.com/syncthing/syncthing/lib/model"
|
||||
@@ -92,6 +91,7 @@ type service struct {
|
||||
startupErr error
|
||||
listenerAddr net.Addr
|
||||
exitChan chan *svcutil.FatalErr
|
||||
miscDB *db.NamespacedKV
|
||||
|
||||
guiErrors logger.Recorder
|
||||
systemLog logger.Recorder
|
||||
@@ -105,7 +105,7 @@ type Service interface {
|
||||
WaitForStart() error
|
||||
}
|
||||
|
||||
func New(id protocol.DeviceID, cfg config.Wrapper, assetDir, tlsDefaultCommonName string, m model.Model, defaultSub, diskSub events.BufferedSubscription, evLogger events.Logger, discoverer discover.Manager, connectionsService connections.Service, urService *ur.Service, fss model.FolderSummaryService, errors, systemLog logger.Recorder, noUpgrade bool) Service {
|
||||
func New(id protocol.DeviceID, cfg config.Wrapper, assetDir, tlsDefaultCommonName string, m model.Model, defaultSub, diskSub events.BufferedSubscription, evLogger events.Logger, discoverer discover.Manager, connectionsService connections.Service, urService *ur.Service, fss model.FolderSummaryService, errors, systemLog logger.Recorder, noUpgrade bool, miscDB *db.NamespacedKV) Service {
|
||||
return &service{
|
||||
id: id,
|
||||
cfg: cfg,
|
||||
@@ -128,6 +128,7 @@ func New(id protocol.DeviceID, cfg config.Wrapper, assetDir, tlsDefaultCommonNam
|
||||
configChanged: make(chan struct{}),
|
||||
startedOnce: make(chan struct{}),
|
||||
exitChan: make(chan *svcutil.FatalErr, 1),
|
||||
miscDB: miscDB,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,20 +366,21 @@ func (s *service) Serve(ctx context.Context) error {
|
||||
|
||||
// Wrap everything in CSRF protection. The /rest prefix should be
|
||||
// protected, other requests will grant cookies.
|
||||
var handler http.Handler = newCsrfManager(s.id.String()[:5], "/rest", guiCfg, mux, locations.Get(locations.CsrfTokens))
|
||||
var handler http.Handler = newCsrfManager(s.id.Short().String(), "/rest", guiCfg, mux, s.miscDB)
|
||||
|
||||
// Add our version and ID as a header to responses
|
||||
handler = withDetailsMiddleware(s.id, handler)
|
||||
|
||||
// Wrap everything in basic auth, if user/password is set.
|
||||
if guiCfg.IsAuthEnabled() {
|
||||
sessionCookieName := "sessionid-" + s.id.String()[:5]
|
||||
handler = basicAuthAndSessionMiddleware(sessionCookieName, guiCfg, s.cfg.LDAP(), handler, s.evLogger)
|
||||
handlePasswordAuth := passwordAuthHandler(sessionCookieName, guiCfg, s.cfg.LDAP(), s.evLogger)
|
||||
restMux.Handler(http.MethodPost, "/rest/noauth/auth/password", handlePasswordAuth)
|
||||
sessionCookieName := "sessionid-" + s.id.Short().String()
|
||||
authMW := newBasicAuthAndSessionMiddleware(sessionCookieName, s.id.Short().String(), guiCfg, s.cfg.LDAP(), handler, s.evLogger, s.miscDB)
|
||||
handler = authMW
|
||||
|
||||
restMux.Handler(http.MethodPost, "/rest/noauth/auth/password", http.HandlerFunc(authMW.passwordAuthHandler))
|
||||
|
||||
// Logout is a no-op without a valid session cookie, so /noauth/ is fine here
|
||||
restMux.Handler(http.MethodPost, "/rest/noauth/auth/logout", handleLogout(sessionCookieName))
|
||||
restMux.Handler(http.MethodPost, "/rest/noauth/auth/logout", http.HandlerFunc(authMW.handleLogout))
|
||||
}
|
||||
|
||||
// Redirect to HTTPS if we are supposed to
|
||||
@@ -719,6 +721,7 @@ func (*service) getSystemPaths(w http.ResponseWriter, _ *http.Request) {
|
||||
func (s *service) getJSMetadata(w http.ResponseWriter, _ *http.Request) {
|
||||
meta, _ := json.Marshal(map[string]interface{}{
|
||||
"deviceID": s.id.String(),
|
||||
"deviceIDShort": s.id.Short().String(),
|
||||
"authenticated": true,
|
||||
})
|
||||
w.Header().Set("Content-Type", "application/javascript")
|
||||
@@ -1348,11 +1351,6 @@ func (s *service) getDBIgnores(w http.ResponseWriter, r *http.Request) {
|
||||
folder := qs.Get("folder")
|
||||
|
||||
lines, patterns, err := s.model.LoadIgnores(folder)
|
||||
if err != nil && !ignore.IsParseError(err) {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
sendJSON(w, map[string]interface{}{
|
||||
"ignore": lines,
|
||||
"expanded": patterns,
|
||||
|
||||
@@ -16,15 +16,16 @@ import (
|
||||
|
||||
ldap "github.com/go-ldap/ldap/v3"
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/db"
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var (
|
||||
sessions = make(map[string]bool)
|
||||
sessionsMut = sync.NewMutex()
|
||||
const (
|
||||
maxSessionLifetime = 7 * 24 * time.Hour
|
||||
maxActiveSessions = 25
|
||||
randomTokenLength = 64
|
||||
)
|
||||
|
||||
func emitLoginAttempt(success bool, username, address string, evLogger events.Logger) {
|
||||
@@ -42,8 +43,8 @@ func antiBruteForceSleep() {
|
||||
time.Sleep(time.Duration(rand.Intn(100)+100) * time.Millisecond)
|
||||
}
|
||||
|
||||
func unauthorized(w http.ResponseWriter) {
|
||||
w.Header().Set("WWW-Authenticate", "Basic realm=\"Authorization Required\"")
|
||||
func unauthorized(w http.ResponseWriter, shortID string) {
|
||||
w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm="Authorization Required (%s)"`, shortID))
|
||||
http.Error(w, "Not Authorized", http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
@@ -78,70 +79,91 @@ func isNoAuthPath(path string) bool {
|
||||
})
|
||||
}
|
||||
|
||||
func basicAuthAndSessionMiddleware(cookieName string, guiCfg config.GUIConfiguration, ldapCfg config.LDAPConfiguration, next http.Handler, evLogger events.Logger) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if hasValidAPIKeyHeader(r, guiCfg) {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
type basicAuthAndSessionMiddleware struct {
|
||||
cookieName string
|
||||
shortID string
|
||||
guiCfg config.GUIConfiguration
|
||||
ldapCfg config.LDAPConfiguration
|
||||
next http.Handler
|
||||
evLogger events.Logger
|
||||
tokens *tokenManager
|
||||
}
|
||||
|
||||
cookie, err := r.Cookie(cookieName)
|
||||
if err == nil && cookie != nil {
|
||||
sessionsMut.Lock()
|
||||
_, ok := sessions[cookie.Value]
|
||||
sessionsMut.Unlock()
|
||||
if ok {
|
||||
next.ServeHTTP(w, r)
|
||||
func newBasicAuthAndSessionMiddleware(cookieName, shortID string, guiCfg config.GUIConfiguration, ldapCfg config.LDAPConfiguration, next http.Handler, evLogger events.Logger, miscDB *db.NamespacedKV) *basicAuthAndSessionMiddleware {
|
||||
return &basicAuthAndSessionMiddleware{
|
||||
cookieName: cookieName,
|
||||
shortID: shortID,
|
||||
guiCfg: guiCfg,
|
||||
ldapCfg: ldapCfg,
|
||||
next: next,
|
||||
evLogger: evLogger,
|
||||
tokens: newTokenManager("sessions", miscDB, maxSessionLifetime, maxActiveSessions),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *basicAuthAndSessionMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if hasValidAPIKeyHeader(r, m.guiCfg) {
|
||||
m.next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
for _, cookie := range r.Cookies() {
|
||||
// We iterate here since there may, historically, be multiple
|
||||
// cookies with the same name but different path. Any "old" ones
|
||||
// won't match an existing session and will be ignored, then
|
||||
// later removed on logout or when timing out.
|
||||
if cookie.Name == m.cookieName {
|
||||
if m.tokens.Check(cookie.Value) {
|
||||
m.next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to Basic auth if provided
|
||||
if username, ok := attemptBasicAuth(r, guiCfg, ldapCfg, evLogger); ok {
|
||||
createSession(cookieName, username, guiCfg, evLogger, w, r)
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
// Fall back to Basic auth if provided
|
||||
if username, ok := attemptBasicAuth(r, m.guiCfg, m.ldapCfg, m.evLogger); ok {
|
||||
m.createSession(username, false, w, r)
|
||||
m.next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Exception for static assets and REST calls that don't require authentication.
|
||||
if isNoAuthPath(r.URL.Path) {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
// Exception for static assets and REST calls that don't require authentication.
|
||||
if isNoAuthPath(r.URL.Path) {
|
||||
m.next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Some browsers don't send the Authorization request header unless prompted by a 401 response.
|
||||
// This enables https://user:pass@localhost style URLs to keep working.
|
||||
if guiCfg.SendBasicAuthPrompt {
|
||||
unauthorized(w)
|
||||
return
|
||||
}
|
||||
// Some browsers don't send the Authorization request header unless prompted by a 401 response.
|
||||
// This enables https://user:pass@localhost style URLs to keep working.
|
||||
if m.guiCfg.SendBasicAuthPrompt {
|
||||
unauthorized(w, m.shortID)
|
||||
return
|
||||
}
|
||||
|
||||
forbidden(w)
|
||||
})
|
||||
forbidden(w)
|
||||
}
|
||||
|
||||
func passwordAuthHandler(cookieName string, guiCfg config.GUIConfiguration, ldapCfg config.LDAPConfiguration, evLogger events.Logger) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
if err := unmarshalTo(r.Body, &req); err != nil {
|
||||
l.Debugln("Failed to parse username and password:", err)
|
||||
http.Error(w, "Failed to parse username and password.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
func (m *basicAuthAndSessionMiddleware) passwordAuthHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Username string
|
||||
Password string
|
||||
StayLoggedIn bool
|
||||
}
|
||||
if err := unmarshalTo(r.Body, &req); err != nil {
|
||||
l.Debugln("Failed to parse username and password:", err)
|
||||
http.Error(w, "Failed to parse username and password.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if auth(req.Username, req.Password, guiCfg, ldapCfg) {
|
||||
createSession(cookieName, req.Username, guiCfg, evLogger, w, r)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
if auth(req.Username, req.Password, m.guiCfg, m.ldapCfg) {
|
||||
m.createSession(req.Username, req.StayLoggedIn, w, r)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
emitLoginAttempt(false, req.Username, r.RemoteAddr, evLogger)
|
||||
antiBruteForceSleep()
|
||||
forbidden(w)
|
||||
})
|
||||
emitLoginAttempt(false, req.Username, r.RemoteAddr, m.evLogger)
|
||||
antiBruteForceSleep()
|
||||
forbidden(w)
|
||||
}
|
||||
|
||||
func attemptBasicAuth(r *http.Request, guiCfg config.GUIConfiguration, ldapCfg config.LDAPConfiguration, evLogger events.Logger) (string, bool) {
|
||||
@@ -167,11 +189,8 @@ func attemptBasicAuth(r *http.Request, guiCfg config.GUIConfiguration, ldapCfg c
|
||||
return "", false
|
||||
}
|
||||
|
||||
func createSession(cookieName string, username string, guiCfg config.GUIConfiguration, evLogger events.Logger, w http.ResponseWriter, r *http.Request) {
|
||||
sessionid := rand.String(32)
|
||||
sessionsMut.Lock()
|
||||
sessions[sessionid] = true
|
||||
sessionsMut.Unlock()
|
||||
func (m *basicAuthAndSessionMiddleware) createSession(username string, persistent bool, w http.ResponseWriter, r *http.Request) {
|
||||
sessionid := m.tokens.New()
|
||||
|
||||
// Best effort detection of whether the connection is HTTPS --
|
||||
// either directly to us, or as used by the client towards a reverse
|
||||
@@ -181,40 +200,45 @@ func createSession(cookieName string, username string, guiCfg config.GUIConfigur
|
||||
strings.Contains(strings.ToLower(r.Header.Get("forwarded")), "proto=https")
|
||||
// If the connection is HTTPS, or *should* be HTTPS, set the Secure
|
||||
// bit in cookies.
|
||||
useSecureCookie := connectionIsHTTPS || guiCfg.UseTLS()
|
||||
useSecureCookie := connectionIsHTTPS || m.guiCfg.UseTLS()
|
||||
|
||||
maxAge := 0
|
||||
if persistent {
|
||||
maxAge = int(maxSessionLifetime.Seconds())
|
||||
}
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieName,
|
||||
Name: m.cookieName,
|
||||
Value: sessionid,
|
||||
// In HTTP spec Max-Age <= 0 means delete immediately,
|
||||
// but in http.Cookie MaxAge = 0 means unspecified (session) and MaxAge < 0 means delete immediately
|
||||
MaxAge: 0,
|
||||
MaxAge: maxAge,
|
||||
Secure: useSecureCookie,
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
emitLoginAttempt(true, username, r.RemoteAddr, evLogger)
|
||||
emitLoginAttempt(true, username, r.RemoteAddr, m.evLogger)
|
||||
}
|
||||
|
||||
func handleLogout(cookieName string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
cookie, err := r.Cookie(cookieName)
|
||||
if err == nil && cookie != nil {
|
||||
sessionsMut.Lock()
|
||||
delete(sessions, cookie.Value)
|
||||
sessionsMut.Unlock()
|
||||
}
|
||||
// else: If there is no session cookie, that's also a successful logout in terms of user experience.
|
||||
func (m *basicAuthAndSessionMiddleware) handleLogout(w http.ResponseWriter, r *http.Request) {
|
||||
for _, cookie := range r.Cookies() {
|
||||
// We iterate here since there may, historically, be multiple
|
||||
// cookies with the same name but different path. We drop them
|
||||
// all.
|
||||
if cookie.Name == m.cookieName {
|
||||
m.tokens.Delete(cookie.Value)
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieName,
|
||||
Value: "",
|
||||
MaxAge: -1,
|
||||
Secure: true,
|
||||
Path: "/",
|
||||
})
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
// Delete the cookie
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: m.cookieName,
|
||||
Value: "",
|
||||
MaxAge: -1,
|
||||
Secure: cookie.Secure,
|
||||
Path: cookie.Path,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func auth(username string, password string, guiCfg config.GUIConfiguration, ldapCfg config.LDAPConfiguration) bool {
|
||||
|
||||
@@ -8,8 +8,12 @@ package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/db"
|
||||
"github.com/syncthing/syncthing/lib/db/backend"
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
)
|
||||
|
||||
var guiCfg config.GUIConfiguration
|
||||
@@ -110,3 +114,76 @@ func TestEscapeForLDAPDN(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type mockClock struct {
|
||||
now time.Time
|
||||
}
|
||||
|
||||
func (c *mockClock) Now() time.Time {
|
||||
c.now = c.now.Add(1) // time always ticks by at least 1 ns
|
||||
return c.now
|
||||
}
|
||||
|
||||
func (c *mockClock) wind(t time.Duration) {
|
||||
c.now = c.now.Add(t)
|
||||
}
|
||||
|
||||
func TestTokenManager(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mdb, _ := db.NewLowlevel(backend.OpenMemory(), events.NoopLogger)
|
||||
kdb := db.NewNamespacedKV(mdb, "test")
|
||||
clock := &mockClock{now: time.Now()}
|
||||
|
||||
// Token manager keeps up to three tokens with a validity time of 24 hours.
|
||||
tm := newTokenManager("testTokens", kdb, 24*time.Hour, 3)
|
||||
tm.timeNow = clock.Now
|
||||
|
||||
// Create three tokens
|
||||
t0 := tm.New()
|
||||
t1 := tm.New()
|
||||
t2 := tm.New()
|
||||
|
||||
// Check that the tokens are valid
|
||||
if !tm.Check(t0) {
|
||||
t.Errorf("token %q should be valid", t0)
|
||||
}
|
||||
if !tm.Check(t1) {
|
||||
t.Errorf("token %q should be valid", t1)
|
||||
}
|
||||
if !tm.Check(t2) {
|
||||
t.Errorf("token %q should be valid", t2)
|
||||
}
|
||||
|
||||
// Create a fourth token
|
||||
t3 := tm.New()
|
||||
// It should be valid
|
||||
if !tm.Check(t3) {
|
||||
t.Errorf("token %q should be valid", t3)
|
||||
}
|
||||
// But the first token should have been removed
|
||||
if tm.Check(t0) {
|
||||
t.Errorf("token %q should be invalid", t0)
|
||||
}
|
||||
|
||||
// Wind the clock by 12 hours
|
||||
clock.wind(12 * time.Hour)
|
||||
// The second token should still be valid (and checking it will give it more life)
|
||||
if !tm.Check(t1) {
|
||||
t.Errorf("token %q should be valid", t1)
|
||||
}
|
||||
|
||||
// Wind the clock by 12 hours
|
||||
clock.wind(12 * time.Hour)
|
||||
// The second token should still be valid
|
||||
if !tm.Check(t1) {
|
||||
t.Errorf("token %q should be valid", t1)
|
||||
}
|
||||
// But the third and fourth tokens should have expired
|
||||
if tm.Check(t2) {
|
||||
t.Errorf("token %q should be invalid", t2)
|
||||
}
|
||||
if tm.Check(t3) {
|
||||
t.Errorf("token %q should be invalid", t3)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,33 +7,24 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
"github.com/syncthing/syncthing/lib/db"
|
||||
)
|
||||
|
||||
const maxCsrfTokens = 25
|
||||
const (
|
||||
maxCSRFTokenLifetime = time.Hour
|
||||
maxActiveCSRFTokens = 25
|
||||
)
|
||||
|
||||
type csrfManager struct {
|
||||
// tokens is a list of valid tokens. It is sorted so that the most
|
||||
// recently used token is first in the list. New tokens are added to the front
|
||||
// of the list (as it is the most recently used at that time). The list is
|
||||
// pruned to a maximum of maxCsrfTokens, throwing away the least recently used
|
||||
// tokens.
|
||||
tokens []string
|
||||
tokensMut sync.Mutex
|
||||
|
||||
unique string
|
||||
prefix string
|
||||
apiKeyValidator apiKeyValidator
|
||||
next http.Handler
|
||||
saveLocation string
|
||||
tokens *tokenManager
|
||||
}
|
||||
|
||||
type apiKeyValidator interface {
|
||||
@@ -43,17 +34,14 @@ type apiKeyValidator interface {
|
||||
// Check for CSRF token on /rest/ URLs. If a correct one is not given, reject
|
||||
// the request with 403. For / and /index.html, set a new CSRF cookie if none
|
||||
// is currently set.
|
||||
func newCsrfManager(unique string, prefix string, apiKeyValidator apiKeyValidator, next http.Handler, saveLocation string) *csrfManager {
|
||||
func newCsrfManager(unique string, prefix string, apiKeyValidator apiKeyValidator, next http.Handler, miscDB *db.NamespacedKV) *csrfManager {
|
||||
m := &csrfManager{
|
||||
tokensMut: sync.NewMutex(),
|
||||
tokens: make([]string, 0, maxCsrfTokens),
|
||||
unique: unique,
|
||||
prefix: prefix,
|
||||
apiKeyValidator: apiKeyValidator,
|
||||
next: next,
|
||||
saveLocation: saveLocation,
|
||||
tokens: newTokenManager("csrfTokens", miscDB, maxCSRFTokenLifetime, maxActiveCSRFTokens),
|
||||
}
|
||||
m.load()
|
||||
return m
|
||||
}
|
||||
|
||||
@@ -78,11 +66,11 @@ func (m *csrfManager) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// and set a CSRF cookie if there isn't already a valid one.
|
||||
if !strings.HasPrefix(r.URL.Path, m.prefix) {
|
||||
cookie, err := r.Cookie("CSRF-Token-" + m.unique)
|
||||
if err != nil || !m.validToken(cookie.Value) {
|
||||
if err != nil || !m.tokens.Check(cookie.Value) {
|
||||
l.Debugln("new CSRF cookie in response to request for", r.URL)
|
||||
cookie = &http.Cookie{
|
||||
Name: "CSRF-Token-" + m.unique,
|
||||
Value: m.newToken(),
|
||||
Value: m.tokens.New(),
|
||||
}
|
||||
http.SetCookie(w, cookie)
|
||||
}
|
||||
@@ -99,7 +87,7 @@ func (m *csrfManager) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Verify the CSRF token
|
||||
token := r.Header.Get("X-CSRF-Token-" + m.unique)
|
||||
if !m.validToken(token) {
|
||||
if !m.tokens.Check(token) {
|
||||
http.Error(w, "CSRF Error", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
@@ -107,78 +95,6 @@ func (m *csrfManager) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
m.next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (m *csrfManager) validToken(token string) bool {
|
||||
m.tokensMut.Lock()
|
||||
defer m.tokensMut.Unlock()
|
||||
for i, t := range m.tokens {
|
||||
if t == token {
|
||||
if i > 0 {
|
||||
// Move this token to the head of the list. Copy the tokens at
|
||||
// the front one step to the right and then replace the token
|
||||
// at the head.
|
||||
copy(m.tokens[1:], m.tokens[:i])
|
||||
m.tokens[0] = token
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *csrfManager) newToken() string {
|
||||
token := rand.String(32)
|
||||
|
||||
m.tokensMut.Lock()
|
||||
defer m.tokensMut.Unlock()
|
||||
|
||||
if len(m.tokens) < maxCsrfTokens {
|
||||
m.tokens = append(m.tokens, "")
|
||||
}
|
||||
copy(m.tokens[1:], m.tokens)
|
||||
m.tokens[0] = token
|
||||
|
||||
m.save()
|
||||
|
||||
return token
|
||||
}
|
||||
|
||||
func (m *csrfManager) save() {
|
||||
// We're ignoring errors in here. It's not super critical and there's
|
||||
// nothing relevant we can do about them anyway...
|
||||
|
||||
if m.saveLocation == "" {
|
||||
return
|
||||
}
|
||||
|
||||
f, err := osutil.CreateAtomic(m.saveLocation)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, t := range m.tokens {
|
||||
fmt.Fprintln(f, t)
|
||||
}
|
||||
|
||||
f.Close()
|
||||
}
|
||||
|
||||
func (m *csrfManager) load() {
|
||||
if m.saveLocation == "" {
|
||||
return
|
||||
}
|
||||
|
||||
f, err := os.Open(m.saveLocation)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
m.tokens = append(m.tokens, s.Text())
|
||||
}
|
||||
}
|
||||
|
||||
func hasValidAPIKeyHeader(r *http.Request, validator apiKeyValidator) bool {
|
||||
if key := r.Header.Get("X-API-Key"); validator.IsValidAPIKey(key) {
|
||||
return true
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -29,6 +28,8 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/build"
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
connmocks "github.com/syncthing/syncthing/lib/connections/mocks"
|
||||
"github.com/syncthing/syncthing/lib/db"
|
||||
"github.com/syncthing/syncthing/lib/db/backend"
|
||||
discovermocks "github.com/syncthing/syncthing/lib/discover/mocks"
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
eventmocks "github.com/syncthing/syncthing/lib/events/mocks"
|
||||
@@ -72,71 +73,6 @@ func TestMain(m *testing.M) {
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
func TestCSRFToken(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
max := 10 * maxCsrfTokens
|
||||
int := 5
|
||||
if testing.Short() {
|
||||
max = 1 + maxCsrfTokens
|
||||
int = 2
|
||||
}
|
||||
|
||||
m := newCsrfManager("unique", "prefix", config.GUIConfiguration{}, nil, "")
|
||||
|
||||
t1 := m.newToken()
|
||||
t2 := m.newToken()
|
||||
|
||||
t3 := m.newToken()
|
||||
if !m.validToken(t3) {
|
||||
t.Fatal("t3 should be valid")
|
||||
}
|
||||
|
||||
valid := make(map[string]struct{}, maxCsrfTokens)
|
||||
for _, token := range m.tokens {
|
||||
valid[token] = struct{}{}
|
||||
}
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
if i%int == 0 {
|
||||
// t1 and t2 should remain valid by virtue of us checking them now
|
||||
// and then.
|
||||
if !m.validToken(t1) {
|
||||
t.Fatal("t1 should be valid at iteration", i)
|
||||
}
|
||||
if !m.validToken(t2) {
|
||||
t.Fatal("t2 should be valid at iteration", i)
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.tokens) == maxCsrfTokens {
|
||||
// We're about to add a token, which will remove the last token
|
||||
// from m.tokens.
|
||||
delete(valid, m.tokens[len(m.tokens)-1])
|
||||
}
|
||||
|
||||
// The newly generated token is always valid
|
||||
t4 := m.newToken()
|
||||
if !m.validToken(t4) {
|
||||
t.Fatal("t4 should be valid at iteration", i)
|
||||
}
|
||||
valid[t4] = struct{}{}
|
||||
|
||||
v := make(map[string]struct{}, maxCsrfTokens)
|
||||
for _, token := range m.tokens {
|
||||
v[token] = struct{}{}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(v, valid) {
|
||||
t.Fatalf("want valid tokens %v, got %v", valid, v)
|
||||
}
|
||||
}
|
||||
|
||||
if m.validToken(t3) {
|
||||
t.Fatal("t3 should have expired by now")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopAfterBrokenConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -148,7 +84,9 @@ func TestStopAfterBrokenConfig(t *testing.T) {
|
||||
}
|
||||
w := config.Wrap("/dev/null", cfg, protocol.LocalDeviceID, events.NoopLogger)
|
||||
|
||||
srv := New(protocol.LocalDeviceID, w, "", "syncthing", nil, nil, nil, events.NoopLogger, nil, nil, nil, nil, nil, nil, false).(*service)
|
||||
mdb, _ := db.NewLowlevel(backend.OpenMemory(), events.NoopLogger)
|
||||
kdb := db.NewMiscDataNamespace(mdb)
|
||||
srv := New(protocol.LocalDeviceID, w, "", "syncthing", nil, nil, nil, events.NoopLogger, nil, nil, nil, nil, nil, nil, false, kdb).(*service)
|
||||
defer os.Remove(token)
|
||||
|
||||
srv.started = make(chan string)
|
||||
@@ -926,7 +864,9 @@ func startHTTP(cfg config.Wrapper) (string, context.CancelFunc, error) {
|
||||
|
||||
// Instantiate the API service
|
||||
urService := ur.New(cfg, m, connections, false)
|
||||
svc := New(protocol.LocalDeviceID, cfg, assetDir, "syncthing", m, eventSub, diskEventSub, events.NoopLogger, discoverer, connections, urService, mockedSummary, errorLog, systemLog, false).(*service)
|
||||
mdb, _ := db.NewLowlevel(backend.OpenMemory(), events.NoopLogger)
|
||||
kdb := db.NewMiscDataNamespace(mdb)
|
||||
svc := New(protocol.LocalDeviceID, cfg, assetDir, "syncthing", m, eventSub, diskEventSub, events.NoopLogger, discoverer, connections, urService, mockedSummary, errorLog, systemLog, false, kdb).(*service)
|
||||
defer os.Remove(token)
|
||||
svc.started = addrChan
|
||||
|
||||
@@ -1467,7 +1407,9 @@ func TestEventMasks(t *testing.T) {
|
||||
cfg := newMockedConfig()
|
||||
defSub := new(eventmocks.BufferedSubscription)
|
||||
diskSub := new(eventmocks.BufferedSubscription)
|
||||
svc := New(protocol.LocalDeviceID, cfg, "", "syncthing", nil, defSub, diskSub, events.NoopLogger, nil, nil, nil, nil, nil, nil, false).(*service)
|
||||
mdb, _ := db.NewLowlevel(backend.OpenMemory(), events.NoopLogger)
|
||||
kdb := db.NewMiscDataNamespace(mdb)
|
||||
svc := New(protocol.LocalDeviceID, cfg, "", "syncthing", nil, defSub, diskSub, events.NoopLogger, nil, nil, nil, nil, nil, nil, false, kdb).(*service)
|
||||
defer os.Remove(token)
|
||||
|
||||
if mask := svc.getEventMask(""); mask != DefaultEventMask {
|
||||
|
||||
137
lib/api/tokenmanager.go
Normal file
137
lib/api/tokenmanager.go
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright (C) 2024 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/db"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type tokenManager struct {
|
||||
key string
|
||||
miscDB *db.NamespacedKV
|
||||
lifetime time.Duration
|
||||
maxItems int
|
||||
|
||||
timeNow func() time.Time // can be overridden for testing
|
||||
|
||||
mut sync.Mutex
|
||||
tokens *TokenSet
|
||||
saveTimer *time.Timer
|
||||
}
|
||||
|
||||
func newTokenManager(key string, miscDB *db.NamespacedKV, lifetime time.Duration, maxItems int) *tokenManager {
|
||||
tokens := &TokenSet{
|
||||
Tokens: make(map[string]int64),
|
||||
}
|
||||
if bs, ok, _ := miscDB.Bytes(key); ok {
|
||||
_ = tokens.Unmarshal(bs) // best effort
|
||||
}
|
||||
return &tokenManager{
|
||||
key: key,
|
||||
miscDB: miscDB,
|
||||
lifetime: lifetime,
|
||||
maxItems: maxItems,
|
||||
timeNow: time.Now,
|
||||
mut: sync.NewMutex(),
|
||||
tokens: tokens,
|
||||
}
|
||||
}
|
||||
|
||||
// Check returns true if the token is valid, and updates the token's expiry
|
||||
// time. The token is removed if it is expired.
|
||||
func (m *tokenManager) Check(token string) bool {
|
||||
m.mut.Lock()
|
||||
defer m.mut.Unlock()
|
||||
|
||||
expires, ok := m.tokens.Tokens[token]
|
||||
if ok {
|
||||
if expires < m.timeNow().UnixNano() {
|
||||
// The token is expired.
|
||||
m.saveLocked() // removes expired tokens
|
||||
return false
|
||||
}
|
||||
|
||||
// Give the token further life.
|
||||
m.tokens.Tokens[token] = m.timeNow().Add(m.lifetime).UnixNano()
|
||||
m.saveLocked()
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// New creates a new token and returns it.
|
||||
func (m *tokenManager) New() string {
|
||||
token := rand.String(randomTokenLength)
|
||||
|
||||
m.mut.Lock()
|
||||
defer m.mut.Unlock()
|
||||
|
||||
m.tokens.Tokens[token] = m.timeNow().Add(m.lifetime).UnixNano()
|
||||
m.saveLocked()
|
||||
|
||||
return token
|
||||
}
|
||||
|
||||
// Delete removes a token.
|
||||
func (m *tokenManager) Delete(token string) {
|
||||
m.mut.Lock()
|
||||
defer m.mut.Unlock()
|
||||
|
||||
delete(m.tokens.Tokens, token)
|
||||
m.saveLocked()
|
||||
}
|
||||
|
||||
func (m *tokenManager) saveLocked() {
|
||||
// Remove expired tokens.
|
||||
now := m.timeNow().UnixNano()
|
||||
for token, expiry := range m.tokens.Tokens {
|
||||
if expiry < now {
|
||||
delete(m.tokens.Tokens, token)
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a limit on the number of tokens, remove the oldest ones.
|
||||
if m.maxItems > 0 && len(m.tokens.Tokens) > m.maxItems {
|
||||
// Sort the tokens by expiry time, oldest first.
|
||||
type tokenExpiry struct {
|
||||
token string
|
||||
expiry int64
|
||||
}
|
||||
var tokens []tokenExpiry
|
||||
for token, expiry := range m.tokens.Tokens {
|
||||
tokens = append(tokens, tokenExpiry{token, expiry})
|
||||
}
|
||||
slices.SortFunc(tokens, func(i, j tokenExpiry) int {
|
||||
return int(i.expiry - j.expiry)
|
||||
})
|
||||
// Remove the oldest tokens.
|
||||
for _, token := range tokens[:len(tokens)-m.maxItems] {
|
||||
delete(m.tokens.Tokens, token.token)
|
||||
}
|
||||
}
|
||||
|
||||
// Postpone saving until one second of inactivity.
|
||||
if m.saveTimer == nil {
|
||||
m.saveTimer = time.AfterFunc(time.Second, m.scheduledSave)
|
||||
} else {
|
||||
m.saveTimer.Reset(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *tokenManager) scheduledSave() {
|
||||
m.mut.Lock()
|
||||
defer m.mut.Unlock()
|
||||
|
||||
m.saveTimer = nil
|
||||
|
||||
bs, _ := m.tokens.Marshal() // can't fail
|
||||
_ = m.miscDB.PutBytes(m.key, bs) // can fail, but what are we going to do?
|
||||
}
|
||||
411
lib/api/tokenset.pb.go
Normal file
411
lib/api/tokenset.pb.go
Normal file
@@ -0,0 +1,411 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: lib/api/tokenset.proto
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type TokenSet struct {
|
||||
// token -> expiry time (epoch nanoseconds)
|
||||
Tokens map[string]int64 `protobuf:"bytes,1,rep,name=tokens,proto3" json:"tokens" xml:"token" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
|
||||
}
|
||||
|
||||
func (m *TokenSet) Reset() { *m = TokenSet{} }
|
||||
func (m *TokenSet) String() string { return proto.CompactTextString(m) }
|
||||
func (*TokenSet) ProtoMessage() {}
|
||||
func (*TokenSet) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_9ea8707737c33b38, []int{0}
|
||||
}
|
||||
func (m *TokenSet) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *TokenSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_TokenSet.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *TokenSet) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TokenSet.Merge(m, src)
|
||||
}
|
||||
func (m *TokenSet) XXX_Size() int {
|
||||
return m.ProtoSize()
|
||||
}
|
||||
func (m *TokenSet) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_TokenSet.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_TokenSet proto.InternalMessageInfo
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*TokenSet)(nil), "api.TokenSet")
|
||||
proto.RegisterMapType((map[string]int64)(nil), "api.TokenSet.TokensEntry")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("lib/api/tokenset.proto", fileDescriptor_9ea8707737c33b38) }
|
||||
|
||||
var fileDescriptor_9ea8707737c33b38 = []byte{
|
||||
// 260 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xcb, 0xc9, 0x4c, 0xd2,
|
||||
0x4f, 0x2c, 0xc8, 0xd4, 0x2f, 0xc9, 0xcf, 0x4e, 0xcd, 0x2b, 0x4e, 0x2d, 0xd1, 0x2b, 0x28, 0xca,
|
||||
0x2f, 0xc9, 0x17, 0x62, 0x4e, 0x2c, 0xc8, 0x54, 0x3a, 0xce, 0xc8, 0xc5, 0x11, 0x02, 0x12, 0x0f,
|
||||
0x4e, 0x2d, 0x11, 0x0a, 0xe0, 0x62, 0x83, 0xa8, 0x91, 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x36, 0x92,
|
||||
0xd4, 0x4b, 0x2c, 0xc8, 0xd4, 0x83, 0x49, 0x43, 0x18, 0xc5, 0xae, 0x79, 0x25, 0x45, 0x95, 0x4e,
|
||||
0xb2, 0x27, 0xee, 0xc9, 0x33, 0xbc, 0xba, 0x27, 0x0f, 0xd5, 0xf0, 0xe9, 0x9e, 0x3c, 0x77, 0x45,
|
||||
0x6e, 0x8e, 0x95, 0x12, 0x98, 0xab, 0x14, 0x04, 0x15, 0x96, 0xca, 0xe4, 0xe2, 0x46, 0xd2, 0x25,
|
||||
0xa4, 0xc6, 0xc5, 0x9c, 0x9d, 0x5a, 0x29, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0xe9, 0x24, 0xf2, 0xea,
|
||||
0x9e, 0x3c, 0x88, 0xfb, 0xe9, 0x9e, 0x3c, 0x27, 0x58, 0x6f, 0x76, 0x6a, 0xa5, 0x52, 0x10, 0x48,
|
||||
0x44, 0x48, 0x8f, 0x8b, 0xb5, 0x2c, 0x31, 0xa7, 0x34, 0x55, 0x82, 0x49, 0x81, 0x51, 0x83, 0xd9,
|
||||
0x49, 0xe2, 0xd5, 0x3d, 0x79, 0x88, 0x00, 0xdc, 0x1e, 0x30, 0x4f, 0x29, 0x08, 0x22, 0x6a, 0xc5,
|
||||
0x64, 0xc1, 0xe8, 0xe4, 0x71, 0xe2, 0xa1, 0x1c, 0xc3, 0x85, 0x87, 0x72, 0x0c, 0x27, 0x1e, 0xc9,
|
||||
0x31, 0x5e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x82, 0xc7, 0x72, 0x8c, 0x17, 0x1e,
|
||||
0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0xa5, 0x96, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97,
|
||||
0x9c, 0x9f, 0xab, 0x5f, 0x5c, 0x99, 0x97, 0x5c, 0x92, 0x91, 0x99, 0x97, 0x8e, 0xc4, 0x82, 0x86,
|
||||
0x53, 0x12, 0x1b, 0x38, 0x7c, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xfe, 0x25, 0x31, 0x49,
|
||||
0x39, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *TokenSet) Marshal() (dAtA []byte, err error) {
|
||||
size := m.ProtoSize()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *TokenSet) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.ProtoSize()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *TokenSet) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Tokens) > 0 {
|
||||
for k := range m.Tokens {
|
||||
v := m.Tokens[k]
|
||||
baseI := i
|
||||
i = encodeVarintTokenset(dAtA, i, uint64(v))
|
||||
i--
|
||||
dAtA[i] = 0x10
|
||||
i -= len(k)
|
||||
copy(dAtA[i:], k)
|
||||
i = encodeVarintTokenset(dAtA, i, uint64(len(k)))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
i = encodeVarintTokenset(dAtA, i, uint64(baseI-i))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintTokenset(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovTokenset(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return base
|
||||
}
|
||||
func (m *TokenSet) ProtoSize() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Tokens) > 0 {
|
||||
for k, v := range m.Tokens {
|
||||
_ = k
|
||||
_ = v
|
||||
mapEntrySize := 1 + len(k) + sovTokenset(uint64(len(k))) + 1 + sovTokenset(uint64(v))
|
||||
n += mapEntrySize + 1 + sovTokenset(uint64(mapEntrySize))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovTokenset(x uint64) (n int) {
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozTokenset(x uint64) (n int) {
|
||||
return sovTokenset(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *TokenSet) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTokenset
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: TokenSet: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: TokenSet: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Tokens", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTokenset
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthTokenset
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTokenset
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.Tokens == nil {
|
||||
m.Tokens = make(map[string]int64)
|
||||
}
|
||||
var mapkey string
|
||||
var mapvalue int64
|
||||
for iNdEx < postIndex {
|
||||
entryPreIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTokenset
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
if fieldNum == 1 {
|
||||
var stringLenmapkey uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTokenset
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLenmapkey |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLenmapkey := int(stringLenmapkey)
|
||||
if intStringLenmapkey < 0 {
|
||||
return ErrInvalidLengthTokenset
|
||||
}
|
||||
postStringIndexmapkey := iNdEx + intStringLenmapkey
|
||||
if postStringIndexmapkey < 0 {
|
||||
return ErrInvalidLengthTokenset
|
||||
}
|
||||
if postStringIndexmapkey > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
mapkey = string(dAtA[iNdEx:postStringIndexmapkey])
|
||||
iNdEx = postStringIndexmapkey
|
||||
} else if fieldNum == 2 {
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTokenset
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
mapvalue |= int64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
iNdEx = entryPreIndex
|
||||
skippy, err := skipTokenset(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthTokenset
|
||||
}
|
||||
if (iNdEx + skippy) > postIndex {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
m.Tokens[mapkey] = mapvalue
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipTokenset(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthTokenset
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipTokenset(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowTokenset
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wireType := int(wire & 0x7)
|
||||
switch wireType {
|
||||
case 0:
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowTokenset
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx++
|
||||
if dAtA[iNdEx-1] < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowTokenset
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
length |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthTokenset
|
||||
}
|
||||
iNdEx += length
|
||||
case 3:
|
||||
depth++
|
||||
case 4:
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupTokenset
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthTokenset
|
||||
}
|
||||
if depth == 0 {
|
||||
return iNdEx, nil
|
||||
}
|
||||
}
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthTokenset = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowTokenset = fmt.Errorf("proto: integer overflow")
|
||||
ErrUnexpectedEndOfGroupTokenset = fmt.Errorf("proto: unexpected end of group")
|
||||
)
|
||||
@@ -36,7 +36,7 @@ var (
|
||||
LongVersion string
|
||||
Extra string
|
||||
|
||||
allowedVersionExp = regexp.MustCompile(`^v\d+\.\d+\.\d+(-[a-z0-9]+)*(\.\d+)*(\+\d+-g[0-9a-f]+)?(-[^\s]+)?$`)
|
||||
allowedVersionExp = regexp.MustCompile(`^v\d+\.\d+\.\d+(-[a-z0-9]+)*(\.\d+)*(\+\d+-g[0-9a-f]+|\+[0-9a-z]+)?(-[^\s]+)?$`)
|
||||
|
||||
envTags = []string{
|
||||
"STGUIASSETS",
|
||||
|
||||
@@ -27,6 +27,11 @@ func TestAllowedVersions(t *testing.T) {
|
||||
{"v0.13.0-some-weird-but-allowed-tag", true},
|
||||
{"v0.13.0-allowed.to.do.this", true},
|
||||
{"v0.13.0+not.allowed.to.do.this", false},
|
||||
{"v1.27.0+xyz", true},
|
||||
{"v1.27.0-abc.1+xyz", true},
|
||||
{"v1.0.0+45", true},
|
||||
{"v1.0.0-noupgrade", true},
|
||||
{"v1.0.0+noupgrade", true},
|
||||
}
|
||||
|
||||
for i, c := range testcases {
|
||||
|
||||
@@ -77,7 +77,15 @@ func (t *tcpListener) serve(ctx context.Context) error {
|
||||
l.Infof("TCP listener (%v) starting", tcaddr)
|
||||
defer l.Infof("TCP listener (%v) shutting down", tcaddr)
|
||||
|
||||
mapping := t.natService.NewMapping(nat.TCP, tcaddr.IP, tcaddr.Port)
|
||||
var ipVersion nat.IPVersion
|
||||
if t.uri.Scheme == "tcp4" {
|
||||
ipVersion = nat.IPv4Only
|
||||
} else if t.uri.Scheme == "tcp6" {
|
||||
ipVersion = nat.IPv6Only
|
||||
} else {
|
||||
ipVersion = nat.IPvAny
|
||||
}
|
||||
mapping := t.natService.NewMapping(nat.TCP, ipVersion, tcaddr.IP, tcaddr.Port)
|
||||
mapping.OnChanged(func() {
|
||||
t.notifyAddressesChanged(t)
|
||||
})
|
||||
|
||||
@@ -19,8 +19,8 @@ func fixupPort(uri *url.URL, defaultPort int) *url.URL {
|
||||
copyURI := *uri
|
||||
|
||||
host, port, err := net.SplitHostPort(uri.Host)
|
||||
if err != nil && strings.Contains(err.Error(), "missing port") {
|
||||
// addr is on the form "1.2.3.4" or "[fe80::1]"
|
||||
if e, ok := err.(*net.AddrError); ok && strings.Contains(e.Err, "missing port") {
|
||||
// addr is of the form "1.2.3.4" or "[fe80::1]"
|
||||
host = uri.Host
|
||||
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||
// net.JoinHostPort will add the brackets again
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user