mirror of
https://github.com/syncthing/syncthing.git
synced 2025-12-23 22:18:14 -05:00
Compare commits
377 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7b494b7cf | ||
|
|
962b917150 | ||
|
|
f57e92c20a | ||
|
|
b9ab05af02 | ||
|
|
43d826913f | ||
|
|
801ef0e22d | ||
|
|
e5dfd2c549 | ||
|
|
5800d1acc3 | ||
|
|
fd9dcbb8c2 | ||
|
|
bc7e56fdcd | ||
|
|
7f7f5d87df | ||
|
|
49f2736adb | ||
|
|
cde867cf74 | ||
|
|
70292b4902 | ||
|
|
553c02f244 | ||
|
|
ce884e5d72 | ||
|
|
5f702c1406 | ||
|
|
a6bcd02739 | ||
|
|
488c33aef5 | ||
|
|
9241a475e9 | ||
|
|
01eef47bbc | ||
|
|
c518d99c35 | ||
|
|
81c99e07db | ||
|
|
5279330c1d | ||
|
|
194b59b3ed | ||
|
|
8e796ddb94 | ||
|
|
7c9d06b4d2 | ||
|
|
df8d8c276e | ||
|
|
98cf5872e9 | ||
|
|
c883f49a24 | ||
|
|
d84280107c | ||
|
|
d97fd638bc | ||
|
|
a1069a0d70 | ||
|
|
465804161b | ||
|
|
d08f483811 | ||
|
|
655b4568c1 | ||
|
|
c6a887865f | ||
|
|
b4565c87ee | ||
|
|
20d2406a0e | ||
|
|
d3d3fc2d0e | ||
|
|
f8c44923c7 | ||
|
|
6f0acacbd2 | ||
|
|
932b4ce9bd | ||
|
|
b3e3ca7294 | ||
|
|
41b4c5cd5e | ||
|
|
eb4eb7524d | ||
|
|
a64c5396e9 | ||
|
|
5595113074 | ||
|
|
ea19ec64bf | ||
|
|
9de6c5ed69 | ||
|
|
d037681fd1 | ||
|
|
1b0eaa093a | ||
|
|
3382ccc3f1 | ||
|
|
9ee208b441 | ||
|
|
dd90e8ec7a | ||
|
|
aa6ae0f3b0 | ||
|
|
e8b256793a | ||
|
|
8233279a65 | ||
|
|
8e5d5802cc | ||
|
|
25ae01b0d7 | ||
|
|
66583927f8 | ||
|
|
f0328abeaa | ||
|
|
4b8d07d91c | ||
|
|
c33daca3b4 | ||
|
|
a533f453f8 | ||
|
|
3c9e87d994 | ||
|
|
f0180cb014 | ||
|
|
a99a730c0c | ||
|
|
36254473a3 | ||
|
|
800596139e | ||
|
|
f48782e4df | ||
|
|
922cc7544e | ||
|
|
9e262d84de | ||
|
|
42db6280e6 | ||
|
|
8d8adae310 | ||
|
|
12ba4b6aea | ||
|
|
372e3c26b0 | ||
|
|
01e2426a56 | ||
|
|
6e9ccf7211 | ||
|
|
4986fc1676 | ||
|
|
5ff050e665 | ||
|
|
fc40dc8af2 | ||
|
|
541678ad9e | ||
|
|
fafc3ba45e | ||
|
|
da7a75a823 | ||
|
|
e41d6b9c1e | ||
|
|
21ad99c80a | ||
|
|
4ad3f07691 | ||
|
|
4459438245 | ||
|
|
2306c6d989 | ||
|
|
0de55ef262 | ||
|
|
d083682418 | ||
|
|
c918299eab | ||
|
|
b59443f136 | ||
|
|
7189a3ebff | ||
|
|
6ed4cca691 | ||
|
|
958f51ace6 | ||
|
|
07f1320e00 | ||
|
|
3da449cfa3 | ||
|
|
655ef63c74 | ||
|
|
01257e838b | ||
|
|
e54f51c9c5 | ||
|
|
a259a009c8 | ||
|
|
8151bcddff | ||
|
|
d776657b52 | ||
|
|
0416103f26 | ||
|
|
7bfcdfb577 | ||
|
|
e6a9b09527 | ||
|
|
c8f52ba1bc | ||
|
|
3058aa6315 | ||
|
|
60160db23a | ||
|
|
66b28e9aed | ||
|
|
755daaa7b7 | ||
|
|
33b5c3c62e | ||
|
|
ffb30392e8 | ||
|
|
7a76685d7e | ||
|
|
370bbb8f26 | ||
|
|
9ea6c9c3c3 | ||
|
|
8f117a4417 | ||
|
|
bbf48ae334 | ||
|
|
fcf4916086 | ||
|
|
5d8033343f | ||
|
|
c74d2a9872 | ||
|
|
3da84804b6 | ||
|
|
5b75c6ddcb | ||
|
|
ae03854575 | ||
|
|
ad196173d0 | ||
|
|
d682220305 | ||
|
|
29e10e00d2 | ||
|
|
34f61ce464 | ||
|
|
adcbd31e62 | ||
|
|
431da839cf | ||
|
|
836045ee87 | ||
|
|
49462448d0 | ||
|
|
e3424ad503 | ||
|
|
5703423c00 | ||
|
|
356ec26c87 | ||
|
|
d37cb02e40 | ||
|
|
953944e54e | ||
|
|
6e26fab3a0 | ||
|
|
532e30eb6b | ||
|
|
54bb987fae | ||
|
|
74367d2f66 | ||
|
|
0f6750c8f5 | ||
|
|
c8c38f735f | ||
|
|
fa4bd5c057 | ||
|
|
36fb5425a5 | ||
|
|
32a913c0ff | ||
|
|
e8cfc8acfb | ||
|
|
7c07610ab2 | ||
|
|
ff88430efb | ||
|
|
06dd8ee6d7 | ||
|
|
c0aa7b436c | ||
|
|
b80fa9dcd2 | ||
|
|
95187bcc64 | ||
|
|
f2a5b62733 | ||
|
|
385ca6772c | ||
|
|
88c307b65b | ||
|
|
9d425b0588 | ||
|
|
cf84a260ca | ||
|
|
c4e024c7e3 | ||
|
|
c5a29b5b26 | ||
|
|
4c64843d60 | ||
|
|
b4ff96d754 | ||
|
|
21c5ac2161 | ||
|
|
6fc0b41f97 | ||
|
|
0b0b2143ed | ||
|
|
af64140c61 | ||
|
|
1c68062231 | ||
|
|
4d92855d76 | ||
|
|
1c6f542cb7 | ||
|
|
b28066c85d | ||
|
|
71c8a2c36f | ||
|
|
e4ab7b4ff3 | ||
|
|
8b978d4712 | ||
|
|
7b319111d3 | ||
|
|
cb7cea93a2 | ||
|
|
20257faf54 | ||
|
|
c14abebd68 | ||
|
|
b1a1a90045 | ||
|
|
8afc9855f2 | ||
|
|
4215058911 | ||
|
|
9fb1a18dbf | ||
|
|
064213ceb8 | ||
|
|
0211251b34 | ||
|
|
1903da569b | ||
|
|
b6a7beca1f | ||
|
|
7b83e7403e | ||
|
|
1915a470e9 | ||
|
|
0b100296e1 | ||
|
|
e6ed3acf5f | ||
|
|
9f95bf3573 | ||
|
|
b05ece0681 | ||
|
|
5381178c46 | ||
|
|
e7f4f8306c | ||
|
|
9922a3abd9 | ||
|
|
40ab668a73 | ||
|
|
10d20c4800 | ||
|
|
700bb75016 | ||
|
|
e25de22705 | ||
|
|
ef6d561c66 | ||
|
|
3c92999406 | ||
|
|
e61b8a1ae8 | ||
|
|
706409d2f3 | ||
|
|
e9133ef82b | ||
|
|
67ba20d777 | ||
|
|
21da0d7890 | ||
|
|
ebbe57d0ab | ||
|
|
9cf8fca03f | ||
|
|
f4abc71dcc | ||
|
|
2712f566ab | ||
|
|
8aa02da93a | ||
|
|
ac3f390afa | ||
|
|
0e560486db | ||
|
|
57d413099d | ||
|
|
8d37e8f307 | ||
|
|
1fdf07933c | ||
|
|
d49df1e44c | ||
|
|
50480f6ccb | ||
|
|
15f693d93c | ||
|
|
8e934a8c69 | ||
|
|
c50678618f | ||
|
|
8094b459e4 | ||
|
|
6765867a2e | ||
|
|
4fb8ee6a6f | ||
|
|
79bac43800 | ||
|
|
674834ccf4 | ||
|
|
3bd2bff23b | ||
|
|
40660c5fb7 | ||
|
|
d940d094a1 | ||
|
|
9d67727989 | ||
|
|
6f51700a7f | ||
|
|
43d33dbeb5 | ||
|
|
bb91f53641 | ||
|
|
598915193a | ||
|
|
905e5ec07f | ||
|
|
5945a8c5bd | ||
|
|
4075b886d0 | ||
|
|
e39dcc5c58 | ||
|
|
46d2c59b24 | ||
|
|
54f6b5c2ee | ||
|
|
cade790198 | ||
|
|
98555a9a80 | ||
|
|
48b757cac1 | ||
|
|
58c85fc9db | ||
|
|
ddd98a818a | ||
|
|
99b707c141 | ||
|
|
39d6692109 | ||
|
|
64b5a1b738 | ||
|
|
1a131a56f2 | ||
|
|
beda37f28b | ||
|
|
1b8a8032f0 | ||
|
|
3423de24ea | ||
|
|
6532715641 | ||
|
|
2532ac35cf | ||
|
|
832fa094a3 | ||
|
|
bcd30ceaec | ||
|
|
78bfe643a8 | ||
|
|
0a58747eb2 | ||
|
|
96b03fac04 | ||
|
|
085455d72e | ||
|
|
72849690c9 | ||
|
|
f09cca52f2 | ||
|
|
9a3493c2f4 | ||
|
|
fa404d5a0d | ||
|
|
73ad18fbfb | ||
|
|
a4db309b39 | ||
|
|
4bc17bc588 | ||
|
|
964c8d7d65 | ||
|
|
bacf506e90 | ||
|
|
1dd264894a | ||
|
|
8c3d2f3bc5 | ||
|
|
702ed8ecc1 | ||
|
|
70bb4459be | ||
|
|
b038650810 | ||
|
|
821d6f43ac | ||
|
|
a16bf555c0 | ||
|
|
fa7b81e1cf | ||
|
|
516f1aa0c7 | ||
|
|
6b94599467 | ||
|
|
f183d1cbec | ||
|
|
cd6ea60fa1 | ||
|
|
58bf2b5515 | ||
|
|
d28be1b711 | ||
|
|
47e3147d0b | ||
|
|
2159dfd27d | ||
|
|
0bf21d9db2 | ||
|
|
f61843ef2e | ||
|
|
23e8366f8d | ||
|
|
ed252ed6d7 | ||
|
|
93e72cc83f | ||
|
|
abe34fc1f6 | ||
|
|
be002362b3 | ||
|
|
190dff142c | ||
|
|
c667ada63a | ||
|
|
50480b89fc | ||
|
|
93ae30d889 | ||
|
|
486eebc4ac | ||
|
|
ff33d976d1 | ||
|
|
69890b4282 | ||
|
|
533c9a6ab0 | ||
|
|
9521bb3931 | ||
|
|
25e03ef9ab | ||
|
|
e46a0f99c3 | ||
|
|
ed6575411f | ||
|
|
ed97e365b2 | ||
|
|
b4776ea4e0 | ||
|
|
b5ffd0a796 | ||
|
|
c74299b59a | ||
|
|
8b6d837483 | ||
|
|
3e74b3dee2 | ||
|
|
2902da996c | ||
|
|
f6f144bf17 | ||
|
|
ab5c42f4a0 | ||
|
|
780b8fd3bc | ||
|
|
7db3f7eaac | ||
|
|
f0b666269b | ||
|
|
190a59842c | ||
|
|
40888c1a66 | ||
|
|
ddea2e449c | ||
|
|
7cfa871d58 | ||
|
|
95b39a791d | ||
|
|
fa0d933e49 | ||
|
|
e0c1abc5fe | ||
|
|
8372c0288f | ||
|
|
5f5d672a7d | ||
|
|
d23cd197e1 | ||
|
|
d7ca483df1 | ||
|
|
e48be98cd5 | ||
|
|
cbded11c43 | ||
|
|
d5aa991b73 | ||
|
|
05210d0325 | ||
|
|
55da878452 | ||
|
|
e9a2ff3aa6 | ||
|
|
c9650fc7d5 | ||
|
|
cf1cf85ce6 | ||
|
|
2301f72c5b | ||
|
|
7d51b1b620 | ||
|
|
f7c8efd93c | ||
|
|
3e7ccf7c48 | ||
|
|
fa3b9acca3 | ||
|
|
bae976905c | ||
|
|
6bc2784e9a | ||
|
|
1dbdd6b720 | ||
|
|
f15d50c2e8 | ||
|
|
8a2d8ebf81 | ||
|
|
b88aea34b6 | ||
|
|
82a0dd8eaa | ||
|
|
4096a35b86 | ||
|
|
86cbc2486f | ||
|
|
0bcc31d058 | ||
|
|
2c3a890d2f | ||
|
|
f9007ed106 | ||
|
|
2953630bc3 | ||
|
|
a99e670ebb | ||
|
|
05cc6b0f43 | ||
|
|
1efcfeb3ad | ||
|
|
93195911bd | ||
|
|
6085e3a5eb | ||
|
|
e5b72da607 | ||
|
|
0d6117d585 | ||
|
|
f1e136a17b | ||
|
|
025905fcdf | ||
|
|
b1c8f88a44 | ||
|
|
1a25ae32ca | ||
|
|
629971687d | ||
|
|
3c955a9706 | ||
|
|
7f3c8dbff1 | ||
|
|
7762e39fb3 | ||
|
|
4235b2c406 | ||
|
|
3fd090bfa7 | ||
|
|
6dfa54efa6 | ||
|
|
67575e1736 | ||
|
|
65923fc255 | ||
|
|
aea763868f | ||
|
|
26b134ae7b | ||
|
|
893071d2ba |
1
.github/ISSUE_TEMPLATE/01-feature.yml
vendored
1
.github/ISSUE_TEMPLATE/01-feature.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: Feature request
|
||||
description: File a new feature request
|
||||
labels: ["enhancement", "needs-triage"]
|
||||
type: Feature
|
||||
body:
|
||||
|
||||
- type: textarea
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/02-bug.yml
vendored
1
.github/ISSUE_TEMPLATE/02-bug.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: Bug report
|
||||
description: If you're actually looking for support instead, see "I need help / I have a question".
|
||||
labels: ["bug", "needs-triage"]
|
||||
type: Bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
23
.github/labeler.yml
vendored
Normal file
23
.github/labeler.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
version: 1
|
||||
labels:
|
||||
|
||||
- label: enhancement
|
||||
title: ^feat\b
|
||||
|
||||
- label: bug
|
||||
title: ^fix\b
|
||||
|
||||
- label: documentation
|
||||
title: ^docs\b
|
||||
|
||||
- label: chore
|
||||
title: ^chore\b
|
||||
|
||||
- label: chore
|
||||
title: ^refactor\b
|
||||
|
||||
- label: build
|
||||
title: ^build\b
|
||||
|
||||
- label: dependencies
|
||||
title: ^build\(deps\)\b
|
||||
52
.github/regsync.yml
vendored
Normal file
52
.github/regsync.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
version: 1
|
||||
creds:
|
||||
- registry: docker.io
|
||||
user: "{{env \"DOCKERHUB_USERNAME\"}}"
|
||||
pass: "{{env \"DOCKERHUB_TOKEN\"}}"
|
||||
|
||||
defaults:
|
||||
ratelimit:
|
||||
min: 100
|
||||
retry: 1m
|
||||
parallel: 4
|
||||
|
||||
sync:
|
||||
|
||||
- source: ghcr.io/syncthing/syncthing
|
||||
target: docker.io/syncthing/syncthing
|
||||
type: repository
|
||||
tags:
|
||||
allow:
|
||||
- latest
|
||||
- rc
|
||||
- edge
|
||||
- \d+
|
||||
- \d+\.\d+
|
||||
- \d+\.\d+\.\d+
|
||||
- \d+\.\d+\.\d+-rc\.\d+
|
||||
|
||||
- source: ghcr.io/syncthing/relaysrv
|
||||
target: docker.io/syncthing/relaysrv
|
||||
type: repository
|
||||
tags:
|
||||
allow:
|
||||
- latest
|
||||
- rc
|
||||
- edge
|
||||
- \d+
|
||||
- \d+\.\d+
|
||||
- \d+\.\d+\.\d+
|
||||
- \d+\.\d+\.\d+-rc\.\d+
|
||||
|
||||
- source: ghcr.io/syncthing/discosrv
|
||||
target: docker.io/syncthing/discosrv
|
||||
type: repository
|
||||
tags:
|
||||
allow:
|
||||
- latest
|
||||
- rc
|
||||
- edge
|
||||
- \d+
|
||||
- \d+\.\d+
|
||||
- \d+\.\d+\.\d+
|
||||
- \d+\.\d+\.\d+-rc\.\d+
|
||||
17
.github/release.yml
vendored
Normal file
17
.github/release.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
changelog:
|
||||
exclude:
|
||||
labels:
|
||||
- dependencies
|
||||
|
||||
categories:
|
||||
- title: Fixes
|
||||
labels:
|
||||
- bug
|
||||
|
||||
- title: Features
|
||||
labels:
|
||||
- enhancement
|
||||
|
||||
- title: Other
|
||||
labels:
|
||||
- '*'
|
||||
11
.github/workflows/build-infra-dockers.yaml
vendored
11
.github/workflows/build-infra-dockers.yaml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
- infra-*
|
||||
|
||||
env:
|
||||
GO_VERSION: "~1.24.0"
|
||||
GO_VERSION: "~1.25.0"
|
||||
CGO_ENABLED: "0"
|
||||
BUILD_USER: docker
|
||||
BUILD_HOST: github.syncthing.net
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
name: Build and push Docker images
|
||||
if: github.repository == 'syncthing/syncthing'
|
||||
runs-on: ubuntu-latest
|
||||
environment: release
|
||||
environment: docker
|
||||
strategy:
|
||||
matrix:
|
||||
pkg:
|
||||
@@ -30,11 +30,11 @@ jobs:
|
||||
- stupgrades
|
||||
- ursrv
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
@@ -59,6 +59,9 @@ jobs:
|
||||
mv ${{ matrix.pkg }} ${{ matrix.pkg }}-linux-"$arch"
|
||||
done
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
|
||||
18
.github/workflows/build-nightly.yaml
vendored
Normal file
18
.github/workflows/build-nightly.yaml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: Build Syncthing (Nightly)
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run nightly build at 05:00 UTC
|
||||
- cron: '00 05 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build-syncthing:
|
||||
uses: ./.github/workflows/build-syncthing.yaml
|
||||
# if we only want nightlies to run for specific users:
|
||||
# if: contains(fromJSON('["syncthing", "calmh"]'), github.repository_owner)
|
||||
secrets: inherit
|
||||
702
.github/workflows/build-syncthing.yaml
vendored
702
.github/workflows/build-syncthing.yaml
vendored
File diff suppressed because it is too large
Load Diff
18
.github/workflows/mirrors.yaml
vendored
Normal file
18
.github/workflows/mirrors.yaml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: Mirrors
|
||||
|
||||
on: [push, delete]
|
||||
|
||||
jobs:
|
||||
codeberg:
|
||||
name: Mirror to Codeberg
|
||||
if: github.repository_owner == 'syncthing'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: yesolutions/mirror-action@master
|
||||
with:
|
||||
REMOTE: ssh://git@codeberg.org/${{ github.repository }}.git
|
||||
GIT_SSH_PRIVATE_KEY: ${{ secrets.CODEBERG_PUSH_KEY }}
|
||||
GIT_SSH_NO_VERIFY_HOST: "true"
|
||||
20
.github/workflows/org-members.yaml
vendored
Normal file
20
.github/workflows/org-members.yaml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Org membership recommendations
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 1 * *'
|
||||
|
||||
jobs:
|
||||
|
||||
run-recommendation:
|
||||
runs-on: ubuntu-latest
|
||||
name: Check for a recommendation
|
||||
steps:
|
||||
|
||||
- uses: docker://ghcr.io/calmh/github-org-members:latest
|
||||
env:
|
||||
GITHUB_ORGANISATION: syncthing
|
||||
GITHUB_TOKEN: ${{ secrets.GOM_GITHUB_TOKEN }}
|
||||
GOM_IGNORE_USERS: ${{ secrets.GOM_IGNORE_USERS }}
|
||||
GOM_ALSO_REPOS: ${{ secrets.GOM_ALSO_REPOS }}
|
||||
27
.github/workflows/pr-metadata.yaml
vendored
Normal file
27
.github/workflows/pr-metadata.yaml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: PR metadata
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- edited
|
||||
- synchronize
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
|
||||
#
|
||||
# Set labels on PRs, which are then used to categorise release notes
|
||||
#
|
||||
|
||||
labels:
|
||||
name: Set labels
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: srvaroa/labeler@v1
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
60
.github/workflows/release-syncthing.yaml
vendored
Normal file
60
.github/workflows/release-syncthing.yaml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
name: Release Syncthing
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- release
|
||||
- release-rc*
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
create-release-tag:
|
||||
name: Create release tag
|
||||
runs-on: ubuntu-latest
|
||||
environment: release
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
|
||||
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
|
||||
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: stable
|
||||
|
||||
- name: Determine version to release
|
||||
run: |
|
||||
if [[ "$GITHUB_REF_NAME" == "release" ]] ; then
|
||||
next=$(go run ./script/next-version.go)
|
||||
else
|
||||
next=$(go run ./script/next-version.go --pre)
|
||||
fi
|
||||
echo "NEXT=$next" >> $GITHUB_ENV
|
||||
echo "Next version is $next"
|
||||
|
||||
prev=$(git describe --exclude "*-*" --abbrev=0)
|
||||
echo "PREV=$prev" >> $GITHUB_ENV
|
||||
echo "Previous version is $prev"
|
||||
|
||||
- name: Determine release notes
|
||||
run: |
|
||||
go run ./script/relnotes.go --new-ver "$NEXT" --branch "$GITHUB_REF_NAME" --prev-ver "$PREV" > notes.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
|
||||
|
||||
- name: Create and push tag
|
||||
run: |
|
||||
git config --global user.name 'Syncthing Release Automation'
|
||||
git config --global user.email 'release@syncthing.net'
|
||||
git tag -a -F notes.md --cleanup=whitespace "$NEXT"
|
||||
git push origin "$NEXT"
|
||||
|
||||
- name: Trigger the build
|
||||
uses: benc-uk/workflow-dispatch@v1
|
||||
with:
|
||||
workflow: build-syncthing.yaml
|
||||
ref: refs/tags/${{ env.NEXT }}
|
||||
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
|
||||
3
.github/workflows/trigger-nightly.yaml
vendored
3
.github/workflows/trigger-nightly.yaml
vendored
@@ -8,11 +8,12 @@ on:
|
||||
jobs:
|
||||
|
||||
trigger-nightly:
|
||||
if: github.repository_owner == 'syncthing'
|
||||
runs-on: ubuntu-latest
|
||||
name: Push to release-nightly to trigger build
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -10,11 +10,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Update translations and documentation
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: stable
|
||||
- run: |
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,5 +17,4 @@ deb
|
||||
*.bz2
|
||||
/repos
|
||||
/proto/scripts/protoc-gen-gosyncthing
|
||||
/gui/next-gen-gui
|
||||
/compat.json
|
||||
|
||||
@@ -1,39 +1,96 @@
|
||||
version: "2"
|
||||
linters:
|
||||
enable-all: true
|
||||
default: all
|
||||
disable:
|
||||
- cyclop
|
||||
- depguard
|
||||
- err113
|
||||
- exhaustive
|
||||
- exhaustruct
|
||||
- forbidigo
|
||||
- funcorder
|
||||
- funlen
|
||||
- gci
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- gocognit
|
||||
- goconst
|
||||
- gocyclo
|
||||
- godot
|
||||
- godox
|
||||
- gofmt
|
||||
- goimports
|
||||
- gomoddirectives
|
||||
- inamedparam
|
||||
- interfacebloat
|
||||
- ireturn
|
||||
- lll
|
||||
- maintidx
|
||||
- mnd
|
||||
- musttag
|
||||
- nestif
|
||||
- nlreturn
|
||||
- noinlineerr
|
||||
- nonamedreturns
|
||||
- paralleltest
|
||||
- prealloc
|
||||
- predeclared
|
||||
- protogetter
|
||||
- scopelint
|
||||
- recvcheck
|
||||
- revive
|
||||
- tagalign
|
||||
- tagliatelle
|
||||
- testpackage
|
||||
- usetesting # go 1.24
|
||||
- varnamelen
|
||||
- whitespace
|
||||
- wrapcheck
|
||||
- wsl
|
||||
|
||||
issues:
|
||||
exclude-dirs:
|
||||
- internal/gen
|
||||
- cmd/dev
|
||||
- repos
|
||||
- wsl_v5
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- internal/gen
|
||||
- internal/db/olddb
|
||||
- cmd/dev
|
||||
- repos
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
- _test\.go$
|
||||
rules:
|
||||
# relax the slog rules for debug lines, for now
|
||||
- linters: [sloglint]
|
||||
source: Debug
|
||||
# contexts are irrelevant for SQLite
|
||||
- linters: [noctx]
|
||||
text: database/sql
|
||||
# Rollback errors can be ignored
|
||||
- linters: [errcheck]
|
||||
source: Rollback
|
||||
# Embedded fields named in selectors may add clarity
|
||||
- linters: [staticcheck]
|
||||
text: QF1008
|
||||
# Don't necessarily rewrite !(foo || bar) to !foo && !bar
|
||||
- linters: [staticcheck]
|
||||
text: QF1001
|
||||
settings:
|
||||
sloglint:
|
||||
context: "scope"
|
||||
static-msg: true
|
||||
msg-style: capitalized
|
||||
key-naming-case: camel
|
||||
formatters:
|
||||
enable:
|
||||
- gofumpt
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- internal/gen
|
||||
- cmd/dev
|
||||
- repos
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
||||
23
.policy.yml
23
.policy.yml
@@ -8,17 +8,20 @@
|
||||
policy:
|
||||
approval:
|
||||
- subject is conventional commit
|
||||
- project metadata requires maintainer approval
|
||||
- or:
|
||||
- project metadata requires maintainer approval
|
||||
- a maintainer claims responsibility
|
||||
- or:
|
||||
- is approved by a syncthing contributor
|
||||
- is a translation or dependency update by a contributor
|
||||
- is a trivial change by a contributor
|
||||
- a maintainer claims responsibility
|
||||
|
||||
# Additionally, contributors can disapprove of a PR
|
||||
# Additionally, maintainers can disapprove of a PR
|
||||
disapproval:
|
||||
requires:
|
||||
teams:
|
||||
- syncthing/contributors
|
||||
- syncthing/maintainers
|
||||
|
||||
# The rules for the policy are described below.
|
||||
|
||||
@@ -49,7 +52,7 @@ approval_rules:
|
||||
- syncthing/maintainers
|
||||
options:
|
||||
ignore_update_merges: true
|
||||
allow_contributor: true
|
||||
allow_non_author_contributor: true
|
||||
|
||||
# Regular pull requests require approval by an active contributor
|
||||
- name: is approved by a syncthing contributor
|
||||
@@ -59,7 +62,7 @@ approval_rules:
|
||||
- syncthing/contributors
|
||||
options:
|
||||
ignore_update_merges: true
|
||||
allow_contributor: true
|
||||
allow_non_author_contributor: true
|
||||
|
||||
# Changes to some files (translations, dependencies, compatibility) do not
|
||||
# require approval if they were proposed by a contributor and have a
|
||||
@@ -96,3 +99,13 @@ approval_rules:
|
||||
has_author_in:
|
||||
teams:
|
||||
- syncthing/contributors
|
||||
|
||||
# A member of the maintainers group can take responsibility by adding the
|
||||
# appropriate label.
|
||||
- name: a maintainer claims responsibility
|
||||
if:
|
||||
has_labels:
|
||||
- maintainer-responsibility
|
||||
has_author_in:
|
||||
teams:
|
||||
- syncthing/maintainers
|
||||
|
||||
117
AUTHORS
117
AUTHORS
@@ -13,119 +13,115 @@
|
||||
# contents of this file.
|
||||
#
|
||||
|
||||
Aaron Bieber (qbit) <qbit@deftly.net>
|
||||
Jakob Borg (calmh) <jakob@nym.se> <jakob@kastelo.net> <jborg@coreweave.com>
|
||||
Audrius Butkevicius (AudriusButkevicius) <audrius.butkevicius@gmail.com> <github@audrius.rocks>
|
||||
Simon Frei (imsodin) <freisim93@gmail.com>
|
||||
Tomasz Wilczyński <5626656+tomasz1986@users.noreply.github.com> <twilczynski@naver.com>
|
||||
Alexander Graf (alex2108) <register-github@alex-graf.de>
|
||||
Alexandre Viau (aviau) <alexandre@alexandreviau.net> <aviau@debian.org>
|
||||
Anderson Mesquita (andersonvom) <andersonvom@gmail.com>
|
||||
André Colomb (acolomb) <src@andre.colomb.de> <github.com@andre.colomb.de>
|
||||
Antony Male (canton7) <antony.male@gmail.com>
|
||||
Ben Schulz (uok) <ueomkail@gmail.com> <uok@users.noreply.github.com>
|
||||
bt90 <btom1990@googlemail.com>
|
||||
Caleb Callaway (cqcallaw) <enlightened.despot@gmail.com>
|
||||
Daniel Harte (norgeous) <daniel@harte.me> <daniel@danielharte.co.uk> <norgeous@users.noreply.github.com>
|
||||
Emil Lundberg <emil@emlun.se>
|
||||
Eric P <eric@kastelo.net>
|
||||
Evgeny Kuznetsov <evgeny@kuznetsov.md>
|
||||
greatroar <61184462+greatroar@users.noreply.github.com>
|
||||
Lars K.W. Gohlke (lkwg82) <lkwg82@gmx.de>
|
||||
Lode Hoste (Zillode) <zillode@zillode.be>
|
||||
Marcus B Spencer <marcus@marcusspencer.xyz> <marcus@marcusspencer.us>
|
||||
Michael Ploujnikov (plouj) <ploujj@gmail.com>
|
||||
Ross Smith II (rasa) <ross@smithii.com>
|
||||
Stefan Tatschner (rumpelsepp) <stefan@sevenbyte.org> <rumpelsepp@sevenbyte.org> <stefan@rumpelsepp.org>
|
||||
Tommy van der Vorst <tommy-github@pixelspark.nl> <tommy@pixelspark.nl>
|
||||
Wulf Weich (wweich) <wweich@users.noreply.github.com> <wweich@gmx.de> <wulf@weich-kr.de>
|
||||
Adam Piggott (ProactiveServices) <aD@simplypeachy.co.uk> <simplypeachy@users.noreply.github.com> <ProactiveServices@users.noreply.github.com> <adam@proactiveservices.co.uk>
|
||||
Adel Qalieh (adelq) <aqalieh95@gmail.com> <adelq@users.noreply.github.com>
|
||||
Alan Pope <alan@popey.com>
|
||||
Alberto Donato <albertodonato@users.noreply.github.com>
|
||||
Aleksey Vasenev <margtu-fivt@ya.ru>
|
||||
Alessandro G. (alessandro.g89) <alessandro.g89@gmail.com>
|
||||
Alex Ionescu <github@ionescu.sh>
|
||||
Alex Lindeman <139387+aelindeman@users.noreply.github.com>
|
||||
Alex Xu <alex.hello71@gmail.com>
|
||||
Alexander Graf (alex2108) <register-github@alex-graf.de>
|
||||
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>
|
||||
Andrew Dunham (andrew-d) <andrew@du.nham.ca>
|
||||
Andrew Meyer <andrewm.bpi@gmail.com>
|
||||
Andrew Rabert (nvllsvm) <ar@nullsum.net> <6550543+nvllsvm@users.noreply.github.com>
|
||||
Andrey D (scienmind) <scintertech@cryptolab.net> <scienmind@users.noreply.github.com>
|
||||
André Colomb (acolomb) <src@andre.colomb.de> <github.com@andre.colomb.de>
|
||||
andyleap <andyleap@gmail.com>
|
||||
Anjan Momi <anjan@momi.ca>
|
||||
Anthony Goeckner <agoeckner@users.noreply.github.com>
|
||||
Antoine Lamielle (0x010C) <antoine.lamielle@0x010c.fr> <gh@0x010c.fr>
|
||||
Antony Male (canton7) <antony.male@gmail.com>
|
||||
Anur <anurnomeru@163.com>
|
||||
Aranjedeath <Aranjedeath@users.noreply.github.com>
|
||||
ardevd <ardevd@users.noreply.github.com>
|
||||
Arkadiusz Tymiński <gevleeog@gmail.com>
|
||||
Aroun <login@b-vo.fr>
|
||||
Arthur Axel fREW Schmidt (frioux) <frew@afoolishmanifesto.com> <frioux@gmail.com>
|
||||
Artur Zubilewicz <AkaZecik@users.noreply.github.com>
|
||||
Audrius Butkevicius (AudriusButkevicius) <audrius.butkevicius@gmail.com> <github@audrius.rocks>
|
||||
Ashish Bhate <bhate.ashish@gmail.com>
|
||||
Aurélien Rainone <476650+arl@users.noreply.github.com>
|
||||
BAHADIR YILMAZ <bahadiryilmaz32@gmail.com>
|
||||
Bart De Vries (mogwa1) <devriesb@gmail.com>
|
||||
Beat Reichenbach <44111292+beatreichenbach@users.noreply.github.com>
|
||||
Ben Curthoys (bencurthoys) <ben@bencurthoys.com>
|
||||
Ben Schulz (uok) <ueomkail@gmail.com> <uok@users.noreply.github.com>
|
||||
Ben Shepherd (benshep) <bjashepherd@gmail.com>
|
||||
Ben Sidhom (bsidhom) <bsidhom@gmail.com>
|
||||
Benedikt Heine (bebehei) <bebe@bebehei.de>
|
||||
Benedikt Morbach <benedikt.morbach@googlemail.com>
|
||||
Benjamin Nater <17193640+bn4t@users.noreply.github.com>
|
||||
Benno Fünfstück <benno.fuenfstueck@gmail.com>
|
||||
Benny Ng (tpng) <benny.tpng@gmail.com>
|
||||
boomsquared <54829195+boomsquared@users.noreply.github.com>
|
||||
Boqin Qin <bobbqqin@bupt.edu.cn>
|
||||
Boris Rybalkin <ribalkin@gmail.com>
|
||||
Brandon Philips (philips) <brandon@ifup.org>
|
||||
Brendan Long (brendanlong) <self@brendanlong.com>
|
||||
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>
|
||||
Chih-Hsuan Yen <yan12125@gmail.com> <1937689+yan12125@users.noreply.github.com>
|
||||
Choongkyu <choongkyu.kim+gh@gmail.com> <vapidlyrapid+gh@gmail.com>
|
||||
Chris Howie (cdhowie) <me@chrishowie.com>
|
||||
Chris Joel (cdata) <chris@scriptolo.gy>
|
||||
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>
|
||||
Cyprien Devillez <cypx@users.noreply.github.com>
|
||||
d-volution <49024624+d-volution@users.noreply.github.com>
|
||||
Dale Visser <dale.visser@live.com>
|
||||
Dan <benda.daniel@gmail.com>
|
||||
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>
|
||||
Daniil Gentili <daniil@daniil.it>
|
||||
Darshil Chanpura (dtchanpura) <dtchanpura@gmail.com> <dcprime314@gmail.com>
|
||||
dashangcun <907225865@qq.com>
|
||||
David Rimmer (dinosore) <dinosore@dbrsoftware.co.uk>
|
||||
deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
|
||||
DeflateAwning <11021263+DeflateAwning@users.noreply.github.com>
|
||||
Denis A. (dva) <denisva@gmail.com>
|
||||
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>
|
||||
domain <32405309+szu17dmy@users.noreply.github.com>
|
||||
Domenic Horner <domenic@tgxn.net>
|
||||
Dominik Heidler (asdil12) <dominik@heidler.eu>
|
||||
Elias Jarlebring (jarlebring) <jarlebring@gmail.com>
|
||||
Elliot Huffman <thelich2@gmail.com>
|
||||
Emil Hessman (ceh) <emil@hessman.se>
|
||||
Emil Lundberg <emil@emlun.se>
|
||||
Eng Zer Jun <engzerjun@gmail.com>
|
||||
entity0xfe <109791748+entity0xfe@users.noreply.github.com> <entity0xfe@my.domain>
|
||||
Eric Lesiuta <elesiuta@gmail.com>
|
||||
Eric P <eric@kastelo.net>
|
||||
Erik Meitner (WSGCSysadmin) <e.meitner@willystreet.coop>
|
||||
Evan Spensley <94762716+0evan@users.noreply.github.com>
|
||||
Evgeny Kuznetsov <evgeny@kuznetsov.md>
|
||||
Federico Castagnini (facastagnini) <federico.castagnini@gmail.com>
|
||||
Felix <53702818+f-eliks@users.noreply.github.com>
|
||||
Felix Ableitner (Nutomic) <me@nutomic.com>
|
||||
@@ -139,7 +135,6 @@ ghjklw <malo@jaffre.info>
|
||||
Gilli Sigurdsson (gillisig) <gilli@vx.is>
|
||||
Gleb Sinyavskiy <zhulik.gleb@gmail.com>
|
||||
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>
|
||||
@@ -147,56 +142,42 @@ Gusted <postmaster@gusted.xyz> <williamzijl7@hotmail.com>
|
||||
Han Boetes <han@boetes.org>
|
||||
HansK-p <42314815+HansK-p@users.noreply.github.com>
|
||||
Harrison Jones (harrisonhjones) <harrisonhjones@users.noreply.github.com>
|
||||
Hazem Krimi <me@hazemkrimi.tech>
|
||||
Heiko Zuerker (Smiley73) <heiko@zuerker.org>
|
||||
Hireworks <129852174+hireworksltd@users.noreply.github.com>
|
||||
Hugo Locurcio <hugo.locurcio@hugo.pro>
|
||||
Iain Barnett <iainspeed@gmail.com>
|
||||
Ian Johnson (anonymouse64) <ian.johnson@canonical.com> <person.uwsome@gmail.com>
|
||||
ignacy123 <ignacy.buczek@onet.pl>
|
||||
Ikko Ashimine <eltociear@gmail.com>
|
||||
Ilya Brin <464157+ilyabrin@users.noreply.github.com>
|
||||
Iskander Sharipov (Alex) <quasilyte@gmail.com>
|
||||
Jaakko Hannikainen (jgke) <jgke@jgke.fi>
|
||||
Jacek Szafarkiewicz (hadogenes) <szafar@linux.pl>
|
||||
Jack Croft <jccroft1@users.noreply.github.com>
|
||||
Jacob <jyundt@gmail.com>
|
||||
Jake Peterson (acogdev) <jake@acogdev.com>
|
||||
Jakob Borg (calmh) <jakob@nym.se> <jakob@kastelo.net> <jborg@coreweave.com>
|
||||
James O'Beirne <wild-github@au92.org>
|
||||
James Patterson (jpjp) <jamespatterson@operamail.com> <jpjp@users.noreply.github.com>
|
||||
janost <janost@tuta.io>
|
||||
Jaroslav Lichtblau <svetlemodry@users.noreply.github.com>
|
||||
Jaroslav Malec (dzarda) <dzardacz@gmail.com>
|
||||
jaseg <githubaccount@jaseg.net>
|
||||
Jaspitta <ste.scarpitta@gmail.com>
|
||||
Jauder Ho <jauderho@users.noreply.github.com>
|
||||
Jaya Chithra (jayachithra) <s.k.jayachithra@gmail.com>
|
||||
Jaya Kumar <jaya.kumar@ict.nl>
|
||||
Jeffery To <jeffery.to@gmail.com>
|
||||
jelle van der Waa <jelle@vdwaa.nl>
|
||||
Jens Diemer (jedie) <github.com@jensdiemer.de> <git@jensdiemer.de>
|
||||
Jerry Jacobs (xor-gate) <jerry.jacobs@xor-gate.org> <xor-gate@users.noreply.github.com>
|
||||
Jesse Lucas <jesse@jesselucas.com>
|
||||
Jochen Voss (seehuhn) <voss@seehuhn.de>
|
||||
Johan Andersson <j@i19.se>
|
||||
Johan Vromans (sciurius) <jvromans@squirrel.nl>
|
||||
John Rinehart (fuzzybear3965) <johnrichardrinehart@gmail.com>
|
||||
Jonas Thelemann <e-mail@jonas-thelemann.de>
|
||||
Jonathan <artback@protonmail.com> <jonagn@gmail.com>
|
||||
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>
|
||||
Kalle Laine <pahakalle@protonmail.com>
|
||||
Kapil Sareen <kapilsareen584@gmail.com>
|
||||
Karol Różycki (krozycki) <rozycki.karol@gmail.com>
|
||||
Kebin Liu <lkebin@gmail.com>
|
||||
Keith Harrison <keithh@protonmail.com>
|
||||
Keith Turner <kturner@apache.org>
|
||||
Kelong Cong (kc1212) <kc04bc@gmx.com> <kc1212@users.noreply.github.com>
|
||||
Ken'ichi Kamada (kamadak) <kamada@nanohz.org>
|
||||
Kevin Allen (ironmig) <kma1660@gmail.com>
|
||||
@@ -205,31 +186,26 @@ Kevin White, Jr. (kwhite17) <kevinwhite1710@gmail.com>
|
||||
klemens <ka7@github.com>
|
||||
Kurt Fitzner (Kudalufi) <kurt@va1der.ca> <kurt.fitzner@gmail.com>
|
||||
kylosus <33132401+kylosus@users.noreply.github.com>
|
||||
Lars K.W. Gohlke (lkwg82) <lkwg82@gmx.de>
|
||||
Lars Lehtonen <lars.lehtonen@gmail.com>
|
||||
Laurent Arnoud <laurent@spkdev.net>
|
||||
Laurent Etiemble (letiemble) <laurent.etiemble@gmail.com> <laurent.etiemble@monobjc.net>
|
||||
Leo Arias (elopio) <yo@elopio.net>
|
||||
Liu Siyuan (liusy182) <liusy182@gmail.com> <liusy182@hotmail.com>
|
||||
Lode Hoste (Zillode) <zillode@zillode.be>
|
||||
Lord Landon Agahnim (LordLandon) <lordlandon@gmail.com>
|
||||
LSmithx2 <42276854+lsmithx2@users.noreply.github.com>
|
||||
luchenhan <168071714+luchenhan@users.noreply.github.com>
|
||||
Lukas Lihotzki <lukas@lihotzki.de>
|
||||
Luke Hamburg <1992842+luckman212@users.noreply.github.com>
|
||||
luzpaz <luzpaz@users.noreply.github.com>
|
||||
Majed Abdulaziz (majedev) <majed.alhajry@gmail.com>
|
||||
Marc Laporte (marclaporte) <marc@marclaporte.com> <marc@laporte.name>
|
||||
Marc Pujol (kilburn) <kilburn@la3.org>
|
||||
Marcel Meyer <mm.marcelmeyer@gmail.com>
|
||||
Marcin Dziadus (marcindziadus) <dziadus.marcin@gmail.com>
|
||||
marco-m <marco.molteni@laposte.net>
|
||||
Marcus Legendre <marcus.legendre@gmail.com>
|
||||
Mario Majila <mariustshipichik@gmail.com>
|
||||
Mark Pulford (mpx) <mark@kyne.com.au>
|
||||
Martchus <martchus@gmx.net>
|
||||
Martin Polehla <p0l0us@users.noreply.github.com>
|
||||
Mateusz Naściszewski (mateon1) <matin1111@wp.pl>
|
||||
Mateusz Ż <thedead4fun@live.com>
|
||||
mathias4833 <67101597+mathias4833@users.noreply.github.com>
|
||||
Matic Potočnik <hairyfotr@gmail.com>
|
||||
Matt Burke (burkemw3) <mburke@amplify.com> <burkemw3@gmail.com>
|
||||
Matt Robenolt <matt@ydekproductions.com>
|
||||
@@ -237,15 +213,10 @@ Matteo Ruina <matteo.ruina@gmail.com>
|
||||
Maurizio Tomasi <ziotom78@gmail.com>
|
||||
Max <github@germancoding.com>
|
||||
Max Schulze (kralo) <max.schulze@online.de> <kralo@users.noreply.github.com>
|
||||
maxice8 <30738253+maxice8@users.noreply.github.com>
|
||||
MaximAL <almaximal@ya.ru>
|
||||
Maxime Thirouin <m@moox.io>
|
||||
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>
|
||||
Michael Rienstra <mrienstra@gmail.com>
|
||||
Michael Tilli (pyfisch) <pyfisch@gmail.com>
|
||||
MichaIng <micha@dietpi.com>
|
||||
Migelo <miha@filetki.si>
|
||||
Mike Boone <mike@boonedocks.net>
|
||||
@@ -254,7 +225,6 @@ MikolajTwarog <43782609+MikolajTwarog@users.noreply.github.com>
|
||||
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>
|
||||
@@ -269,14 +239,13 @@ NoLooseEnds <jon.koslung@gmail.com>
|
||||
Oliver Freyermuth <o.freyermuth@googlemail.com>
|
||||
orangekame3 <miya.org.0309@gmail.com>
|
||||
otbutz <tbutz@optitool.de>
|
||||
Otiel <Otiel@users.noreply.github.com>
|
||||
overkill <22098433+0verk1ll@users.noreply.github.com>
|
||||
Oyebanji Jacob Mayowa <oyebanji05@gmail.com>
|
||||
Pablo <pbaeyens31+github@gmail.com>
|
||||
Pascal Jungblut (pascalj) <github@pascalj.com> <mail@pascal-jungblut.com>
|
||||
Paul Brit <paulbrit44@gmail.com>
|
||||
Paul Donald <newtwen+github@gmail.com>
|
||||
Pawel Palenica (qepasa) <pawelpalenica11@gmail.com>
|
||||
Paweł Rozlach <vespian@users.noreply.github.com>
|
||||
perewa <cavalcante.ten@gmail.com>
|
||||
Peter Badida <KeyWeeUsr@users.noreply.github.com>
|
||||
Peter Dave Hello <hsu@peterdavehello.org>
|
||||
@@ -286,18 +255,16 @@ Phani Rithvij <phanirithvij2000@gmail.com>
|
||||
Phil Davis <phil.davis@inf.org>
|
||||
Philippe Schommers (filoozoom) <philippe@schommers.be>
|
||||
Phill Luby (pluby) <phill.luby@newredo.com>
|
||||
Pier Paolo Ramon <ramonpierre@gmail.com>
|
||||
Piotr Bejda (piobpl) <piotrb10@gmail.com>
|
||||
Pramodh KP (pramodhkp) <pramodh.p@directi.com> <1507241+pramodhkp@users.noreply.github.com>
|
||||
polyfloyd <polyfloyd@users.noreply.github.com>
|
||||
pullmerge <166967364+pullmerge@users.noreply.github.com>
|
||||
Quentin Hibon <qh.public@yahoo.com>
|
||||
Rahmi Pruitt <rjpruitt16@gmail.com>
|
||||
red_led <red-led@users.noreply.github.com>
|
||||
Richard Hartmann <RichiH@users.noreply.github.com>
|
||||
Robert Carosi (nov1n) <robert@carosi.nl>
|
||||
Roberto Santalla <roobre@users.noreply.github.com>
|
||||
Robin Schoonover <robin@cornhooves.org>
|
||||
Roman Zaynetdinov (zaynetro) <romanznet@gmail.com>
|
||||
Ross Smith II (rasa) <ross@smithii.com>
|
||||
rubenbe <github-com-00ff86@vandamme.email>
|
||||
Ruslan Yevdokymov <38809160+ruslanye@users.noreply.github.com>
|
||||
Ryan Qian <i@bitbili.net>
|
||||
@@ -309,33 +276,27 @@ Sergey Mishin (ralder) <ralder@yandex.ru>
|
||||
Sertonix <83883937+Sertonix@users.noreply.github.com>
|
||||
Severin von Wnuck-Lipinski <ss7@live.de>
|
||||
Shaarad Dalvi <60266155+shaaraddalvi@users.noreply.github.com> <shdalv@microsoft.com>
|
||||
Simon Frei (imsodin) <freisim93@gmail.com>
|
||||
Simon Mwepu <simonmwepu@gmail.com>
|
||||
Simon Pickup <simon@pickupinfinity.com>
|
||||
Sly_tom_cat <slytomcat@mail.ru>
|
||||
Sonu Kumar Saw <31889738+dev-saw99@users.noreply.github.com>
|
||||
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>
|
||||
Sébastien WENSKE <sebastien@wenske.fr>
|
||||
Taylor Khan (nelsonkhan) <nelsonkhan@gmail.com>
|
||||
Terrance <git@terrance.allofti.me>
|
||||
TheCreeper <TheCreeper@users.noreply.github.com>
|
||||
Thomas <9749173+uhthomas@users.noreply.github.com>
|
||||
Thomas Hipp <thomashipp@gmail.com>
|
||||
Tim Abell (timabell) <tim@timwise.co.uk>
|
||||
Tim Howes (timhowes) <timhowes@berkeley.edu>
|
||||
Tim Nordenfur <tim@gurka.se>
|
||||
Tobias Frölich <40638719+tobifroe@users.noreply.github.com>
|
||||
Tobias Klauser <tobias.klauser@gmail.com>
|
||||
Tobias Nygren (tnn2) <tnn@nygren.pp.se>
|
||||
Tobias Tom (tobiastom) <t.tom@succont.de>
|
||||
Tom Jakubowski <tom@crystae.net>
|
||||
Tomasz Wilczyński <5626656+tomasz1986@users.noreply.github.com> <twilczynski@naver.com>
|
||||
Tommy Thorn <tommy-github-email@thorn.ws>
|
||||
Tommy van der Vorst <tommy-github@pixelspark.nl> <tommy@pixelspark.nl>
|
||||
Tully Robinson (tojrobinson) <tully@tojr.org>
|
||||
Tyler Brazier (tylerbrazier) <tyler@tylerbrazier.com>
|
||||
Tyler Kropp <kropptyler@gmail.com>
|
||||
@@ -352,10 +313,10 @@ WangXi <xib1102@icloud.com>
|
||||
Will Rouesnel <wrouesnel@wrouesnel.com>
|
||||
William A. Kennington III (wkennington) <william@wkennington.com>
|
||||
wouter bolsterlee <wouter@bolsterl.ee>
|
||||
Wulf Weich (wweich) <wweich@users.noreply.github.com> <wweich@gmx.de> <wulf@weich-kr.de>
|
||||
xarx00 <xarx00@users.noreply.github.com>
|
||||
Xavier O. (damajor) <damajor@gmail.com>
|
||||
xjtdy888 (xjtdy888) <xjtdy888@163.com>
|
||||
xjtdy888 (xjtdy888) <xjtdy888@163.com> <xjtdy888@gmail.com>
|
||||
Yannic A. (eipiminus1) <eipiminusone+github@gmail.com> <eipiminus1@users.noreply.github.com>
|
||||
yparitcher <y@paritcher.com>
|
||||
佛跳墙 <daoquan@qq.com>
|
||||
落心 <luoxin.ttt@gmail.com>
|
||||
|
||||
162
CONTRIBUTING.md
162
CONTRIBUTING.md
@@ -34,19 +34,163 @@ Note that the previously used service at
|
||||
retired and we kindly ask you to sign up on Weblate for continued
|
||||
involvement.
|
||||
|
||||
## Contributing Code
|
||||
|
||||
Every contribution is welcome. If you want to contribute but are unsure
|
||||
where to start, any open issues are fair game! See the [Contribution
|
||||
Guidelines](https://docs.syncthing.net/dev/contributing.html) for the full
|
||||
story on committing code.
|
||||
|
||||
## Contributing Documentation
|
||||
|
||||
Updates to the [documentation site](https://docs.syncthing.net/) can be
|
||||
made as pull requests on the [documentation
|
||||
repository](https://github.com/syncthing/docs).
|
||||
|
||||
## Contributing Code
|
||||
|
||||
Every contribution is welcome. If you want to contribute but are unsure
|
||||
where to start, any open issues are fair game! Here's a short rundown of
|
||||
what you need to keep in mind:
|
||||
|
||||
- Don't worry. You are not expected to get everything right on the first
|
||||
attempt, we'll guide you through it.
|
||||
|
||||
- Make sure there is an
|
||||
[issue](https://github.com/syncthing/syncthing/issues) that describes the
|
||||
change you want to do. If the thing you want to do does not have an issue
|
||||
yet, please file one before starting work on it.
|
||||
|
||||
- Fork the repository and make your changes in a new branch. Once it's ready
|
||||
for review, create a pull request.
|
||||
|
||||
### Authorship
|
||||
|
||||
All code authors are listed in the AUTHORS file. When your first pull
|
||||
request is accepted your details are added to the AUTHORS file and the list
|
||||
of authors in the GUI. Commits must be made with the same name and email as
|
||||
listed in the AUTHORS file. To accomplish this, ensure that your git
|
||||
configuration is set correctly prior to making your first commit:
|
||||
|
||||
$ git config --global user.name "Jane Doe"
|
||||
$ git config --global user.email janedoe@example.com
|
||||
|
||||
You must be reachable on the given email address. If you do not wish to use
|
||||
your real name for whatever reason, using a nickname or pseudonym is
|
||||
perfectly acceptable.
|
||||
|
||||
### The Developer Certificate of Origin (DCO)
|
||||
|
||||
The Syncthing project requires the Developer Certificate of Origin (DCO)
|
||||
sign-off on pull requests (PRs). This means that all commit messages must
|
||||
contain a signature line to indicate that the developer accepts the DCO.
|
||||
|
||||
The DCO is a lightweight way for contributors to certify that they wrote (or
|
||||
otherwise have the right to submit) the code and changes they are
|
||||
contributing to the project. Here is the full [text of the
|
||||
DCO](https://developercertificate.org):
|
||||
|
||||
---
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
1. The contribution was created in whole or in part by me and I have the
|
||||
right to submit it under the open source license indicated in the file;
|
||||
or
|
||||
|
||||
2. The contribution is based upon previous work that, to the best of my
|
||||
knowledge, is covered under an appropriate open source license and I have
|
||||
the right under that license to submit that work with modifications,
|
||||
whether created in whole or in part by me, under the same open source
|
||||
license (unless I am permitted to submit under a different license), as
|
||||
indicated in the file; or
|
||||
|
||||
3. The contribution was provided directly to me by some other person who
|
||||
certified (1), (2) or (3) and I have not modified it.
|
||||
|
||||
4. I understand and agree that this project and the contribution are public
|
||||
and that a record of the contribution (including all personal information
|
||||
I submit with it, including my sign-off) is maintained indefinitely and
|
||||
may be redistributed consistent with this project or the open source
|
||||
license(s) involved.
|
||||
|
||||
---
|
||||
|
||||
Contributors indicate that they adhere to these requirements by adding
|
||||
a `Signed-off-by` line to their commit messages. For example:
|
||||
|
||||
This is my commit message
|
||||
|
||||
Signed-off-by: Random J Developer <random@developer.example.org>
|
||||
|
||||
The name and email address in this line must match those of the committing
|
||||
author, and be the same as what you want in the AUTHORS file as per above.
|
||||
|
||||
### Coding Style
|
||||
|
||||
#### General
|
||||
|
||||
- All text files use Unix line endings. The git settings already present in
|
||||
the repository attempt to enforce this.
|
||||
|
||||
- When making changes, follow the brace and parenthesis style of the
|
||||
surrounding code.
|
||||
|
||||
#### Go Specific
|
||||
|
||||
- Follow the conventions laid out in [Effective
|
||||
Go](https://go.dev/doc/effective_go) as much as makes sense. The review
|
||||
guidelines in [Go Code Review
|
||||
Comments](https://github.com/golang/go/wiki/CodeReviewComments) should
|
||||
generally be followed.
|
||||
|
||||
- Each commit should be `go fmt` clean.
|
||||
|
||||
- Imports are grouped per `goimports` standard; that is, standard
|
||||
library first, then third party libraries after a blank line.
|
||||
|
||||
### Commits
|
||||
|
||||
- Commit messages (and pull request titles) should follow the [conventional
|
||||
commits](https://www.conventionalcommits.org/en/v1.0.0/) specification and
|
||||
be in lower case.
|
||||
|
||||
- We use a scope description in the commit message subject. This is the
|
||||
component of Syncthing that the commit affects. For example, `gui`,
|
||||
`protocol`, `scanner`, `upnp`, etc -- typically, the part after
|
||||
`internal/`, `lib/` or `cmd/` in the package path. If the commit doesn't
|
||||
affect a specific component, such as for changes to the build system or
|
||||
documentation, the scope should be omitted. The same goes for changes that
|
||||
affect many components which would be cumbersome to list.
|
||||
|
||||
- Commits that resolve an existing issue must include the issue number
|
||||
as `(fixes #123)` at the end of the commit message subject. A correctly
|
||||
formatted commit message subject looks like this:
|
||||
|
||||
feat(dialer): add env var to disable proxy fallback (fixes #3006)
|
||||
|
||||
- If the commit message subject doesn't say it all, one or more paragraphs of
|
||||
describing text should be added to the commit message. This should explain
|
||||
why the change is made and what it accomplishes.
|
||||
|
||||
- When drafting a pull request, please feel free to add commits with
|
||||
corrections and merge from `main` when necessary. This provides a clear time
|
||||
line with changes and simplifies review. Do not, in general, rebase your
|
||||
commits, as this makes review harder.
|
||||
|
||||
- Pull requests are merged to `main` using squash merge. The "stream of
|
||||
consciousness" set of commits described in the previous point will be reduced
|
||||
to a single commit at merge time. The pull request title and description will
|
||||
be used as the commit message.
|
||||
|
||||
### Tests
|
||||
|
||||
Yes please, do add tests when adding features or fixing bugs. Also, when a
|
||||
pull request is filed a number of automatic tests are run on the code. This
|
||||
includes:
|
||||
|
||||
- That the code actually builds and the test suite passes.
|
||||
|
||||
- That the code is correctly formatted (`go fmt`).
|
||||
|
||||
- That the commits are based on a reasonably recent `main`.
|
||||
|
||||
- That the output from `go lint` and `go vet` is clean. (This checks for a
|
||||
number of potential problems the compiler doesn't catch.)
|
||||
|
||||
## Licensing
|
||||
|
||||
All contributions are made available under the same license as the already
|
||||
@@ -59,10 +203,6 @@ otherwise stated this means MPLv2, but there are exceptions:
|
||||
- The documentation (man/...) is licensed under the Creative Commons
|
||||
Attribution 4.0 International License.
|
||||
|
||||
- Projects under vendor/... are copyright by and licensed from their
|
||||
respective original authors. Contributions should be made to the original
|
||||
project, not here.
|
||||
|
||||
Regardless of the license in effect, you retain the copyright to your
|
||||
contribution.
|
||||
|
||||
|
||||
@@ -23,52 +23,7 @@ example `UMASK=002`.
|
||||
**Docker cli**
|
||||
```
|
||||
$ docker pull syncthing/syncthing
|
||||
$ docker run -p 8384:8384 -p 22000:22000/tcp -p 22000:22000/udp -p 21027:21027/udp \
|
||||
-v /wherever/st-sync:/var/syncthing \
|
||||
--hostname=my-syncthing \
|
||||
syncthing/syncthing:latest
|
||||
```
|
||||
|
||||
**Docker compose**
|
||||
```yml
|
||||
---
|
||||
version: "3"
|
||||
services:
|
||||
syncthing:
|
||||
image: syncthing/syncthing
|
||||
container_name: syncthing
|
||||
hostname: my-syncthing
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
volumes:
|
||||
- /wherever/st-sync:/var/syncthing
|
||||
ports:
|
||||
- 8384:8384 # Web UI
|
||||
- 22000:22000/tcp # TCP file transfers
|
||||
- 22000:22000/udp # QUIC file transfers
|
||||
- 21027:21027/udp # Receive local discovery broadcasts
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: curl -fkLsS -m 2 127.0.0.1:8384/rest/noauth/health | grep -o --color=never OK || exit 1
|
||||
interval: 1m
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
## Discovery
|
||||
|
||||
Note that Docker's default network mode prevents local IP addresses from
|
||||
being discovered, as Syncthing is only able to see the internal IP of the
|
||||
container on the `172.17.0.0/16` subnet. This will result in poor transfer rates
|
||||
if local device addresses are not manually configured.
|
||||
|
||||
It is therefore advisable to use the [host network mode](https://docs.docker.com/network/host/) instead:
|
||||
|
||||
**Docker cli**
|
||||
```
|
||||
$ docker pull syncthing/syncthing
|
||||
$ docker run --network=host \
|
||||
$ docker run --network=host -e STGUIADDRESS= \
|
||||
-v /wherever/st-sync:/var/syncthing \
|
||||
syncthing/syncthing:latest
|
||||
```
|
||||
@@ -85,6 +40,7 @@ services:
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- STGUIADDRESS=
|
||||
volumes:
|
||||
- /wherever/st-sync:/var/syncthing
|
||||
network_mode: host
|
||||
@@ -96,27 +52,27 @@ services:
|
||||
retries: 3
|
||||
```
|
||||
|
||||
## Discovery
|
||||
|
||||
Please note that Docker's default network mode prevents local IP addresses
|
||||
from being discovered, as Syncthing can only see the internal IP address of
|
||||
the container on the `172.17.0.0/16` subnet. This would likely break the ability
|
||||
for nodes to establish LAN connections properly, resulting in poor transfer
|
||||
rates unless local device addresses are configured manually.
|
||||
|
||||
It is therefore strongly recommended to stick to the [host network mode](https://docs.docker.com/network/host/),
|
||||
as shown above.
|
||||
|
||||
Be aware that syncthing alone is now in control of what interfaces and ports it
|
||||
listens on. You can edit the syncthing configuration to change the defaults if
|
||||
there are conflicts.
|
||||
|
||||
## GUI Security
|
||||
|
||||
By default Syncthing inside the Docker image listens on 0.0.0.0:8384 to
|
||||
allow GUI connections via the Docker proxy. This is set by the
|
||||
`STGUIADDRESS` environment variable in the Dockerfile, as it differs from
|
||||
what Syncthing would otherwise use by default. This means you should set up
|
||||
authentication in the GUI, like for any other externally reachable Syncthing
|
||||
instance. If you do not require the GUI, or you use host networking, you can
|
||||
unset the `STGUIADDRESS` variable to have Syncthing fall back to listening
|
||||
on 127.0.0.1:
|
||||
|
||||
```
|
||||
$ docker pull syncthing/syncthing
|
||||
$ docker run -e STGUIADDRESS= \
|
||||
-v /wherever/st-sync:/var/syncthing \
|
||||
syncthing/syncthing:latest
|
||||
```
|
||||
|
||||
With the environment variable unset Syncthing will follow what is set in the
|
||||
configuration file / GUI settings dialog.
|
||||
By default Syncthing inside the Docker image listens on `0.0.0.0:8384`. This
|
||||
allows GUI connections when running without host network mode. The example
|
||||
above unsets the `STGUIADDRESS` environment variable to have Syncthing fall
|
||||
back to listening on what has been configured in the configuration file or the
|
||||
GUI settings dialog. By default this is the localhost IP address `127.0.0.1`.
|
||||
If you configure your GUI to be externally reachable, make sure you set up
|
||||
authentication and enable TLS.
|
||||
|
||||
122
build.go
122
build.go
@@ -38,27 +38,26 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
goarch string
|
||||
goos string
|
||||
noupgrade bool
|
||||
version string
|
||||
goCmd string
|
||||
race bool
|
||||
debug = os.Getenv("BUILDDEBUG") != ""
|
||||
extraTags string
|
||||
installSuffix string
|
||||
pkgdir string
|
||||
cc string
|
||||
run string
|
||||
benchRun string
|
||||
buildOut string
|
||||
debugBinary bool
|
||||
coverage bool
|
||||
long bool
|
||||
timeout = "120s"
|
||||
longTimeout = "600s"
|
||||
numVersions = 5
|
||||
withNextGenGUI = os.Getenv("BUILD_NEXT_GEN_GUI") != ""
|
||||
goarch string
|
||||
goos string
|
||||
noupgrade bool
|
||||
version string
|
||||
goCmd string
|
||||
race bool
|
||||
debug = os.Getenv("BUILDDEBUG") != ""
|
||||
extraTags string
|
||||
installSuffix string
|
||||
pkgdir string
|
||||
cc string
|
||||
run string
|
||||
benchRun string
|
||||
buildOut string
|
||||
debugBinary bool
|
||||
coverage bool
|
||||
long bool
|
||||
timeout = "120s"
|
||||
longTimeout = "600s"
|
||||
numVersions = 5
|
||||
)
|
||||
|
||||
type target struct {
|
||||
@@ -289,10 +288,10 @@ func runCommand(cmd string, target target) {
|
||||
build(target, tags)
|
||||
|
||||
case "test":
|
||||
test(strings.Fields(extraTags), "github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...")
|
||||
test(strings.Fields(extraTags), "github.com/syncthing/syncthing/internal/...", "github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...")
|
||||
|
||||
case "bench":
|
||||
bench(strings.Fields(extraTags), "github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...")
|
||||
bench(strings.Fields(extraTags), "github.com/syncthing/syncthing/internal/...", "github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...")
|
||||
|
||||
case "integration":
|
||||
integration(false)
|
||||
@@ -330,7 +329,7 @@ func runCommand(cmd string, target target) {
|
||||
writeCompatJSON()
|
||||
|
||||
case "deb":
|
||||
buildDeb(target)
|
||||
buildDeb(target, tags)
|
||||
|
||||
case "vet":
|
||||
metalintShort()
|
||||
@@ -380,7 +379,6 @@ func parseFlags() {
|
||||
flag.IntVar(&numVersions, "num-versions", numVersions, "Number of versions for changelog command")
|
||||
flag.StringVar(&run, "run", "", "Specify which tests to run")
|
||||
flag.StringVar(&benchRun, "bench", "", "Specify which benchmarks to run")
|
||||
flag.BoolVar(&withNextGenGUI, "with-next-gen-gui", withNextGenGUI, "Also build 'newgui'")
|
||||
flag.StringVar(&buildOut, "build-out", "", "Set the '-o' value for 'go build'")
|
||||
flag.Parse()
|
||||
}
|
||||
@@ -453,10 +451,6 @@ func benchArgs() []string {
|
||||
}
|
||||
|
||||
func install(target target, tags []string) {
|
||||
if (target.name == "syncthing" || target.name == "") && !withNextGenGUI {
|
||||
log.Println("Notice: Next generation GUI will not be built; see --with-next-gen-gui.")
|
||||
}
|
||||
|
||||
lazyRebuildAssets()
|
||||
|
||||
tags = append(target.tags, tags...)
|
||||
@@ -480,16 +474,12 @@ func install(target target, tags []string) {
|
||||
defer shouldCleanupSyso(sysoPath)
|
||||
}
|
||||
|
||||
args := []string{"install", "-v"}
|
||||
args := []string{"install"}
|
||||
args = appendParameters(args, tags, target.buildPkgs...)
|
||||
runPrint(goCmd, args...)
|
||||
}
|
||||
|
||||
func build(target target, tags []string) {
|
||||
if (target.name == "syncthing" || target.name == "") && !withNextGenGUI {
|
||||
log.Println("Notice: Next generation GUI will not be built; see --with-next-gen-gui.")
|
||||
}
|
||||
|
||||
lazyRebuildAssets()
|
||||
tags = append(target.tags, tags...)
|
||||
|
||||
@@ -512,7 +502,7 @@ func build(target target, tags []string) {
|
||||
defer shouldCleanupSyso(sysoPath)
|
||||
}
|
||||
|
||||
args := []string{"build", "-v"}
|
||||
args := []string{"build"}
|
||||
if buildOut != "" {
|
||||
args = append(args, "-o", buildOut)
|
||||
}
|
||||
@@ -524,13 +514,6 @@ func setBuildEnvVars() {
|
||||
os.Setenv("GOOS", goos)
|
||||
os.Setenv("GOARCH", goarch)
|
||||
os.Setenv("CC", cc)
|
||||
if os.Getenv("CGO_ENABLED") == "" {
|
||||
switch goos {
|
||||
case "darwin", "solaris":
|
||||
default:
|
||||
os.Setenv("CGO_ENABLED", "0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func appendParameters(args []string, tags []string, pkgs ...string) []string {
|
||||
@@ -609,7 +592,7 @@ func buildZip(target target, tags []string) {
|
||||
fmt.Println(filename)
|
||||
}
|
||||
|
||||
func buildDeb(target target) {
|
||||
func buildDeb(target target, tags []string) {
|
||||
os.RemoveAll("deb")
|
||||
|
||||
// "goarch" here is set to whatever the Debian packages expect. We correct
|
||||
@@ -623,7 +606,7 @@ func buildDeb(target target) {
|
||||
goarch = "arm"
|
||||
}
|
||||
|
||||
build(target, []string{"noupgrade"})
|
||||
build(target, append(tags, "noupgrade"))
|
||||
|
||||
for i := range target.installationFiles {
|
||||
target.installationFiles[i].src = strings.Replace(target.installationFiles[i].src, "{{binary}}", target.BinaryName(), 1)
|
||||
@@ -645,6 +628,9 @@ func buildDeb(target target) {
|
||||
// than just 0.14.26. This rectifies that.
|
||||
debver = strings.Replace(debver, "-", "~", -1)
|
||||
}
|
||||
if strings.Contains(debver, "_") {
|
||||
debver = strings.Replace(debver, "_", "~", -1)
|
||||
}
|
||||
args := []string{
|
||||
"-t", "deb",
|
||||
"-s", "dir",
|
||||
@@ -746,12 +732,9 @@ func shouldBuildSyso(dir string) (string, error) {
|
||||
sysoPath := filepath.Join(dir, "cmd", "syncthing", "resource.syso")
|
||||
|
||||
// See https://github.com/josephspurrier/goversioninfo#command-line-flags
|
||||
armOption := ""
|
||||
if strings.Contains(goarch, "arm") {
|
||||
armOption = "-arm=true"
|
||||
}
|
||||
|
||||
if _, err := runError("goversioninfo", "-o", sysoPath, armOption); err != nil {
|
||||
arm := strings.HasPrefix(goarch, "arm")
|
||||
a64 := strings.Contains(goarch, "64")
|
||||
if _, err := runError("goversioninfo", "-o", sysoPath, fmt.Sprintf("-arm=%v", arm), fmt.Sprintf("-64=%v", a64)); err != nil {
|
||||
return "", errors.New("failed to create " + sysoPath + ": " + err.Error())
|
||||
}
|
||||
|
||||
@@ -823,43 +806,11 @@ func lazyRebuildAssets() {
|
||||
shouldRebuild := shouldRebuildAssets("lib/api/auto/gui.files.go", "gui") ||
|
||||
shouldRebuildAssets("cmd/infra/strelaypoolsrv/auto/gui.files.go", "cmd/infra/strelaypoolsrv/gui")
|
||||
|
||||
if withNextGenGUI {
|
||||
shouldRebuild = buildNextGenGUI() || shouldRebuild
|
||||
}
|
||||
|
||||
if shouldRebuild {
|
||||
rebuildAssets()
|
||||
}
|
||||
}
|
||||
|
||||
func buildNextGenGUI() bool {
|
||||
// Check if we need to run the npm process, and if so also set the flag
|
||||
// to rebuild Go assets afterwards. The index.html is regenerated every
|
||||
// time by the build process. This assumes the new GUI ends up in
|
||||
// next-gen-gui/dist/next-gen-gui.
|
||||
|
||||
if !shouldRebuildAssets("gui/next-gen-gui/index.html", "next-gen-gui") {
|
||||
// The GUI is up to date.
|
||||
return false
|
||||
}
|
||||
|
||||
runPrintInDir("next-gen-gui", "npm", "install")
|
||||
runPrintInDir("next-gen-gui", "npm", "run", "build", "--", "--prod", "--subresource-integrity")
|
||||
|
||||
rmr("gui/tech-ui")
|
||||
|
||||
for _, src := range listFiles("next-gen-gui/dist") {
|
||||
rel, _ := filepath.Rel("next-gen-gui/dist", src)
|
||||
dst := filepath.Join("gui", rel)
|
||||
if err := copyFile(src, dst, 0o644); err != nil {
|
||||
fmt.Println("copy:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func shouldRebuildAssets(target, srcdir string) bool {
|
||||
info, err := os.Stat(target)
|
||||
if err != nil {
|
||||
@@ -919,7 +870,6 @@ func testmocks() {
|
||||
"github.com/syncthing/syncthing/lib/connections",
|
||||
"github.com/syncthing/syncthing/lib/discover",
|
||||
"github.com/syncthing/syncthing/lib/events",
|
||||
"github.com/syncthing/syncthing/lib/logger",
|
||||
"github.com/syncthing/syncthing/lib/model",
|
||||
"github.com/syncthing/syncthing/lib/protocol",
|
||||
}
|
||||
@@ -950,6 +900,7 @@ func weblate() {
|
||||
func ldflags(tags []string) string {
|
||||
b := new(strings.Builder)
|
||||
b.WriteString("-w")
|
||||
b.WriteString(" -buildid=")
|
||||
fmt.Fprintf(b, " -X github.com/syncthing/syncthing/lib/build.Version=%s", version)
|
||||
fmt.Fprintf(b, " -X github.com/syncthing/syncthing/lib/build.Stamp=%d", buildStamp())
|
||||
fmt.Fprintf(b, " -X github.com/syncthing/syncthing/lib/build.User=%s", buildUser())
|
||||
@@ -971,6 +922,9 @@ func rmr(paths ...string) {
|
||||
}
|
||||
|
||||
func getReleaseVersion() (string, error) {
|
||||
if ver := os.Getenv("VERSION"); ver != "" {
|
||||
return strings.TrimSpace(ver), nil
|
||||
}
|
||||
bs, err := os.ReadFile("RELEASE")
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -1015,7 +969,7 @@ func getGitVersion() (string, error) {
|
||||
}
|
||||
|
||||
func getVersion() string {
|
||||
// First try for a RELEASE file,
|
||||
// First try for a RELEASE file or $VERSION env var,
|
||||
if ver, err := getReleaseVersion(); err == nil {
|
||||
return ver
|
||||
}
|
||||
|
||||
1
build.sh
1
build.sh
@@ -23,6 +23,7 @@ case "${1:-default}" in
|
||||
|
||||
prerelease)
|
||||
script authors
|
||||
script copyrights
|
||||
build weblate
|
||||
pushd man ; ./refresh.sh ; popd
|
||||
git add -A gui man AUTHORS
|
||||
|
||||
@@ -72,7 +72,7 @@ func main() {
|
||||
if *standardBlocks || blockSize < protocol.MinBlockSize {
|
||||
blockSize = protocol.BlockSize(fi.Size())
|
||||
}
|
||||
bs, err := scanner.Blocks(context.TODO(), fd, blockSize, fi.Size(), nil, true)
|
||||
bs, err := scanner.Blocks(context.TODO(), fd, blockSize, fi.Size(), nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"io"
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"slices"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -177,8 +178,8 @@ func (d *diskStore) inventory() error {
|
||||
})
|
||||
return nil
|
||||
})
|
||||
sort.Slice(d.currentFiles, func(i, j int) bool {
|
||||
return d.currentFiles[i].mtime < d.currentFiles[j].mtime
|
||||
slices.SortFunc(d.currentFiles, func(a, b currentFile) int {
|
||||
return cmp.Compare(a.mtime, b.mtime)
|
||||
})
|
||||
var oldest time.Duration
|
||||
if len(d.currentFiles) > 0 {
|
||||
|
||||
@@ -44,7 +44,7 @@ type cli struct {
|
||||
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"`
|
||||
MetricsListen string `help:"HTTP listen address for metrics" default:":8081" env:"METRICS_LISTEN_ADDRESS"`
|
||||
IngorePatterns string `help:"File containing ignore patterns (regexp)" env:"IGNORE_PATTERNS" type:"existingfile"`
|
||||
IgnorePatterns string `help:"File containing ignore patterns (regexp)" env:"IGNORE_PATTERNS" type:"existingfile"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -68,9 +68,9 @@ func main() {
|
||||
go ss.Serve(context.Background())
|
||||
|
||||
var ip *ignorePatterns
|
||||
if params.IngorePatterns != "" {
|
||||
if params.IgnorePatterns != "" {
|
||||
var err error
|
||||
ip, err = loadIgnorePatterns(params.IngorePatterns)
|
||||
ip, err = loadIgnorePatterns(params.IgnorePatterns)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load ignore patterns: %v", err)
|
||||
}
|
||||
@@ -118,7 +118,7 @@ func handleFailureFn(dsn, failureDir string, ignore *ignorePatterns) func(w http
|
||||
bs, err := io.ReadAll(lr)
|
||||
req.Body.Close()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ func handleFailureFn(dsn, failureDir string, ignore *ignorePatterns) func(w http
|
||||
var reports []ur.FailureReport
|
||||
err = json.Unmarshal(bs, &reports)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 400)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if len(reports) == 0 {
|
||||
@@ -141,7 +141,7 @@ func handleFailureFn(dsn, failureDir string, ignore *ignorePatterns) func(w http
|
||||
|
||||
version, err := build.ParseVersion(reports[0].Version)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 400)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
for _, r := range reports {
|
||||
|
||||
@@ -90,7 +90,7 @@ func sendReport(dsn string, pkt *raven.Packet, userID string) error {
|
||||
}
|
||||
|
||||
// The client sets release and such on the packet before sending, in the
|
||||
// misguided idea that it knows this better than than the packet we give
|
||||
// misguided idea that it knows this better than the packet we give
|
||||
// it. So we copy the values from the packet to the client first...
|
||||
cli.SetRelease(pkt.Release)
|
||||
cli.SetEnvironment(pkt.Environment)
|
||||
@@ -136,7 +136,7 @@ func parseCrashReport(path string, report []byte) (*raven.Packet, error) {
|
||||
|
||||
r := bytes.NewReader(report)
|
||||
ctx, _, err := stack.ScanSnapshot(r, io.Discard, stack.DefaultOpts())
|
||||
if err != nil && err != io.EOF {
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
return nil, err
|
||||
}
|
||||
if ctx == nil || len(ctx.Goroutines) == 0 {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
// remote IP, and the current month.
|
||||
func userIDFor(req *http.Request) string {
|
||||
addr := req.RemoteAddr
|
||||
if fwd := req.Header.Get("x-forwarded-for"); fwd != "" {
|
||||
if fwd := req.Header.Get("X-Forwarded-For"); fwd != "" {
|
||||
addr = fwd
|
||||
}
|
||||
if host, _, err := net.SplitHostPort(addr); err == nil {
|
||||
@@ -32,7 +32,7 @@ func userIDFor(req *http.Request) string {
|
||||
now := time.Now().Format("200601")
|
||||
salt := "stcrashreporter"
|
||||
hash := sha256.Sum256([]byte(salt + addr + now))
|
||||
return fmt.Sprintf("%x", hash[:8])
|
||||
return hex.EncodeToString(hash[:8])
|
||||
}
|
||||
|
||||
// 01234567890abcdef... => 01/23
|
||||
|
||||
@@ -10,7 +10,7 @@ to NAT or firewall issues.
|
||||
|
||||
There is very little reason why you'd want to run this yourself, as
|
||||
`relaypoolsrv` is just used for announcement and lookup of public relay
|
||||
servers. If you are looking to setup a private or a public relay, please
|
||||
servers. If you are looking to set up a private or a public relay, please
|
||||
check the documentation for
|
||||
[relaysrv](https://github.com/syncthing/relaysrv), which also explains how
|
||||
to join the default public pool.
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -29,8 +30,8 @@ import (
|
||||
_ "github.com/syncthing/syncthing/lib/automaxprocs"
|
||||
"github.com/syncthing/syncthing/lib/geoip"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
"github.com/syncthing/syncthing/lib/relay/client"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
"github.com/syncthing/syncthing/lib/tlsutil"
|
||||
)
|
||||
|
||||
@@ -110,10 +111,11 @@ var (
|
||||
requestProcessors = 8
|
||||
geoipLicenseKey = os.Getenv("GEOIP_LICENSE_KEY")
|
||||
geoipAccountID, _ = strconv.Atoi(os.Getenv("GEOIP_ACCOUNT_ID"))
|
||||
maxRelaysReturned = 100
|
||||
|
||||
requests chan request
|
||||
|
||||
mut = sync.NewRWMutex()
|
||||
mut sync.RWMutex
|
||||
knownRelays = make([]*relay, 0)
|
||||
permanentRelays = make([]*relay, 0)
|
||||
evictionTimers = make(map[string]*time.Timer)
|
||||
@@ -141,6 +143,7 @@ func main() {
|
||||
flag.IntVar(&requestQueueLen, "request-queue", requestQueueLen, "Queue length for incoming test requests")
|
||||
flag.IntVar(&requestProcessors, "request-processors", requestProcessors, "Number of request processor routines")
|
||||
flag.StringVar(&geoipLicenseKey, "geoip-license-key", geoipLicenseKey, "License key for GeoIP database")
|
||||
flag.IntVar(&maxRelaysReturned, "max-relays-returned", maxRelaysReturned, "Maximum number of relays returned for a normal endpoint query")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
@@ -159,7 +162,7 @@ func main() {
|
||||
|
||||
testCert = createTestCertificate()
|
||||
|
||||
for i := 0; i < requestProcessors; i++ {
|
||||
for range requestProcessors {
|
||||
go requestProcessor(geoip)
|
||||
}
|
||||
|
||||
@@ -177,7 +180,7 @@ func main() {
|
||||
relayTestsTotal.WithLabelValues("success").Inc()
|
||||
}
|
||||
}
|
||||
// Run the the stats refresher once the relays are loaded.
|
||||
// Run the stats refresher once the relays are loaded.
|
||||
statsRefresher(statsRefresh)
|
||||
}()
|
||||
|
||||
@@ -331,6 +334,10 @@ func handleEndpointShort(rw http.ResponseWriter, r *http.Request) {
|
||||
relays = append(relays, relayShort{URL: slimURL(r.URL)})
|
||||
}
|
||||
mut.RUnlock()
|
||||
if len(relays) > maxRelaysReturned {
|
||||
rand.Shuffle(relays)
|
||||
relays = relays[:maxRelaysReturned]
|
||||
}
|
||||
|
||||
_ = json.NewEncoder(rw).Encode(map[string][]relayShort{
|
||||
"relays": relays,
|
||||
@@ -613,7 +620,7 @@ func createTestCertificate() tls.Certificate {
|
||||
}
|
||||
|
||||
certFile, keyFile := filepath.Join(tmpDir, "cert.pem"), filepath.Join(tmpDir, "key.pem")
|
||||
cert, err := tlsutil.NewCertificate(certFile, keyFile, "relaypoolsrv", 20*365)
|
||||
cert, err := tlsutil.NewCertificate(certFile, keyFile, "relaypoolsrv", 20*365, false)
|
||||
if err != nil {
|
||||
log.Fatalln("Failed to create test X509 key pair:", err)
|
||||
}
|
||||
@@ -646,6 +653,7 @@ func getLocation(host string, geoip *geoip.Provider) location {
|
||||
|
||||
type loggingResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
|
||||
statusCode int
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -28,8 +27,6 @@ func init() {
|
||||
{URL: "known2"},
|
||||
{URL: "known3"},
|
||||
}
|
||||
|
||||
mut = new(sync.RWMutex)
|
||||
}
|
||||
|
||||
// Regression test: handleGetRequest should not modify permanentRelays.
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -104,7 +104,7 @@ func refreshStats() {
|
||||
mut.RUnlock()
|
||||
|
||||
now := time.Now()
|
||||
wg := sync.NewWaitGroup()
|
||||
var wg sync.WaitGroup
|
||||
|
||||
results := make(chan statsFetchResult, len(relays))
|
||||
for _, rel := range relays {
|
||||
@@ -173,7 +173,7 @@ func fetchStats(relay *relay) *stats {
|
||||
|
||||
var stats stats
|
||||
|
||||
if json.NewDecoder(response.Body).Decode(&stats); err != nil {
|
||||
if err := json.NewDecoder(response.Body).Decode(&stats); err != nil {
|
||||
return nil
|
||||
}
|
||||
return &stats
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/syncthing/syncthing/internal/slogutil"
|
||||
_ "github.com/syncthing/syncthing/lib/automaxprocs"
|
||||
"github.com/syncthing/syncthing/lib/httpcache"
|
||||
"github.com/syncthing/syncthing/lib/upgrade"
|
||||
@@ -58,10 +59,10 @@ func server(params *cli) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("metrics: %w", err)
|
||||
}
|
||||
slog.Info("Metrics listener started", "addr", params.MetricsListen)
|
||||
slog.Info("Metrics listener started", slogutil.Address(params.MetricsListen))
|
||||
go func() {
|
||||
if err := http.Serve(metricsListen, mux); err != nil {
|
||||
slog.Warn("Metrics server returned", "error", err)
|
||||
slog.Warn("Metrics server returned", slogutil.Error(err))
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -75,9 +76,9 @@ func server(params *cli) error {
|
||||
|
||||
go func() {
|
||||
for range time.NewTicker(params.CacheTime).C {
|
||||
slog.Info("Refreshing cached releases", "url", params.URL)
|
||||
slog.Info("Refreshing cached releases", slogutil.URI(params.URL))
|
||||
if err := cache.Update(context.Background()); err != nil {
|
||||
slog.Error("Failed to refresh cached releases", "url", params.URL, "error", err)
|
||||
slog.Error("Failed to refresh cached releases", slogutil.URI(params.URL), slogutil.Error(err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -109,7 +110,7 @@ func server(params *cli) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("listen: %w", err)
|
||||
}
|
||||
slog.Info("Main listener started", "addr", params.Listen)
|
||||
slog.Info("Main listener started", slogutil.Address(params.Listen))
|
||||
|
||||
return srv.Serve(srvListener)
|
||||
}
|
||||
@@ -137,7 +138,7 @@ func (p *githubReleases) serveReleases(w http.ResponseWriter, req *http.Request)
|
||||
osv := req.Header.Get("Syncthing-Os-Version")
|
||||
if ua != "" && osv != "" {
|
||||
// We should determine the compatibility of the releases.
|
||||
rels = filterForCompabitility(rels, ua, osv)
|
||||
rels = filterForCompatibility(rels, ua, osv)
|
||||
} else {
|
||||
metricFilterCalls.WithLabelValues("no-ua-or-osversion").Inc()
|
||||
}
|
||||
@@ -201,17 +202,21 @@ func (p *proxy) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
// looking for a prerelease at all.
|
||||
func filterForLatest(rels []upgrade.Release) []upgrade.Release {
|
||||
var filtered []upgrade.Release
|
||||
var havePre bool
|
||||
havePre := make(map[string]bool)
|
||||
haveStable := make(map[string]bool)
|
||||
for _, rel := range rels {
|
||||
if !rel.Prerelease {
|
||||
// We found a stable version, we're good now.
|
||||
major, _, _ := strings.Cut(rel.Tag, ".")
|
||||
if !rel.Prerelease && !haveStable[major] {
|
||||
// Remember the first non-pre for each major
|
||||
filtered = append(filtered, rel)
|
||||
break
|
||||
haveStable[major] = true
|
||||
continue
|
||||
}
|
||||
if rel.Prerelease && !havePre {
|
||||
// We remember the first prerelease we find.
|
||||
if rel.Prerelease && !havePre[major] && !haveStable[major] {
|
||||
// We remember the first prerelease we find, unless we've
|
||||
// already found a non-pre of the same major.
|
||||
filtered = append(filtered, rel)
|
||||
havePre = true
|
||||
havePre[major] = true
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
@@ -219,7 +224,7 @@ func filterForLatest(rels []upgrade.Release) []upgrade.Release {
|
||||
|
||||
var userAgentOSArchExp = regexp.MustCompile(`^syncthing.*\(.+ (\w+)-(\w+)\)$`)
|
||||
|
||||
func filterForCompabitility(rels []upgrade.Release, ua, osv string) []upgrade.Release {
|
||||
func filterForCompatibility(rels []upgrade.Release, ua, osv string) []upgrade.Release {
|
||||
osArch := userAgentOSArchExp.FindStringSubmatch(ua)
|
||||
if len(osArch) != 3 {
|
||||
metricFilterCalls.WithLabelValues("bad-os-arch").Inc()
|
||||
@@ -258,9 +263,10 @@ func filterForCompabitility(rels []upgrade.Release, ua, osv string) []upgrade.Re
|
||||
}
|
||||
|
||||
type cachedReleases struct {
|
||||
url string
|
||||
mut sync.RWMutex
|
||||
current []upgrade.Release
|
||||
url string
|
||||
mut sync.RWMutex
|
||||
current []upgrade.Release
|
||||
latestRel, latestPre string
|
||||
}
|
||||
|
||||
func (c *cachedReleases) Releases() []upgrade.Release {
|
||||
@@ -274,8 +280,26 @@ func (c *cachedReleases) Update(ctx context.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
latestRel, latestPre := "", ""
|
||||
for _, rel := range rels {
|
||||
if !rel.Prerelease && latestRel == "" {
|
||||
latestRel = rel.Tag
|
||||
}
|
||||
if rel.Prerelease && latestPre == "" {
|
||||
latestPre = rel.Tag
|
||||
}
|
||||
if latestRel != "" && latestPre != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
c.mut.Lock()
|
||||
c.current = rels
|
||||
if latestRel != c.latestRel || latestPre != c.latestPre {
|
||||
metricLatestReleaseInfo.DeleteLabelValues(c.latestRel, c.latestPre)
|
||||
metricLatestReleaseInfo.WithLabelValues(latestRel, latestPre).Set(1)
|
||||
c.latestRel = latestRel
|
||||
c.latestPre = latestPre
|
||||
}
|
||||
c.mut.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -27,4 +27,10 @@ var (
|
||||
Subsystem: "upgrade",
|
||||
Name: "http_requests",
|
||||
}, []string{"target", "result"})
|
||||
metricLatestReleaseInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: "syncthing",
|
||||
Subsystem: "upgrade",
|
||||
Name: "latest_release_info",
|
||||
Help: "Release information",
|
||||
}, []string{"latest_release", "latest_pre"})
|
||||
)
|
||||
|
||||
@@ -32,6 +32,21 @@ var (
|
||||
Subsystem: "ursrv_v2",
|
||||
Name: "collect_seconds_last",
|
||||
})
|
||||
metricsRecalcsTotal = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Namespace: "syncthing",
|
||||
Subsystem: "ursrv_v2",
|
||||
Name: "recalcs_total",
|
||||
})
|
||||
metricsRecalcSecondsTotal = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Namespace: "syncthing",
|
||||
Subsystem: "ursrv_v2",
|
||||
Name: "recalc_seconds_total",
|
||||
})
|
||||
metricsRecalcSecondsLast = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: "syncthing",
|
||||
Subsystem: "ursrv_v2",
|
||||
Name: "recalc_seconds_last",
|
||||
})
|
||||
metricsWriteSecondsLast = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: "syncthing",
|
||||
Subsystem: "ursrv_v2",
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
package serve
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strconv"
|
||||
@@ -28,7 +31,7 @@ type metricsSet struct {
|
||||
gaugeVecLabels map[string][]string
|
||||
summaries map[string]*metricSummary
|
||||
|
||||
collectMut sync.Mutex
|
||||
collectMut sync.RWMutex
|
||||
collectCutoff time.Duration
|
||||
}
|
||||
|
||||
@@ -44,7 +47,7 @@ func newMetricsSet(srv *server) *metricsSet {
|
||||
|
||||
var initForType func(reflect.Type)
|
||||
initForType = func(t reflect.Type) {
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
for i := range t.NumField() {
|
||||
field := t.Field(i)
|
||||
if field.Type.Kind() == reflect.Struct {
|
||||
initForType(field.Type)
|
||||
@@ -108,6 +111,60 @@ func nameConstLabels(name string) (string, prometheus.Labels) {
|
||||
return name, m
|
||||
}
|
||||
|
||||
func (s *metricsSet) Serve(ctx context.Context) error {
|
||||
s.recalc()
|
||||
|
||||
const recalcInterval = 5 * time.Minute
|
||||
next := time.Until(time.Now().Truncate(recalcInterval).Add(recalcInterval))
|
||||
recalcTimer := time.NewTimer(next)
|
||||
defer recalcTimer.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-recalcTimer.C:
|
||||
s.recalc()
|
||||
next := time.Until(time.Now().Truncate(recalcInterval).Add(recalcInterval))
|
||||
recalcTimer.Reset(next)
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *metricsSet) recalc() {
|
||||
s.collectMut.Lock()
|
||||
defer s.collectMut.Unlock()
|
||||
|
||||
t0 := time.Now()
|
||||
defer func() {
|
||||
dur := time.Since(t0)
|
||||
slog.Info("Metrics recalculated", "d", dur.String())
|
||||
metricsRecalcSecondsLast.Set(dur.Seconds())
|
||||
metricsRecalcSecondsTotal.Add(dur.Seconds())
|
||||
metricsRecalcsTotal.Inc()
|
||||
}()
|
||||
|
||||
for _, g := range s.gauges {
|
||||
g.Set(0)
|
||||
}
|
||||
for _, g := range s.gaugeVecs {
|
||||
g.Reset()
|
||||
}
|
||||
for _, g := range s.summaries {
|
||||
g.Reset()
|
||||
}
|
||||
|
||||
cutoff := time.Now().Add(s.collectCutoff)
|
||||
s.srv.reports.Range(func(key string, r *contract.Report) bool {
|
||||
if s.collectCutoff < 0 && r.Received.Before(cutoff) {
|
||||
s.srv.reports.Delete(key)
|
||||
return true
|
||||
}
|
||||
s.addReport(r)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (s *metricsSet) addReport(r *contract.Report) {
|
||||
gaugeVecs := make(map[string][]string)
|
||||
s.addReportStruct(reflect.ValueOf(r).Elem(), gaugeVecs)
|
||||
@@ -118,7 +175,7 @@ func (s *metricsSet) addReport(r *contract.Report) {
|
||||
|
||||
func (s *metricsSet) addReportStruct(v reflect.Value, gaugeVecs map[string][]string) {
|
||||
t := v.Type()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
for i := range v.NumField() {
|
||||
field := v.Field(i)
|
||||
if field.Kind() == reflect.Struct {
|
||||
s.addReportStruct(field, gaugeVecs)
|
||||
@@ -198,8 +255,8 @@ func (s *metricsSet) Describe(c chan<- *prometheus.Desc) {
|
||||
}
|
||||
|
||||
func (s *metricsSet) Collect(c chan<- prometheus.Metric) {
|
||||
s.collectMut.Lock()
|
||||
defer s.collectMut.Unlock()
|
||||
s.collectMut.RLock()
|
||||
defer s.collectMut.RUnlock()
|
||||
|
||||
t0 := time.Now()
|
||||
defer func() {
|
||||
@@ -209,26 +266,6 @@ func (s *metricsSet) Collect(c chan<- prometheus.Metric) {
|
||||
metricsCollectsTotal.Inc()
|
||||
}()
|
||||
|
||||
for _, g := range s.gauges {
|
||||
g.Set(0)
|
||||
}
|
||||
for _, g := range s.gaugeVecs {
|
||||
g.Reset()
|
||||
}
|
||||
for _, g := range s.summaries {
|
||||
g.Reset()
|
||||
}
|
||||
|
||||
cutoff := time.Now().Add(s.collectCutoff)
|
||||
s.srv.reports.Range(func(key string, r *contract.Report) bool {
|
||||
if s.collectCutoff < 0 && r.Received.Before(cutoff) {
|
||||
s.srv.reports.Delete(key)
|
||||
return true
|
||||
}
|
||||
s.addReport(r)
|
||||
return true
|
||||
})
|
||||
|
||||
for _, g := range s.gauges {
|
||||
c <- g
|
||||
}
|
||||
@@ -299,12 +336,12 @@ func (q *metricSummary) Collect(c chan<- prometheus.Metric) {
|
||||
}
|
||||
|
||||
slices.Sort(vs)
|
||||
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[0], append(labelVals, "0")...)
|
||||
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[len(vs)*5/100], append(labelVals, "0.05")...)
|
||||
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[len(vs)/2], append(labelVals, "0.5")...)
|
||||
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[len(vs)*9/10], append(labelVals, "0.9")...)
|
||||
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[len(vs)*95/100], append(labelVals, "0.95")...)
|
||||
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[len(vs)-1], append(labelVals, "1")...)
|
||||
|
||||
pctiles := []float64{0, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 0.975, 0.99, 1}
|
||||
for _, pct := range pctiles {
|
||||
idx := int(float64(len(vs)-1) * pct)
|
||||
c <- prometheus.MustNewConstMetric(q.qDesc, prometheus.GaugeValue, vs[idx], append(labelVals, fmt.Sprint(pct))...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,14 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/puzpuzpuz/xsync/v3"
|
||||
"github.com/syncthing/syncthing/internal/blob"
|
||||
"github.com/syncthing/syncthing/internal/blob/azureblob"
|
||||
"github.com/syncthing/syncthing/internal/blob/s3"
|
||||
"github.com/syncthing/syncthing/internal/slogutil"
|
||||
"github.com/syncthing/syncthing/lib/build"
|
||||
"github.com/syncthing/syncthing/lib/geoip"
|
||||
"github.com/syncthing/syncthing/lib/s3"
|
||||
"github.com/syncthing/syncthing/lib/ur/contract"
|
||||
"github.com/thejerf/suture/v4"
|
||||
)
|
||||
|
||||
type CLI struct {
|
||||
@@ -40,11 +44,15 @@ type CLI struct {
|
||||
DumpFile string `env:"UR_DUMP_FILE" default:"reports.jsons.gz"`
|
||||
DumpInterval time.Duration `env:"UR_DUMP_INTERVAL" default:"5m"`
|
||||
|
||||
S3Endpoint string `name:"s3-endpoint" hidden:"true" env:"UR_S3_ENDPOINT"`
|
||||
S3Region string `name:"s3-region" hidden:"true" env:"UR_S3_REGION"`
|
||||
S3Bucket string `name:"s3-bucket" hidden:"true" env:"UR_S3_BUCKET"`
|
||||
S3AccessKeyID string `name:"s3-access-key-id" hidden:"true" env:"UR_S3_ACCESS_KEY_ID"`
|
||||
S3SecretKey string `name:"s3-secret-key" hidden:"true" env:"UR_S3_SECRET_KEY"`
|
||||
S3Endpoint string `name:"s3-endpoint" env:"UR_S3_ENDPOINT"`
|
||||
S3Region string `name:"s3-region" env:"UR_S3_REGION"`
|
||||
S3Bucket string `name:"s3-bucket" env:"UR_S3_BUCKET"`
|
||||
S3AccessKeyID string `name:"s3-access-key-id" env:"UR_S3_ACCESS_KEY_ID"`
|
||||
S3SecretKey string `name:"s3-secret-key" env:"UR_S3_SECRET_KEY"`
|
||||
|
||||
AzureBlobAccount string `name:"azure-blob-account" env:"UR_AZUREBLOB_ACCOUNT"`
|
||||
AzureBlobKey string `name:"azure-blob-key" env:"UR_AZUREBLOB_KEY"`
|
||||
AzureBlobContainer string `name:"azure-blob-container" env:"UR_AZUREBLOB_CONTAINER"`
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -71,12 +79,14 @@ var (
|
||||
{regexp.MustCompile(`\svagrant@bullseye`), "F-Droid"},
|
||||
{regexp.MustCompile(`\svagrant@bookworm`), "F-Droid"},
|
||||
|
||||
{regexp.MustCompile(`Anwender@NET2017`), "Syncthing-Fork (3rd party)"},
|
||||
{regexp.MustCompile(`\sreproducible-build@Catfriend1-syncthing-android`), "Syncthing-Fork Catfriend1 (3rd party)"},
|
||||
{regexp.MustCompile(`\sreproducible-build@nel0x-syncthing-android-gplay`), "Syncthing-Fork nel0x (3rd party)"},
|
||||
|
||||
{regexp.MustCompile(`\sbuilduser@(archlinux|svetlemodry)`), "Arch (3rd party)"},
|
||||
{regexp.MustCompile(`\ssyncthing@archlinux`), "Arch (3rd party)"},
|
||||
{regexp.MustCompile(`@debian`), "Debian (3rd party)"},
|
||||
{regexp.MustCompile(`@fedora`), "Fedora (3rd party)"},
|
||||
{regexp.MustCompile(`@openSUSE`), "openSUSE (3rd party)"},
|
||||
{regexp.MustCompile(`\sbrew@`), "Homebrew (3rd party)"},
|
||||
{regexp.MustCompile(`\sroot@buildkitsandbox`), "LinuxServer.io (3rd party)"},
|
||||
{regexp.MustCompile(`\sports@freebsd`), "FreeBSD (3rd party)"},
|
||||
@@ -97,42 +107,48 @@ func (cli *CLI) Run() error {
|
||||
|
||||
urListener, err := net.Listen("tcp", cli.Listen)
|
||||
if err != nil {
|
||||
slog.Error("Failed to listen (usage reports)", "error", err)
|
||||
slog.Error("Failed to listen (usage reports)", slogutil.Error(err))
|
||||
return err
|
||||
}
|
||||
slog.Info("Listening (usage reports)", "address", urListener.Addr())
|
||||
slog.Info("Listening (usage reports)", slogutil.Address(urListener.Addr()))
|
||||
|
||||
internalListener, err := net.Listen("tcp", cli.ListenInternal)
|
||||
if err != nil {
|
||||
slog.Error("Failed to listen (internal)", "error", err)
|
||||
slog.Error("Failed to listen (internal)", slogutil.Error(err))
|
||||
return err
|
||||
}
|
||||
slog.Info("Listening (internal)", "address", internalListener.Addr())
|
||||
slog.Info("Listening (internal)", slogutil.Address(internalListener.Addr()))
|
||||
|
||||
var geo *geoip.Provider
|
||||
if cli.GeoIPAccountID != 0 && cli.GeoIPLicenseKey != "" {
|
||||
geo, err = geoip.NewGeoLite2CityProvider(context.Background(), cli.GeoIPAccountID, cli.GeoIPLicenseKey, os.TempDir())
|
||||
if err != nil {
|
||||
slog.Error("Failed to load GeoIP", "error", err)
|
||||
slog.Error("Failed to load GeoIP", slogutil.Error(err))
|
||||
return err
|
||||
}
|
||||
go geo.Serve(context.TODO())
|
||||
}
|
||||
|
||||
// s3
|
||||
// Blob storage
|
||||
|
||||
var s3sess *s3.Session
|
||||
var blobs blob.Store
|
||||
if cli.S3Endpoint != "" {
|
||||
s3sess, err = s3.NewSession(cli.S3Endpoint, cli.S3Region, cli.S3Bucket, cli.S3AccessKeyID, cli.S3SecretKey)
|
||||
blobs, err = s3.NewSession(cli.S3Endpoint, cli.S3Region, cli.S3Bucket, cli.S3AccessKeyID, cli.S3SecretKey)
|
||||
if err != nil {
|
||||
slog.Error("Failed to create S3 session", "error", err)
|
||||
slog.Error("Failed to create S3 session", slogutil.Error(err))
|
||||
return err
|
||||
}
|
||||
} else if cli.AzureBlobAccount != "" {
|
||||
blobs, err = azureblob.NewBlobStore(cli.AzureBlobAccount, cli.AzureBlobKey, cli.AzureBlobContainer)
|
||||
if err != nil {
|
||||
slog.Error("Failed to create Azure blob store", slogutil.Error(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(cli.DumpFile); err != nil && s3sess != nil {
|
||||
if err := cli.downloadDumpFile(s3sess); err != nil {
|
||||
slog.Error("Failed to download dump file", "error", err)
|
||||
if _, err := os.Stat(cli.DumpFile); err != nil && blobs != nil {
|
||||
if err := cli.downloadDumpFile(blobs); err != nil {
|
||||
slog.Error("Failed to download dump file", slogutil.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,8 +169,8 @@ func (cli *CLI) Run() error {
|
||||
|
||||
go func() {
|
||||
for range time.Tick(cli.DumpInterval) {
|
||||
if err := cli.saveDumpFile(srv, s3sess); err != nil {
|
||||
slog.Error("Failed to write dump file", "error", err)
|
||||
if err := cli.saveDumpFile(srv, blobs); err != nil {
|
||||
slog.Error("Failed to write dump file", slogutil.Error(err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -173,7 +189,12 @@ func (cli *CLI) Run() error {
|
||||
// New external metrics endpoint accepts reports from clients and serves
|
||||
// aggregated usage reporting metrics.
|
||||
|
||||
main := suture.NewSimple("main")
|
||||
main.ServeBackground(context.Background())
|
||||
|
||||
ms := newMetricsSet(srv)
|
||||
main.Add(ms)
|
||||
|
||||
reg := prometheus.NewRegistry()
|
||||
reg.MustRegister(ms)
|
||||
|
||||
@@ -184,7 +205,7 @@ func (cli *CLI) Run() error {
|
||||
|
||||
metricsSrv := http.Server{
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 15 * time.Second,
|
||||
WriteTimeout: 60 * time.Second,
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
@@ -192,8 +213,8 @@ func (cli *CLI) Run() error {
|
||||
return metricsSrv.Serve(urListener)
|
||||
}
|
||||
|
||||
func (cli *CLI) downloadDumpFile(s3sess *s3.Session) error {
|
||||
latestKey, err := s3sess.LatestKey()
|
||||
func (cli *CLI) downloadDumpFile(blobs blob.Store) error {
|
||||
latestKey, err := blobs.LatestKey(context.Background())
|
||||
if err != nil {
|
||||
return fmt.Errorf("list latest S3 key: %w", err)
|
||||
}
|
||||
@@ -201,7 +222,7 @@ func (cli *CLI) downloadDumpFile(s3sess *s3.Session) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("create dump file: %w", err)
|
||||
}
|
||||
if err := s3sess.Download(fd, latestKey); err != nil {
|
||||
if err := blobs.Download(context.Background(), latestKey, fd); err != nil {
|
||||
_ = fd.Close()
|
||||
return fmt.Errorf("download dump file: %w", err)
|
||||
}
|
||||
@@ -212,7 +233,12 @@ func (cli *CLI) downloadDumpFile(s3sess *s3.Session) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *CLI) saveDumpFile(srv *server, s3sess *s3.Session) error {
|
||||
func (cli *CLI) saveDumpFile(srv *server, blobs blob.Store) error {
|
||||
t0 := time.Now()
|
||||
defer func() {
|
||||
metricsWriteSecondsLast.Set(float64(time.Since(t0)))
|
||||
}()
|
||||
|
||||
fd, err := os.Create(cli.DumpFile + ".tmp")
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating dump file: %w", err)
|
||||
@@ -231,19 +257,20 @@ func (cli *CLI) saveDumpFile(srv *server, s3sess *s3.Session) error {
|
||||
if err := os.Rename(cli.DumpFile+".tmp", cli.DumpFile); err != nil {
|
||||
return fmt.Errorf("renaming dump file: %w", err)
|
||||
}
|
||||
slog.Info("Dump file saved")
|
||||
slog.Info("Dump file saved", "d", time.Since(t0).String())
|
||||
|
||||
if s3sess != nil {
|
||||
if blobs != nil {
|
||||
t1 := time.Now()
|
||||
key := fmt.Sprintf("reports-%s.jsons.gz", time.Now().UTC().Format("2006-01-02"))
|
||||
fd, err := os.Open(cli.DumpFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening dump file: %w", err)
|
||||
}
|
||||
if err := s3sess.Upload(fd, key); err != nil {
|
||||
if err := blobs.Upload(context.Background(), key, fd); err != nil {
|
||||
return fmt.Errorf("uploading dump file: %w", err)
|
||||
}
|
||||
_ = fd.Close()
|
||||
slog.Info("Dump file uploaded")
|
||||
slog.Info("Dump file uploaded", "d", time.Since(t1).String())
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -294,7 +321,7 @@ func (s *server) handleNewData(w http.ResponseWriter, r *http.Request) {
|
||||
lr := &io.LimitedReader{R: r.Body, N: 40 * 1024}
|
||||
bs, _ := io.ReadAll(lr)
|
||||
if err := json.Unmarshal(bs, &rep); err != nil {
|
||||
log.Error("Failed to decode JSON", "error", err)
|
||||
log.Error("Failed to decode JSON", slogutil.Error(err))
|
||||
http.Error(w, "JSON Decode Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -304,7 +331,7 @@ func (s *server) handleNewData(w http.ResponseWriter, r *http.Request) {
|
||||
rep.Address = addr
|
||||
|
||||
if err := rep.Validate(); err != nil {
|
||||
log.Error("Failed to validate report", "error", err)
|
||||
log.Error("Failed to validate report", slogutil.Error(err))
|
||||
http.Error(w, "Validation Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -351,6 +378,16 @@ func (s *server) addReport(rep *contract.Report) bool {
|
||||
break
|
||||
}
|
||||
}
|
||||
rep.DistDist = rep.Distribution
|
||||
rep.DistOS = rep.OS
|
||||
rep.DistArch = rep.Arch
|
||||
|
||||
if strings.HasPrefix(rep.Version, "v2.") {
|
||||
rep.Database.ModernCSQLite = strings.Contains(rep.LongVersion, "modernc-sqlite")
|
||||
rep.Database.MattnSQLite = !rep.Database.ModernCSQLite
|
||||
} else {
|
||||
rep.Database.LevelDB = true
|
||||
}
|
||||
|
||||
_, loaded := s.reports.LoadAndStore(rep.UniqueID, rep)
|
||||
return loaded
|
||||
@@ -371,6 +408,7 @@ func (s *server) save(w io.Writer) error {
|
||||
}
|
||||
|
||||
func (s *server) load(r io.Reader) {
|
||||
t0 := time.Now()
|
||||
dec := json.NewDecoder(r)
|
||||
s.reports.Clear()
|
||||
for {
|
||||
@@ -378,12 +416,12 @@ func (s *server) load(r io.Reader) {
|
||||
if err := dec.Decode(&rep); errors.Is(err, io.EOF) {
|
||||
break
|
||||
} else if err != nil {
|
||||
slog.Error("Failed to load record", "error", err)
|
||||
slog.Error("Failed to load record", slogutil.Error(err))
|
||||
break
|
||||
}
|
||||
s.addReport(&rep)
|
||||
}
|
||||
slog.Info("Loaded reports", "count", s.reports.Size())
|
||||
slog.Info("Loaded reports", "count", s.reports.Size(), "d", time.Since(t0).String())
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"log/slog"
|
||||
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
"github.com/thejerf/suture/v4"
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
type amqpReplicator struct {
|
||||
suture.Service
|
||||
|
||||
broker string
|
||||
sender *amqpSender
|
||||
receiver *amqpReceiver
|
||||
@@ -172,7 +173,7 @@ func (s *amqpReceiver) Serve(ctx context.Context) error {
|
||||
id, err = protocol.DeviceIDFromString(string(rec.Key))
|
||||
}
|
||||
if err != nil {
|
||||
log.Println("Replication device ID:", err)
|
||||
slog.Warn("Failed to parse replication device ID", "error", err)
|
||||
replicationRecvsTotal.WithLabelValues("error").Inc()
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"fmt"
|
||||
io "io"
|
||||
"log"
|
||||
"log/slog"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -66,7 +67,7 @@ type contextKey int
|
||||
|
||||
const idKey contextKey = iota
|
||||
|
||||
func newAPISrv(addr string, cert tls.Certificate, db database, repl replicator, useHTTP, compression bool) *apiSrv {
|
||||
func newAPISrv(addr string, cert tls.Certificate, db database, repl replicator, useHTTP, compression bool, desiredNotFoundRate float64) *apiSrv {
|
||||
return &apiSrv{
|
||||
addr: addr,
|
||||
cert: cert,
|
||||
@@ -77,13 +78,13 @@ func newAPISrv(addr string, cert tls.Certificate, db database, repl replicator,
|
||||
seenTracker: &retryAfterTracker{
|
||||
name: "seenTracker",
|
||||
bucketStarts: time.Now(),
|
||||
desiredRate: 250,
|
||||
desiredRate: desiredNotFoundRate / 2,
|
||||
currentDelay: notFoundRetryUnknownMinSeconds,
|
||||
},
|
||||
notSeenTracker: &retryAfterTracker{
|
||||
name: "notSeenTracker",
|
||||
bucketStarts: time.Now(),
|
||||
desiredRate: 250,
|
||||
desiredRate: desiredNotFoundRate / 2,
|
||||
currentDelay: notFoundRetryUnknownMaxSeconds / 2,
|
||||
},
|
||||
}
|
||||
@@ -93,7 +94,7 @@ func (s *apiSrv) Serve(ctx context.Context) error {
|
||||
if s.useHTTP {
|
||||
listener, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.Println("Listen:", err)
|
||||
slog.ErrorContext(ctx, "Failed to listen", "error", err)
|
||||
return err
|
||||
}
|
||||
s.listener = listener
|
||||
@@ -107,7 +108,7 @@ func (s *apiSrv) Serve(ctx context.Context) error {
|
||||
|
||||
tlsListener, err := tls.Listen("tcp", s.addr, tlsCfg)
|
||||
if err != nil {
|
||||
log.Println("Listen:", err)
|
||||
slog.ErrorContext(ctx, "Failed to listen", "error", err)
|
||||
return err
|
||||
}
|
||||
s.listener = tlsListener
|
||||
@@ -132,7 +133,7 @@ func (s *apiSrv) Serve(ctx context.Context) error {
|
||||
|
||||
err := srv.Serve(s.listener)
|
||||
if err != nil {
|
||||
log.Println("Serve:", err)
|
||||
slog.ErrorContext(ctx, "Failed to serve", "error", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -151,9 +152,7 @@ func (s *apiSrv) handler(w http.ResponseWriter, req *http.Request) {
|
||||
reqID := requestID(rand.Int63())
|
||||
req = req.WithContext(context.WithValue(req.Context(), idKey, reqID))
|
||||
|
||||
if debug {
|
||||
log.Println(reqID, req.Method, req.URL, req.Proto)
|
||||
}
|
||||
slog.Debug("Handling request", "id", reqID, "method", req.Method, "url", req.URL, "proto", req.Proto)
|
||||
|
||||
remoteAddr := &net.TCPAddr{
|
||||
IP: nil,
|
||||
@@ -174,7 +173,7 @@ func (s *apiSrv) handler(w http.ResponseWriter, req *http.Request) {
|
||||
var err error
|
||||
remoteAddr, err = net.ResolveTCPAddr("tcp", req.RemoteAddr)
|
||||
if err != nil {
|
||||
log.Println("remoteAddr:", err)
|
||||
slog.Warn("Failed to resolve remote address", "address", req.RemoteAddr, "error", err)
|
||||
lw.Header().Set("Retry-After", errorRetryAfterString())
|
||||
http.Error(lw, "Internal Server Error", http.StatusInternalServerError)
|
||||
apiRequestsTotal.WithLabelValues("no_remote_addr").Inc()
|
||||
@@ -197,9 +196,7 @@ func (s *apiSrv) handleGET(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
deviceID, err := protocol.DeviceIDFromString(req.URL.Query().Get("device"))
|
||||
if err != nil {
|
||||
if debug {
|
||||
log.Println(reqID, "bad device param:", err)
|
||||
}
|
||||
slog.Debug("Request with bad device param", "id", reqID, "error", err)
|
||||
lookupRequestsTotal.WithLabelValues("bad_request").Inc()
|
||||
w.Header().Set("Retry-After", errorRetryAfterString())
|
||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||
@@ -259,9 +256,7 @@ func (s *apiSrv) handlePOST(remoteAddr *net.TCPAddr, w http.ResponseWriter, req
|
||||
|
||||
rawCert, err := certificateBytes(req)
|
||||
if err != nil {
|
||||
if debug {
|
||||
log.Println(reqID, "no certificates:", err)
|
||||
}
|
||||
slog.Debug("Request without certificates", "id", reqID, "error", err)
|
||||
announceRequestsTotal.WithLabelValues("no_certificate").Inc()
|
||||
w.Header().Set("Retry-After", errorRetryAfterString())
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
@@ -270,9 +265,7 @@ func (s *apiSrv) handlePOST(remoteAddr *net.TCPAddr, w http.ResponseWriter, req
|
||||
|
||||
var ann announcement
|
||||
if err := json.NewDecoder(req.Body).Decode(&ann); err != nil {
|
||||
if debug {
|
||||
log.Println(reqID, "decode:", err)
|
||||
}
|
||||
slog.Debug("Failed to decode request", "id", reqID, "error", err)
|
||||
announceRequestsTotal.WithLabelValues("bad_request").Inc()
|
||||
w.Header().Set("Retry-After", errorRetryAfterString())
|
||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||
@@ -283,9 +276,7 @@ func (s *apiSrv) handlePOST(remoteAddr *net.TCPAddr, w http.ResponseWriter, req
|
||||
|
||||
addresses := fixupAddresses(remoteAddr, ann.Addresses)
|
||||
if len(addresses) == 0 {
|
||||
if debug {
|
||||
log.Println(reqID, "no addresses")
|
||||
}
|
||||
slog.Debug("Request without addresses", "id", reqID, "error", err)
|
||||
announceRequestsTotal.WithLabelValues("bad_request").Inc()
|
||||
w.Header().Set("Retry-After", errorRetryAfterString())
|
||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||
@@ -293,9 +284,7 @@ func (s *apiSrv) handlePOST(remoteAddr *net.TCPAddr, w http.ResponseWriter, req
|
||||
}
|
||||
|
||||
if err := s.handleAnnounce(deviceID, addresses); err != nil {
|
||||
if debug {
|
||||
log.Println(reqID, "handle:", err)
|
||||
}
|
||||
slog.Debug("Failed to handle request", "id", reqID, "error", err)
|
||||
announceRequestsTotal.WithLabelValues("internal_error").Inc()
|
||||
w.Header().Set("Retry-After", errorRetryAfterString())
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
@@ -306,9 +295,7 @@ func (s *apiSrv) handlePOST(remoteAddr *net.TCPAddr, w http.ResponseWriter, req
|
||||
|
||||
w.Header().Set("Reannounce-After", reannounceAfterString())
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
if debug {
|
||||
log.Println(reqID, "announced", deviceID, addresses)
|
||||
}
|
||||
slog.Debug("Device announced", "id", reqID, "device", deviceID, "addresses", addresses)
|
||||
}
|
||||
|
||||
func (s *apiSrv) Stop() {
|
||||
@@ -350,7 +337,7 @@ func certificateBytes(req *http.Request) ([]byte, error) {
|
||||
|
||||
var bs []byte
|
||||
|
||||
if hdr := req.Header.Get("X-SSL-Cert"); hdr != "" {
|
||||
if hdr := req.Header.Get("X-Ssl-Cert"); hdr != "" {
|
||||
if strings.Contains(hdr, "%") {
|
||||
// Nginx using $ssl_client_escaped_cert
|
||||
// The certificate is in PEM format with url encoding.
|
||||
@@ -513,6 +500,7 @@ func fixupAddresses(remote *net.TCPAddr, addresses []string) []string {
|
||||
|
||||
type loggingResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
|
||||
statusCode int
|
||||
}
|
||||
|
||||
|
||||
@@ -111,11 +111,11 @@ func BenchmarkAPIRequests(b *testing.B) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go db.Serve(ctx)
|
||||
api := newAPISrv("127.0.0.1:0", tls.Certificate{}, db, nil, true, true)
|
||||
api := newAPISrv("127.0.0.1:0", tls.Certificate{}, db, nil, true, true, 1000)
|
||||
srv := httptest.NewServer(http.HandlerFunc(api.handler))
|
||||
|
||||
kf := b.TempDir() + "/cert"
|
||||
crt, err := tlsutil.NewCertificate(kf+".crt", kf+".key", "localhost", 7)
|
||||
crt, err := tlsutil.NewCertificate(kf+".crt", kf+".key", "localhost", 7, true)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
@@ -24,11 +24,11 @@ import (
|
||||
"github.com/puzpuzpuz/xsync/v3"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/syncthing/syncthing/internal/blob"
|
||||
"github.com/syncthing/syncthing/internal/gen/discosrv"
|
||||
"github.com/syncthing/syncthing/internal/protoutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
"github.com/syncthing/syncthing/lib/s3"
|
||||
)
|
||||
|
||||
type clock interface {
|
||||
@@ -51,12 +51,12 @@ type inMemoryStore struct {
|
||||
m *xsync.MapOf[protocol.DeviceID, *discosrv.DatabaseRecord]
|
||||
dir string
|
||||
flushInterval time.Duration
|
||||
s3 *s3.Session
|
||||
blobs blob.Store
|
||||
objKey string
|
||||
clock clock
|
||||
}
|
||||
|
||||
func newInMemoryStore(dir string, flushInterval time.Duration, s3sess *s3.Session) *inMemoryStore {
|
||||
func newInMemoryStore(dir string, flushInterval time.Duration, blobs blob.Store) *inMemoryStore {
|
||||
hn, err := os.Hostname()
|
||||
if err != nil {
|
||||
hn = rand.String(8)
|
||||
@@ -65,33 +65,33 @@ func newInMemoryStore(dir string, flushInterval time.Duration, s3sess *s3.Sessio
|
||||
m: xsync.NewMapOf[protocol.DeviceID, *discosrv.DatabaseRecord](),
|
||||
dir: dir,
|
||||
flushInterval: flushInterval,
|
||||
s3: s3sess,
|
||||
blobs: blobs,
|
||||
objKey: hn + ".db",
|
||||
clock: defaultClock{},
|
||||
}
|
||||
nr, err := s.read()
|
||||
if os.IsNotExist(err) && s3sess != nil {
|
||||
// Try to read from AWS
|
||||
latestKey, cerr := s3sess.LatestKey()
|
||||
if os.IsNotExist(err) && blobs != nil {
|
||||
// Try to read from blob storage
|
||||
latestKey, cerr := blobs.LatestKey(context.Background())
|
||||
if cerr != nil {
|
||||
log.Println("Error reading database from S3:", err)
|
||||
slog.Error("Failed to find database in blob storage", "error", cerr)
|
||||
return s
|
||||
}
|
||||
fd, cerr := os.Create(path.Join(s.dir, "records.db"))
|
||||
if cerr != nil {
|
||||
log.Println("Error creating database file:", err)
|
||||
slog.Error("Failed to create database file", "error", cerr)
|
||||
return s
|
||||
}
|
||||
if cerr := s3sess.Download(fd, latestKey); cerr != nil {
|
||||
log.Printf("Error reading database from S3: %v", err)
|
||||
if cerr := blobs.Download(context.Background(), latestKey, fd); cerr != nil {
|
||||
slog.Error("Failed to download database from blob storage", "error", cerr)
|
||||
}
|
||||
_ = fd.Close()
|
||||
nr, err = s.read()
|
||||
}
|
||||
if err != nil {
|
||||
log.Println("Error reading database:", err)
|
||||
slog.Error("Failed to read database", "error", err)
|
||||
}
|
||||
log.Printf("Read %d records from database", nr)
|
||||
slog.Info("Loaded database", "records", nr)
|
||||
s.expireAndCalculateStatistics()
|
||||
return s
|
||||
}
|
||||
@@ -153,13 +153,13 @@ loop:
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
log.Println("Calculating statistics")
|
||||
slog.InfoContext(ctx, "Calculating statistics")
|
||||
s.expireAndCalculateStatistics()
|
||||
log.Println("Flushing database")
|
||||
slog.InfoContext(ctx, "Flushing database")
|
||||
if err := s.write(); err != nil {
|
||||
log.Println("Error writing database:", err)
|
||||
slog.ErrorContext(ctx, "Failed to write database", "error", err)
|
||||
}
|
||||
log.Println("Finished flushing database")
|
||||
slog.InfoContext(ctx, "Finished flushing database")
|
||||
t.Reset(s.flushInterval)
|
||||
|
||||
case <-ctx.Done():
|
||||
@@ -256,7 +256,7 @@ func (s *inMemoryStore) write() (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bw := bufio.NewWriter(fd)
|
||||
bw := bufio.NewWriterSize(fd, 1<<20)
|
||||
|
||||
var buf []byte
|
||||
var rangeErr error
|
||||
@@ -310,18 +310,24 @@ func (s *inMemoryStore) write() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Upload to S3
|
||||
if s.s3 != nil {
|
||||
if info, err := os.Lstat(dbf); err == nil {
|
||||
slog.Info("Saved database", "name", dbf, "size", info.Size(), "modtime", info.ModTime())
|
||||
} else {
|
||||
slog.Warn("Failed to stat database after save", "error", err)
|
||||
}
|
||||
|
||||
// Upload to blob storage
|
||||
if s.blobs != nil {
|
||||
fd, err = os.Open(dbf)
|
||||
if err != nil {
|
||||
log.Printf("Error uploading database to S3: %v", err)
|
||||
slog.Error("Failed to upload database to blob storage", "error", err)
|
||||
return nil
|
||||
}
|
||||
defer fd.Close()
|
||||
if err := s.s3.Upload(fd, s.objKey); err != nil {
|
||||
log.Printf("Error uploading database to S3: %v", err)
|
||||
if err := s.blobs.Upload(context.Background(), s.objKey, fd); err != nil {
|
||||
slog.Error("Failed to upload database to blob storage", "error", err)
|
||||
}
|
||||
log.Println("Finished uploading database")
|
||||
slog.Info("Finished uploading database")
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -360,7 +366,7 @@ func (s *inMemoryStore) read() (int, error) {
|
||||
key, err = protocol.DeviceIDFromString(string(rec.Key))
|
||||
}
|
||||
if err != nil {
|
||||
log.Println("Bad device ID:", err)
|
||||
slog.Error("Got bad device ID while reading database", "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,8 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
@@ -21,17 +20,19 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/thejerf/suture/v4"
|
||||
|
||||
"github.com/syncthing/syncthing/internal/blob"
|
||||
"github.com/syncthing/syncthing/internal/blob/azureblob"
|
||||
"github.com/syncthing/syncthing/internal/blob/s3"
|
||||
"github.com/syncthing/syncthing/internal/slogutil"
|
||||
_ "github.com/syncthing/syncthing/lib/automaxprocs"
|
||||
"github.com/syncthing/syncthing/lib/build"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/rand"
|
||||
"github.com/syncthing/syncthing/lib/s3"
|
||||
"github.com/syncthing/syncthing/lib/tlsutil"
|
||||
)
|
||||
|
||||
const (
|
||||
addressExpiryTime = 2 * time.Hour
|
||||
databaseStatisticsInterval = 5 * time.Minute
|
||||
addressExpiryTime = 2 * time.Hour
|
||||
|
||||
// Reannounce-After is set to reannounceAfterSeconds +
|
||||
// random(reannounzeFuzzSeconds), similar for Retry-After
|
||||
@@ -58,12 +59,13 @@ const (
|
||||
var debug = false
|
||||
|
||||
type CLI struct {
|
||||
Cert string `group:"Listen" help:"Certificate file" default:"./cert.pem" env:"DISCOVERY_CERT_FILE"`
|
||||
Key string `group:"Listen" help:"Key file" default:"./key.pem" env:"DISCOVERY_KEY_FILE"`
|
||||
HTTP bool `group:"Listen" help:"Listen on HTTP (behind an HTTPS proxy)" env:"DISCOVERY_HTTP"`
|
||||
Compression bool `group:"Listen" help:"Enable GZIP compression of responses" env:"DISCOVERY_COMPRESSION"`
|
||||
Listen string `group:"Listen" help:"Listen address" default:":8443" env:"DISCOVERY_LISTEN"`
|
||||
MetricsListen string `group:"Listen" help:"Metrics listen address" env:"DISCOVERY_METRICS_LISTEN"`
|
||||
Cert string `group:"Listen" help:"Certificate file" default:"./cert.pem" env:"DISCOVERY_CERT_FILE"`
|
||||
Key string `group:"Listen" help:"Key file" default:"./key.pem" env:"DISCOVERY_KEY_FILE"`
|
||||
HTTP bool `group:"Listen" help:"Listen on HTTP (behind an HTTPS proxy)" env:"DISCOVERY_HTTP"`
|
||||
Compression bool `group:"Listen" help:"Enable GZIP compression of responses" env:"DISCOVERY_COMPRESSION"`
|
||||
Listen string `group:"Listen" help:"Listen address" default:":8443" env:"DISCOVERY_LISTEN"`
|
||||
MetricsListen string `group:"Listen" help:"Metrics listen address" env:"DISCOVERY_METRICS_LISTEN"`
|
||||
DesiredNotFoundRate float64 `group:"Listen" help:"Desired maximum rate of not-found replies (/s)" default:"1000"`
|
||||
|
||||
DBDir string `group:"Database" help:"Database directory" default:"." env:"DISCOVERY_DB_DIR"`
|
||||
DBFlushInterval time.Duration `group:"Database" help:"Interval between database flushes" default:"5m" env:"DISCOVERY_DB_FLUSH_INTERVAL"`
|
||||
@@ -74,6 +76,10 @@ type CLI struct {
|
||||
DBS3AccessKeyID string `name:"db-s3-access-key-id" group:"Database (S3 backup)" hidden:"true" help:"S3 access key ID for database" env:"DISCOVERY_DB_S3_ACCESS_KEY_ID"`
|
||||
DBS3SecretKey string `name:"db-s3-secret-key" group:"Database (S3 backup)" hidden:"true" help:"S3 secret key for database" env:"DISCOVERY_DB_S3_SECRET_KEY"`
|
||||
|
||||
DBAzureBlobAccount string `name:"db-azure-blob-account" env:"DISCOVERY_DB_AZUREBLOB_ACCOUNT"`
|
||||
DBAzureBlobKey string `name:"db-azure-blob-key" env:"DISCOVERY_DB_AZUREBLOB_KEY"`
|
||||
DBAzureBlobContainer string `name:"db-azure-blob-container" env:"DISCOVERY_DB_AZUREBLOB_CONTAINER"`
|
||||
|
||||
AMQPAddress string `group:"AMQP replication" hidden:"true" help:"Address to AMQP broker" env:"DISCOVERY_AMQP_ADDRESS"`
|
||||
|
||||
Debug bool `short:"d" help:"Print debug output" env:"DISCOVERY_DEBUG"`
|
||||
@@ -81,13 +87,16 @@ type CLI struct {
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetOutput(os.Stdout)
|
||||
|
||||
var cli CLI
|
||||
kong.Parse(&cli)
|
||||
debug = cli.Debug
|
||||
|
||||
log.Println(build.LongVersionFor("stdiscosrv"))
|
||||
level := slog.LevelInfo
|
||||
if cli.Debug {
|
||||
level = slog.LevelDebug
|
||||
}
|
||||
slogutil.SetDefaultLevel(level)
|
||||
|
||||
slog.Info(build.LongVersionFor("stdiscosrv"))
|
||||
if cli.Version {
|
||||
return
|
||||
}
|
||||
@@ -99,16 +108,18 @@ func main() {
|
||||
var err error
|
||||
cert, err = tls.LoadX509KeyPair(cli.Cert, cli.Key)
|
||||
if os.IsNotExist(err) {
|
||||
log.Println("Failed to load keypair. Generating one, this might take a while...")
|
||||
cert, err = tlsutil.NewCertificate(cli.Cert, cli.Key, "stdiscosrv", 20*365)
|
||||
slog.Info("Failed to load keypair. Generating one, this might take a while...")
|
||||
cert, err = tlsutil.NewCertificate(cli.Cert, cli.Key, "stdiscosrv", 20*365, false)
|
||||
if err != nil {
|
||||
log.Fatalln("Failed to generate X509 key pair:", err)
|
||||
slog.Error("Failed to generate X509 key pair", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else if err != nil {
|
||||
log.Fatalln("Failed to load keypair:", err)
|
||||
slog.Error("Failed to load keypair", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
devID := protocol.NewDeviceID(cert.Certificate[0])
|
||||
log.Println("Server device ID is", devID)
|
||||
slog.Info("Loaded certificate keypair", "deviceID", devID)
|
||||
}
|
||||
|
||||
// Root of the service tree.
|
||||
@@ -117,18 +128,21 @@ func main() {
|
||||
Timeout: 2 * time.Minute,
|
||||
})
|
||||
|
||||
// If configured, use S3 for database backups.
|
||||
var s3c *s3.Session
|
||||
// If configured, use blob storage for database backups.
|
||||
var blobs blob.Store
|
||||
var err error
|
||||
if cli.DBS3Endpoint != "" {
|
||||
var err error
|
||||
s3c, err = s3.NewSession(cli.DBS3Endpoint, cli.DBS3Region, cli.DBS3Bucket, cli.DBS3AccessKeyID, cli.DBS3SecretKey)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create S3 session: %v", err)
|
||||
}
|
||||
blobs, err = s3.NewSession(cli.DBS3Endpoint, cli.DBS3Region, cli.DBS3Bucket, cli.DBS3AccessKeyID, cli.DBS3SecretKey)
|
||||
} else if cli.DBAzureBlobAccount != "" {
|
||||
blobs, err = azureblob.NewBlobStore(cli.DBAzureBlobAccount, cli.DBAzureBlobKey, cli.DBAzureBlobContainer)
|
||||
}
|
||||
if err != nil {
|
||||
slog.Error("Failed to create blob store", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Start the database.
|
||||
db := newInMemoryStore(cli.DBDir, cli.DBFlushInterval, s3c)
|
||||
db := newInMemoryStore(cli.DBDir, cli.DBFlushInterval, blobs)
|
||||
main.Add(db)
|
||||
|
||||
// If we have an AMQP broker for replication, start that
|
||||
@@ -141,7 +155,7 @@ func main() {
|
||||
}
|
||||
|
||||
// Start the main API server.
|
||||
qs := newAPISrv(cli.Listen, cert, db, repl, cli.HTTP, cli.Compression)
|
||||
qs := newAPISrv(cli.Listen, cert, db, repl, cli.HTTP, cli.Compression, cli.DesiredNotFoundRate)
|
||||
main.Add(qs)
|
||||
|
||||
// If we have a metrics port configured, start a metrics handler.
|
||||
@@ -149,7 +163,9 @@ func main() {
|
||||
go func() {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/metrics", promhttp.Handler())
|
||||
log.Fatal(http.ListenAndServe(cli.MetricsListen, mux))
|
||||
err := http.ListenAndServe(cli.MetricsListen, mux)
|
||||
slog.Error("Failed to serve", "error", err)
|
||||
os.Exit(1)
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -161,7 +177,7 @@ func main() {
|
||||
signal.Notify(signalChan, os.Interrupt)
|
||||
go func() {
|
||||
sig := <-signalChan
|
||||
log.Printf("Received signal %s; shutting down", sig)
|
||||
slog.Info("Received signal; shutting down", "signal", sig)
|
||||
cancel()
|
||||
}()
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ func protocolConnectionHandler(tcpConn net.Conn, config *tls.Config, token strin
|
||||
continue
|
||||
}
|
||||
// requestedPeer is the server, id is the client
|
||||
ses := newSession(requestedPeer, id, sessionLimiter, globalLimiter)
|
||||
ses := newSession(requestedPeer, id, sessionLimitBps, globalLimiter)
|
||||
|
||||
go ses.Serve()
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
@@ -51,7 +52,6 @@ var (
|
||||
globalLimitBps int
|
||||
overLimit atomic.Bool
|
||||
descriptorLimit int64
|
||||
sessionLimiter *rate.Limiter
|
||||
globalLimiter *rate.Limiter
|
||||
networkBufferSize int
|
||||
|
||||
@@ -158,7 +158,7 @@ func main() {
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
log.Println("Failed to load keypair. Generating one, this might take a while...")
|
||||
cert, err = tlsutil.NewCertificate(certFile, keyFile, "strelaysrv", 20*365)
|
||||
cert, err = tlsutil.NewCertificate(certFile, keyFile, "strelaysrv", 20*365, false)
|
||||
if err != nil {
|
||||
log.Fatalln("Failed to generate X509 key pair:", err)
|
||||
}
|
||||
@@ -228,9 +228,6 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
if sessionLimitBps > 0 {
|
||||
sessionLimiter = rate.NewLimiter(rate.Limit(sessionLimitBps), 2*sessionLimitBps)
|
||||
}
|
||||
if globalLimitBps > 0 {
|
||||
globalLimiter = rate.NewLimiter(rate.Limit(globalLimitBps), 2*globalLimitBps)
|
||||
}
|
||||
@@ -251,10 +248,10 @@ func main() {
|
||||
query.Set("pingInterval", pingInterval.String())
|
||||
query.Set("networkTimeout", networkTimeout.String())
|
||||
if sessionLimitBps > 0 {
|
||||
query.Set("sessionLimitBps", fmt.Sprint(sessionLimitBps))
|
||||
query.Set("sessionLimitBps", strconv.Itoa(sessionLimitBps))
|
||||
}
|
||||
if globalLimitBps > 0 {
|
||||
query.Set("globalLimitBps", fmt.Sprint(globalLimitBps))
|
||||
query.Set("globalLimitBps", strconv.Itoa(globalLimitBps))
|
||||
}
|
||||
if statusAddr != "" {
|
||||
query.Set("statusAddr", statusAddr)
|
||||
|
||||
@@ -27,7 +27,7 @@ var (
|
||||
bytesProxied atomic.Int64
|
||||
)
|
||||
|
||||
func newSession(serverid, clientid syncthingprotocol.DeviceID, sessionRateLimit, globalRateLimit *rate.Limiter) *session {
|
||||
func newSession(serverid, clientid syncthingprotocol.DeviceID, sessionLimitBps int, globalRateLimit *rate.Limiter) *session {
|
||||
serverkey := make([]byte, 32)
|
||||
_, err := rand.Read(serverkey)
|
||||
if err != nil {
|
||||
@@ -40,12 +40,17 @@ func newSession(serverid, clientid syncthingprotocol.DeviceID, sessionRateLimit,
|
||||
return nil
|
||||
}
|
||||
|
||||
var sessionRateLimit *rate.Limiter
|
||||
if sessionLimitBps > 0 {
|
||||
sessionRateLimit = rate.NewLimiter(rate.Limit(sessionLimitBps), 2*sessionLimitBps)
|
||||
}
|
||||
ses := &session{
|
||||
serverkey: serverkey,
|
||||
serverid: serverid,
|
||||
clientkey: clientkey,
|
||||
clientid: clientid,
|
||||
rateLimit: makeRateLimitFunc(sessionRateLimit, globalRateLimit),
|
||||
limiter: sessionRateLimit,
|
||||
connsChan: make(chan net.Conn),
|
||||
conns: make([]net.Conn, 0, 2),
|
||||
}
|
||||
@@ -109,6 +114,7 @@ type session struct {
|
||||
clientid syncthingprotocol.DeviceID
|
||||
|
||||
rateLimit func(bytes int)
|
||||
limiter *rate.Limiter
|
||||
|
||||
connsChan chan net.Conn
|
||||
conns []net.Conn
|
||||
|
||||
@@ -122,7 +122,7 @@ func (r *rateCalculator) updateRates(interval time.Duration) {
|
||||
|
||||
func (r *rateCalculator) rate(periods int) int64 {
|
||||
var tot int64
|
||||
for i := 0; i < periods; i++ {
|
||||
for i := range periods {
|
||||
tot += r.rates[i]
|
||||
}
|
||||
return tot / int64(periods)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
@@ -133,7 +134,8 @@ func connectToStdio(stdin <-chan string, conn net.Conn) {
|
||||
conn.SetReadDeadline(time.Now().Add(time.Millisecond))
|
||||
n, err := conn.Read(buf[0:])
|
||||
if err != nil {
|
||||
nerr, ok := err.(net.Error)
|
||||
var nerr net.Error
|
||||
ok := errors.As(err, &nerr)
|
||||
if !ok || !nerr.Timeout() {
|
||||
log.Println(err)
|
||||
return
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func setTCPOptions(conn net.Conn) error {
|
||||
tcpConn, ok := conn.(*net.TCPConn)
|
||||
if !ok {
|
||||
return errors.New("Not a TCP connection")
|
||||
return errors.New("not a TCP connection")
|
||||
}
|
||||
if err := tcpConn.SetLinger(0); err != nil {
|
||||
return err
|
||||
|
||||
@@ -8,11 +8,14 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/internal/slogutil"
|
||||
)
|
||||
|
||||
func startBlockProfiler() {
|
||||
@@ -20,10 +23,10 @@ func startBlockProfiler() {
|
||||
if profiler == nil {
|
||||
panic("Couldn't find block profiler")
|
||||
}
|
||||
l.Debugln("Starting block profiling")
|
||||
slog.Debug("Starting block profiling")
|
||||
go func() {
|
||||
err := saveBlockingProfiles(profiler) // Only returns on error
|
||||
l.Warnln("Block profiler failed:", err)
|
||||
slog.Error("Block profiler failed", slogutil.Error(err))
|
||||
panic("Block profiler failed")
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ type APIClient interface {
|
||||
|
||||
type apiClient struct {
|
||||
http.Client
|
||||
|
||||
cfg config.GUIConfiguration
|
||||
apikey string
|
||||
}
|
||||
@@ -91,11 +92,11 @@ func loadGUIConfig() (config.GUIConfiguration, error) {
|
||||
guiCfg := cfg.GUI()
|
||||
|
||||
if guiCfg.Address() == "" {
|
||||
return config.GUIConfiguration{}, errors.New("Could not find GUI Address")
|
||||
return config.GUIConfiguration{}, errors.New("could not find GUI Address")
|
||||
}
|
||||
|
||||
if guiCfg.APIKey == "" {
|
||||
return config.GUIConfiguration{}, errors.New("Could not find GUI API key")
|
||||
return config.GUIConfiguration{}, errors.New("could not find GUI API key")
|
||||
}
|
||||
|
||||
return guiCfg, nil
|
||||
@@ -113,7 +114,7 @@ func (c *apiClient) Endpoint() string {
|
||||
}
|
||||
|
||||
func (c *apiClient) Do(req *http.Request) (*http.Response, error) {
|
||||
req.Header.Set("X-API-Key", c.apikey)
|
||||
req.Header.Set("X-Api-Key", c.apikey)
|
||||
resp, err := c.Client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/AudriusButkevicius/recli"
|
||||
@@ -18,6 +19,53 @@ import (
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// Try to mimic the kong output format through custom help templates
|
||||
var customAppHelpTemplate = `Usage: {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .Commands}} <command> [flags]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
|
||||
|
||||
{{.Description}}{{if .VisibleFlags}}
|
||||
|
||||
Flags:
|
||||
{{range $index, $option := .VisibleFlags}}{{if $index}}
|
||||
{{end}}{{$option}}{{end}}{{end}}{{if .VisibleCommands}}
|
||||
|
||||
Commands:{{range .VisibleCategories}}{{if .Name}}
|
||||
|
||||
{{.Name}}:{{range .VisibleCommands}}
|
||||
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}}
|
||||
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}
|
||||
`
|
||||
|
||||
var customCommandHelpTemplate = `Usage: {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [flags]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
|
||||
|
||||
{{.Usage}}{{if .VisibleFlags}}
|
||||
|
||||
Flags:
|
||||
{{range $index, $option := .VisibleFlags}}{{if $index}}
|
||||
{{end}}{{$option}}{{end}}{{end}}{{if .Category}}
|
||||
|
||||
Category:
|
||||
{{.Category}}{{end}}{{if .Description}}
|
||||
|
||||
{{.Description}}{{end}}
|
||||
`
|
||||
|
||||
var customSubcommandHelpTemplate = `Usage: {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} <command>{{if .VisibleFlags}} [flags]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
|
||||
|
||||
{{.Description}}{{else}}{{if .Usage}}
|
||||
|
||||
{{.Usage}}{{end}}{{end}}{{if .VisibleFlags}}
|
||||
|
||||
Flags:
|
||||
{{range $index, $option := .VisibleFlags}}{{if $index}}
|
||||
{{end}}{{$option}}{{end}}{{end}}{{if .VisibleCommands}}
|
||||
|
||||
Commands:{{range .VisibleCategories}}{{if .Name}}
|
||||
|
||||
{{.Name}}:{{range .VisibleCommands}}
|
||||
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}}
|
||||
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}
|
||||
`
|
||||
|
||||
type configHandler struct {
|
||||
original, cfg config.Configuration
|
||||
client APIClient
|
||||
@@ -28,13 +76,18 @@ type configCommand struct {
|
||||
Args []string `arg:"" default:"-h"`
|
||||
}
|
||||
|
||||
func (c *configCommand) Run(ctx Context, _ *kong.Context) error {
|
||||
func (c *configCommand) Run(ctx Context, outerCtx *kong.Context) error {
|
||||
app := cli.NewApp()
|
||||
app.Name = "syncthing"
|
||||
app.Author = "The Syncthing Authors"
|
||||
app.Name = "syncthing cli config"
|
||||
app.HelpName = "syncthing cli config"
|
||||
app.Description = outerCtx.Selected().Help
|
||||
app.Metadata = map[string]interface{}{
|
||||
"clientFactory": ctx.clientFactory,
|
||||
}
|
||||
app.CustomAppHelpTemplate = customAppHelpTemplate
|
||||
// Override global templates, as this is out only usage of the package
|
||||
cli.CommandHelpTemplate = customCommandHelpTemplate
|
||||
cli.SubcommandHelpTemplate = customSubcommandHelpTemplate
|
||||
|
||||
h := new(configHandler)
|
||||
h.client, h.err = ctx.clientFactory.getClient()
|
||||
@@ -55,6 +108,8 @@ func (c *configCommand) Run(ctx Context, _ *kong.Context) error {
|
||||
|
||||
app.Commands = commands
|
||||
app.HideHelp = true
|
||||
// Explicitly re-add help only as flags, not as commands
|
||||
app.Flags = []cli.Flag{cli.HelpFlag}
|
||||
app.Before = h.configBefore
|
||||
app.After = h.configAfter
|
||||
|
||||
@@ -86,7 +141,7 @@ func (h *configHandler) configAfter(_ *cli.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, err := responseToBArray(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -41,5 +41,4 @@ func (p *profileCommand) Run(ctx Context) error {
|
||||
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)"`
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ package cli
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kong"
|
||||
@@ -34,7 +35,7 @@ func (e *errorsPushCommand) Run(ctx Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response.StatusCode != 200 {
|
||||
if response.StatusCode != http.StatusOK {
|
||||
errStr = fmt.Sprint("Failed to push error\nStatus code: ", response.StatusCode)
|
||||
bytes, err := responseToBArray(response)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright (C) 2014 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 cli
|
||||
|
||||
import (
|
||||
"github.com/alecthomas/kong"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright (C) 2020 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 cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
// indexAccount prints key and data size statistics per class
|
||||
func indexAccount() error {
|
||||
ldb, err := getDB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
it, err := ldb.NewPrefixIterator(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ksizes [256]int
|
||||
var dsizes [256]int
|
||||
var counts [256]int
|
||||
var max [256]int
|
||||
|
||||
for it.Next() {
|
||||
key := it.Key()
|
||||
t := key[0]
|
||||
ds := len(it.Value())
|
||||
ks := len(key)
|
||||
s := ks + ds
|
||||
|
||||
counts[t]++
|
||||
ksizes[t] += ks
|
||||
dsizes[t] += ds
|
||||
if s > max[t] {
|
||||
max[t] = s
|
||||
}
|
||||
}
|
||||
|
||||
tw := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', tabwriter.AlignRight)
|
||||
toti, totds, totks := 0, 0, 0
|
||||
for t := range ksizes {
|
||||
if ksizes[t] > 0 {
|
||||
// yes metric kilobytes 🤘
|
||||
fmt.Fprintf(tw, "0x%02x:\t%d items,\t%d KB keys +\t%d KB data,\t%d B +\t%d B avg,\t%d B max\t\n", t, counts[t], ksizes[t]/1000, dsizes[t]/1000, ksizes[t]/counts[t], dsizes[t]/counts[t], max[t])
|
||||
toti += counts[t]
|
||||
totds += dsizes[t]
|
||||
totks += ksizes[t]
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(tw, "Total\t%d items,\t%d KB keys +\t%d KB data.\t\n", toti, totks/1000, totds/1000)
|
||||
tw.Flush()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
// Copyright (C) 2015 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 cli
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/syncthing/syncthing/internal/gen/bep"
|
||||
"github.com/syncthing/syncthing/internal/gen/dbproto"
|
||||
"github.com/syncthing/syncthing/lib/db"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
)
|
||||
|
||||
func indexDump() error {
|
||||
ldb, err := getDB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
it, err := ldb.NewPrefixIterator(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for it.Next() {
|
||||
key := it.Key()
|
||||
switch key[0] {
|
||||
case db.KeyTypeDevice:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
device := binary.BigEndian.Uint32(key[1+4:])
|
||||
name := nulString(key[1+4+4:])
|
||||
fmt.Printf("[device] F:%d D:%d N:%q", folder, device, name)
|
||||
|
||||
var f bep.FileInfo
|
||||
err := proto.Unmarshal(it.Value(), &f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf(" V:%v\n", &f)
|
||||
|
||||
case db.KeyTypeGlobal:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
name := nulString(key[1+4:])
|
||||
var flv dbproto.VersionList
|
||||
proto.Unmarshal(it.Value(), &flv)
|
||||
fmt.Printf("[global] F:%d N:%q V:%s\n", folder, name, &flv)
|
||||
|
||||
case db.KeyTypeBlock:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
hash := key[1+4 : 1+4+32]
|
||||
name := nulString(key[1+4+32:])
|
||||
fmt.Printf("[block] F:%d H:%x N:%q I:%d\n", folder, hash, name, binary.BigEndian.Uint32(it.Value()))
|
||||
|
||||
case db.KeyTypeDeviceStatistic:
|
||||
fmt.Printf("[dstat] K:%x V:%x\n", key, it.Value())
|
||||
|
||||
case db.KeyTypeFolderStatistic:
|
||||
fmt.Printf("[fstat] K:%x V:%x\n", key, it.Value())
|
||||
|
||||
case db.KeyTypeVirtualMtime:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
name := nulString(key[1+4:])
|
||||
val := it.Value()
|
||||
var realTime, virtualTime time.Time
|
||||
realTime.UnmarshalBinary(val[:len(val)/2])
|
||||
virtualTime.UnmarshalBinary(val[len(val)/2:])
|
||||
fmt.Printf("[mtime] F:%d N:%q R:%v V:%v\n", folder, name, realTime, virtualTime)
|
||||
|
||||
case db.KeyTypeFolderIdx:
|
||||
key := binary.BigEndian.Uint32(key[1:])
|
||||
fmt.Printf("[folderidx] K:%d V:%q\n", key, it.Value())
|
||||
|
||||
case db.KeyTypeDeviceIdx:
|
||||
key := binary.BigEndian.Uint32(key[1:])
|
||||
val := it.Value()
|
||||
device := "<nil>"
|
||||
if len(val) > 0 {
|
||||
dev, err := protocol.DeviceIDFromBytes(val)
|
||||
if err != nil {
|
||||
device = fmt.Sprintf("<invalid %d bytes>", len(val))
|
||||
} else {
|
||||
device = dev.String()
|
||||
}
|
||||
}
|
||||
fmt.Printf("[deviceidx] K:%d V:%s\n", key, device)
|
||||
|
||||
case db.KeyTypeIndexID:
|
||||
device := binary.BigEndian.Uint32(key[1:])
|
||||
folder := binary.BigEndian.Uint32(key[5:])
|
||||
fmt.Printf("[indexid] D:%d F:%d I:%x\n", device, folder, it.Value())
|
||||
|
||||
case db.KeyTypeFolderMeta:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
fmt.Printf("[foldermeta] F:%d", folder)
|
||||
var cs dbproto.CountsSet
|
||||
if err := proto.Unmarshal(it.Value(), &cs); err != nil {
|
||||
fmt.Printf(" (invalid)\n")
|
||||
} else {
|
||||
fmt.Printf(" V:%v\n", &cs)
|
||||
}
|
||||
|
||||
case db.KeyTypeMiscData:
|
||||
fmt.Printf("[miscdata] K:%q V:%q\n", key[1:], it.Value())
|
||||
|
||||
case db.KeyTypeSequence:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
seq := binary.BigEndian.Uint64(key[5:])
|
||||
fmt.Printf("[sequence] F:%d S:%d V:%q\n", folder, seq, it.Value())
|
||||
|
||||
case db.KeyTypeNeed:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
file := string(key[5:])
|
||||
fmt.Printf("[need] F:%d V:%q\n", folder, file)
|
||||
|
||||
case db.KeyTypeBlockList:
|
||||
fmt.Printf("[blocklist] H:%x\n", key[1:])
|
||||
|
||||
case db.KeyTypeBlockListMap:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
hash := key[5:37]
|
||||
fileName := string(key[37:])
|
||||
fmt.Printf("[blocklistmap] F:%d H:%x N:%s\n", folder, hash, fileName)
|
||||
|
||||
case db.KeyTypeVersion:
|
||||
fmt.Printf("[version] H:%x", key[1:])
|
||||
var v bep.Vector
|
||||
err := proto.Unmarshal(it.Value(), &v)
|
||||
if err != nil {
|
||||
fmt.Printf(" (invalid)\n")
|
||||
} else {
|
||||
fmt.Printf(" V:%v\n", &v)
|
||||
}
|
||||
|
||||
case db.KeyTypePendingFolder:
|
||||
device := binary.BigEndian.Uint32(key[1:])
|
||||
folder := string(key[5:])
|
||||
var of dbproto.ObservedFolder
|
||||
proto.Unmarshal(it.Value(), &of)
|
||||
fmt.Printf("[pendingFolder] D:%d F:%s V:%v\n", device, folder, &of)
|
||||
|
||||
case db.KeyTypePendingDevice:
|
||||
device := "<invalid>"
|
||||
dev, err := protocol.DeviceIDFromBytes(key[1:])
|
||||
if err == nil {
|
||||
device = dev.String()
|
||||
}
|
||||
var od dbproto.ObservedDevice
|
||||
proto.Unmarshal(it.Value(), &od)
|
||||
fmt.Printf("[pendingDevice] D:%v V:%v\n", device, &od)
|
||||
|
||||
default:
|
||||
fmt.Printf("[??? %d]\n %x\n %x\n", key[0], key, it.Value())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
// Copyright (C) 2015 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 cli
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/db"
|
||||
)
|
||||
|
||||
func indexDumpSize() error {
|
||||
type sizedElement struct {
|
||||
key string
|
||||
size int
|
||||
}
|
||||
|
||||
ldb, err := getDB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
it, err := ldb.NewPrefixIterator(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elems []sizedElement
|
||||
for it.Next() {
|
||||
var ele sizedElement
|
||||
|
||||
key := it.Key()
|
||||
switch key[0] {
|
||||
case db.KeyTypeDevice:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
device := binary.BigEndian.Uint32(key[1+4:])
|
||||
name := nulString(key[1+4+4:])
|
||||
ele.key = fmt.Sprintf("DEVICE:%d:%d:%s", folder, device, name)
|
||||
|
||||
case db.KeyTypeGlobal:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
name := nulString(key[1+4:])
|
||||
ele.key = fmt.Sprintf("GLOBAL:%d:%s", folder, name)
|
||||
|
||||
case db.KeyTypeBlock:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
hash := key[1+4 : 1+4+32]
|
||||
name := nulString(key[1+4+32:])
|
||||
ele.key = fmt.Sprintf("BLOCK:%d:%x:%s", folder, hash, name)
|
||||
|
||||
case db.KeyTypeDeviceStatistic:
|
||||
ele.key = fmt.Sprintf("DEVICESTATS:%s", key[1:])
|
||||
|
||||
case db.KeyTypeFolderStatistic:
|
||||
ele.key = fmt.Sprintf("FOLDERSTATS:%s", key[1:])
|
||||
|
||||
case db.KeyTypeVirtualMtime:
|
||||
ele.key = fmt.Sprintf("MTIME:%s", key[1:])
|
||||
|
||||
case db.KeyTypeFolderIdx:
|
||||
id := binary.BigEndian.Uint32(key[1:])
|
||||
ele.key = fmt.Sprintf("FOLDERIDX:%d", id)
|
||||
|
||||
case db.KeyTypeDeviceIdx:
|
||||
id := binary.BigEndian.Uint32(key[1:])
|
||||
ele.key = fmt.Sprintf("DEVICEIDX:%d", id)
|
||||
|
||||
default:
|
||||
ele.key = fmt.Sprintf("UNKNOWN:%x", key)
|
||||
}
|
||||
ele.size = len(it.Value())
|
||||
elems = append(elems, ele)
|
||||
}
|
||||
|
||||
sort.Slice(elems, func(i, j int) bool {
|
||||
return elems[i].size > elems[j].size
|
||||
})
|
||||
for _, ele := range elems {
|
||||
fmt.Println(ele.key, ele.size)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,434 +0,0 @@
|
||||
// Copyright (C) 2018 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 cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/syncthing/syncthing/internal/gen/bep"
|
||||
"github.com/syncthing/syncthing/internal/gen/dbproto"
|
||||
"github.com/syncthing/syncthing/lib/db"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
)
|
||||
|
||||
type fileInfoKey struct {
|
||||
folder uint32
|
||||
device uint32
|
||||
name string
|
||||
}
|
||||
|
||||
type globalKey struct {
|
||||
folder uint32
|
||||
name string
|
||||
}
|
||||
|
||||
type sequenceKey struct {
|
||||
folder uint32
|
||||
sequence uint64
|
||||
}
|
||||
|
||||
func indexCheck() (err error) {
|
||||
ldb, err := getDB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
folders := make(map[uint32]string)
|
||||
devices := make(map[uint32]string)
|
||||
deviceToIDs := make(map[string]uint32)
|
||||
fileInfos := make(map[fileInfoKey]*bep.FileInfo)
|
||||
globals := make(map[globalKey]*dbproto.VersionList)
|
||||
sequences := make(map[sequenceKey]string)
|
||||
needs := make(map[globalKey]struct{})
|
||||
blocklists := make(map[string]struct{})
|
||||
versions := make(map[string]*bep.Vector)
|
||||
usedBlocklists := make(map[string]struct{})
|
||||
usedVersions := make(map[string]struct{})
|
||||
var localDeviceKey uint32
|
||||
success := true
|
||||
defer func() {
|
||||
if err == nil {
|
||||
if success {
|
||||
fmt.Println("Index check completed successfully.")
|
||||
} else {
|
||||
err = errors.New("Inconsistencies found in the index")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
it, err := ldb.NewPrefixIterator(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for it.Next() {
|
||||
key := it.Key()
|
||||
switch key[0] {
|
||||
case db.KeyTypeDevice:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
device := binary.BigEndian.Uint32(key[1+4:])
|
||||
name := nulString(key[1+4+4:])
|
||||
|
||||
var f bep.FileInfo
|
||||
err := proto.Unmarshal(it.Value(), &f)
|
||||
if err != nil {
|
||||
fmt.Println("Unable to unmarshal FileInfo:", err)
|
||||
success = false
|
||||
continue
|
||||
}
|
||||
|
||||
fileInfos[fileInfoKey{folder, device, name}] = &f
|
||||
|
||||
case db.KeyTypeGlobal:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
name := nulString(key[1+4:])
|
||||
var flv dbproto.VersionList
|
||||
if err := proto.Unmarshal(it.Value(), &flv); err != nil {
|
||||
fmt.Println("Unable to unmarshal VersionList:", err)
|
||||
success = false
|
||||
continue
|
||||
}
|
||||
globals[globalKey{folder, name}] = &flv
|
||||
|
||||
case db.KeyTypeFolderIdx:
|
||||
key := binary.BigEndian.Uint32(it.Key()[1:])
|
||||
folders[key] = string(it.Value())
|
||||
|
||||
case db.KeyTypeDeviceIdx:
|
||||
key := binary.BigEndian.Uint32(it.Key()[1:])
|
||||
devices[key] = string(it.Value())
|
||||
deviceToIDs[string(it.Value())] = key
|
||||
if bytes.Equal(it.Value(), protocol.LocalDeviceID[:]) {
|
||||
localDeviceKey = key
|
||||
}
|
||||
|
||||
case db.KeyTypeSequence:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
seq := binary.BigEndian.Uint64(key[5:])
|
||||
val := it.Value()
|
||||
sequences[sequenceKey{folder, seq}] = string(val[9:])
|
||||
|
||||
case db.KeyTypeNeed:
|
||||
folder := binary.BigEndian.Uint32(key[1:])
|
||||
name := nulString(key[1+4:])
|
||||
needs[globalKey{folder, name}] = struct{}{}
|
||||
|
||||
case db.KeyTypeBlockList:
|
||||
hash := string(key[1:])
|
||||
blocklists[hash] = struct{}{}
|
||||
|
||||
case db.KeyTypeVersion:
|
||||
hash := string(key[1:])
|
||||
var v bep.Vector
|
||||
if err := proto.Unmarshal(it.Value(), &v); err != nil {
|
||||
fmt.Println("Unable to unmarshal Vector:", err)
|
||||
success = false
|
||||
continue
|
||||
}
|
||||
versions[hash] = &v
|
||||
}
|
||||
}
|
||||
|
||||
if localDeviceKey == 0 {
|
||||
fmt.Println("Missing key for local device in device index (bailing out)")
|
||||
success = false
|
||||
return
|
||||
}
|
||||
|
||||
var missingSeq []sequenceKey
|
||||
for fk, fi := range fileInfos {
|
||||
if fk.name != fi.Name {
|
||||
fmt.Printf("Mismatching FileInfo name, %q (key) != %q (actual)\n", fk.name, fi.Name)
|
||||
success = false
|
||||
}
|
||||
|
||||
folder := folders[fk.folder]
|
||||
if folder == "" {
|
||||
fmt.Printf("Unknown folder ID %d for FileInfo %q\n", fk.folder, fk.name)
|
||||
success = false
|
||||
continue
|
||||
}
|
||||
if devices[fk.device] == "" {
|
||||
fmt.Printf("Unknown device ID %d for FileInfo %q, folder %q\n", fk.folder, fk.name, folder)
|
||||
success = false
|
||||
}
|
||||
|
||||
if fk.device == localDeviceKey {
|
||||
sk := sequenceKey{fk.folder, uint64(fi.Sequence)}
|
||||
name, ok := sequences[sk]
|
||||
if !ok {
|
||||
fmt.Printf("Sequence entry missing for FileInfo %q, folder %q, seq %d\n", fi.Name, folder, fi.Sequence)
|
||||
missingSeq = append(missingSeq, sk)
|
||||
success = false
|
||||
continue
|
||||
}
|
||||
if name != fi.Name {
|
||||
fmt.Printf("Sequence entry refers to wrong name, %q (seq) != %q (FileInfo), folder %q, seq %d\n", name, fi.Name, folder, fi.Sequence)
|
||||
success = false
|
||||
}
|
||||
}
|
||||
|
||||
if len(fi.Blocks) == 0 && len(fi.BlocksHash) != 0 {
|
||||
key := string(fi.BlocksHash)
|
||||
if _, ok := blocklists[key]; !ok {
|
||||
fmt.Printf("Missing block list for file %q, block list hash %x\n", fi.Name, fi.BlocksHash)
|
||||
success = false
|
||||
} else {
|
||||
usedBlocklists[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if fi.VersionHash != nil {
|
||||
key := string(fi.VersionHash)
|
||||
if _, ok := versions[key]; !ok {
|
||||
fmt.Printf("Missing version vector for file %q, version hash %x\n", fi.Name, fi.VersionHash)
|
||||
success = false
|
||||
} else {
|
||||
usedVersions[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
_, ok := globals[globalKey{fk.folder, fk.name}]
|
||||
if !ok {
|
||||
fmt.Printf("Missing global for file %q\n", fi.Name)
|
||||
success = false
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregate the ranges of missing sequence entries, print them
|
||||
|
||||
sort.Slice(missingSeq, func(a, b int) bool {
|
||||
if missingSeq[a].folder != missingSeq[b].folder {
|
||||
return missingSeq[a].folder < missingSeq[b].folder
|
||||
}
|
||||
return missingSeq[a].sequence < missingSeq[b].sequence
|
||||
})
|
||||
|
||||
var folder uint32
|
||||
var startSeq, prevSeq uint64
|
||||
for _, sk := range missingSeq {
|
||||
if folder != sk.folder || sk.sequence != prevSeq+1 {
|
||||
if folder != 0 {
|
||||
fmt.Printf("Folder %d missing %d sequence entries: #%d - #%d\n", folder, prevSeq-startSeq+1, startSeq, prevSeq)
|
||||
}
|
||||
startSeq = sk.sequence
|
||||
folder = sk.folder
|
||||
}
|
||||
prevSeq = sk.sequence
|
||||
}
|
||||
if folder != 0 {
|
||||
fmt.Printf("Folder %d missing %d sequence entries: #%d - #%d\n", folder, prevSeq-startSeq+1, startSeq, prevSeq)
|
||||
}
|
||||
|
||||
for gk, vl := range globals {
|
||||
folder := folders[gk.folder]
|
||||
if folder == "" {
|
||||
fmt.Printf("Unknown folder ID %d for VersionList %q\n", gk.folder, gk.name)
|
||||
success = false
|
||||
}
|
||||
checkGlobal := func(i int, device []byte, version protocol.Vector, invalid, deleted bool) {
|
||||
dev, ok := deviceToIDs[string(device)]
|
||||
if !ok {
|
||||
fmt.Printf("VersionList %q, folder %q refers to unknown device %q\n", gk.name, folder, device)
|
||||
success = false
|
||||
}
|
||||
fi, ok := fileInfos[fileInfoKey{gk.folder, dev, gk.name}]
|
||||
if !ok {
|
||||
fmt.Printf("VersionList %q, folder %q, entry %d refers to unknown FileInfo\n", gk.name, folder, i)
|
||||
success = false
|
||||
}
|
||||
|
||||
fiv := fi.Version
|
||||
if fi.VersionHash != nil {
|
||||
fiv = versions[string(fi.VersionHash)]
|
||||
}
|
||||
if !protocol.VectorFromWire(fiv).Equal(version) {
|
||||
fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo version mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, version, fi.Version)
|
||||
success = false
|
||||
}
|
||||
ffi := protocol.FileInfoFromDB(fi)
|
||||
if ffi.IsInvalid() != invalid {
|
||||
fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo invalid mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, invalid, ffi.IsInvalid())
|
||||
success = false
|
||||
}
|
||||
if ffi.IsDeleted() != deleted {
|
||||
fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo deleted mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, deleted, ffi.IsDeleted())
|
||||
success = false
|
||||
}
|
||||
}
|
||||
for i, fv := range vl.Versions {
|
||||
ver := protocol.VectorFromWire(fv.Version)
|
||||
for _, device := range fv.Devices {
|
||||
checkGlobal(i, device, ver, false, fv.Deleted)
|
||||
}
|
||||
for _, device := range fv.InvalidDevices {
|
||||
checkGlobal(i, device, ver, true, fv.Deleted)
|
||||
}
|
||||
}
|
||||
|
||||
// If we need this file we should have a need entry for it. False
|
||||
// positives from needsLocally for deleted files, where we might
|
||||
// legitimately lack an entry if we never had it, and ignored files.
|
||||
if needsLocally(vl) {
|
||||
_, ok := needs[gk]
|
||||
if !ok {
|
||||
fv, _ := vlGetGlobal(vl)
|
||||
devB, _ := fvFirstDevice(fv)
|
||||
dev := deviceToIDs[string(devB)]
|
||||
fi := protocol.FileInfoFromDB(fileInfos[fileInfoKey{gk.folder, dev, gk.name}])
|
||||
if !fi.IsDeleted() && !fi.IsIgnored() {
|
||||
fmt.Printf("Missing need entry for needed file %q, folder %q\n", gk.name, folder)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
seenSeq := make(map[fileInfoKey]uint64)
|
||||
for sk, name := range sequences {
|
||||
folder := folders[sk.folder]
|
||||
if folder == "" {
|
||||
fmt.Printf("Unknown folder ID %d for sequence entry %d, %q\n", sk.folder, sk.sequence, name)
|
||||
success = false
|
||||
continue
|
||||
}
|
||||
|
||||
if prev, ok := seenSeq[fileInfoKey{folder: sk.folder, name: name}]; ok {
|
||||
fmt.Printf("Duplicate sequence entry for %q, folder %q, seq %d (prev %d)\n", name, folder, sk.sequence, prev)
|
||||
success = false
|
||||
}
|
||||
seenSeq[fileInfoKey{folder: sk.folder, name: name}] = sk.sequence
|
||||
|
||||
fi, ok := fileInfos[fileInfoKey{sk.folder, localDeviceKey, name}]
|
||||
if !ok {
|
||||
fmt.Printf("Missing FileInfo for sequence entry %d, folder %q, %q\n", sk.sequence, folder, name)
|
||||
success = false
|
||||
continue
|
||||
}
|
||||
if fi.Sequence != int64(sk.sequence) {
|
||||
fmt.Printf("Sequence mismatch for %q, folder %q, %d (key) != %d (FileInfo)\n", name, folder, sk.sequence, fi.Sequence)
|
||||
success = false
|
||||
}
|
||||
}
|
||||
|
||||
for nk := range needs {
|
||||
folder := folders[nk.folder]
|
||||
if folder == "" {
|
||||
fmt.Printf("Unknown folder ID %d for need entry %q\n", nk.folder, nk.name)
|
||||
success = false
|
||||
continue
|
||||
}
|
||||
|
||||
vl, ok := globals[nk]
|
||||
if !ok {
|
||||
fmt.Printf("Missing global for need entry %q, folder %q\n", nk.name, folder)
|
||||
success = false
|
||||
continue
|
||||
}
|
||||
|
||||
if !needsLocally(vl) {
|
||||
fmt.Printf("Need entry for file we don't need, %q, folder %q\n", nk.name, folder)
|
||||
success = false
|
||||
}
|
||||
}
|
||||
|
||||
if d := len(blocklists) - len(usedBlocklists); d > 0 {
|
||||
fmt.Printf("%d block list entries out of %d needs GC\n", d, len(blocklists))
|
||||
}
|
||||
if d := len(versions) - len(usedVersions); d > 0 {
|
||||
fmt.Printf("%d version entries out of %d needs GC\n", d, len(versions))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func needsLocally(vl *dbproto.VersionList) bool {
|
||||
gfv, gok := vlGetGlobal(vl)
|
||||
if !gok { // That's weird, but we hardly need something non-existent
|
||||
return false
|
||||
}
|
||||
fv, ok := vlGet(vl, protocol.LocalDeviceID[:])
|
||||
return db.Need(gfv, ok, protocol.VectorFromWire(fv.Version))
|
||||
}
|
||||
|
||||
// Get returns a FileVersion that contains the given device and whether it has
|
||||
// been found at all.
|
||||
func vlGet(vl *dbproto.VersionList, device []byte) (*dbproto.FileVersion, bool) {
|
||||
_, i, _, ok := vlFindDevice(vl, device)
|
||||
if !ok {
|
||||
return &dbproto.FileVersion{}, false
|
||||
}
|
||||
return vl.Versions[i], true
|
||||
}
|
||||
|
||||
// GetGlobal returns the current global FileVersion. The returned FileVersion
|
||||
// may be invalid, if all FileVersions are invalid. Returns false only if
|
||||
// VersionList is empty.
|
||||
func vlGetGlobal(vl *dbproto.VersionList) (*dbproto.FileVersion, bool) {
|
||||
i := vlFindGlobal(vl)
|
||||
if i == -1 {
|
||||
return nil, false
|
||||
}
|
||||
return vl.Versions[i], true
|
||||
}
|
||||
|
||||
// findGlobal returns the first version that isn't invalid, or if all versions are
|
||||
// invalid just the first version (i.e. 0) or -1, if there's no versions at all.
|
||||
func vlFindGlobal(vl *dbproto.VersionList) int {
|
||||
for i := range vl.Versions {
|
||||
if !fvIsInvalid(vl.Versions[i]) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
if len(vl.Versions) == 0 {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// findDevice returns whether the device is in InvalidVersions or Versions and
|
||||
// in InvalidDevices or Devices (true for invalid), the positions in the version
|
||||
// and device slices and whether it has been found at all.
|
||||
func vlFindDevice(vl *dbproto.VersionList, device []byte) (bool, int, int, bool) {
|
||||
for i, v := range vl.Versions {
|
||||
if j := deviceIndex(v.Devices, device); j != -1 {
|
||||
return false, i, j, true
|
||||
}
|
||||
if j := deviceIndex(v.InvalidDevices, device); j != -1 {
|
||||
return true, i, j, true
|
||||
}
|
||||
}
|
||||
return false, -1, -1, false
|
||||
}
|
||||
|
||||
func deviceIndex(devices [][]byte, device []byte) int {
|
||||
for i, dev := range devices {
|
||||
if bytes.Equal(device, dev) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func fvFirstDevice(fv *dbproto.FileVersion) ([]byte, bool) {
|
||||
if len(fv.Devices) != 0 {
|
||||
return fv.Devices[0], true
|
||||
}
|
||||
if len(fv.InvalidDevices) != 0 {
|
||||
return fv.InvalidDevices[0], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func fvIsInvalid(fv *dbproto.FileVersion) bool {
|
||||
return fv == nil || len(fv.Devices) == 0
|
||||
}
|
||||
@@ -14,15 +14,12 @@ import (
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/kballard/go-shellquote"
|
||||
|
||||
"github.com/syncthing/syncthing/cmd/syncthing/cmdutil"
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
)
|
||||
|
||||
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"`
|
||||
GUIAddress string `name:"gui-address" env:"STGUIADDRESS"`
|
||||
GUIAPIKey string `name:"gui-apikey" env:"STGUIAPIKEY"`
|
||||
|
||||
Show showCommand `cmd:"" help:"Show command group"`
|
||||
Debug debugCommand `cmd:"" help:"Debug command group"`
|
||||
@@ -37,11 +34,6 @@ type Context struct {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
clientFactory := &apiClientFactory{
|
||||
cfg: config.GUIConfiguration{
|
||||
RawAddress: cli.GUIAddress,
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/alecthomas/kong"
|
||||
@@ -63,7 +64,7 @@ func (f *folderOverrideCommand) Run(ctx Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if response.StatusCode != 200 {
|
||||
if response.StatusCode != http.StatusOK {
|
||||
errStr := fmt.Sprint("Failed to override changes\nStatus code: ", response.StatusCode)
|
||||
bytes, err := responseToBArray(response)
|
||||
if err != nil {
|
||||
|
||||
@@ -17,8 +17,6 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/db/backend"
|
||||
"github.com/syncthing/syncthing/lib/locations"
|
||||
)
|
||||
|
||||
func responseToBArray(response *http.Response) ([]byte, error) {
|
||||
@@ -133,19 +131,6 @@ func prettyPrintResponse(response *http.Response) error {
|
||||
return prettyPrintJSON(data)
|
||||
}
|
||||
|
||||
func getDB() (backend.Backend, error) {
|
||||
return backend.OpenLevelDBRO(locations.Get(locations.Database))
|
||||
}
|
||||
|
||||
func nulString(bs []byte) string {
|
||||
for i := range bs {
|
||||
if bs[i] == 0 {
|
||||
return string(bs[:i])
|
||||
}
|
||||
}
|
||||
return string(bs)
|
||||
}
|
||||
|
||||
func normalizePath(path string) string {
|
||||
return filepath.ToSlash(filepath.Clean(path))
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (C) 2021 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 cmdutil
|
||||
|
||||
// CommonOptions are reused among several subcommands
|
||||
type CommonOptions struct {
|
||||
buildCommonOptions
|
||||
ConfDir string `name:"config" placeholder:"PATH" env:"STCONFDIR" help:"Set configuration directory (config and keys)"`
|
||||
HomeDir string `name:"home" placeholder:"PATH" env:"STHOMEDIR" help:"Set configuration and data directory"`
|
||||
NoDefaultFolder bool `env:"STNODEFAULTFOLDER" help:"Don't create the \"default\" folder on first startup"`
|
||||
SkipPortProbing bool `help:"Don't try to find free ports for GUI and listen addresses on first startup"`
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright (C) 2014 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 cmdutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/locations"
|
||||
)
|
||||
|
||||
func SetConfigDataLocationsFromFlags(homeDir, confDir, dataDir string) error {
|
||||
homeSet := homeDir != ""
|
||||
confSet := confDir != ""
|
||||
dataSet := dataDir != ""
|
||||
switch {
|
||||
case dataSet != confSet:
|
||||
return errors.New("either both or none of --config and --data must be given, use --home to set both at once")
|
||||
case homeSet && dataSet:
|
||||
return errors.New("--home must not be used together with --config and --data")
|
||||
case homeSet:
|
||||
confDir = homeDir
|
||||
dataDir = homeDir
|
||||
fallthrough
|
||||
case dataSet:
|
||||
if err := locations.SetBaseDir(locations.ConfigBaseDir, confDir); err != nil {
|
||||
return err
|
||||
}
|
||||
return locations.SetBaseDir(locations.DataBaseDir, dataDir)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -11,12 +11,15 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/internal/slogutil"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,16 +31,18 @@ const (
|
||||
// directory to the crash reporting server as urlBase. Uploads are attempted
|
||||
// with the newest log first.
|
||||
//
|
||||
// This can can block for a long time. The context can set a final deadline
|
||||
// This can block for a long time. The context can set a final deadline
|
||||
// for this.
|
||||
func uploadPanicLogs(ctx context.Context, urlBase, dir string) {
|
||||
files, err := filepath.Glob(filepath.Join(dir, "panic-*.log"))
|
||||
if err != nil {
|
||||
l.Warnln("Failed to list panic logs:", err)
|
||||
slog.ErrorContext(ctx, "Failed to list panic logs", slogutil.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
sort.Sort(sort.Reverse(sort.StringSlice(files)))
|
||||
slices.SortFunc(files, func(a, b string) int {
|
||||
return strings.Compare(b, a)
|
||||
})
|
||||
for _, file := range files {
|
||||
if strings.Contains(file, ".reported.") {
|
||||
// We've already sent this file. It'll be cleaned out at some
|
||||
@@ -46,7 +51,7 @@ func uploadPanicLogs(ctx context.Context, urlBase, dir string) {
|
||||
}
|
||||
|
||||
if err := uploadPanicLog(ctx, urlBase, file); err != nil {
|
||||
l.Warnln("Reporting crash:", err)
|
||||
slog.ErrorContext(ctx, "Reporting crash", slogutil.Error(err))
|
||||
} else {
|
||||
// Rename the log so we don't have to try to report it again. This
|
||||
// succeeds, or it does not. There is no point complaining about it.
|
||||
@@ -69,7 +74,7 @@ func uploadPanicLog(ctx context.Context, urlBase, file string) error {
|
||||
data = filterLogLines(data)
|
||||
|
||||
hash := fmt.Sprintf("%x", sha256.Sum256(data))
|
||||
l.Infof("Reporting crash found in %s (report ID %s) ...\n", filepath.Base(file), hash[:8])
|
||||
slog.InfoContext(ctx, "Reporting crash", slogutil.FilePath(filepath.Base(file)), slog.String("id", hash[:8]))
|
||||
|
||||
url := fmt.Sprintf("%s/%s", urlBase, hash)
|
||||
headReq, err := http.NewRequest(http.MethodHead, url, nil)
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/syncthing/syncthing/lib/logger"
|
||||
)
|
||||
import "github.com/syncthing/syncthing/internal/slogutil"
|
||||
|
||||
var l = logger.DefaultLogger.NewFacility("main", "Main package")
|
||||
func init() { slogutil.RegisterPackage("Main package") }
|
||||
|
||||
@@ -238,7 +238,7 @@ func (c *CLI) decryptFile(encFi *protocol.FileInfo, plainFi *protocol.FileInfo,
|
||||
}
|
||||
|
||||
// Verify the hash against the plaintext block info
|
||||
if !scanner.Validate(dec, plainBlock.Hash, 0) {
|
||||
if !scanner.Validate(dec, plainBlock.Hash) {
|
||||
// The block decrypted correctly but fails the hash check. This
|
||||
// is odd and unexpected, but it it's still a valid block from
|
||||
// the source. The file might have changed while we pulled it?
|
||||
|
||||
@@ -11,42 +11,25 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/syncthing/syncthing/cmd/syncthing/cmdutil"
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/locations"
|
||||
"github.com/syncthing/syncthing/lib/logger"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"github.com/syncthing/syncthing/lib/syncthing"
|
||||
)
|
||||
|
||||
type CLI struct {
|
||||
cmdutil.CommonOptions
|
||||
GUIUser string `placeholder:"STRING" help:"Specify new GUI authentication user name"`
|
||||
GUIPassword string `placeholder:"STRING" help:"Specify new GUI authentication password (use - to read from standard input)"`
|
||||
GUIUser string `placeholder:"STRING" help:"Specify new GUI authentication user name"`
|
||||
GUIPassword string `placeholder:"STRING" help:"Specify new GUI authentication password (use - to read from standard input)"`
|
||||
NoPortProbing bool `help:"Don't try to find free ports for GUI and listen addresses on first startup" env:"STNOPORTPROBING"`
|
||||
}
|
||||
|
||||
func (c *CLI) Run(l logger.Logger) error {
|
||||
if c.HideConsole {
|
||||
osutil.HideConsole()
|
||||
}
|
||||
|
||||
if c.HomeDir != "" {
|
||||
if c.ConfDir != "" {
|
||||
return errors.New("--home must not be used together with --config")
|
||||
}
|
||||
c.ConfDir = c.HomeDir
|
||||
}
|
||||
if c.ConfDir == "" {
|
||||
c.ConfDir = locations.GetBaseDir(locations.ConfigBaseDir)
|
||||
}
|
||||
|
||||
func (c *CLI) Run() error {
|
||||
// Support reading the password from a pipe or similar
|
||||
if c.GUIPassword == "-" {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
@@ -57,13 +40,13 @@ func (c *CLI) Run(l logger.Logger) error {
|
||||
c.GUIPassword = string(password)
|
||||
}
|
||||
|
||||
if err := Generate(l, c.ConfDir, c.GUIUser, c.GUIPassword, c.NoDefaultFolder, c.SkipPortProbing); err != nil {
|
||||
if err := Generate(locations.GetBaseDir(locations.ConfigBaseDir), c.GUIUser, c.GUIPassword, c.NoPortProbing); err != nil {
|
||||
return fmt.Errorf("failed to generate config and keys: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Generate(l logger.Logger, confDir, guiUser, guiPassword string, noDefaultFolder, skipPortProbing bool) error {
|
||||
func Generate(confDir, guiUser, guiPassword string, skipPortProbing bool) error {
|
||||
dir, err := fs.ExpandTilde(confDir)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -78,20 +61,21 @@ func Generate(l logger.Logger, confDir, guiUser, guiPassword string, noDefaultFo
|
||||
certFile, keyFile := locations.Get(locations.CertFile), locations.Get(locations.KeyFile)
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err == nil {
|
||||
l.Warnln("Key exists; will not overwrite.")
|
||||
slog.Warn("Key exists; will not overwrite")
|
||||
} else {
|
||||
cert, err = syncthing.GenerateCertificate(certFile, keyFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create certificate: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
myID = protocol.NewDeviceID(cert.Certificate[0])
|
||||
l.Infoln("Device ID:", myID)
|
||||
slog.Info("Calculated device ID", slog.String("device", myID.String()))
|
||||
|
||||
cfgFile := locations.Get(locations.ConfigFile)
|
||||
cfg, _, err := config.Load(cfgFile, myID, events.NoopLogger)
|
||||
if fs.IsNotExist(err) {
|
||||
if cfg, err = syncthing.DefaultConfig(cfgFile, myID, events.NoopLogger, noDefaultFolder, skipPortProbing); err != nil {
|
||||
if cfg, err = syncthing.DefaultConfig(cfgFile, myID, events.NoopLogger, skipPortProbing); err != nil {
|
||||
return fmt.Errorf("create config: %w", err)
|
||||
}
|
||||
} else if err != nil {
|
||||
@@ -104,7 +88,7 @@ func Generate(l logger.Logger, confDir, guiUser, guiPassword string, noDefaultFo
|
||||
|
||||
var updateErr error
|
||||
waiter, err := cfg.Modify(func(cfg *config.Configuration) {
|
||||
updateErr = updateGUIAuthentication(l, &cfg.GUI, guiUser, guiPassword)
|
||||
updateErr = updateGUIAuthentication(&cfg.GUI, guiUser, guiPassword)
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("modify config: %w", err)
|
||||
@@ -120,17 +104,17 @@ func Generate(l logger.Logger, confDir, guiUser, guiPassword string, noDefaultFo
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateGUIAuthentication(l logger.Logger, guiCfg *config.GUIConfiguration, guiUser, guiPassword string) error {
|
||||
func updateGUIAuthentication(guiCfg *config.GUIConfiguration, guiUser, guiPassword string) error {
|
||||
if guiUser != "" && guiCfg.User != guiUser {
|
||||
guiCfg.User = guiUser
|
||||
l.Infoln("Updated GUI authentication user name:", guiUser)
|
||||
slog.Info("Updated GUI authentication user", "name", guiUser)
|
||||
}
|
||||
|
||||
if guiPassword != "" && guiCfg.Password != guiPassword {
|
||||
if err := guiCfg.SetPassword(guiPassword); err != nil {
|
||||
return fmt.Errorf("failed to set GUI authentication password: %w", err)
|
||||
}
|
||||
l.Infoln("Updated GUI authentication password.")
|
||||
slog.Info("Updated GUI authentication password")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,18 +8,21 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/internal/slogutil"
|
||||
)
|
||||
|
||||
func startHeapProfiler() {
|
||||
l.Debugln("Starting heap profiling")
|
||||
slog.Debug("Starting heap profiling")
|
||||
go func() {
|
||||
err := saveHeapProfiles(1) // Only returns on error
|
||||
l.Warnln("Heap profiler failed:", err)
|
||||
slog.Error("Heap profiler failed", slogutil.Error(err))
|
||||
panic("Heap profiler failed")
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package cmdutil
|
||||
package main
|
||||
|
||||
type buildCommonOptions struct {
|
||||
type buildSpecificOptions struct {
|
||||
HideConsole bool `hidden:""`
|
||||
}
|
||||
@@ -4,8 +4,8 @@
|
||||
// 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 cmdutil
|
||||
package main
|
||||
|
||||
type buildCommonOptions struct {
|
||||
HideConsole bool `name:"no-console" help:"Hide console window"`
|
||||
type buildSpecificOptions struct {
|
||||
HideConsole bool `name:"no-console" help:"Hide console window" env:"STHIDECONSOLE"`
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,28 +9,31 @@ package main
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/internal/slogutil"
|
||||
"github.com/syncthing/syncthing/lib/build"
|
||||
"github.com/syncthing/syncthing/lib/fs"
|
||||
"github.com/syncthing/syncthing/lib/locations"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/svcutil"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
)
|
||||
|
||||
var (
|
||||
stdoutFirstLines []string // The first 10 lines of stdout
|
||||
stdoutLastLines []string // The last 50 lines of stdout
|
||||
stdoutMut = sync.NewMutex()
|
||||
stdoutMut sync.Mutex
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -43,9 +46,7 @@ const (
|
||||
panicUploadNoticeWait = 10 * time.Second
|
||||
)
|
||||
|
||||
func monitorMain(options serveOptions) {
|
||||
l.SetPrefix("[monitor] ")
|
||||
|
||||
func (c *serveCmd) monitorMain() {
|
||||
var dst io.Writer = os.Stdout
|
||||
|
||||
logFile := locations.Get(locations.LogFile)
|
||||
@@ -58,13 +59,13 @@ func monitorMain(options serveOptions) {
|
||||
open := func(name string) (io.WriteCloser, error) {
|
||||
return newAutoclosedFile(name, logFileAutoCloseDelay, logFileMaxOpenTime)
|
||||
}
|
||||
if options.LogMaxSize > 0 {
|
||||
fileDst, err = newRotatedFile(logFile, open, int64(options.LogMaxSize), options.LogMaxFiles)
|
||||
if c.LogMaxSize > 0 {
|
||||
fileDst, err = newRotatedFile(logFile, open, int64(c.LogMaxSize), c.LogMaxFiles)
|
||||
} else {
|
||||
fileDst, err = open(logFile)
|
||||
}
|
||||
if err != nil {
|
||||
l.Warnln("Failed to setup logging to file, proceeding with logging to stdout only:", err)
|
||||
slog.Error("Failed to set up logging to file, proceeding with logging to stdout only", slogutil.Error(err))
|
||||
} else {
|
||||
if build.IsWindows {
|
||||
// Translate line breaks to Windows standard
|
||||
@@ -78,14 +79,14 @@ func monitorMain(options serveOptions) {
|
||||
// Log to both stdout and file.
|
||||
dst = io.MultiWriter(dst, fileDst)
|
||||
|
||||
l.Infof(`Log output saved to file "%s"`, logFile)
|
||||
slog.Info("Saved log output", slogutil.FilePath(logFile))
|
||||
}
|
||||
}
|
||||
|
||||
args := os.Args
|
||||
binary, err := getBinary(args[0])
|
||||
if err != nil {
|
||||
l.Warnln("Error starting the main Syncthing process:", err)
|
||||
slog.Error("Failed to start the main Syncthing process", slogutil.Error(err))
|
||||
panic("Error starting the main Syncthing process")
|
||||
}
|
||||
var restarts [restartCounts]time.Time
|
||||
@@ -102,7 +103,7 @@ func monitorMain(options serveOptions) {
|
||||
maybeReportPanics()
|
||||
|
||||
if t := time.Since(restarts[0]); t < restartLoopThreshold {
|
||||
l.Warnf("%d restarts in %v; not retrying further", restartCounts, t)
|
||||
slog.Error("Too many restarts; not retrying further", slog.Int("count", restartCounts), slog.Any("interval", t))
|
||||
os.Exit(svcutil.ExitError.AsInt())
|
||||
}
|
||||
|
||||
@@ -122,10 +123,10 @@ func monitorMain(options serveOptions) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
l.Debugln("Starting syncthing")
|
||||
slog.Debug("Starting syncthing")
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
l.Warnln("Error starting the main Syncthing process:", err)
|
||||
slog.Error("Failed to start the main Syncthing process", slogutil.Error(err))
|
||||
panic("Error starting the main Syncthing process")
|
||||
}
|
||||
|
||||
@@ -134,7 +135,7 @@ func monitorMain(options serveOptions) {
|
||||
stdoutLastLines = make([]string, 0, 50)
|
||||
stdoutMut.Unlock()
|
||||
|
||||
wg := sync.NewWaitGroup()
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
@@ -158,13 +159,13 @@ func monitorMain(options serveOptions) {
|
||||
stopped := false
|
||||
select {
|
||||
case s := <-stopSign:
|
||||
l.Infof("Signal %d received; exiting", s)
|
||||
slog.Info("Received signal; exiting", "signal", s)
|
||||
cmd.Process.Signal(sigTerm)
|
||||
err = <-exit
|
||||
stopped = true
|
||||
|
||||
case s := <-restartSign:
|
||||
l.Infof("Signal %d received; restarting", s)
|
||||
slog.Info("Received signal; restarting", "signal", s)
|
||||
cmd.Process.Signal(sigHup)
|
||||
err = <-exit
|
||||
|
||||
@@ -176,27 +177,28 @@ func monitorMain(options serveOptions) {
|
||||
os.Exit(svcutil.ExitSuccess.AsInt())
|
||||
}
|
||||
|
||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||
exiterr := &exec.ExitError{}
|
||||
if errors.As(err, &exiterr) {
|
||||
exitCode := exiterr.ExitCode()
|
||||
if stopped || options.NoRestart {
|
||||
if stopped || c.NoRestart {
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
if exitCode == svcutil.ExitUpgrade.AsInt() {
|
||||
// Restart the monitor process to release the .old
|
||||
// binary as part of the upgrade process.
|
||||
l.Infoln("Restarting monitor...")
|
||||
slog.Info("Restarting monitor...")
|
||||
if err = restartMonitor(binary, args); err != nil {
|
||||
l.Warnln("Restart:", err)
|
||||
slog.Error("Failed to restart monitor", slogutil.Error(err))
|
||||
}
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
if options.NoRestart {
|
||||
if c.NoRestart {
|
||||
os.Exit(svcutil.ExitError.AsInt())
|
||||
}
|
||||
|
||||
l.Infoln("Syncthing exited:", err)
|
||||
slog.Info("Syncthing exited", slogutil.Error(err))
|
||||
time.Sleep(restartPause)
|
||||
|
||||
if first {
|
||||
@@ -238,47 +240,29 @@ func copyStderr(stderr io.Reader, dst io.Writer) {
|
||||
return
|
||||
}
|
||||
|
||||
if panicFd == nil {
|
||||
dst.Write([]byte(line))
|
||||
dst.Write([]byte(line))
|
||||
|
||||
if strings.HasPrefix(line, "panic:") || strings.HasPrefix(line, "fatal error:") {
|
||||
panicFd, err = os.Create(locations.GetTimestamped(locations.PanicLog))
|
||||
if err != nil {
|
||||
l.Warnln("Create panic log:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
l.Warnf("Panic detected, writing to \"%s\"", panicFd.Name())
|
||||
if strings.Contains(line, "leveldb") && strings.Contains(line, "corrupt") {
|
||||
l.Warnln(`
|
||||
*********************************************************************************
|
||||
* Crash due to corrupt database. *
|
||||
* *
|
||||
* This crash usually occurs due to one of the following reasons: *
|
||||
* - Syncthing being stopped abruptly (killed/loss of power) *
|
||||
* - Bad hardware (memory/disk issues) *
|
||||
* - Software that affects disk writes (SSD caching software and similar) *
|
||||
* *
|
||||
* Please see the following URL for instructions on how to recover: *
|
||||
* https://docs.syncthing.net/users/faq.html#my-syncthing-database-is-corrupt *
|
||||
*********************************************************************************
|
||||
`)
|
||||
} else {
|
||||
l.Warnln("Please check for existing issues with similar panic message at https://github.com/syncthing/syncthing/issues/")
|
||||
l.Warnln("If no issue with similar panic message exists, please create a new issue with the panic log attached")
|
||||
}
|
||||
|
||||
stdoutMut.Lock()
|
||||
for _, line := range stdoutFirstLines {
|
||||
panicFd.WriteString(line)
|
||||
}
|
||||
panicFd.WriteString("...\n")
|
||||
for _, line := range stdoutLastLines {
|
||||
panicFd.WriteString(line)
|
||||
}
|
||||
stdoutMut.Unlock()
|
||||
if panicFd == nil && (strings.HasPrefix(line, "panic:") || strings.HasPrefix(line, "fatal error:")) {
|
||||
panicFd, err = os.Create(locations.GetTimestamped(locations.PanicLog))
|
||||
if err != nil {
|
||||
slog.Error("Failed to create panic log", slogutil.Error(err))
|
||||
continue
|
||||
}
|
||||
|
||||
slog.Error("Panic detected, writing to file", slogutil.FilePath(panicFd.Name()))
|
||||
slog.Info("Please check for existing issues with similar panic message at https://github.com/syncthing/syncthing/issues/")
|
||||
slog.Info("If no issue with similar panic message exists, please create a new issue with the panic log attached")
|
||||
|
||||
stdoutMut.Lock()
|
||||
for _, line := range stdoutFirstLines {
|
||||
panicFd.WriteString(line)
|
||||
}
|
||||
panicFd.WriteString("...\n")
|
||||
for _, line := range stdoutLastLines {
|
||||
panicFd.WriteString(line)
|
||||
}
|
||||
stdoutMut.Unlock()
|
||||
|
||||
panicFd.WriteString("Panic at " + time.Now().Format(time.RFC3339) + "\n")
|
||||
}
|
||||
|
||||
@@ -448,7 +432,6 @@ func newAutoclosedFile(name string, closeDelay, maxOpenTime time.Duration) (*aut
|
||||
name: name,
|
||||
closeDelay: closeDelay,
|
||||
maxOpenTime: maxOpenTime,
|
||||
mut: sync.NewMutex(),
|
||||
closed: make(chan struct{}),
|
||||
closeTimer: time.NewTimer(time.Minute),
|
||||
}
|
||||
@@ -556,7 +539,7 @@ func maybeReportPanics() {
|
||||
// Try to get a config to see if/where panics should be reported.
|
||||
cfg, err := loadOrDefaultConfig()
|
||||
if err != nil {
|
||||
l.Warnln("Couldn't load config; not reporting crash")
|
||||
slog.Error("Couldn't load config; not reporting crash")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -576,7 +559,7 @@ func maybeReportPanics() {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(panicUploadNoticeWait):
|
||||
l.Warnln("Uploading crash reports is taking a while, please wait...")
|
||||
slog.Warn("Uploading crash reports is taking a while, please wait")
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -16,7 +16,11 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/build"
|
||||
"github.com/syncthing/syncthing/lib/locations"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
func startPerfStats() {
|
||||
@@ -29,37 +33,55 @@ func savePerfStats(file string) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var prevUsage int64
|
||||
var prevTime int64
|
||||
var rusage syscall.Rusage
|
||||
var memstats runtime.MemStats
|
||||
var prevTime time.Time
|
||||
var curRus, prevRus syscall.Rusage
|
||||
var curMem, prevMem runtime.MemStats
|
||||
var prevIn, prevOut int64
|
||||
|
||||
t0 := time.Now()
|
||||
syscall.Getrusage(syscall.RUSAGE_SELF, &prevRus)
|
||||
runtime.ReadMemStats(&prevMem)
|
||||
|
||||
fmt.Fprintf(fd, "TIME_S\tCPU_S\tHEAP_KIB\tRSS_KIB\tNETIN_KBPS\tNETOUT_KBPS\tDBSIZE_KIB\n")
|
||||
|
||||
for t := range time.NewTicker(250 * time.Millisecond).C {
|
||||
if err := syscall.Getrusage(syscall.RUSAGE_SELF, &rusage); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
curTime := time.Now().UnixNano()
|
||||
timeDiff := curTime - prevTime
|
||||
curUsage := rusage.Utime.Nano() + rusage.Stime.Nano()
|
||||
usageDiff := curUsage - prevUsage
|
||||
cpuUsagePercent := 100 * float64(usageDiff) / float64(timeDiff)
|
||||
prevTime = curTime
|
||||
prevUsage = curUsage
|
||||
syscall.Getrusage(syscall.RUSAGE_SELF, &curRus)
|
||||
runtime.ReadMemStats(&curMem)
|
||||
in, out := protocol.TotalInOut()
|
||||
var inRate, outRate float64
|
||||
if timeDiff > 0 {
|
||||
inRate = float64(in-prevIn) / (float64(timeDiff) / 1e9) // bytes per second
|
||||
outRate = float64(out-prevOut) / (float64(timeDiff) / 1e9) // bytes per second
|
||||
timeDiff := t.Sub(prevTime)
|
||||
|
||||
rss := curRus.Maxrss
|
||||
if build.IsDarwin {
|
||||
rss /= 1024
|
||||
}
|
||||
|
||||
fmt.Fprintf(fd, "%.03f\t%f\t%d\t%d\t%.0f\t%.0f\t%d\n",
|
||||
t.Sub(t0).Seconds(),
|
||||
rate(cpusec(&prevRus), cpusec(&curRus), timeDiff, 1),
|
||||
(curMem.Sys-curMem.HeapReleased)/1024,
|
||||
rss,
|
||||
rate(prevIn, in, timeDiff, 1e3),
|
||||
rate(prevOut, out, timeDiff, 1e3),
|
||||
osutil.DirSize(locations.Get(locations.Database))/1024,
|
||||
)
|
||||
|
||||
prevTime = t
|
||||
prevRus = curRus
|
||||
prevMem = curMem
|
||||
prevIn, prevOut = in, out
|
||||
|
||||
runtime.ReadMemStats(&memstats)
|
||||
|
||||
startms := int(t.Sub(t0).Seconds() * 1000)
|
||||
|
||||
fmt.Fprintf(fd, "%d\t%f\t%d\t%d\t%.0f\t%.0f\n", startms, cpuUsagePercent, memstats.Alloc, memstats.Sys-memstats.HeapReleased, inRate, outRate)
|
||||
}
|
||||
}
|
||||
|
||||
func cpusec(r *syscall.Rusage) float64 {
|
||||
return float64(r.Utime.Nano()+r.Stime.Nano()) / float64(time.Second)
|
||||
}
|
||||
|
||||
type number interface {
|
||||
constraints.Float | constraints.Integer
|
||||
}
|
||||
|
||||
func rate[T number](prev, cur T, d time.Duration, div float64) float64 {
|
||||
diff := cur - prev
|
||||
rate := float64(diff) / d.Seconds() / div
|
||||
return rate
|
||||
}
|
||||
|
||||
@@ -32,3 +32,10 @@
|
||||
darwin: "20"
|
||||
linux: "3.2"
|
||||
windows: "10.0"
|
||||
|
||||
- runtime: go1.25
|
||||
requirements:
|
||||
# macOS 12 (Monterey) per https://tip.golang.org/doc/go1.25#darwin
|
||||
darwin: "21"
|
||||
linux: "3.2"
|
||||
windows: "10.0"
|
||||
|
||||
@@ -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=syncthing --browser-only
|
||||
Exec=syncthing browser
|
||||
Icon=syncthing
|
||||
Terminal=false
|
||||
Type=Application
|
||||
|
||||
@@ -7,7 +7,10 @@ StartLimitBurst=4
|
||||
|
||||
[Service]
|
||||
User=%i
|
||||
ExecStart=/usr/bin/syncthing serve --no-browser --no-restart --logflags=0
|
||||
Environment="STLOGFORMATTIMESTAMP="
|
||||
Environment="STLOGFORMATLEVELSTRING=false"
|
||||
Environment="STLOGFORMATLEVELSYSLOG=true"
|
||||
ExecStart=/usr/bin/syncthing serve --no-browser --no-restart
|
||||
Restart=on-failure
|
||||
RestartSec=1
|
||||
SuccessExitStatus=3 4
|
||||
|
||||
@@ -5,7 +5,10 @@ StartLimitIntervalSec=60
|
||||
StartLimitBurst=4
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/syncthing serve --no-browser --no-restart --logflags=0
|
||||
Environment="STLOGFORMATTIMESTAMP="
|
||||
Environment="STLOGFORMATLEVELSTRING=false"
|
||||
Environment="STLOGFORMATLEVELSYSLOG=true"
|
||||
ExecStart=/usr/bin/syncthing serve --no-browser --no-restart
|
||||
Restart=on-failure
|
||||
RestartSec=1
|
||||
SuccessExitStatus=3 4
|
||||
|
||||
@@ -18,4 +18,4 @@ env STNORESTART=yes
|
||||
respawn
|
||||
|
||||
# the syncthing command Upstart is to execute when it is started up
|
||||
exec $SYNCTHING_EXE -no-browser
|
||||
exec $SYNCTHING_EXE --no-browser
|
||||
|
||||
102
go.mod
102
go.mod
@@ -1,100 +1,120 @@
|
||||
module github.com/syncthing/syncthing
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f
|
||||
github.com/alecthomas/kong v1.8.1
|
||||
github.com/aws/aws-sdk-go v1.55.6
|
||||
github.com/AudriusButkevicius/recli v0.0.7
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2
|
||||
github.com/alecthomas/kong v1.12.1
|
||||
github.com/aws/aws-sdk-go v1.55.8
|
||||
github.com/calmh/incontainer v1.0.0
|
||||
github.com/calmh/xdr v1.2.0
|
||||
github.com/ccding/go-stun v0.1.5
|
||||
github.com/chmduquesne/rollinghash v4.0.0+incompatible
|
||||
github.com/coreos/go-semver v0.3.1
|
||||
github.com/d4l3k/messagediff v1.2.1
|
||||
github.com/getsentry/raven-go v0.2.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.10
|
||||
github.com/go-ldap/ldap/v3 v3.4.11
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/greatroar/blobloom v0.8.0
|
||||
github.com/gofrs/flock v0.12.1
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/jackpal/gateway v1.0.16
|
||||
github.com/jackpal/go-nat-pmp v1.0.2
|
||||
github.com/jmoiron/sqlx v1.4.0
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/maruel/panicparse/v2 v2.4.0
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2
|
||||
github.com/maruel/panicparse/v2 v2.5.0
|
||||
github.com/mattn/go-sqlite3 v1.14.31
|
||||
github.com/maxmind/geoipupdate/v6 v6.1.0
|
||||
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75
|
||||
github.com/oschwald/geoip2-golang v1.11.0
|
||||
github.com/oschwald/geoip2-golang v1.13.0
|
||||
github.com/pierrec/lz4/v4 v4.1.22
|
||||
github.com/prometheus/client_golang v1.21.0
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1
|
||||
github.com/quic-go/quic-go v0.50.0
|
||||
github.com/quic-go/quic-go v0.56.0
|
||||
github.com/rabbitmq/amqp091-go v1.10.0
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
|
||||
github.com/shirou/gopsutil/v4 v4.25.2
|
||||
github.com/syncthing/notify v0.0.0-20250207082249-f0fa8f99c2bc
|
||||
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9
|
||||
github.com/shirou/gopsutil/v4 v4.25.6 // https://github.com/shirou/gopsutil/issues/1898
|
||||
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
|
||||
github.com/thejerf/suture/v4 v4.0.6
|
||||
github.com/urfave/cli v1.22.16
|
||||
github.com/urfave/cli v1.22.17
|
||||
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
|
||||
github.com/willabides/kongplete v0.4.0
|
||||
github.com/wlynxg/anet v0.0.5
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
golang.org/x/crypto v0.35.0
|
||||
golang.org/x/net v0.35.0
|
||||
golang.org/x/sys v0.30.0
|
||||
golang.org/x/text v0.22.0
|
||||
golang.org/x/time v0.10.0
|
||||
golang.org/x/tools v0.30.0
|
||||
google.golang.org/protobuf v1.36.5
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
golang.org/x/crypto v0.44.0
|
||||
golang.org/x/exp v0.0.0-20250811191247-51f88131bc50
|
||||
golang.org/x/net v0.47.0
|
||||
golang.org/x/sys v0.38.0
|
||||
golang.org/x/text v0.31.0
|
||||
golang.org/x/time v0.12.0
|
||||
google.golang.org/protobuf v1.36.7
|
||||
modernc.org/sqlite v1.38.2
|
||||
sigs.k8s.io/yaml v1.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/ebitengine/purego v0.8.2 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/ebitengine/purego v0.8.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20241009165004-a3522334989c // indirect
|
||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/nxadm/tail v1.4.11 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.20.2 // indirect
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/posener/complete v1.2.3 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.65.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.9.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||
golang.org/x/mod v0.23.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.66.3 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
||||
// https://github.com/gobwas/glob/pull/55
|
||||
replace github.com/gobwas/glob v0.2.3 => github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b
|
||||
|
||||
// https://github.com/mattn/go-sqlite3/pull/1338
|
||||
replace github.com/mattn/go-sqlite3 v1.14.31 => github.com/calmh/go-sqlite3 v1.14.32-0.20250812195006-80712c77b76a
|
||||
|
||||
tool (
|
||||
github.com/calmh/xdr/cmd/genxdr
|
||||
github.com/maxbrunsfeld/counterfeiter/v6
|
||||
golang.org/x/tools/cmd/goimports
|
||||
)
|
||||
|
||||
290
go.sum
290
go.sum
@@ -1,22 +1,38 @@
|
||||
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f h1:GmH5lT+moM7PbAJFBq57nH9WJ+wRnBXr/tyaYWbSAx8=
|
||||
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/AudriusButkevicius/recli v0.0.7 h1:9zjbYlTupi+W5SJXm2cR2sV2mJAIg1sIfDcsW7hrkPM=
|
||||
github.com/AudriusButkevicius/recli v0.0.7/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 h1:Wc1ml6QlJs2BHQ/9Bqu1jiyggbsSjramq2oUmp5WeIo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2 h1:FwladfywkNirM+FZYLBR2kBz5C8Tg0fw5w5Y7meRXWI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2/go.mod h1:vv5Ad0RrIoT1lJFdWBZwt4mB1+j+V8DUroixmKDTCdk=
|
||||
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.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/kong v1.8.1 h1:6aamvWBE/REnR/BCq10EcozmcpUPc5aGI1lPAWdB0EE=
|
||||
github.com/alecthomas/kong v1.8.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
|
||||
github.com/alecthomas/kong v1.12.1 h1:iq6aMJDcFYP9uFrLdsiZQ2ZMmcshduyGv4Pek0MQPW0=
|
||||
github.com/alecthomas/kong v1.12.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
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/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
|
||||
github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||
github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=
|
||||
github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=
|
||||
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=
|
||||
github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b/go.mod h1:91K7jfEsgJSyfSrX+gmrRfZMtntx6JsHolWubGXDopg=
|
||||
github.com/calmh/go-sqlite3 v1.14.32-0.20250812195006-80712c77b76a h1:lTe5qJApKNO+zZCa3/P/7UxM4c58CXWOegv9eODPWvs=
|
||||
github.com/calmh/go-sqlite3 v1.14.32-0.20250812195006-80712c77b76a/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/calmh/incontainer v1.0.0 h1:g2cTUtZuFGmMGX8GoykPkN1Judj2uw8/3/aEtq4Z/rg=
|
||||
github.com/calmh/incontainer v1.0.0/go.mod h1:eOhqnw15c9X+4RNBe0W3HlUZFfX16O0EDsCOInTndHY=
|
||||
github.com/calmh/xdr v1.2.0 h1:GaGSNH4ZDw9kNdYqle6+RcAENiaQ8/611Ok+jQbBEeU=
|
||||
@@ -29,20 +45,22 @@ github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chmduquesne/rollinghash v4.0.0+incompatible h1:hnREQO+DXjqIw3rUTzWN7/+Dpw+N5Um8zpKV0JOEgbo=
|
||||
github.com/chmduquesne/rollinghash v4.0.0+incompatible/go.mod h1:Uc2I36RRfTAf7Dge82bi3RU0OQUmXT9iweIcPqvr8A0=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
||||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U=
|
||||
github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
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/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
@@ -51,20 +69,20 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
||||
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.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-ldap/ldap/v3 v3.4.10 h1:ot/iwPOhfpNVgB1o+AVXljizWZ9JTp7YF5oeyONmcJU=
|
||||
github.com/go-ldap/ldap/v3 v3.4.10/go.mod h1:JXh4Uxgi40P6E9rdsYqpUtbW46D9UTjJ9QSwGRznplY=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
|
||||
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
|
||||
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=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
@@ -80,26 +98,20 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20241009165004-a3522334989c h1:NDovD0SMpBYXlE1zJmS1q55vWB/fUQBcPAqAboZSccA=
|
||||
github.com/google/pprof v0.0.0-20241009165004-a3522334989c/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
|
||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/greatroar/blobloom v0.8.0 h1:I9RlEkfqK9/6f1v9mFmDYegDQ/x0mISCpiNpAm23Pt4=
|
||||
github.com/greatroar/blobloom v0.8.0/go.mod h1:mjMJ1hh1wjGVfr93QIHJ6FfDNVrA0IELv8OvMHJxHKs=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
@@ -128,30 +140,39 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
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-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/maruel/panicparse/v2 v2.4.0 h1:yQKMIbQ0DKfinzVkTkcUzQyQ60UCiNnYfR7PWwTs2VI=
|
||||
github.com/maruel/panicparse/v2 v2.4.0/go.mod h1:nOY2OKe8csO3F3SA5+hsxot05JLgukrF54B9x88fVp4=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2 h1:yVCLo4+ACVroOEr4iFU1iH46Ldlzz2rTuu18Ra7M8sU=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2/go.mod h1:VzB2VoMh1Y32/QqDfg9ZJYHj99oM4LiGtqPZydTiQSQ=
|
||||
github.com/maruel/panicparse/v2 v2.5.0 h1:yCtuS0FWjfd0RTYMXGpDvWcb0kINm8xJGu18/xMUh00=
|
||||
github.com/maruel/panicparse/v2 v2.5.0/go.mod h1:DA2fDiBk63bKfBf4CVZP9gb4fuvzdPbLDsSI873hweQ=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.0 h1:aOeI7xAOVdK+R6xbVsZuU9HmCZYmQVmZgPf9xJUd2Sg=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.0/go.mod h1:0hZWbtfeCYUQeAQdPLUzETiBhUSns7O6LDj9vH88xKA=
|
||||
github.com/maxmind/geoipupdate/v6 v6.1.0 h1:sdtTHzzQNJlXF5+fd/EoPTucRHyMonYt/Cok8xzzfqA=
|
||||
github.com/maxmind/geoipupdate/v6 v6.1.0/go.mod h1:cZYCDzfMzTY4v6dKRdV7KTB6SStxtn3yFkiJ1btTGGc=
|
||||
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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
|
||||
@@ -162,20 +183,20 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv
|
||||
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.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
|
||||
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
|
||||
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.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
|
||||
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w=
|
||||
github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
|
||||
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
|
||||
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
|
||||
github.com/oschwald/geoip2-golang v1.13.0 h1:Q44/Ldc703pasJeP5V9+aFSZFmBN7DKHbNsSFzQATJI=
|
||||
github.com/oschwald/geoip2-golang v1.13.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
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=
|
||||
@@ -187,32 +208,34 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA=
|
||||
github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
|
||||
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||
github.com/quic-go/quic-go v0.50.0 h1:3H/ld1pa3CYhkcc20TPIyG1bNsdhn9qZBGN3b9/UyUo=
|
||||
github.com/quic-go/quic-go v0.50.0/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
|
||||
github.com/quic-go/quic-go v0.56.0 h1:q/TW+OLismmXAehgFLczhCDTYB3bFmua4D9lsNBWxvY=
|
||||
github.com/quic-go/quic-go v0.56.0/go.mod h1:9gx5KsFQtw2oZ6GZTyh+7YEvOxWCL9WZAepnHxgAo6c=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
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/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
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/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
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/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
|
||||
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
|
||||
github.com/shirou/gopsutil/v4 v4.25.2 h1:NMscG3l2CqtWFS86kj3vP7soOczqrQYIEhO/pMvvQkk=
|
||||
github.com/shirou/gopsutil/v4 v4.25.2/go.mod h1:34gBYJzyqCDT11b6bMHP0XCvWeU3J61XRT7a2EmCRTA=
|
||||
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
|
||||
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=
|
||||
@@ -223,90 +246,66 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/syncthing/notify v0.0.0-20250207082249-f0fa8f99c2bc h1:xc3UfSFlH/X5hRw3h21RF6WXnRUYKmGRx06FEaVxfkM=
|
||||
github.com/syncthing/notify v0.0.0-20250207082249-f0fa8f99c2bc/go.mod h1:J0q59IWjLtpRIJulohwqEZvjzwOfTEPp8SVhDJl+y0Y=
|
||||
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465 h1:yhxdTGmFkAM2TFA65c3NgGwpnIkUM8oVqPX2e9S7IVg=
|
||||
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465/go.mod h1:J0q59IWjLtpRIJulohwqEZvjzwOfTEPp8SVhDJl+y0Y=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
|
||||
github.com/thejerf/suture/v4 v4.0.6 h1:QsuCEsCqb03xF9tPAsWAj8QOAJBgQI1c0VqJNaingg8=
|
||||
github.com/thejerf/suture/v4 v4.0.6/go.mod h1:gu9Y4dXNUWFrByqRt30Rm9/UZ0wzRSt9AJS6xu/ZGxU=
|
||||
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
|
||||
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
|
||||
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
|
||||
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
|
||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ=
|
||||
github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po=
|
||||
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
|
||||
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
|
||||
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.4.0 h1:eivXxkp5ud5+4+NVN9e4goxC5mSh3n1RHov+gsblM2g=
|
||||
github.com/willabides/kongplete v0.4.0/go.mod h1:0P0jtWD9aTsqPSUAl4de35DLghrr57XcayPyvqSi2X8=
|
||||
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||
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.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
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.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
||||
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
||||
golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 h1:3yiSh9fhy5/RhCSntf4Sy0Tnx50DmMpQ4MQdKKk4yg4=
|
||||
golang.org/x/exp v0.0.0-20250811191247-51f88131bc50/go.mod h1:rT6SFzZ7oxADUDx58pcaKFTcZ+inxAa9fTrYx/uVYwg=
|
||||
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.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
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=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
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.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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-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.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
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=
|
||||
@@ -326,49 +325,28 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 h1:E2/AqCUMZGgd73TQkxUMcMla25GB9i/5HOdLr+uH7Vo=
|
||||
golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ=
|
||||
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=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
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/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
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-20201224043029-2b0845dc783e/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.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
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=
|
||||
@@ -382,8 +360,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.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
|
||||
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -399,5 +377,31 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
|
||||
modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
||||
modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM=
|
||||
modernc.org/fileutil v1.3.8/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ=
|
||||
modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek=
|
||||
modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"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",
|
||||
"API Key": "مفتاح واجهة برمجة التطبيقات \"API\"",
|
||||
"About": "حول",
|
||||
"Action": "إجراء",
|
||||
"Actions": "الإجراءات",
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "الشبكات المسموح بها",
|
||||
"Alphabetic": "أبجدية",
|
||||
"Altered by ignoring deletes.": "تغير بتجاهل عمليات الحذف.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "مفعل دائمًا عندما يكون نوع المجلد هو \"{{foldertype}}\".",
|
||||
"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?": "هل تريد الانتقال الى التصميم الجديد لتقرير الاستخدام المجهول ؟",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "جسم:",
|
||||
"Bugs": "أخطاء برمجية",
|
||||
"Cancel": "إلغاء",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "لا يمكن تفعيله عندما يكون نوع المجلد هو \"{{foldertype}}\".",
|
||||
"Changelog": "سجل التغيير",
|
||||
"Clean out after": "نظف بعد",
|
||||
"Cleaning Versions": "إصدارات نظيفة",
|
||||
@@ -154,7 +156,7 @@
|
||||
"Failed Items": "العناصر الفاشلة",
|
||||
"Failed to load file versions.": "لم يُتَوَصَّل لنسخة الملف.",
|
||||
"Failed to load ignore patterns.": "فشل التَّوَصُّل إلى مُرَشِّحات التجاهل.",
|
||||
"Failed to setup, retrying": "فشل الإعداد، تجري المحاولة مرة أخرى",
|
||||
"Failed to set up, retrying": "فشل الإعداد، تجري المحاولة مرة أخرى",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "يُتوقع فشل الاتصال بخوادم IPv6، إذا لم يكن IPv6 متاحا.",
|
||||
"File Pull Order": "ترتيب استيراد الملفات",
|
||||
"File Versioning": "إصدارات الملف",
|
||||
@@ -368,7 +370,7 @@
|
||||
"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": "إغلاق",
|
||||
"Shut Down": "إغلاق",
|
||||
"Shutdown Complete": "أُغلِق",
|
||||
"Simple": "بسيط",
|
||||
"Simple File Versioning": "التقسيم البسيط لإصدارات الملفات",
|
||||
|
||||
2
gui/default/assets/lang/lang-az.json
Normal file
2
gui/default/assets/lang/lang-az.json
Normal file
@@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Дазволеныя Сеткі",
|
||||
"Alphabetic": "Па Алфавіту",
|
||||
"Altered by ignoring deletes.": "Зменена з-за ігнаравання выдаленняў.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Заўсёды ўключана для папак з тыпам \"{{foldertype}}\".",
|
||||
"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?": "Фармат ананімнай спрадвыздачы аб выкарыстанні быў зменены. Ці жадаеце вы выкарыстоўваць новы фармат?",
|
||||
@@ -152,7 +153,7 @@
|
||||
"Show ID": "Паказаць ID",
|
||||
"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 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.": "Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.",
|
||||
"Shutdown": "Выключыць",
|
||||
"Shut Down": "Выключыць",
|
||||
"Shutdown Complete": "Выключэньне завершанае",
|
||||
"Simple File Versioning": "Простае захоўваньне вэрсій",
|
||||
"Single level wildcard (matches within a directory only)": "Single level wildcard (matches within a directory only)",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Разрешени мрежи",
|
||||
"Alphabetic": "Азбучен ред",
|
||||
"Altered by ignoring deletes.": "Промяна чрез пренебрегване на премахваните елементи.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Винаги включено, когато вида на папката е „{{foldertype}}“.",
|
||||
"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?": "Форматът на данните за анонимно отчитане на употреба е променен. Желаете ли да използвате него вместо стария?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Съдържание:",
|
||||
"Bugs": "Дефекти",
|
||||
"Cancel": "Отказ",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Не мож да бъде включено, ако вида на папката е „{{foldertype}}“.",
|
||||
"Changelog": "Дневник на промените",
|
||||
"Clean out after": "Почистване след",
|
||||
"Cleaning Versions": "Почистване на версии",
|
||||
@@ -80,6 +82,7 @@
|
||||
"Custom Range": "В периода",
|
||||
"Danger!": "Опасност!",
|
||||
"Database Location": "Местоположение на хранилището",
|
||||
"Debug": "Отстраняване на дефекти",
|
||||
"Debugging Facilities": "Отстраняване на дефекти",
|
||||
"Default": "По подразбиране",
|
||||
"Default Configuration": "Настройки по подразбиране",
|
||||
@@ -154,7 +157,7 @@
|
||||
"Failed Items": "Елементи с грешка",
|
||||
"Failed to load file versions.": "Грешка при зареждане на версии.",
|
||||
"Failed to load ignore patterns.": "Грешка при зареждане на шаблони за пренебрегване.",
|
||||
"Failed to setup, retrying": "Грешка при настройване, извършва се повторен опит",
|
||||
"Failed to set up, retrying": "Грешка при настройване, извършва се повторен опит",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Неуспешна връзка към сървъри по IPv6 може да се очаква ако няма свързаност по IPv6.",
|
||||
"File Pull Order": "Ред на изтегляне",
|
||||
"File Versioning": "Версии на файловете",
|
||||
@@ -174,7 +177,7 @@
|
||||
"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.": "Грешка при започване на наблюдението за промени на следните папки. Всяка минута ще бъде извършван нов опит, така че грешката скоро може да изчезне. Ако все пак не изчезне, отстранете нейната първопричина или потърсете помощ ако не съумявате.",
|
||||
"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.": "Грешка при започване на наблюдението за промени на следните папки. Всяка минута ще бъде извършван нов опит, така че грешката скоро може да изчезне. Ако все пак не изчезне, отстранете първопричината или ако не съумявате потърсете помощ.",
|
||||
"Forever": "Завинаги",
|
||||
"Full Rescan Interval (s)": "Интервал на пълно обхождане (секунди)",
|
||||
"GUI": "Интерфейс",
|
||||
@@ -208,6 +211,7 @@
|
||||
"Incoming Rate Limit (KiB/s)": "Ограничение при изтегляне (КиБ/с)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Неправилни настройки могат да повредят файлове и да попречат на синхронизирането.",
|
||||
"Incorrect user name or password.": "Грешно потребителско име или парола.",
|
||||
"Info": "Сведения",
|
||||
"Internally used paths:": "Вътрешно използвани пътища:",
|
||||
"Introduced By": "Предложено от",
|
||||
"Introducer": "Поръчител",
|
||||
@@ -225,6 +229,7 @@
|
||||
"Learn more": "Научете повече",
|
||||
"Learn more at {%url%}": "Научете повече на {{url}}",
|
||||
"Limit": "Ограничение",
|
||||
"Limit Bandwidth in LAN": "Огранич. на скоростта в местната мрежа",
|
||||
"Listener Failures": "Грешки при очакване на връзка",
|
||||
"Listener Status": "Очакване на връзка",
|
||||
"Listeners": "Очакване на връзка",
|
||||
@@ -368,7 +373,7 @@
|
||||
"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": "Изключване",
|
||||
"Shut Down": "Изключване",
|
||||
"Shutdown Complete": "Спирането завършено",
|
||||
"Simple": "Обикновени",
|
||||
"Simple File Versioning": "Обикновени версии",
|
||||
@@ -526,7 +531,7 @@
|
||||
"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 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": "премахнато",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Xarxes permeses",
|
||||
"Alphabetic": "Alfabètic",
|
||||
"Altered by ignoring deletes.": "S'ha alterat ignorant les supressions.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Sempre activat quan el tipus de carpeta és \"{{foldertype}}\".",
|
||||
"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.": "Una ordre externa gestiona la versió. Ha d'eliminar el fitxer de la carpeta compartida. Si el camí a l'aplicació conté espais, s'ha de citar.",
|
||||
"Anonymous Usage Reporting": "Informe anònim d'ús",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "El format de l'informe d'ús anònim ha canviat. Voleu canviar a aquest nou format?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Cos de text:",
|
||||
"Bugs": "Errors (Bugs)",
|
||||
"Cancel": "Cancel·la",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "No es pot habilitar quan el tipus de carpeta és \"{{foldertype}}\".",
|
||||
"Changelog": "Historial de canvis",
|
||||
"Clean out after": "Netejar després",
|
||||
"Cleaning Versions": "Netejant versions",
|
||||
@@ -154,7 +156,7 @@
|
||||
"Failed Items": "Elements fallats",
|
||||
"Failed to load file versions.": "No s'han pogut carregar les versions dels fitxers.",
|
||||
"Failed to load ignore patterns.": "No s'han pogut carregar els patrons ignorats.",
|
||||
"Failed to setup, retrying": "No s'ha pogut configurar, s'està tornant a provar",
|
||||
"Failed to set up, retrying": "No s'ha pogut configurar, s'està tornant a provar",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "S'espera que no es pugui connectar als servidors IPv6 si no hi ha connectivitat IPv6.",
|
||||
"File Pull Order": "Ordre d'agafar fitxers",
|
||||
"File Versioning": "Versionat de Fitxers",
|
||||
@@ -368,7 +370,7 @@
|
||||
"Show diff with previous version": "Mostra la diferència amb la versió anterior",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Mostrat en comptes del ID del Node en l'estat del cluster. Serà advertit als altres dispositius com un nom opcional per defecte.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Mostrat en comptes del ID del Node en l'estat del cluster. S'actualitzarà al nom del dispositiu si es deixa buit.",
|
||||
"Shutdown": "Apaga",
|
||||
"Shut Down": "Apaga",
|
||||
"Shutdown Complete": "Apagat complet",
|
||||
"Simple": "Simple",
|
||||
"Simple File Versioning": "Versionat de Fitxers Senzill",
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
"Failed Items": "Objectes fallits",
|
||||
"Failed to load file versions.": "No s'han pogut carregar les versions dels fitxers.",
|
||||
"Failed to load ignore patterns.": "No s'han pogut carregar els patrons ignorats.",
|
||||
"Failed to setup, retrying": "Errada en la configuració, reintentant",
|
||||
"Failed to set up, retrying": "Errada en la configuració, reintentant",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "És possible que es produïsca una fallada al connectar als servidors IPv6 si no hi ha connectivitat IPv6.",
|
||||
"File Pull Order": "Ordre de fitxers del pull",
|
||||
"File Versioning": "Versionat de fitxer",
|
||||
@@ -353,7 +353,7 @@
|
||||
"Show diff with previous version": "Mostrar les diferències amb la versió prèvia",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Mostrat en lloc de l'ID del dispositiu en l'estat del grup (cluster). S'anunciarà als altres dispositius com el nom opcional per defecte.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Mostrat en lloc de l'ID del dispositiu en l'estat del grup (cluster). S'actualitzarà al nom que el dispositiu anuncia si es deixa buit.",
|
||||
"Shutdown": "Apagar",
|
||||
"Shut Down": "Apagar",
|
||||
"Shutdown Complete": "Apagar completament",
|
||||
"Simple": "Senzill",
|
||||
"Simple File Versioning": "Versionat de fitxers senzill",
|
||||
|
||||
2
gui/default/assets/lang/lang-ckb.json
Normal file
2
gui/default/assets/lang/lang-ckb.json
Normal file
@@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
||||
@@ -154,7 +154,7 @@
|
||||
"Failed Items": "Nezdařené položky",
|
||||
"Failed to load file versions.": "Nepodařilo se nahrát verze souboru.",
|
||||
"Failed to load ignore patterns.": "Načtení vzorů ignorovaného se nezdařilo.",
|
||||
"Failed to setup, retrying": "Nastavování se nezdařilo, zkouší se znovu",
|
||||
"Failed to set up, retrying": "Nastavování se nezdařilo, zkouší se znovu",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Je v pořádku, když se připojení k IPv6 serverům nezdaří, pokud není k dispozici IPv6 konektivita.",
|
||||
"File Pull Order": "Pořadí stahování souborů",
|
||||
"File Versioning": "Správa verzí souborů",
|
||||
@@ -360,7 +360,7 @@
|
||||
"Show diff with previous version": "Ukázat rozdíl oproti předchozí verzi",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Zobrazeno místo identifikátoru zařízení na náhledu stavu clusteru. Bude odesíláno ostatním zařízením jako výchozí název zařízení.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Zobrazeno místo identifikátoru zařízení na náhledu stavu clusteru. Pokud nebude vyplněno, bude nastaveno na název, který zařízení odesílá.",
|
||||
"Shutdown": "Vypnout",
|
||||
"Shut Down": "Vypnout",
|
||||
"Shutdown Complete": "Vypnutí dokončeno",
|
||||
"Simple": "Jednoduché",
|
||||
"Simple File Versioning": "Jednoduchá správa verzí souborů",
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
"Failed Items": "Mislykkede filer",
|
||||
"Failed to load file versions.": "Fil versioner kunne ikke indlæses.",
|
||||
"Failed to load ignore patterns.": "Ignorerings-mønstre kunne ikke indlæses.",
|
||||
"Failed to setup, retrying": "Opsætning mislykkedes; prøver igen",
|
||||
"Failed to set up, retrying": "Opsætning mislykkedes; prøver igen",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Fejl i forbindelse med opkobling til IPv6-servere skal forventes, hvis der ikke er IPv6-forbindelse.",
|
||||
"File Pull Order": "Hentningsrækkefølge for filer",
|
||||
"File Versioning": "Filversionering",
|
||||
@@ -368,7 +368,7 @@
|
||||
"Show diff with previous version": "Vis forskelle fra tidligere version",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Vises i stedet for enheds-ID i klyngestatus. Vil blive sendt til andre enheder som valgfrit standardnavn.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Vises i stedet for enheds-ID i klyngestatus. Vil blive opdateret til det navn, som enheden sender, hvis det ikke er udfyldt.",
|
||||
"Shutdown": "Luk ned",
|
||||
"Shut Down": "Luk ned",
|
||||
"Shutdown Complete": "Nedlukning fuldført",
|
||||
"Simple": "Enkel",
|
||||
"Simple File Versioning": "Simpel filversionering",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Erlaubte Netzwerke",
|
||||
"Alphabetic": "Alphabetisch",
|
||||
"Altered by ignoring deletes.": "Weicht ab, weil Löschungen ignoriert werden.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Immer eingeschaltet, wenn der Ordnertyp „{{foldertype}}“ ist.",
|
||||
"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?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Nachrichtentext:",
|
||||
"Bugs": "Fehler",
|
||||
"Cancel": "Abbrechen",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Kann nicht aktiviert werden, wenn der Ordnertyp „{{foldertype}}“ ist.",
|
||||
"Changelog": "Änderungsprotokoll",
|
||||
"Clean out after": "Löschen nach",
|
||||
"Cleaning Versions": "Versionen bereinigen",
|
||||
@@ -59,7 +61,7 @@
|
||||
"Click to see full identification string and QR code.": "Klicken, um die vollständige Kennung und den QR-Code anzuzeigen.",
|
||||
"Close": "Schließen",
|
||||
"Command": "Befehl",
|
||||
"Comment, when used at the start of a line": "Kommentar, wenn am Anfang der Zeile verwendet.",
|
||||
"Comment, when used at the start of a line": "Kommentar, wenn am Anfang der Zeile verwendet",
|
||||
"Compression": "Komprimierung",
|
||||
"Configuration Directory": "Konfigurationsverzeichnis",
|
||||
"Configuration File": "Konfigurationsdatei",
|
||||
@@ -80,6 +82,7 @@
|
||||
"Custom Range": "Eigener Zeitraum",
|
||||
"Danger!": "Achtung!",
|
||||
"Database Location": "Datenbank-Speicherort",
|
||||
"Debug": "Debug",
|
||||
"Debugging Facilities": "Debugging-Möglichkeiten",
|
||||
"Default": "Vorgabe",
|
||||
"Default Configuration": "Vorgabekonfiguration",
|
||||
@@ -102,7 +105,7 @@
|
||||
"Device Status": "Gerätestatus",
|
||||
"Device is untrusted, enter encryption password": "Gerät wird nicht vertraut, Verschlüsselungspasswort eingeben",
|
||||
"Device rate limits": "Datenratenbegrenzungen fürs Gerät",
|
||||
"Device that last modified the item": "Gerät, das das Element zuletzt geändert hat",
|
||||
"Device that last modified the item": "Gerät, welches das Element zuletzt geändert hat",
|
||||
"Devices": "Geräte",
|
||||
"Disable Crash Reporting": "Absturzmeldung deaktivieren",
|
||||
"Disabled": "Deaktiviert",
|
||||
@@ -154,7 +157,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 Einrichten, erneuter Versuch",
|
||||
"Failed to set up, 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",
|
||||
@@ -181,7 +184,7 @@
|
||||
"GUI / API HTTPS Certificate": "GUI / API HTTPS-Zertifikat",
|
||||
"GUI Authentication Password": "Passwort für Zugang zur Benutzeroberfläche",
|
||||
"GUI Authentication User": "Benutzername für Zugang zur Benutzeroberfläche",
|
||||
"GUI Authentication: Set User and Password": "Authentifizierung für die Benutzeroberfläche: Geben Sie Benutzer und Passwort ein.",
|
||||
"GUI Authentication: Set User and Password": "Authentifizierung für die Benutzeroberfläche: Geben Sie Benutzer und Passwort ein",
|
||||
"GUI Listen Address": "Adresse der Benutzeroberfläche",
|
||||
"GUI Override Directory": "GUI-Ersatz-Verzeichnis",
|
||||
"GUI Theme": "GUI-Design",
|
||||
@@ -208,6 +211,7 @@
|
||||
"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.",
|
||||
"Info": "Info",
|
||||
"Internally used paths:": "Intern verwendete Pfade:",
|
||||
"Introduced By": "Verteilt von",
|
||||
"Introducer": "Verteilergerät",
|
||||
@@ -225,6 +229,7 @@
|
||||
"Learn more": "Mehr erfahren",
|
||||
"Learn more at {%url%}": "Erfahren Sie mehr unter {{url}}",
|
||||
"Limit": "Limit",
|
||||
"Limit Bandwidth in LAN": "Bandbreite im LAN begrenzen",
|
||||
"Listener Failures": "Fehler bei Listener",
|
||||
"Listener Status": "Status der Listener",
|
||||
"Listeners": "Zuhörer",
|
||||
@@ -368,7 +373,7 @@
|
||||
"Show diff with previous version": "Unterschied zur vorherigen Version anzeigen",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Wird anstelle der Gerätekennung im Verbundstatus angezeigt. Wird anderen Geräten als optionaler Standardname bekannt gegeben.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Wird anstelle der Gerätekennung im Verbundstatus angezeigt. Wird auf den Namen aktualisiert, den das Gerät anzeigt, wenn er leer bleibt.",
|
||||
"Shutdown": "Herunterfahren",
|
||||
"Shut Down": "Herunterfahren",
|
||||
"Shutdown Complete": "Vollständig heruntergefahren",
|
||||
"Simple": "Einfach",
|
||||
"Simple File Versioning": "Einfache Dateiversionierung",
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
"Failed Items": "Αρχεία που απέτυχαν",
|
||||
"Failed to load file versions.": "Η φόρτωση των εκδόσεων αρχείων απέτυχε.",
|
||||
"Failed to load ignore patterns.": "Αποτυχία φόρτωσης μοτίβων παράβλεψης.",
|
||||
"Failed to setup, retrying": "Αποτυχία ενεργοποίησης, γίνεται νέα προσπάθεια",
|
||||
"Failed to set up, retrying": "Αποτυχία ενεργοποίησης, γίνεται νέα προσπάθεια",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Είναι φυσιολογική η αποτυχία σύνδεσης σε εξυπηρετητές IPv6 όταν δεν υπάρχει συνδεσιμότητα IPv6.",
|
||||
"File Pull Order": "Σειρά με την οποία θα κατεβαίνουν τα αρχεία",
|
||||
"File Versioning": "Τήρηση εκδόσεων αρχείων",
|
||||
@@ -368,7 +368,7 @@
|
||||
"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": "Απενεργοποίηση",
|
||||
"Shut Down": "Απενεργοποίηση",
|
||||
"Shutdown Complete": "Πλήρης απενεργοποίηση",
|
||||
"Simple": "Απλό",
|
||||
"Simple File Versioning": "Απλή τήρηση εκδόσεων",
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
"Failed Items": "Failed Items",
|
||||
"Failed to load file versions.": "Failed to load file versions.",
|
||||
"Failed to load ignore patterns.": "Failed to load ignore patterns.",
|
||||
"Failed to setup, retrying": "Failed to setup, retrying",
|
||||
"Failed to set up, retrying": "Failed to set up, retrying",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.",
|
||||
"File Pull Order": "File Pull Order",
|
||||
"File Versioning": "File Versioning",
|
||||
@@ -353,7 +353,7 @@
|
||||
"Show diff with previous version": "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 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.": "Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.",
|
||||
"Shutdown": "Shutdown",
|
||||
"Shut Down": "Shut Down",
|
||||
"Shutdown Complete": "Shutdown Complete",
|
||||
"Simple": "Simple",
|
||||
"Simple File Versioning": "Simple File Versioning",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Allowed Networks",
|
||||
"Alphabetic": "Alphabetic",
|
||||
"Altered by ignoring deletes.": "Altered by ignoring deletes.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Always turned on when the folder type is \"{{foldertype}}\".",
|
||||
"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.": "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 Reporting",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Anonymous usage report format has changed. Would you like to move to the new format?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Body:",
|
||||
"Bugs": "Bugs",
|
||||
"Cancel": "Cancel",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Cannot be enabled when the folder type is \"{{foldertype}}\".",
|
||||
"Changelog": "Changelog",
|
||||
"Clean out after": "Clean out after",
|
||||
"Cleaning Versions": "Cleaning Versions",
|
||||
@@ -80,6 +82,7 @@
|
||||
"Custom Range": "Custom Range",
|
||||
"Danger!": "Danger!",
|
||||
"Database Location": "Database Location",
|
||||
"Debug": "Debug",
|
||||
"Debugging Facilities": "Debugging Facilities",
|
||||
"Default": "Default",
|
||||
"Default Configuration": "Default Configuration",
|
||||
@@ -154,7 +157,7 @@
|
||||
"Failed Items": "Failed Items",
|
||||
"Failed to load file versions.": "Failed to load file versions.",
|
||||
"Failed to load ignore patterns.": "Failed to load ignore patterns.",
|
||||
"Failed to setup, retrying": "Failed to setup, retrying",
|
||||
"Failed to set up, retrying": "Failed to set up, retrying",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.",
|
||||
"File Pull Order": "File Pull Order",
|
||||
"File Versioning": "File Versioning",
|
||||
@@ -208,6 +211,7 @@
|
||||
"Incoming Rate Limit (KiB/s)": "Incoming Rate Limit (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.": "Incorrect username or password.",
|
||||
"Info": "Info",
|
||||
"Internally used paths:": "Internally used paths:",
|
||||
"Introduced By": "Introduced By",
|
||||
"Introducer": "Introducer",
|
||||
@@ -225,6 +229,7 @@
|
||||
"Learn more": "Learn more",
|
||||
"Learn more at {%url%}": "Learn more at {{url}}",
|
||||
"Limit": "Limit",
|
||||
"Limit Bandwidth in LAN": "Limit Bandwidth in LAN",
|
||||
"Listener Failures": "Listener Failures",
|
||||
"Listener Status": "Listener Status",
|
||||
"Listeners": "Listeners",
|
||||
@@ -368,7 +373,7 @@
|
||||
"Show diff with previous version": "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 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.": "Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.",
|
||||
"Shutdown": "Shutdown",
|
||||
"Shut Down": "Shut Down",
|
||||
"Shutdown Complete": "Shutdown Complete",
|
||||
"Simple": "Simple",
|
||||
"Simple File Versioning": "Simple File Versioning",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Allowed Networks",
|
||||
"Alphabetic": "Alphabetic",
|
||||
"Altered by ignoring deletes.": "Altered by ignoring deletes.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Always turned on when the folder type is \"{{foldertype}}\".",
|
||||
"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.": "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 Reporting",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Anonymous usage report format has changed. Would you like to move to the new format?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Body:",
|
||||
"Bugs": "Bugs",
|
||||
"Cancel": "Cancel",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Cannot be enabled when the folder type is \"{{foldertype}}\".",
|
||||
"Changelog": "Changelog",
|
||||
"Clean out after": "Clean out after",
|
||||
"Cleaning Versions": "Cleaning Versions",
|
||||
@@ -80,6 +82,7 @@
|
||||
"Custom Range": "Custom Range",
|
||||
"Danger!": "Danger!",
|
||||
"Database Location": "Database Location",
|
||||
"Debug": "Debug",
|
||||
"Debugging Facilities": "Debugging Facilities",
|
||||
"Default": "Default",
|
||||
"Default Configuration": "Default Configuration",
|
||||
@@ -154,7 +157,7 @@
|
||||
"Failed Items": "Failed Items",
|
||||
"Failed to load file versions.": "Failed to load file versions.",
|
||||
"Failed to load ignore patterns.": "Failed to load ignore patterns.",
|
||||
"Failed to setup, retrying": "Failed to setup, retrying",
|
||||
"Failed to set up, retrying": "Failed to set up, retrying",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.",
|
||||
"File Pull Order": "File Pull Order",
|
||||
"File Versioning": "File Versioning",
|
||||
@@ -208,6 +211,7 @@
|
||||
"Incoming Rate Limit (KiB/s)": "Incoming Rate Limit (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.": "Incorrect user name or password.",
|
||||
"Info": "Info",
|
||||
"Internally used paths:": "Internally used paths:",
|
||||
"Introduced By": "Introduced By",
|
||||
"Introducer": "Introducer",
|
||||
@@ -225,6 +229,7 @@
|
||||
"Learn more": "Learn more",
|
||||
"Learn more at {%url%}": "Learn more at {{url}}",
|
||||
"Limit": "Limit",
|
||||
"Limit Bandwidth in LAN": "Limit Bandwidth in LAN",
|
||||
"Listener Failures": "Listener Failures",
|
||||
"Listener Status": "Listener Status",
|
||||
"Listeners": "Listeners",
|
||||
@@ -368,7 +373,7 @@
|
||||
"Show diff with previous version": "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 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.": "Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.",
|
||||
"Shutdown": "Shutdown",
|
||||
"Shut Down": "Shut Down",
|
||||
"Shutdown Complete": "Shutdown Complete",
|
||||
"Simple": "Simple",
|
||||
"Simple File Versioning": "Simple File Versioning",
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
"Error": "Eraro",
|
||||
"External File Versioning": "Ekstera Versionado de Dosiero",
|
||||
"Failed Items": "Malsukcesaj Eroj",
|
||||
"Failed to setup, retrying": "Malsukcesis agordi, provante denove",
|
||||
"Failed to set up, retrying": "Malsukcesis agordi, provante denove",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Malsukceso por konekti al IPv6 serviloj atendante se ekzistas neniu IPv6 konektebleco.",
|
||||
"File Pull Order": "Ordo por Tiri Dosieron",
|
||||
"File Versioning": "Versionado de Dosieroj",
|
||||
@@ -258,7 +258,7 @@
|
||||
"Show diff with previous version": "Montri diferenco kun antaŭa versio",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Montrita anstataŭ ID de Aparato en la statuso de la grupo. Estos anoncita al aliaj aparatoj kiel laŭvola defaŭlta nomo.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Montri anstataŭ ID de Aparato en la statuso de la grupo. Estos ĝisdatigita al la nomo de la aparato sciigante se ĝi estas lasita malplena.",
|
||||
"Shutdown": "Sistemfermo",
|
||||
"Shut Down": "Sistemfermo",
|
||||
"Shutdown Complete": "Sistemfermo Tuta",
|
||||
"Simple File Versioning": "Simpla Versionado de Dosieroj",
|
||||
"Single level wildcard (matches within a directory only)": "Ununivela ĵokero (egalas nur ene de dosierujo)",
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
"About": "Acerca de",
|
||||
"Action": "Acción",
|
||||
"Actions": "Acciones",
|
||||
"Active filter rules": "Activadas las reglas del filtro",
|
||||
"Add": "Agregar",
|
||||
"Add Device": "Agregar el dispositivo",
|
||||
"Add Folder": "Agregar Carpeta",
|
||||
"Add Remote Device": "Añadir un dispositivo remoto",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "Añadir dispositivos del presentador a nuestra lista de dispositivos para las carpetas compartidas mutuamente.",
|
||||
"Add filter entry": "Añadir una entrada al filtro",
|
||||
"Add ignore patterns": "Agregar patrones a ignorar",
|
||||
"Add new folder?": "¿Agregar una carpeta nueva?",
|
||||
"Active filter rules": "Reglas habilitadas del filtro",
|
||||
"Add": "Añadir",
|
||||
"Add Device": "Añadir dispositivo",
|
||||
"Add Folder": "Añadir carpeta",
|
||||
"Add Remote Device": "Añadir dispositivo remoto",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "Añade dispositivos de la lista del presentador a nuestra lista de dispositivos, para carpetas compartidas simultáneamente.",
|
||||
"Add filter entry": "Añadir regla",
|
||||
"Add ignore patterns": "Añadir patrones a ignorar",
|
||||
"Add new folder?": "¿Añadir carpeta nueva?",
|
||||
"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.": "Además, se aumentará el intervalo de reescaneo completo (60 veces, es decir, nuevo valor predeterminado de 1 hora). También puede configurarlo manualmente para cada carpeta después de elegir No.",
|
||||
"Address": "Dirección",
|
||||
"Addresses": "Direcciones",
|
||||
@@ -22,23 +22,24 @@
|
||||
"Advanced Configuration": "Configuración Avanzada",
|
||||
"All Data": "Todos los datos",
|
||||
"All Time": "Todo el tiempo",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Todas las carpetas compartidas con este dispositivo deben estar protegidas por una contraseña, de forma que todos los datos enviados sean ilegibles sin la contraseña indicada.",
|
||||
"Allow Anonymous Usage Reporting?": "¿Deseas permitir el envío anónimo de informes de uso?",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Todas las carpetas compartidas con este dispositivo deben estar protegidas por una contraseña, de forma que todos los datos enviados sean ilegibles sin dicha contraseña.",
|
||||
"Allow Anonymous Usage Reporting?": "¿Permites el informe de uso anónimo?",
|
||||
"Allowed Networks": "Redes permitidas",
|
||||
"Alphabetic": "Alfabético",
|
||||
"Altered by ignoring deletes.": "Alterado ignorando eliminaciones.",
|
||||
"Altered by ignoring deletes.": "Alterado, ignorando eliminaciones.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Siempre habilitado cuando el tipo de carpeta es \"{{foldertype}}\".",
|
||||
"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.": "Un comando externo maneja las versiones. Tienes que eliminar el archivo de la carpeta compartida. Si la ruta a la aplicación contiene espacios, ésta debe estar entre comillas.",
|
||||
"Anonymous Usage Reporting": "Informe anónimo de uso",
|
||||
"Anonymous Usage Reporting": "Informe de uso anónimo",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "El formato del informe de uso anónimo a cambiado. ¿Le gustaría pasar al nuevo formato?",
|
||||
"Applied to LAN": "Aplicado a la LAN",
|
||||
"Apply": "Solicitar",
|
||||
"Are you sure you want to override all remote changes?": "¿Está seguro(a) de que desea sobreescribir todos los cambios remotos?",
|
||||
"Are you sure you want to permanently delete all these files?": "¿Está seguro de que desea eliminar permanentemente todos estos archivos?",
|
||||
"Are you sure you want to remove device {%name%}?": "¿Está seguro que desea eliminar el dispositivo {{name}}?",
|
||||
"Are you sure you want to remove folder {%label%}?": "¿Está seguro que desea eliminar la carpeta {{label}}?",
|
||||
"Are you sure you want to restore {%count%} files?": "¿Está seguro que desea restaurar {{count}} archivos?",
|
||||
"Are you sure you want to revert all local changes?": "¿Está seguro(a) de que desea revertir todos los cambios locales?",
|
||||
"Are you sure you want to upgrade?": "¿Está seguro(a) de que desea actualizar?",
|
||||
"Are you sure you want to override all remote changes?": "¿Estás seguro de que quieres sobreescribir todos los cambios remotos?",
|
||||
"Are you sure you want to permanently delete all these files?": "¿Estás seguro de que quieres eliminar permanentemente todos estos archivos?",
|
||||
"Are you sure you want to remove device {%name%}?": "¿Estás seguro de que quieres eliminar el dispositivo {{name}}?",
|
||||
"Are you sure you want to remove folder {%label%}?": "¿Estás seguro de que quieres eliminar la carpeta {{label}}?",
|
||||
"Are you sure you want to restore {%count%} files?": "¿Estás seguro de que quieres restaurar {{count}} archivos?",
|
||||
"Are you sure you want to revert all local changes?": "¿Estás seguro de que quieres revertir todos los cambios locales?",
|
||||
"Are you sure you want to upgrade?": "¿Estás seguro de que quieres actualizar?",
|
||||
"Authentication Required": "Autenticación requerida",
|
||||
"Authors": "Autores",
|
||||
"Auto Accept": "Aceptar automáticamente",
|
||||
@@ -46,27 +47,28 @@
|
||||
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Ahora la actualización automática permite elegir entre versiones estables o versiones candidatas.",
|
||||
"Automatic upgrades": "Actualizaciones automáticas",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "Las actualizaciones automáticas siempre están activadas para las versiones candidatas.",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "Crear o compartir automáticamente carpetas que este dispositivo anuncia en la ruta por defecto.",
|
||||
"Available debug logging facilities:": "Funciones de registro de depuración disponibles:",
|
||||
"Automatically create or share folders that this device advertises at the default path.": "Crea o comparte automáticamente las carpetas que el dispositivo anuncia en la ruta predeterminada.",
|
||||
"Available debug logging facilities:": "Servicios de depuración disponibles:",
|
||||
"Be careful!": "¡Ten cuidado!",
|
||||
"Body:": "Contenido:",
|
||||
"Bugs": "Errores",
|
||||
"Cancel": "Cancelar",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "No se puede habilitar cuando el tipo de carpeta es \"{{foldertype}}\".",
|
||||
"Changelog": "Registro de cambios",
|
||||
"Clean out after": "Limpiar tras",
|
||||
"Cleaning Versions": "Limpiando Versiones",
|
||||
"Cleanup Interval": "Intervalo de Limpieza",
|
||||
"Click to see full identification string and QR code.": "Haga clic para ver la cadena de identificación completa y su código QR.",
|
||||
"Cleaning Versions": "Limpiando versiones",
|
||||
"Cleanup Interval": "Intervalo de limpieza",
|
||||
"Click to see full identification string and QR code.": "Haz clic para ver el identificador completo y su código QR.",
|
||||
"Close": "Cerrar",
|
||||
"Command": "Dominio",
|
||||
"Comment, when used at the start of a line": "Comentar, cuando se usa al comienzo de una línea",
|
||||
"Comment, when used at the start of a line": "Comenta la línea cuando se usa al comienzo (no se tiene en cuenta)",
|
||||
"Compression": "Compresión",
|
||||
"Configuration Directory": "Carpeta de la configuración",
|
||||
"Configuration File": "Archivo de configuración",
|
||||
"Configured": "Configurado",
|
||||
"Connected (Unused)": "Conectado (Sin Uso)",
|
||||
"Connection Error": "Error de conexión",
|
||||
"Connection Management": "Gestión de las conexiones",
|
||||
"Connection Management": "Gestión de conexiones",
|
||||
"Connection Type": "Tipo de conexión",
|
||||
"Connections": "Conexiones",
|
||||
"Connections via relays might be rate limited by the relay": "Las conexiones a través de relés pueden estar limitadas por la velocidad del relé",
|
||||
@@ -75,40 +77,41 @@
|
||||
"Copied from original": "Copiado del original",
|
||||
"Copied!": "¡Copiado!",
|
||||
"Copy": "Copiar",
|
||||
"Copy failed! Try to select and copy manually.": "¡Copia fallida! Intente seleccionar y copiar manualmente.",
|
||||
"Currently Shared With Devices": "Actualmente Compartida con los Dispositivos",
|
||||
"Copy failed! Try to select and copy manually.": "¡Copia fallida! Intenta seleccionar y copiar manualmente.",
|
||||
"Currently Shared With Devices": "Actualmente compartida con",
|
||||
"Custom Range": "Rango personalizado",
|
||||
"Danger!": "¡Peligro!",
|
||||
"Database Location": "Ubicación de la base de datos",
|
||||
"Debug": "Depurar",
|
||||
"Debugging Facilities": "Servicios de depuración",
|
||||
"Default": "Predeterminado",
|
||||
"Default Configuration": "Configuración Predeterminada",
|
||||
"Default Configuration": "Configuración predeterminada",
|
||||
"Default Device": "Dispositivo Predeterminado",
|
||||
"Default Folder": "Carpeta Predeterminada",
|
||||
"Default Ignore Patterns": "Ignorar patrones por defecto",
|
||||
"Defaults": "Valores Predeterminados",
|
||||
"Delete": "Borrar",
|
||||
"Delete Unexpected Items": "Borrar Elementos Inesperados",
|
||||
"Default Ignore Patterns": "Ignorar patrones predeterminados",
|
||||
"Defaults": "Valores predeterminados",
|
||||
"Delete": "Eliminar",
|
||||
"Delete Unexpected Items": "Eliminar Elementos Inesperados",
|
||||
"Deleted {%file%}": "Eliminado {{file}}",
|
||||
"Deselect All": "Deseleccionar Todo",
|
||||
"Deselect devices to stop sharing this folder with.": "Deseleccionar dispositivos con los cuales dejar de compartir esta carpeta.",
|
||||
"Deselect folders to stop sharing with this device.": "Deseleccionar carpetas para dejar de compartir con este dispositivo.",
|
||||
"Deselect All": "Deseleccionar todo",
|
||||
"Deselect devices to stop sharing this folder with.": "Deselecciona dispositivos para dejar de compartir esta carpeta.",
|
||||
"Deselect folders to stop sharing with this device.": "Deselecciona carpetas para dejar de compartir con este dispositivo.",
|
||||
"Device": "Dispositivo",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "El dispositivo \"{{name}}\" ({{device}} en la dirección {{address}}) quiere conectarse. Añadir nuevo dispositivo?",
|
||||
"Device Certificate": "Certificado del dispositivo",
|
||||
"Device ID": "ID del Dispositivo",
|
||||
"Device Identification": "Identificación del Dispositivo",
|
||||
"Device Name": "Nombre del Dispositivo",
|
||||
"Device ID": "ID del dispositivo",
|
||||
"Device Identification": "Identificador 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 is untrusted, enter encryption password": "El dispositivo no es de confianza, introduce 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",
|
||||
"Devices": "Dispositivos",
|
||||
"Disable Crash Reporting": "Desactivar Informes de Fallos",
|
||||
"Disable Crash Reporting": "Desactivar informes de fallos",
|
||||
"Disabled": "Deshabilitado",
|
||||
"Disabled periodic scanning and disabled watching for changes": "Se desactivó el escaneo periódico y se desactivó el control de cambios",
|
||||
"Disabled periodic scanning and enabled watching for changes": "Se desactivó el escaneo periódico y se activó el control de cambios",
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Se desactivó el escaneo periódico y falló la configuración para detectar cambios, volviendo a intentarlo cada 1 m:",
|
||||
"Disabled periodic scanning and disabled watching for changes": "Escaneo periódico desactivado y control de cambios desactivado",
|
||||
"Disabled periodic scanning and enabled watching for changes": "Escaneo periódico desactivado y control de cambios activado",
|
||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Escaneo periódico desactivado y fallo en la detección de cambios, reintentando cada 1m:",
|
||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Desactiva la comparación y sincronización de los permisos de los archivos. Útil en sistemas con permisos inexistentes o personalizados (por ejemplo, FAT, exFAT, Synology, Android).",
|
||||
"Discard": "Descartar",
|
||||
"Disconnected": "Desconectado",
|
||||
@@ -116,137 +119,139 @@
|
||||
"Disconnected (Unused)": "Desconectado (Sin Uso)",
|
||||
"Discovered": "Descubierto",
|
||||
"Discovery": "Descubrimiento",
|
||||
"Discovery Failures": "Fallos de Descubrimiento",
|
||||
"Discovery Status": "Estado de Descubrimiento",
|
||||
"Discovery Failures": "Fallos de descubrimiento",
|
||||
"Discovery Status": "Estado de descubrimiento",
|
||||
"Dismiss": "Descartar",
|
||||
"Do not add it to the ignore list, so this notification may recur.": "No agregarlo a la lista de ignorados, de modo que esta notificación sea recurrente.",
|
||||
"Do not add it to the ignore list, so this notification may recur.": "No añadirlo a la lista de ignorados, de modo que esta notificación sea recurrente.",
|
||||
"Do not restore": "No restaurar",
|
||||
"Do not restore all": "No restaurar todos",
|
||||
"Do you want to enable watching for changes for all your folders?": "¿Deseas activar el control de cambios en todas tus carpetas?",
|
||||
"Do you want to enable watching for changes for all your folders?": "¿Quieres activar el control de cambios en todas tus carpetas?",
|
||||
"Documentation": "Documentación",
|
||||
"Download Rate": "Velocidad de descarga",
|
||||
"Downloaded": "Descargado",
|
||||
"Downloading": "Descargando",
|
||||
"Edit": "Editar",
|
||||
"Edit Device": "Editar Dispositivo",
|
||||
"Edit Device Defaults": "Editar Valores Predeterminados del Dispositivo",
|
||||
"Edit Device Defaults": "Editar valores predeterminados del dispositivo",
|
||||
"Edit Folder": "Editar Carpeta",
|
||||
"Edit Folder Defaults": "Editar Valores Predeterminados de las Carpeta",
|
||||
"Edit Folder Defaults": "Editar valores predeterminados de las carpeta",
|
||||
"Editing {%path%}.": "Editando {{path}}.",
|
||||
"Enable Crash Reporting": "Activar Informes de Fallos",
|
||||
"Enable Crash Reporting": "Activar informes de fallos",
|
||||
"Enable NAT traversal": "Permitir NAT transversal",
|
||||
"Enable Relaying": "Habilitar Retransmisión",
|
||||
"Enable Relaying": "Habilitar retransmisión",
|
||||
"Enabled": "Activado",
|
||||
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Permite enviar atributos ampliados a otros dispositivos y aplicar atributos ampliados entrantes. Puede ser necesario ejecutarlo con privilegios elevados.",
|
||||
"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.": "Permite enviar atributos ampliados a otros dispositivos, pero no aplicar los atributos ampliados entrantes. Esto puede tener un impacto significativo en el rendimiento. Siempre se activa cuando \"Sincronizar atributos extendidos\" está activado.",
|
||||
"Enables sending ownership information to other devices, and applying incoming ownership information. Typically requires running with elevated privileges.": "Hace que la información de propiedad se envíe a otros dispositivos y que se aplique la información de propiedad recibida. Por lo general, requiere ejecutarse con privilegios elevados.",
|
||||
"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.": "Permite enviar información de propiedad a otros dispositivos, pero no aplicar la información de propiedad entrante. Esto puede tener un impacto significativo en el rendimiento. Siempre se activa cuando \"Sincronizar propiedad\" está activado.",
|
||||
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Permite enviar atributos extendidos a otros dispositivos y aplicar atributos extendidos entrantes. Puede ser necesaria la ejecución con privilegios elevados.",
|
||||
"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.": "Permite enviar atributos extendidos a otros dispositivos, pero no aplica los atributos extendidos entrantes. Puede tener impacto en el rendimiento. Siempre se habilita cuando \"Sincronizar atributos extendidos\" está habilitado.",
|
||||
"Enables sending ownership information to other devices, and applying incoming ownership information. Typically requires running with elevated privileges.": "Permite enviar información de propietario a otros dispositivos y que se aplique la información de propietario recibida. Puede ser necesaria la ejecución con privilegios elevados.",
|
||||
"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.": "Permite enviar información de propietario a otros dispositivos, pero no aplicar la información de propiedad entrante. Puede tener impacto en el rendimiento. Siempre se habilita cuando \"Sincronizar propietario\" está habilitado.",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Introduce un número no negativo (por ejemplo, \"2.35\") y selecciona una unidad. Los porcentajes son como parte del tamaño total del disco.",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Introduce un puerto sin privilegios (1024 - 65535).",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Introduzca direcciones separadas por comas (\"tcp://ip:port\", \"tcp://host:port\") o \"dynamic\" para realizar el descubrimiento automático de la dirección.",
|
||||
"Enter ignore patterns, one per line.": "Introducir patrones a ignorar, uno por línea.",
|
||||
"Enter up to three octal digits.": "Introduzca hasta tres dígitos octales.",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Introduce direcciones separadas por comas (\"tcp://ip:port\", \"tcp://host:port\") o \"dynamic\" para realizar el descubrimiento automático de la dirección.",
|
||||
"Enter ignore patterns, one per line.": "Introduce patrones a ignorar, uno por línea.",
|
||||
"Enter up to three octal digits.": "Introduce hasta tres dígitos octales.",
|
||||
"Error": "Fallo",
|
||||
"Extended Attributes": "Atributos ampliados",
|
||||
"Extended Attributes": "Atributos extendidos",
|
||||
"Extended Attributes Filter": "Filtro de atributos extendidos",
|
||||
"External": "Externo",
|
||||
"External File Versioning": "Versionado externo de fichero",
|
||||
"External File Versioning": "Externo",
|
||||
"Failed Items": "Elementos fallidos",
|
||||
"Failed to load file versions.": "Error al cargar las versiones de los archivos.",
|
||||
"Failed to load ignore patterns.": "No se pudieron cargar los patrones de ignorar.",
|
||||
"Failed to setup, retrying": "Fallo en la configuración, reintentando",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Se espera un fallo al conectar a los servidores IPv6 si no hay conectividad IPv6.",
|
||||
"File Pull Order": "Orden de Obtención de los Archivos",
|
||||
"File Versioning": "Versionado de ficheros",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Los ficheros son movidos a la carpeta .stversions cuando son reemplazados o borrados por Syncthing.",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Los ficheros son movidos a una carpeta .stversions a versiones con control de fecha cuando son reemplazados o borrados por 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.": "Los ficheros son protegidos por los cambios hechos en otros dispositivos, pero los cambios hechos en este dispositivo serán enviados al resto del grupo (cluster).",
|
||||
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Los archivos se sincronizan desde el clúster, pero los cambios realizados localmente no se enviarán a otros dispositivos.",
|
||||
"Filesystem Watcher Errors": "Errores del Vigilante del Sistema de Archivos",
|
||||
"Failed to load ignore patterns.": "No se pudieron cargar los patrones a ignorar.",
|
||||
"Failed to set up, retrying": "Fallo en la configuración, reintentando",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Se espera que la conexión a servidores IPv6 falle si no hay conectividad IPv6.",
|
||||
"File Pull Order": "Orden de descarga",
|
||||
"File Versioning": "Versionado de archivos",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Los archivos son movidos al directorio .stversions cuando son reemplazados o eliminados por Syncthing.",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Los archivos son movidos a versiones con fecha en el directorio .stversions cuando son reemplazados o eliminados por 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.": "Los archivos están protegidos de cambios en los otros dispositivos, pero los cambios en este dispositivo se enviarán al resto.",
|
||||
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Los archivos se sincronizan desde los otros dispositivos, pero los cambios locales no se enviarán a estos.",
|
||||
"Filesystem Watcher Errors": "Errores del watcher del sistema de archivos",
|
||||
"Filter by date": "Filtrar por fecha",
|
||||
"Filter by name": "Filtrar por nombre",
|
||||
"Folder": "Carpeta",
|
||||
"Folder ID": "ID de carpeta",
|
||||
"Folder Label": "Etiqueta de la Carpeta",
|
||||
"Folder ID": "ID de la 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.",
|
||||
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "El tipo de carpeta \"{{receiveEncrypted}}\" sólo puede ser seleccionado al añadir 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, eliminar o descifrar los datos en el disco y volver a añadir la carpeta.",
|
||||
"Folders": "Carpetas",
|
||||
"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.": "En las siguientes carpetas se ha producido un error al empezar a buscar cambios. Se volverá a intentar cada minuto, por lo que los errores podrían solucionarse pronto. Si persisten, trata de arreglar el problema subyacente y pide ayuda si no puedes.",
|
||||
"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.": "Se ha producido un error al comenzar a buscar cambios para las siguientes carpetas. Se hará un reintento cada minuto, por lo que los errores podrían desaparecer pronto. Si persisten trata de arreglar el problema subyacente o pide ayuda.",
|
||||
"Forever": "Para siempre",
|
||||
"Full Rescan Interval (s)": "Intervalo de rescaneo completo (s)",
|
||||
"GUI": "Interfaz gráfica del usuario",
|
||||
"Full Rescan Interval (s)": "Intervalo reescaneo completo",
|
||||
"GUI": "Interfaz gráfica",
|
||||
"GUI / API HTTPS Certificate": "Certificado HTTPS GUI / API",
|
||||
"GUI Authentication Password": "Contraseña de la Interfaz Gráfica de Usuario (GUI)",
|
||||
"GUI Authentication User": "Autentificación de usuario de la Interfaz Gráfica de Usuario (GUI)",
|
||||
"GUI Authentication: Set User and Password": "Autenticación de la GUI: Establezca el Usuario y Contraseña",
|
||||
"GUI Listen Address": "Dirección de la interfaz de usuario",
|
||||
"GUI Authentication Password": "Contraseña de la interfaz gráfica",
|
||||
"GUI Authentication User": "Usuario de la interfaz gráfica",
|
||||
"GUI Authentication: Set User and Password": "Autenticación GUI: configura usuario y contraseña",
|
||||
"GUI Listen Address": "Dirección de la interfaz gráfica",
|
||||
"GUI Override Directory": "Directorio de reemplazo de GUI",
|
||||
"GUI Theme": "Tema GUI",
|
||||
"GUI Theme": "Tema de la interfaz gráfica",
|
||||
"General": "General",
|
||||
"Generate": "Generar",
|
||||
"Global Discovery": "Descubrimiento global",
|
||||
"Global Discovery Servers": "Servidores Globales de Descubrimiento",
|
||||
"Global Discovery Servers": "Servidores globales de descubrimiento",
|
||||
"Global State": "Estado global",
|
||||
"Help": "Ayuda",
|
||||
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Sugerencia: solo se detectan reglas de denegación mientras que el valor predeterminado es denegar. Considere agregar \"permitir cualquiera\" como última regla.",
|
||||
"Hint: only deny-rules detected while the default is deny. Consider adding \"permit any\" as last rule.": "Sugerencia: sólo se detectan reglas para denegar y el valor predeterminado es denegar. Considera añadir \"permitir cualquiera\" como última regla.",
|
||||
"Home page": "Página de inicio",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Sin embargo, su configuración actual indica que puede no quererla activa. Hemos desactivado los informes automáticos de fallos por usted.",
|
||||
"Identification": "Identificación",
|
||||
"If untrusted, enter encryption password": "Si no es de confianza, introduzca la contraseña de cifrado",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Si desea evitar que otros usuarios de esta computadora accedan a Syncthing y, a través de él, a sus archivos, considere establecer la autenticación.",
|
||||
"However, your current settings indicate you might not want it enabled. We have disabled automatic crash reporting for you.": "Sin embargo tu configuración actual indica que puedes no querer habilitarlo. Hemos desactivado los informes automáticos de fallos por tí.",
|
||||
"Identification": "Identificador",
|
||||
"If untrusted, enter encryption password": "Si no es de confianza, introduce la contraseña de cifrado",
|
||||
"If you want to prevent other users on this computer from accessing Syncthing and through it your files, consider setting up authentication.": "Si quieres evitar que otros usuarios de este ordenador accedan a Syncthing y a tus archivos a través de él, considera habilitar la autenticación.",
|
||||
"Ignore": "Ignorar",
|
||||
"Ignore Patterns": "Patrones a ignorar",
|
||||
"Ignore Permissions": "Permisos a ignorar",
|
||||
"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.": "Los patrones a ignorar solo se pueden agregar luego de que la carpeta sea creada. Cuando se marca, se presentará un campo de entrada para introducir los patrones a ignorar después de guardar.",
|
||||
"Ignore Permissions": "Ignorar permisos",
|
||||
"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.": "Los patrones a ignorar solo se pueden añadir después de que la carpeta sea creada. Cuando se marca, después de guardar se presentará un campo de entrada para introducir los patrones a ignorar.",
|
||||
"Ignored Devices": "Dispositivos ignorados",
|
||||
"Ignored Folders": "Carpetas ignoradas",
|
||||
"Ignored at": "Ignorados en",
|
||||
"Included Software": "Programas incluidos",
|
||||
"Incoming Rate Limit (KiB/s)": "Límite de descarga (KiB/s)",
|
||||
"Incoming Rate Limit (KiB/s)": "Limite tráfico de entrada (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Una configuración incorrecta puede corromper el contenido de la carpeta y poner a Syncthing en un estado inoperante.",
|
||||
"Incorrect user name or password.": "Nombre de usuario o contraseña incorrectos.",
|
||||
"Info": "Información",
|
||||
"Internally used paths:": "Rutas de uso interno:",
|
||||
"Introduced By": "Introducido por",
|
||||
"Introduced By": "Presentado por",
|
||||
"Introducer": "Presentador",
|
||||
"Introduction": "Introducción",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Inversión de la condición dada (por ejemplo, \"no excluir\")",
|
||||
"Keep Versions": "Mantener versiones",
|
||||
"Keep Versions": "Versiones a conservar",
|
||||
"LDAP": "LDAP",
|
||||
"Largest First": "Más grande primero",
|
||||
"Largest First": "Mayor tamaño primero",
|
||||
"Last 30 Days": "Últimos 30 días",
|
||||
"Last 7 Days": "Últimos 7 días",
|
||||
"Last Month": "Último mes",
|
||||
"Last Scan": "Último escaneo",
|
||||
"Last seen": "Visto por última vez",
|
||||
"Latest Change": "Último Cambio",
|
||||
"Latest Change": "Último cambio",
|
||||
"Learn more": "Saber más",
|
||||
"Learn more at {%url%}": "Más información en {{url}}",
|
||||
"Limit": "Límite",
|
||||
"Listener Failures": "Fallos de Oyente",
|
||||
"Listener Status": "Estado de Oyente",
|
||||
"Limit Bandwidth in LAN": "Limitar ancho de banda en LAN",
|
||||
"Listener Failures": "Fallos del oyente",
|
||||
"Listener Status": "Estado del oyente",
|
||||
"Listeners": "Oyentes",
|
||||
"Loading data...": "Cargando datos...",
|
||||
"Loading...": "Cargando...",
|
||||
"Local Additions": "Adiciones Locales",
|
||||
"Local Additions": "Cambios Locales",
|
||||
"Local Discovery": "Descubrimiento local",
|
||||
"Local State": "Estado local",
|
||||
"Local State (Total)": "Estado Local (Total)",
|
||||
"Locally Changed Items": "Elementos Cambiados Localmente",
|
||||
"Local State (Total)": "Estado local (Total)",
|
||||
"Locally Changed Items": "Cambiados localmente",
|
||||
"Log": "Registro",
|
||||
"Log File": "Archivo de registro",
|
||||
"Log In": "Iniciar sesión",
|
||||
"Log Out": "Cerrar sesión",
|
||||
"Log in to see paths information.": "Inicia sesión para ver la información sobre las rutas.",
|
||||
"Log in to see version information.": "Inicia sesión para ver la información sobre la versión.",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Seguimiento del registro pausado. Desplácese hasta el final para continuar.",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Seguimiento del registro pausado. Desplázate hasta el final para continuar.",
|
||||
"Login failed, see Syncthing logs for details.": "El inicio de sesión falló, mira los registros de Syncthing para más detalles.",
|
||||
"Logs": "Registros",
|
||||
"Major Upgrade": "Actualización importante",
|
||||
"Mass actions": "Acción masiva",
|
||||
"Maximum Age": "Edad máxima",
|
||||
"Maximum Age": "Antigüedad máxima",
|
||||
"Maximum single entry size": "Tamaño máximo de una entrada",
|
||||
"Maximum total size": "Tamaño máximo total",
|
||||
"Metadata Only": "Sólo metadatos",
|
||||
@@ -259,48 +264,48 @@
|
||||
"Move to top of queue": "Mover al principio de la cola",
|
||||
"Multi level wildcard (matches multiple directory levels)": "Comodín multinivel (coincide con múltiples niveles de directorio)",
|
||||
"Never": "Nunca",
|
||||
"New Device": "Nuevo Dispositivo",
|
||||
"New Folder": "Nueva Carpeta",
|
||||
"Newest First": "El más nuevo primero",
|
||||
"New Device": "Nuevo dispositivo",
|
||||
"New Folder": "Nueva carpeta",
|
||||
"Newest First": "Más reciente primero",
|
||||
"No": "No",
|
||||
"No File Versioning": "Sin versionado de fichero",
|
||||
"No File Versioning": "Sin versionado de archivos",
|
||||
"No files will be deleted as a result of this operation.": "Ningún archivo será eliminado como resultado de esta operación.",
|
||||
"No rules set": "No se han fijado normas",
|
||||
"No upgrades": "Sin actualizaciones",
|
||||
"Not shared": "No Compartido(a)",
|
||||
"Not shared": "No compartida",
|
||||
"Notice": "Aviso",
|
||||
"Number of Connections": "Número de conexiones",
|
||||
"OK": "De acuerdo",
|
||||
"Off": "Desactivado",
|
||||
"Oldest First": "El más antiguo primero",
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "Etiqueta descriptiva opcional para la carpeta. Puede ser diferente en cada dispositivo.",
|
||||
"Oldest First": "Más antiguo primero",
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "Etiqueta descriptiva opcional de la carpeta. Puede ser diferente en cada dispositivo.",
|
||||
"Options": "Opciones",
|
||||
"Out of Sync": "No sincronizado",
|
||||
"Out of Sync Items": "Elementos no sincronizados",
|
||||
"Outgoing Rate Limit (KiB/s)": "Límite de subida (KiB/s)",
|
||||
"Outgoing Rate Limit (KiB/s)": "Limite tráfico de salida (KiB/s)",
|
||||
"Override": "Sobreescribir",
|
||||
"Override Changes": "Anular cambios",
|
||||
"Ownership": "Propiedad",
|
||||
"Ownership": "Propietario",
|
||||
"Password": "Contraseña",
|
||||
"Path": "Ruta",
|
||||
"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": "Ruta a la carpeta en el dispositivo local. Se creará la carpeta si no existe. El carácter de la tilde (~) se puede utilizar como abreviatura de",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "La ruta donde las versiones deben ser almacenadas (dejar vacío para el directorio .stversions por defecto en la carpeta compartida).",
|
||||
"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": "Ruta de la carpeta en el dispositivo local. Si no existe será creada. El carácter (~) se puede usar como abreviatura de",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Ruta donde almacenar las versiones (deja vacío para usar el directorio predeterminado .stversions en la carpeta compartida).",
|
||||
"Paths": "Rutas",
|
||||
"Pause": "Pausar",
|
||||
"Pause All": "Pausar todo",
|
||||
"Paused": "Pausado",
|
||||
"Paused (Unused)": "Pausado(a) (Sin Uso)",
|
||||
"Paused (Unused)": "Pausado (No usado)",
|
||||
"Pending changes": "Cambios pendientes",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Escaneando periódicamente a un intervalo dado y detección de cambios desactivada",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Escaneando periódicamente a un intervalo dado y detección de cambios activada",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Escaneando periódicamente a un intervalo dado y falló la configuración para detectar cambios, volviendo a intentarlo cada 1 m:",
|
||||
"Permanently add it to the ignore list, suppressing further notifications.": "Agregarlo permanentemente a la lista de ignorados, suprimiendo futuras notificaciones.",
|
||||
"Please consult the release notes before performing a major upgrade.": "Por favor, consultar las notas de la versión antes de realizar una actualización importante.",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "Por favor, introduzca un Usuario y Contraseña para la Autenticación de la Interfaz de Usuario en el panel de Ajustes.",
|
||||
"Periodic scanning at given interval and disabled watching for changes": "Escaneo periódico a intervalos, detección de cambios deshabilitada",
|
||||
"Periodic scanning at given interval and enabled watching for changes": "Escaneo periódico a intervalos, detección de cambios habilitada",
|
||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Escaneo periódico a intervalos y fallo en la detección de cambios, reintentando cada 1m:",
|
||||
"Permanently add it to the ignore list, suppressing further notifications.": "Añadirlo permanentemente a la lista de ignorados, suprimiendo futuras notificaciones.",
|
||||
"Please consult the release notes before performing a major upgrade.": "Por favor, consulta las notas de la versión antes de realizar una actualización importante.",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "Por favor, introduce un usuario y contraseña para la autenticación de la interfaz gráfica en Ajustes.",
|
||||
"Please wait": "Por favor, espere",
|
||||
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefijo que indica que el archivo puede ser eliminado si se impide el borrado del directorio",
|
||||
"Prefix indicating that the file can be deleted if preventing directory removal": "Prefijo que indica que el archivo puede ser eliminado si se impide eliminar el directorio",
|
||||
"Prefix indicating that the pattern should be matched without case sensitivity": "Prefijo que indica que el patrón debe coincidir sin distinguir mayúsculas de minúsculas",
|
||||
"Preparing to Sync": "Preparándose para Sincronizar",
|
||||
"Preparing to Sync": "Preparando Sincronización",
|
||||
"Preview": "Vista previa",
|
||||
"Preview Usage Report": "Previsualizar el Informe de Uso",
|
||||
"QR code": "Código QR",
|
||||
@@ -309,22 +314,22 @@
|
||||
"Quick guide to supported patterns": "Guía rápida de patrones soportados",
|
||||
"Random": "Aleatorio",
|
||||
"Receive Encrypted": "Recibir Encriptado",
|
||||
"Receive Only": "Solo Recibir",
|
||||
"Receive Only": "Sólo Recibir",
|
||||
"Received data is already encrypted": "Los datos recibidos ya están cifrados",
|
||||
"Recent Changes": "Cambios recientes",
|
||||
"Reduced by ignore patterns": "Reducido por patrones de ignorar",
|
||||
"Reduced by ignore patterns": "Reducido por patrones a ignorar",
|
||||
"Relay LAN": "Relé LAN",
|
||||
"Relay WAN": "Relé WAN",
|
||||
"Release Notes": "Notas de la versión",
|
||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Las versiones candidatas contienen las últimas funcionalidades y correcciones. Son similares a las tradicionales versiones bisemanales de Syncthing.",
|
||||
"Remote Devices": "Otros dispositivos",
|
||||
"Remote GUI": "GUI Remota",
|
||||
"Remote Devices": "Dispositivos Remotos",
|
||||
"Remote GUI": "GUI remota",
|
||||
"Remove": "Eliminar",
|
||||
"Remove Device": "Eliminar dispositivo",
|
||||
"Remove Folder": "Remover carpeta",
|
||||
"Required identifier for the folder. Must be the same on all cluster devices.": "Identificador requerido para la carpeta. Debe ser el mismo en todos los dispositivos del clúster.",
|
||||
"Rescan": "Volver a analizar",
|
||||
"Rescan All": "Volver a analizar Todo",
|
||||
"Required identifier for the folder. Must be the same on all cluster devices.": "Identificador requerido de la carpeta. Debe ser el mismo en todos los dispositivos del clúster.",
|
||||
"Rescan": "Reescanear",
|
||||
"Rescan All": "Reescanear todo",
|
||||
"Rescans": "Reescaneos",
|
||||
"Restart": "Reiniciar",
|
||||
"Restart Needed": "Reinicio necesario",
|
||||
@@ -335,150 +340,150 @@
|
||||
"Resume All": "Continuar todo",
|
||||
"Reused": "Reutilizado",
|
||||
"Revert": "Revertir",
|
||||
"Revert Local Changes": "Revertir Cambios Locales",
|
||||
"Revert Local Changes": "Revertir cambios locales",
|
||||
"Save": "Guardar",
|
||||
"Saving changes": "Guardar los cambios",
|
||||
"Scan Time Remaining": "Tiempo Restante de Escaneo",
|
||||
"Scanning": "Analizando",
|
||||
"See external versioning help for supported templated command line parameters.": "Vea la ayuda del gestor de versiones externo para los parámetros de linea de comandos que usan una plantilla.",
|
||||
"Select All": "Seleccionar Todo",
|
||||
"Scan Time Remaining": "Tiempo restante de escaneo",
|
||||
"Scanning": "Escaneando",
|
||||
"See external versioning help for supported templated command line parameters.": "Ve a la ayuda del gestor de versiones externo para las parámetros de linea de comandos compatibles.",
|
||||
"Select All": "Seleccionar todo",
|
||||
"Select a version": "Seleccione una versión",
|
||||
"Select additional devices to share this folder with.": "Seleccionar dispositivos adicionales con los cuales compartir esta carpeta.",
|
||||
"Select additional folders to share with this device.": "Seleccionar carpetas adicionales para compartir con este dispositivo.",
|
||||
"Select latest version": "Seleccione la última versión",
|
||||
"Select oldest version": "Seleccione la versión más antigua",
|
||||
"Select additional devices to share this folder with.": "Selecciona dispositivos adicionales para compartir esta carpeta.",
|
||||
"Select additional folders to share with this device.": "Selecciona carpetas adicionales para compartir con este dispositivo.",
|
||||
"Select latest version": "Selecciona la última versión",
|
||||
"Select oldest version": "Selecciona la versión más antigua",
|
||||
"Send & Receive": "Enviar y Recibir",
|
||||
"Send Extended Attributes": "Enviar atributos extendidos",
|
||||
"Send Only": "Solo Enviar",
|
||||
"Send Ownership": "Enviar Titularidad",
|
||||
"Set Ignores on Added Folder": "Establecer Ignorados en Carpeta Agregada",
|
||||
"Send Only": "Sólo Enviar",
|
||||
"Send Ownership": "Enviar propietario",
|
||||
"Set Ignores on Added Folder": "Establecer ignorados en carpeta añadida",
|
||||
"Settings": "Ajustes",
|
||||
"Share": "Compartir",
|
||||
"Share Folder": "Compartir carpeta",
|
||||
"Share by Email": "Compartir por correo electrónico",
|
||||
"Share by Email": "Compartir por email",
|
||||
"Share by SMS": "Compartir por SMS",
|
||||
"Share this folder?": "¿Deseas compartir esta carpeta?",
|
||||
"Shared Folders": "Carpetas Compartidas",
|
||||
"Shared With": "Compartir con",
|
||||
"Sharing": "Compartiendo",
|
||||
"Share this folder?": "¿Quieres compartir esta carpeta?",
|
||||
"Shared Folders": "Carpetas compartidas",
|
||||
"Shared With": "Compartida con",
|
||||
"Sharing": "Compartir",
|
||||
"Show ID": "Mostrar ID",
|
||||
"Show QR": "Mostrar QR",
|
||||
"Show detailed discovery status": "Mostrar estado de descubrimiento detallado",
|
||||
"Show detailed listener status": "Mostrar estado de oyente detallado",
|
||||
"Show diff with previous version": "Mostrar la diferencia con la versión anterior",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Se muestra en lugar del ID del dispositivo en el estado del grupo (cluster). Se notificará a los otros dispositivos como nombre opcional por defecto.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Se muestra en lugar del ID del dispositivo en el estado del grupo (cluster). Se actualizará al nombre que el dispositivo anuncia si se deja vacío.",
|
||||
"Shutdown": "Apagar",
|
||||
"Show diff with previous version": "Mostrar diferencias con la versión anterior",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Se muestra en lugar del ID del dispositivo en el estado del grupo (cluster). Se notificará a los otros dispositivos como nombre opcional predeterminado.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Sustituye al ID del dispositivo en el estado del grupo (cluster). Si se deja vacío se usará el nombre que anuncia el dispositivo.",
|
||||
"Shut Down": "Apagar",
|
||||
"Shutdown Complete": "Apagar completamente",
|
||||
"Simple": "Sencillo",
|
||||
"Simple File Versioning": "Versionado simple de fichero",
|
||||
"Simple": "Simple",
|
||||
"Simple File Versioning": "Simple",
|
||||
"Single level wildcard (matches within a directory only)": "Comodín de nivel único (coincide solamente dentro de un directorio)",
|
||||
"Size": "Tamaño",
|
||||
"Smallest First": "El más pequeño primero",
|
||||
"Smallest First": "Menor tamaño primero",
|
||||
"Some discovery methods could not be established for finding other devices or announcing this device:": "No se han podido establecer algunos métodos de descubrimiento para encontrar otros dispositivos o para anunciar este dispositivo:",
|
||||
"Some items could not be restored:": "Algunos ítemes no pudieron ser restaurados:",
|
||||
"Some listening addresses could not be enabled to accept connections:": "Algunas direcciones de escucha no pudieron ser activadas para aceptar conexiones:",
|
||||
"Some items could not be restored:": "Algunos elementos no pudieron ser restaurados:",
|
||||
"Some listening addresses could not be enabled to accept connections:": "Algunas direcciones de escucha no pudieron activarse para aceptar conexiones:",
|
||||
"Source Code": "Código fuente",
|
||||
"Stable releases and release candidates": "Versiones estables y versiones candidatas",
|
||||
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Las versiones estables son publicadas cada dos semanas. Durante este tiempo son probadas como versiones candidatas.",
|
||||
"Stable releases only": "Solo versiones estables",
|
||||
"Staggered": "Gradual",
|
||||
"Staggered File Versioning": "Versionado escalonado de fichero",
|
||||
"Start Browser": "Iniciar el navegador",
|
||||
"Staggered File Versioning": "Escalonado",
|
||||
"Start Browser": "Iniciar en 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}}\".",
|
||||
"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.": "Sólo almacena y sincroniza datos cifrados. Las carpetas de todos los dispositivos conectados deben estar configuradas con la misma contraseña o ser del tipo \"{{receiveEncrypted}}\".",
|
||||
"Subject:": "Asunto:",
|
||||
"Support": "Forum",
|
||||
"Support Bundle": "Paquete de Soporte",
|
||||
"Sync Extended Attributes": "Atributos ampliados de sincronización",
|
||||
"Sync Ownership": "Sincronizar la información de la propiedad",
|
||||
"Support": "Soporte",
|
||||
"Support Bundle": "Paquete de soporte",
|
||||
"Sync Extended Attributes": "Sincronizar atributos extendidos",
|
||||
"Sync Ownership": "Sincronizar propietario",
|
||||
"Sync Protocol Listen Addresses": "Direcciones de escucha del protocolo de sincronización",
|
||||
"Sync Status": "Estado de la sincronización",
|
||||
"Syncing": "Sincronizando",
|
||||
"Syncthing device ID for \"{%devicename%}\"": "ID del dispositivo de sincronización para \"{{devicename}}\"",
|
||||
"Syncthing has been shut down.": "Syncthing se ha detenido.",
|
||||
"Syncthing includes the following software or portions thereof:": "Syncthing incluye el siguiente software o partes de él:",
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing es Software Libre y de Código Abierto con licencia MPL v2.0.",
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing es software libre de código abierto con licencia 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 es una aplicación para la sincronización continua de archivos. Sincroniza archivos entre dos o más computadoras en tiempo real, con protección contra miradas indiscretas. Tus datos son solo tuyos y mereces elegir dónde se almacenan, si se comparten con terceros y cómo se transmiten a través de Internet.",
|
||||
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing está a la escucha en las siguientes direcciones de red en busca de intentos de conexión de otros dispositivos:",
|
||||
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing no está a la escucha de intentos de conexión de otros dispositivos en ninguna dirección. Solo pueden funcionar las conexiones salientes de este dispositivo.",
|
||||
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing está a la escucha en las siguientes direcciones de red en busca de intentos de conexión desde otros dispositivos:",
|
||||
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing no está a la escucha de intentos de conexión desde otros dispositivos en ninguna dirección. Solo pueden funcionar las conexiones salientes de este dispositivo.",
|
||||
"Syncthing is restarting.": "Syncthing se está reiniciando.",
|
||||
"Syncthing is saving changes.": "La sincronización guarda los cambios.",
|
||||
"Syncthing is upgrading.": "Syncthing se está actualizando.",
|
||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing ahora soporta el reportar automáticamente las fallas a los desarrolladores. Esta característica está habilitada por defecto.",
|
||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing soporta ahora el reporte automático de fallos a los desarrolladores. Esta función está habilitada por defecto.",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Parece que la sincronización no funciona o hay un problema con la conexión a Internet. Intentando lo otra vez…",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing tiene problemas para procesar tu solicitud. Por favor, actualiza la página o reinicia Syncthing si el problema persiste.",
|
||||
"TCP LAN": "TCP LAN",
|
||||
"TCP WAN": "TCP WAN",
|
||||
"Take me back": "Llévame de vuelta",
|
||||
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "La dirección de la Interfaz Gráfica de Ususario (GUI) está sobreescrita por las opciones de inicio. Los cambios aquí no tendrán efecto mientras la sobreescritura esté activa.",
|
||||
"The Syncthing Authors": "Los Autores de Syncthing",
|
||||
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "La dirección de la interfaz gráfica está anulada por las opciones de inicio. Los cambios aquí no tendrán efecto mientras continue la anulación.",
|
||||
"The Syncthing Authors": "Los autores de Syncthing",
|
||||
"The Syncthing admin interface is configured to allow remote access without a password.": "El panel de administración de Syncthing está configurado para permitir el acceso remoto sin contraseña.",
|
||||
"The aggregated statistics are publicly available at the URL below.": "Las estadísticas agragadas están disponibles públicamente en la URL de abajo.",
|
||||
"The cleanup interval cannot be blank.": "El intervalo de limpieza no puede ser nulo.",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "La configuración ha sido grabada pero no activada. Syncthing debe reiniciarse para activar la nueva configuración.",
|
||||
"The device ID cannot be blank.": "La ID del dispositivo no puede estar vacía.",
|
||||
"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).": "El ID del dispositivo que hay que introducir aquí se puede encontrar en el diálogo \"Acciones > Mostrar ID\" en el otro dispositivo. Los espacios y las barras son opcionales (ignorados).",
|
||||
"The cleanup interval cannot be blank.": "El intervalo de limpieza no puede estar vacío.",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "La configuración ha sido guardada pero no activada. Syncthing debe reiniciarse para activar la nueva configuración.",
|
||||
"The device ID cannot be blank.": "El ID del dispositivo no puede estar vacío.",
|
||||
"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).": "El ID del dispositivo a introducir aquí se puede encontrar en el diálogo \"Acciones > Mostrar ID\" del otro dispositivo. Los espacios y barras son opcionales (ignorados).",
|
||||
"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.": "El informe encriptado de uso se envía diariamente. Se usa para rastrear plataformas comunes, tamaños de carpetas y versiones de la aplicación. Si el conjunto de datos enviados en el informes se cambia, se le pedirá a usted autorización de nuevo.",
|
||||
"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.": "La ID del dispositivo introducida no parece válida. Debe ser una cadena de 52 ó 56 caracteres formada por letras y números, con espacios y guiones opcionales.",
|
||||
"The folder ID cannot be blank.": "La ID de la carpeta no puede estar vacía.",
|
||||
"The folder ID must be unique.": "La ID de la carpeta debe ser única.",
|
||||
"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.": "El contenido de las carpetas de otros dispositivos será sobreescritos para que sea idéntico al de este dispositivo. Archivos no presentes aquí serán eliminados de otros dispositivos.",
|
||||
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "El contenido de las carpetas en este dispositivo será sobreescrito para ser idéntico al de otros dispositivos. Los archivos que se agreguen aquí se eliminarán.",
|
||||
"The folder path cannot be blank.": "La ruta de la carpeta no puede estar en blanco.",
|
||||
"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.": "Se utilizan los siguientes intervalos: para la primera hora se mantiene una versión cada 30 segundos, para el primer día se mantiene una versión cada hora, para los primeros 30 días se mantiene una versión diaria hasta la edad máxima de una semana.",
|
||||
"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.": "El ID del dispositivo introducido no parece válido. Debe ser una cadena de 52 ó 56 caracteres formada por letras y números, con espacios y guiones opcionales.",
|
||||
"The folder ID cannot be blank.": "El ID de la carpeta no puede estar vacío.",
|
||||
"The folder ID must be unique.": "El ID de la carpeta debe ser único.",
|
||||
"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.": "El contenido de la carpeta en el resto de dispositivos será sobreescritos para que sea idéntico al de este dispositivo. Los archivos no presentes aquí serán eliminados de los otros dispositivos.",
|
||||
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "El contenido de la carpeta en este dispositivo será sobreescrito para que sea idéntico al resto de dispositivos. Los archivos añadidos localmente serán eliminados.",
|
||||
"The folder path cannot be blank.": "La ruta de la carpeta no puede estar vacía.",
|
||||
"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.": "Se usan los siguientes intervalos: durante la primera hora se conserva una versión cada 30 segundos, durante el primer día se conserva una versión cada hora, durante los primeros 30 días se conserva una versión cada día, y hasta alcanzar la antigüedad máxima se conserva una versión por semana.",
|
||||
"The following items could not be synchronized.": "Los siguientes elementos no pueden ser sincronizados.",
|
||||
"The following items were changed locally.": "Los siguientes elementos fueron cambiados localmente.",
|
||||
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Los siguientes métodos son usados para descubrir otros dispositivos en la red y anunciar este dispositivo para que sea encontrado por otros:",
|
||||
"The following text will automatically be inserted into a new message.": "El siguiente texto se insertará automáticamente en un nuevo mensaje.",
|
||||
"The following unexpected items were found.": "Los siguientes elementos inesperados fueron encontrados.",
|
||||
"The interval must be a positive number of seconds.": "El intervalo debe ser un número de segundos positivo.",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "El intervalo, en segundos, para ejecutar la limpieza del directorio de versiones. Cero para desactivar la limpieza periódica.",
|
||||
"The maximum age must be a number and cannot be blank.": "La edad máxima debe ser un número y no puede estar vacía.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "El tiempo máximo para mantener una versión en días (introducir 0 para mantener las versiones indefinidamente).",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Intervalo en segundos para ejecutar la limpieza del directorio de versiones. Cero desactiva la limpieza periódica.",
|
||||
"The maximum age must be a number and cannot be blank.": "La antigüedad máxima debe ser un número y no puede estar vacía.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Tiempo máximo en días para conservar una versión. Cero significa indefinidamente.",
|
||||
"The number of connections must be a non-negative number.": "El número de las conexiones debe ser un número que no sea negativo.",
|
||||
"The number of days must be a number and cannot be blank.": "El número de días debe ser un número y no puede estar en blanco.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "El número de días para mantener los archivos en la papelera. Cero significa \"para siempre\".",
|
||||
"The number of old versions to keep, per file.": "El número de versiones a antiguas a mantener para cada fichero.",
|
||||
"The number of days must be a number and cannot be blank.": "El número de días debe ser un número y no puede estar vacío.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "Número de días para conservar los archivos en la papelera. Cero significa indefinidamente.",
|
||||
"The number of old versions to keep, per file.": "Número de versiones antiguas a conservar por cada archivo.",
|
||||
"The number of versions must be a number and cannot be blank.": "El número de versiones debe ser un número y no puede estar vacío.",
|
||||
"The path cannot be blank.": "La ruta no puede estar vacía.",
|
||||
"The rate limit is applied to the accumulated traffic of all connections to this device.": "El límite de velocidad se aplica al tráfico acumulado de todas las conexiones a este dispositivo.",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "El límite de velocidad debe ser un número no negativo (0: sin límite)",
|
||||
"The remote device has not accepted sharing this folder.": "El dispositivo remoto no ha aceptado compartir esta carpeta.",
|
||||
"The remote device has paused this folder.": "El dispositivo remoto ha puesto en pausa esta carpeta.",
|
||||
"The rescan interval must be a non-negative number of seconds.": "El intervalo de actualización debe ser un número positivo de segundos.",
|
||||
"There are no devices to share this folder with.": "No hay dispositivos con los cuales compartir esta carpeta.",
|
||||
"The rescan interval must be a non-negative number of seconds.": "El intervalo de reescaneo debe ser un número positivo de segundos.",
|
||||
"There are no devices to share this folder with.": "No hay dispositivos con los que compartir esta carpeta.",
|
||||
"There are no file versions to restore.": "No hay versiones de archivos para restaurar.",
|
||||
"There are no folders to share with this device.": "No hay carpetas para compartir con este dispositivo.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Se reintentarán de forma automática y se sincronizarán cuando se resuelva el error.",
|
||||
"This Device": "Este Dispositivo",
|
||||
"This Month": "Este mes",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Esto podría permitir fácilmente el acceso a hackers para leer y modificar cualquier fichero de tu equipo.",
|
||||
"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.": "Este dispositivo no puede descubrir automáticamente a otros dispositivos o anunciar su propia dirección para que sea encontrado con otros. Solo dispositivos con direcciones configuradas como estáticas pueden conectarse.",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Esto podría permitir fácilmente el acceso a hackers para leer y modificar cualquier archivo de tu equipo.",
|
||||
"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.": "Este dispositivo no puede descubrir automáticamente a otros dispositivos o anunciar su propia dirección para que sea descubierto por otros. Solo dispositivos con direcciones estáticas configuradas pueden conectarse.",
|
||||
"This is a major version upgrade.": "Hay una actualización importante.",
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "Este ajuste controla el espacio libre necesario en el disco principal (por ejemplo, el índice de la base de datos).",
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "Determina el espacio libre necesario en el disco principal (con la. base de datos de índices).",
|
||||
"Time": "Hora",
|
||||
"Time the item was last modified": "Hora en que el ítem fue modificado por última vez",
|
||||
"Time the item was last modified": "Hora de última modificación del elemento",
|
||||
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Para conectarse con el dispositivo Syncthing llamado \"{{devicename}}\", añada un nuevo dispositivo remoto en su extremo con este ID:",
|
||||
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Para permitir una regla, marque la casilla. Para denegar una regla, déjela sin marcar.",
|
||||
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Para permitir una regla, marca la casilla. Para denegar una regla, déjala sin marcar.",
|
||||
"Today": "Hoy",
|
||||
"Trash Can": "Papelera",
|
||||
"Trash Can File Versioning": "Versionado de archivos de la papelera",
|
||||
"Trash Can File Versioning": "Papelera",
|
||||
"Type": "Tipo",
|
||||
"UNIX Permissions": "Permisos de UNIX",
|
||||
"Unavailable": "No disponible",
|
||||
"Unavailable/Disabled by administrator or maintainer": "No disponible/Deshabilitado por el administrador o mantenedor",
|
||||
"Undecided (will prompt)": "No decidido (se preguntará)",
|
||||
"Unexpected Items": "Elementos Inesperados",
|
||||
"Unexpected items have been found in this folder.": "Se han encontrado Elementos Inesperados en esta carpeta.",
|
||||
"Unexpected Items": "Elementos inesperados",
|
||||
"Unexpected items have been found in this folder.": "Se han encontrado elementos inesperados en esta carpeta.",
|
||||
"Unignore": "Dejar de ignorar",
|
||||
"Unknown": "Desconocido",
|
||||
"Unshared": "No compartido",
|
||||
"Unshared Devices": "Dispositivos no Enlazados",
|
||||
"Unshared Folders": "Carpetas no Compartidas",
|
||||
"Untrusted": "No Confiable",
|
||||
"Unshared Devices": "No compartida con",
|
||||
"Unshared Folders": "Carpetas no compartidas",
|
||||
"Untrusted": "No confiable",
|
||||
"Up to Date": "Actualizado",
|
||||
"Updated {%file%}": "Actualizado {{file}}",
|
||||
"Upgrade": "Actualizar",
|
||||
@@ -487,11 +492,11 @@
|
||||
"Upload Rate": "Velocidad de subida",
|
||||
"Uptime": "Tiempo de funcionamiento",
|
||||
"Usage reporting is always enabled for candidate releases.": "El informe de uso está siempre habilitado en las versiones candidatas.",
|
||||
"Use HTTPS for GUI": "Usar HTTPS para la Interfaz Gráfica de Usuario (GUI)",
|
||||
"Use notifications from the filesystem to detect changed items.": "Usar notificaciones del sistema de archivos para detectar elementos cambiados.",
|
||||
"Use HTTPS for GUI": "Usar HTTPS para la interfaz gráfica",
|
||||
"Use notifications from the filesystem to detect changed items.": "Usa las notificaciones del sistema de archivos para detectar elementos cambiados.",
|
||||
"User": "Usuario",
|
||||
"User Home": "Carpeta de inicio del usuario",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "No se ha configurado el nombre de usuario/la contraseña para la autenticación de la GUI. Por favor, considere configurarlos.",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "No se ha configurado el nombre de usuario/la contraseña para la autenticación de la GUI. Por favor, considera configurarlos.",
|
||||
"Using a QUIC connection over LAN": "Usando una conexión QUIC a través de una LAN",
|
||||
"Using a QUIC connection over WAN": "Usando una conexión QUIC a través de una WAN",
|
||||
"Using a direct TCP connection over LAN": "Utilizar una conexión TCP directa a través de LAN",
|
||||
@@ -499,44 +504,44 @@
|
||||
"Version": "Versión",
|
||||
"Versions": "Versiones",
|
||||
"Versions Path": "Ruta de las versiones",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Las versiones se borran automáticamente si son más antiguas que la edad máxima o exceden el número de ficheros permitidos en un intervalo.",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Las versiones se eliminan automáticamente si son más antiguas que la antigüedad máxima o se excede el número de archivos permitidos en un intervalo.",
|
||||
"Waiting to Clean": "Esperando para Limpiar",
|
||||
"Waiting to Scan": "Esperando para Escanear",
|
||||
"Waiting to Sync": "Esperando para Sincronizar",
|
||||
"Warning": "Advertencia",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "¡Peligro! Esta ruta es un directorio principal de la carpeta ya existente \"{{otherFolder}}\".",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Advertencia, esta ruta es una carpeta principal de una carpeta existente \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"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": "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.",
|
||||
"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.": "Cuando añada una nueva carpeta, tenga en cuenta que su ID se usa para unir carpetas entre dispositivos. Son sensibles a las mayúsculas y deben coincidir exactamente entre todos los dispositivos.",
|
||||
"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.": "Cuando se configura en más de uno o en ambos dispositivos, Syncthing intentará establecer múltiples conexiones simultáneamente. Si los valores difieren, se utilizará el más alto. Pon cero para que Syncthing decida por ti.",
|
||||
"Waiting to Scan": "Esperando para escanear",
|
||||
"Waiting to Sync": "Esperando para sincronizar",
|
||||
"Warning": "Atención",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Atención, esta ruta es un directorio principal de la carpeta ya existente \"{{otherFolder}}\".",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Atención, esta ruta es una carpeta principal de una carpeta existente \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Atención, esta ruta es un subdirectorio de una carpeta ya existente llamada \"{{otherFolder}}\".",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Atención, 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.": "Atención: si estás utilizando un observador externo como {{syncthingInotify}}, debes asegurarte de que está desactivado.",
|
||||
"Watch for Changes": "Detectar cambios",
|
||||
"Watching for Changes": "Detectando cambios",
|
||||
"Watching for changes discovers most changes without periodic scanning.": "La detección 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ñadas un nuevo dispositivo, ten en cuenta que este debe añadirse también en el otro lado.",
|
||||
"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.": "Cuando añadas una nueva carpeta, ten en cuenta que su ID se usa para unir carpetas entre dispositivos. Son sensibles a las mayúsculas y deben coincidir exactamente entre todos los dispositivos.",
|
||||
"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.": "Cuando se configura a más de una en ambos dispositivos, Syncthing intentará establecer múltiples conexiones simultáneamente. Si los valores difieren se usará el más alto. Deja a cero para que Syncthing decida.",
|
||||
"Yes": "Si",
|
||||
"Yesterday": "Ayer",
|
||||
"You can also copy and paste the text into a new message manually.": "También puedes copiar y pegar manualmente el texto en un nuevo mensaje.",
|
||||
"You can also select one of these nearby devices:": "También puede seleccionar uno de estos dispositivos cercanos:",
|
||||
"You can change your choice at any time in the Settings dialog.": "Puedes cambiar tu elección en cualquier momento en el panel de Ajustes.",
|
||||
"You can read more about the two release channels at the link below.": "Puedes leer más sobre los dos método de publicación de versiones en el siguiente enlace.",
|
||||
"You have no ignored devices.": "No tienes dispositivos ignorados.",
|
||||
"You have no ignored folders.": "No tienes carpetas ignoradas.",
|
||||
"You have unsaved changes. Do you really want to discard them?": "Tienes cambios sin guardar. ¿Quieres descartarlos realmente?",
|
||||
"You must keep at least one version.": "Debes mantener al menos una versión.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Nunca debe agregar o cambiar nada localmente en una carpeta \"{{receiveEncrypted}}\".",
|
||||
"You can read more about the two release channels at the link below.": "Puedes leer más sobre los dos canales de publicación de versiones en el siguiente enlace.",
|
||||
"You have no ignored devices.": "No hay dispositivos ignorados.",
|
||||
"You have no ignored folders.": "No hay carpetas ignoradas.",
|
||||
"You have unsaved changes. Do you really want to discard them?": "Hay cambios sin guardar. ¿Estás seguro de que quieres descartarlos?",
|
||||
"You must keep at least one version.": "Debes conservar al menos una versión.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Nunca debes añadir o cambiar nada localmente en una carpeta \"{{receiveEncrypted}}\".",
|
||||
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Tu aplicación de SMS debería abrirse para permitirte elegir el destinatario y enviarlo desde tu propio número.",
|
||||
"Your email app should open to let you choose the recipient and send it from your own address.": "Tu aplicación de correo electrónico debería abrirse para permitirte elegir el destinatario y enviarlo desde tu propia dirección.",
|
||||
"Your email app should open to let you choose the recipient and send it from your own address.": "Tu aplicación de email debería abrirse para permitirte elegir el destinatario y enviarlo desde tu propia dirección.",
|
||||
"days": "días",
|
||||
"deleted": "eliminado",
|
||||
"deny": "denegar",
|
||||
"directories": "directorios",
|
||||
"file": "fichero",
|
||||
"file": "archivo",
|
||||
"files": "archivos",
|
||||
"folder": "carpeta",
|
||||
"full documentation": "Documentación completa",
|
||||
"items": "Elementos",
|
||||
"items": "elementos",
|
||||
"modified": "modificado",
|
||||
"permit": "permiso",
|
||||
"seconds": "segundos",
|
||||
@@ -551,5 +556,5 @@
|
||||
"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."
|
||||
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} puede presentar de nuevo este dispositivo."
|
||||
}
|
||||
|
||||
@@ -1,123 +1,415 @@
|
||||
{
|
||||
"A device with that ID is already added.": "Sellise seadme ID'ga seade on juba lisatud.",
|
||||
"A device with that ID is already added.": "Sellise tunnusega seade on juba lisatud.",
|
||||
"A negative number of days doesn't make sense.": "Negatiivne päevade arv ei ole loogiline.",
|
||||
"API Key": "API Võti",
|
||||
"A new major version may not be compatible with previous versions.": "Uus põhiversioon ei pruugi ühilduv varasemate versioonidega.",
|
||||
"API Key": "API võti",
|
||||
"About": "Rakenduse teave",
|
||||
"Action": "Tegevus",
|
||||
"Actions": "Tegevused",
|
||||
"Active filter rules": "Aktiivsed filtrireeglid",
|
||||
"Add": "Lisa",
|
||||
"Add Device": "Lisa seade",
|
||||
"Add Folder": "Lisa kaust",
|
||||
"Add new folder?": "Lisa uus kaust?",
|
||||
"Add Remote Device": "Lisa kaugseade",
|
||||
"Add filter entry": "Lisa filtrikirje",
|
||||
"Add ignore patterns": "Lisa eiramismustreid",
|
||||
"Add new folder?": "Kas lisad uue kausta?",
|
||||
"Address": "Aadress",
|
||||
"Addresses": "Aadressid",
|
||||
"Advanced": "Täiendavad seadistused",
|
||||
"Advanced Configuration": "Täiendavad seadistused",
|
||||
"All Data": "Kõik andmed",
|
||||
"All Time": "Kõik ajad",
|
||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Kõik selle seadmega jagatud kaustad peavad olema kaitstud salasõnaga, mis tähendab, et edastatud andmed on ilma salasõnata loetamatud.",
|
||||
"Allow Anonymous Usage Reporting?": "Kas lubad anonüümset statistikakogumist rakenduse kasutamise kohta?",
|
||||
"Allowed Networks": "Lubatud võrgud",
|
||||
"Alphabetic": "Tähestikuline",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Kui kausta tüüp on „{{foldertype}}“, siis on alati lülitatud sisse.",
|
||||
"Anonymous Usage Reporting": "Anonüümne aruandlus kasutuse kohta",
|
||||
"Applied to LAN": "Kehtib kohtvõrgu puhul",
|
||||
"Apply": "Rakenda",
|
||||
"Are you sure you want to override all remote changes?": "Kas sa soovid sürjutada kõik eemalt tehtud muudatused?",
|
||||
"Are you sure you want to permanently delete all these files?": "Kas sa oled kindel, et soovid jäädavalt kõik need failid kustutada?",
|
||||
"Are you sure you want to remove device {%name%}?": "Kas sa oled kindel, et soovid {{name}} seadme eemaldada?",
|
||||
"Are you sure you want to remove folder {%label%}?": "Kas sa oled kindel, et soovid {{label}} kausta eemaldada?",
|
||||
"Are you sure you want to restore {%count%} files?": "Kas sa oled kindel, et soovid {{count}} faili taastada?",
|
||||
"Are you sure you want to revert all local changes?": "Kas sa oled kindel, et soovid kõik kohalikud muudatused tagasi pöörata?",
|
||||
"Are you sure you want to upgrade?": "Kas sa oled kindel, et soovid uuendamise ette võtta?",
|
||||
"Authentication Required": "Autentimine on vajalik",
|
||||
"Authors": "Autorid",
|
||||
"Auto Accept": "Nõustu automaatselt",
|
||||
"Automatic Crash Reporting": "Automaatne teavitus rakenduste kokkujooksmise kohta",
|
||||
"Automatic upgrades": "Automaatsed uuendused",
|
||||
"Automatic upgrades are always enabled for candidate releases.": "Automaatsed uuendused on kandidaatversioonide puhul alati lubatud.",
|
||||
"Be careful!": "Ettevaatust!",
|
||||
"Body:": "Kirja sisu:",
|
||||
"Bugs": "Vead",
|
||||
"Cancel": "Katkesta",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Kui kausta tüüp on „{{foldertype}}“, siis seda ei saa kasutada.",
|
||||
"Changelog": "Muudatuste ajalugu",
|
||||
"Clean out after": "Puhasta peale järgneva aja möödumist",
|
||||
"Cleaning Versions": "Kustutan versioone",
|
||||
"Cleanup Interval": "Kustutamise välp",
|
||||
"Click to see full identification string and QR code.": "Täispikka tunnust ja QR-koodi näed, kui klõpsad siin.",
|
||||
"Close": "Sulge",
|
||||
"Command": "Käsk",
|
||||
"Compression": "Pakkimine",
|
||||
"Configuration Directory": "Seadistuste kaust",
|
||||
"Configuration File": "Seadistuste fail",
|
||||
"Configured": "Seadistatud",
|
||||
"Connected (Unused)": "Ühendatud (pole kasutusel)",
|
||||
"Connection Error": "Ühenduse viga",
|
||||
"Connection Management": "Ühenduste haldus",
|
||||
"Connection Type": "Ühenduse tüüp",
|
||||
"Connections": "Ühendused",
|
||||
"Copied from elsewhere": "Kopeeritud mujalt",
|
||||
"Copied from original": "Kopeeritud algallikast",
|
||||
"Copied!": "Kopeeritud!",
|
||||
"Copy": "Kopeeri",
|
||||
"Copy failed! Try to select and copy manually.": "Kopeerimine ei õnnestunud! Proovi valida ja kopeerida käsitsi.",
|
||||
"Currently Shared With Devices": "Hetkel jagatud seadmetega",
|
||||
"Custom Range": "Sinu valitud vahemik",
|
||||
"Danger!": "Ohtlik!",
|
||||
"Database Location": "Andmebaasi asukoht",
|
||||
"Debug": "Veaotsing",
|
||||
"Debugging Facilities": "Teadete tüübid veaotsingul",
|
||||
"Default": "Vaikimisi",
|
||||
"Default Configuration": "Vaikimisi seadistus",
|
||||
"Default Device": "Vaikimisi seade",
|
||||
"Default Folder": "Vaikimisi kaust",
|
||||
"Default Ignore Patterns": "Vaikimisi eiramismustrid",
|
||||
"Defaults": "Vaikimisi väärtused",
|
||||
"Delete": "Kustuta",
|
||||
"Deleted {%file%}": "Kustutasin {{file}} faili",
|
||||
"Deselect All": "Eemalda kogu valik",
|
||||
"Deselect devices to stop sharing this folder with.": "Eemalda seadmed, millega sa enam ei taha seda kausta jagada.",
|
||||
"Deselect folders to stop sharing with this device.": "Eemalda kaustad, mida sa enam ei taha selle seadmega jagada.",
|
||||
"Device": "Seade",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Seade \"{{name}}\" ({{device}} aadressil {{address}}) soovib luua ühendust. Kas lisada uus seade?",
|
||||
"Device ID": "Seadme ID",
|
||||
"Device Certificate": "Seadme sertifikaat",
|
||||
"Device ID": "Seadme tunnus",
|
||||
"Device Identification": "Seadme tuvastamine",
|
||||
"Device Name": "Seadme nimi",
|
||||
"Device Status": "Seadme olek",
|
||||
"Device is untrusted, enter encryption password": "Seadme pole usaldusväärne, palun sisesta krüptimise salasõna",
|
||||
"Device rate limits": "Seadme kiiruspiirangud",
|
||||
"Device that last modified the item": "Sead, kus objekt oli viimati muudetud",
|
||||
"Devices": "Seadmed",
|
||||
"Disable Crash Reporting": "Lülita rakenduse kokkujooksmisest teavitamine välja",
|
||||
"Disabled": "Pole kasutusel",
|
||||
"Discard": "Loobu",
|
||||
"Disconnected": "Ühendus puudub",
|
||||
"Disconnected (Inactive)": "Ühendus on katkestatud (pole aktiivne)",
|
||||
"Disconnected (Unused)": "Ühendus on katkestatud (pole kasutatud)",
|
||||
"Discovered": "Tuvastatud",
|
||||
"Discovery": "Avastamine",
|
||||
"Discovery Failures": "Vead avastamisel",
|
||||
"Discovery Status": "Avastamise olek",
|
||||
"Dismiss": "Loobu",
|
||||
"Do not restore": "Ära taasta",
|
||||
"Do not restore all": "Ära taasta kõiki",
|
||||
"Do you want to enable watching for changes for all your folders?": "Kas sa tahad lülitada sisse kõikide oma kaustade muudatuste jälgimise?",
|
||||
"Documentation": "Dokumentatsioon",
|
||||
"Download Rate": "Allalaadimise Kiirus",
|
||||
"Downloaded": "Alla laetud",
|
||||
"Download Rate": "Allalaadimise kiirus",
|
||||
"Downloaded": "Allalaaditud",
|
||||
"Downloading": "Allalaadimine",
|
||||
"Edit": "Muuda",
|
||||
"Edit Device": "Muuda Seadet",
|
||||
"Edit Device": "Muuda seadet",
|
||||
"Edit Device Defaults": "Muuda seadme vaikeseadistusi",
|
||||
"Edit Folder": "Muuda Kausta",
|
||||
"Edit Folder Defaults": "Muuda kaustade vaikimisi seadistusi",
|
||||
"Editing {%path%}.": "{{path}} muutmine.",
|
||||
"Enable Crash Reporting": "Võta kasutusele rakenduse kokkujooksmistest teatamine",
|
||||
"Enable NAT traversal": "Luba NAT traversal",
|
||||
"Enable Relaying": "Luba edastussõlmede kasutamine",
|
||||
"Enabled": "Kasutusel",
|
||||
"Error": "Viga",
|
||||
"Extended Attributes": "Täiendavad atribuudid",
|
||||
"External": "Väline",
|
||||
"External File Versioning": "Väline failide versioonihaldus",
|
||||
"Failed to load file versions.": "Failiversioonide laadimine ei õnnestunud.",
|
||||
"Failed to load ignore patterns.": "Eiramismustrite laadimine ei õnnestunud.",
|
||||
"Failed to set up, retrying": "Seadistamine ei õnnestunud, proovin uuesti",
|
||||
"File Pull Order": "Failide Tirimise Järjekord",
|
||||
"File Versioning": "Failide versioonihaldus",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Failid liigutatakse asendamisel või kustutamisel .stversions kataloogi.",
|
||||
"Filter by date": "Filtreeri kuupäeva alusel",
|
||||
"Filter by name": "Filtreeri nime alusel",
|
||||
"Folder": "Kaust",
|
||||
"Folder ID": "Kausta ID",
|
||||
"Folder Label": "Kausta Silt",
|
||||
"Folder Type": "Kausta Tüüp",
|
||||
"Folder Path": "Kausta asukoht",
|
||||
"Folder Status": "Kausta olek",
|
||||
"Folder Type": "Kausta tüüp",
|
||||
"Folders": "Kaustad",
|
||||
"GUI": "GUI",
|
||||
"GUI Authentication Password": "GUI Autentimise Salasõna",
|
||||
"Forever": "Igavesti",
|
||||
"Full Rescan Interval (s)": "Täiemahulise kordusskaneerimise välp (sek)",
|
||||
"GUI": "Kasutajaliides",
|
||||
"GUI Authentication Password": "Salasõna kasutajaliidese autentimiseks",
|
||||
"GUI Authentication User": "GUI Autentimise Kasutajatunnus",
|
||||
"GUI Authentication: Set User and Password": "Kasutajaliidese autentimine: sisesta kasutajanimi ja salasõna",
|
||||
"GUI Listen Address": "Aadress, mida kasutajaliides kuulab",
|
||||
"GUI Override Directory": "Kasutajaliidese sürjutamise kaust",
|
||||
"GUI Theme": "GUI Teema",
|
||||
"Generate": "Genereeri",
|
||||
"Global State": "Globaalne Olek",
|
||||
"Help": "Abi",
|
||||
"Global Discovery": "Üldine avastamine",
|
||||
"Global State": "Üldine olek",
|
||||
"Help": "Abiteave",
|
||||
"Home page": "Avaleht",
|
||||
"Identification": "Tunnus",
|
||||
"If untrusted, enter encryption password": "Kui usaldusväärsus on veel kinnitamata, siis sisesta krüptosalasõna",
|
||||
"Ignore": "Ignoreeri",
|
||||
"Ignore Patterns": "Ignoreeri Mustreid",
|
||||
"Ignore Patterns": "Eira mustreid",
|
||||
"Ignore Permissions": "Ignoreeri Õigusi",
|
||||
"Incoming Rate Limit (KiB/s)": "Siseneva Kiiruse Piirang (KiB/s)",
|
||||
"Ignored Devices": "Eiratud seadmed",
|
||||
"Ignored Folders": "Eiratud kaustad",
|
||||
"Ignored at": "Eiratud seadmes",
|
||||
"Included Software": "Kaasa arvatud tarkvara",
|
||||
"Incoming Rate Limit (KiB/s)": "Siseneva liikluse kiiruspiirang (KiB/s)",
|
||||
"Incorrect user name or password.": "Vigane kasutajanimi või salasõna.",
|
||||
"Info": "Teave",
|
||||
"Introduction": "Sissejuhatus",
|
||||
"Keep Versions": "Säilita Versioone",
|
||||
"LDAP": "LDAP",
|
||||
"Largest First": "Suurim Enne",
|
||||
"Largest First": "Esmalt suuremad",
|
||||
"Last 30 Days": "Viimased 30 päeva",
|
||||
"Last 7 Days": "Viimased 7 päeva",
|
||||
"Last Month": "Viimane kuu",
|
||||
"Last Scan": "Viimane skaneerimine",
|
||||
"Last seen": "Viimati nähtud",
|
||||
"Latest Change": "Viimane Muudatus",
|
||||
"Learn more": "Veel infot",
|
||||
"Local State": "Kohalik Olek",
|
||||
"Local State (Total)": "Kohalik Olek (Summaarne)",
|
||||
"Maximum Age": "Maksimaalne Vanus",
|
||||
"Metadata Only": "Ainult Meta-andmed",
|
||||
"Learn more at {%url%}": "Lisateavet leiad siit: {{url}}",
|
||||
"Limit": "Piirang",
|
||||
"Limit Bandwidth in LAN": "Piira ribalaiust kohtvõrgus",
|
||||
"Loading data...": "Laadin andmeid…",
|
||||
"Loading...": "Laadin...",
|
||||
"Local Additions": "Kohalikud lisandused",
|
||||
"Local Discovery": "Kohalik avastamine",
|
||||
"Local State": "Kohalik olek",
|
||||
"Local State (Total)": "Kohalik olek (summaarne)",
|
||||
"Locally Changed Items": "Kohalikus seadmes muudetud objektid",
|
||||
"Log": "Logi",
|
||||
"Log File": "Logifail",
|
||||
"Log In": "Logi sisse",
|
||||
"Log Out": "Logi välja",
|
||||
"Log in to see paths information.": "Asukohtade teavet näed peale sisselogimist.",
|
||||
"Log in to see version information.": "Versiooniteavet näed peale sisselogimist.",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Logi lõpu täiendamine on peatunud. Jätkamiseks keri väljund lõpuni.",
|
||||
"Login failed, see Syncthing logs for details.": "Sisselogimine ei õnnestunud. Üksikasjalikku teavet leiad Syncthingi logidest.",
|
||||
"Logs": "Logid",
|
||||
"Major Upgrade": "Suurem versiooniuuendus",
|
||||
"Mass actions": "Pakktöötlused",
|
||||
"Maximum Age": "Maksimaalne vanus",
|
||||
"Maximum single entry size": "Maksimaalne ühe objekti maht",
|
||||
"Maximum total size": "Maksimaalne kogumaht",
|
||||
"Metadata Only": "Ainult metaandmed",
|
||||
"Minimum Free Disk Space": "Minimaalne Vaba Kettaruum",
|
||||
"Mod. Device": "Muudetud seadmes",
|
||||
"Mod. Time": "Muutmise aeg",
|
||||
"More than a month ago": "Enam, kui kuu tagasi",
|
||||
"More than a week ago": "Enam, kui nädal tagasi",
|
||||
"More than a year ago": "Enam, kui aasta tagasi",
|
||||
"Move to top of queue": "Liiguta järjekorra algusesse",
|
||||
"Multi level wildcard (matches multiple directory levels)": "Mitmetasandiline metamärk (vastab mitmele kaustapuu tasandile)",
|
||||
"Never": "Eikunagi",
|
||||
"New Device": "Uus Seade",
|
||||
"New Folder": "Uus Kaust",
|
||||
"Newest First": "Uusimad Ennem",
|
||||
"Newest First": "Esmalt uuemad",
|
||||
"No": "Ei",
|
||||
"OK": "OK",
|
||||
"Oldest First": "Vanimad Ennem",
|
||||
"No File Versioning": "Failide versioonihaldus puudub",
|
||||
"No files will be deleted as a result of this operation.": "Selle tegevuse tulemusel ei kustutata ühtegi faili.",
|
||||
"No rules set": "Ühtegi reeglit pole määratud",
|
||||
"No upgrades": "Uuendusi pole",
|
||||
"Not shared": "Pole jagatud",
|
||||
"Notice": "Märkus",
|
||||
"Number of Connections": "Ühenduste arv",
|
||||
"OK": "Sobib",
|
||||
"Off": "Pole kasutusel",
|
||||
"Oldest First": "Esmalt vanemad",
|
||||
"Optional descriptive label for the folder. Can be different on each device.": "Kausta kirjeldav silt, kui vajad seda. Võib olla igas seadmes erinev.",
|
||||
"Options": "Valikud",
|
||||
"Outgoing Rate Limit (KiB/s)": "Väljuva Kiiruse Piirang (KiB/s)",
|
||||
"Override Changes": "Kirjuta Muudatused Üle",
|
||||
"Out of Sync": "Pole sünkroonis",
|
||||
"Out of Sync Items": "Sünkroonimata objektid",
|
||||
"Outgoing Rate Limit (KiB/s)": "Väljuva kiiruse piirang (KiB/s)",
|
||||
"Override": "Sürjutamine",
|
||||
"Override Changes": "Sürjuta muudatused",
|
||||
"Ownership": "Omand",
|
||||
"Password": "Salasõna",
|
||||
"Path": "Asukoht",
|
||||
"Paths": "Asukohad",
|
||||
"Pause": "Peata",
|
||||
"Pause All": "Peata Kõik",
|
||||
"Paused": "Peatatud",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "Palun seadista GUI Autentimise Kasutajatunnus ning Salasõna Seadistuste dialoogist.",
|
||||
"Paused (Unused)": "Peatatud (pole kasutatud)",
|
||||
"Pending changes": "Ootel muudatused",
|
||||
"Please consult the release notes before performing a major upgrade.": "Enne suurema versiooniuuenduse tegemist palun loe muudatuste logi ja uue versiooni teavet.",
|
||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "Kasutajaliidese autentimiseks sisesta seadistuste vaatest kasutajanimi ja salasõna.",
|
||||
"Please wait": "Palun oota",
|
||||
"Preparing to Sync": "Valmistun sünkroonima",
|
||||
"Preview": "Eelvaade",
|
||||
"Preview Usage Report": "Kasutusaruande eelvaade",
|
||||
"QR code": "QR-kood",
|
||||
"QUIC LAN": "QUIC LAN",
|
||||
"QUIC WAN": "QUIC WAN",
|
||||
"Random": "Juhuslik",
|
||||
"Receive Encrypted": "Vastuvõtmine krüptituna",
|
||||
"Receive Only": "Ainult vastuvõtmine",
|
||||
"Received data is already encrypted": "Vastuvõetud andmed on juba krüptitud",
|
||||
"Recent Changes": "Hiljutised muudatused",
|
||||
"Reduced by ignore patterns": "Hiljutised eiramismustrid",
|
||||
"Release Notes": "Muudatuste logi",
|
||||
"Remote Devices": "Kaugseadmed",
|
||||
"Remote GUI": "Kaugseadme graafiline kasutajaliides",
|
||||
"Remove": "Eemalda",
|
||||
"Remove Device": "Kaugseade",
|
||||
"Remove Folder": "Kaugkaust",
|
||||
"Required identifier for the folder. Must be the same on all cluster devices.": "Kohustuslik kausta identifikaator. Peab olema sama kõigil klastri seadmetel.",
|
||||
"Rescan": "Skaneeri uuesti",
|
||||
"Rescan All": "Skaneeri kõik uuesti",
|
||||
"Rescans": "Uuesti skaneerimised",
|
||||
"Restart": "Taaskäivita",
|
||||
"Restart Needed": "Taaskäivitamine Vajalik",
|
||||
"Restarting": "Taaskäivitamine",
|
||||
"Restore": "Taasta",
|
||||
"Restore Versions": "Taasta versioone",
|
||||
"Resume": "Jätka",
|
||||
"Resume All": "Jätka Kõik",
|
||||
"Reused": "Uuesti kasutatud",
|
||||
"Revert": "Pööra muudatus tagasi",
|
||||
"Revert Local Changes": "Pööra kohalikud muudatused tagasi",
|
||||
"Save": "Salvesta",
|
||||
"Saving changes": "Salvestan muudatusi",
|
||||
"Scan Time Remaining": "Järelejäänud skaneerimisaeg",
|
||||
"Scanning": "Skaneerin",
|
||||
"Select All": "Vali kõik",
|
||||
"Select a version": "Vali versioon",
|
||||
"Select additional devices to share this folder with.": "Vali täiendavad seadmed, millega tahad seda kausta jagada.",
|
||||
"Select additional folders to share with this device.": "Vali täiendavad kaustad, mida tahad selle seadmega jagada.",
|
||||
"Select latest version": "Vali viimane versioon",
|
||||
"Select oldest version": "Vali vanim versioon",
|
||||
"Send & Receive": "Saatmine ja vastuvõtmine",
|
||||
"Send Only": "Ainult saatmine",
|
||||
"Send Ownership": "Edasta omand",
|
||||
"Settings": "Seadistused",
|
||||
"Share": "Jaga",
|
||||
"Share Folder": "Jaga Kausta",
|
||||
"Share this folder?": "Kas jagada seda kausta?",
|
||||
"Share by Email": "Jaga e-kirjaga",
|
||||
"Share by SMS": "Jaga SMS-iga",
|
||||
"Share this folder?": "Kas tahad jagada seda kausta?",
|
||||
"Shared Folders": "Jagatud kaustad",
|
||||
"Shared With": "Jagatud järgnevalt",
|
||||
"Sharing": "Jagamine",
|
||||
"Show ID": "Kuva ID",
|
||||
"Show QR": "Kuva QR",
|
||||
"Shut Down": "Lülita välja",
|
||||
"Shutdown Complete": "Väljalülitamine on lõppenud",
|
||||
"Simple": "Lihtne",
|
||||
"Simple File Versioning": "Lihtne Faili Versioonindus",
|
||||
"Size": "Suurus",
|
||||
"Smallest First": "Esmalt väiksemad",
|
||||
"Some items could not be restored:": "Mõne objekti taastamine polnud võimalik:",
|
||||
"Source Code": "Lähtekood",
|
||||
"Stable releases only": "Ainult stabiilsed väljalasked",
|
||||
"Staggered": "Järkjärguline",
|
||||
"Start Browser": "Käivita Brauser",
|
||||
"Statistics": "Statistika",
|
||||
"Stay logged in": "Jää sisselogituks",
|
||||
"Stopped": "Peatatud",
|
||||
"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.": "Salvestab ja sünkroonib vaid krüptitud andmeid. Kaustad kõikides teistes seadmetes peavad olema seadistatud sama salasõnaga ning nende tüüp peab olema ka „{{receiveEncrypted}}“.",
|
||||
"Subject:": "Teema:",
|
||||
"Support": "Abi",
|
||||
"Support Bundle": "Veaotsingu tugipakett",
|
||||
"Sync Ownership": "Sünkrooni omand",
|
||||
"Sync Status": "Sünkroonimise olek",
|
||||
"Syncing": "Sünkroniseerimine",
|
||||
"Syncthing is restarting.": "Syncthing taaskäivitub.",
|
||||
"Syncthing is saving changes.": "Syncthing salvestab muudatusi.",
|
||||
"Syncthing is upgrading.": "Syncthing uueneb.",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing ei suutnud sinu päringut töödelda. Probleemi püsimisel värskenda lehte või taaskäivita Syncthing.",
|
||||
"The device ID cannot be blank.": "Seadme ID ei tohi olla tühi.",
|
||||
"TCP LAN": "Kohtvõrgu TCP",
|
||||
"TCP WAN": "Laivõrgu TCP",
|
||||
"The Syncthing Authors": "Syncthingi autorid",
|
||||
"The Syncthing admin interface is configured to allow remote access without a password.": "Syncthingi haldusliides on seadistatud lubamaks ligipääsu ilma salasõnata.",
|
||||
"The aggregated statistics are publicly available at the URL below.": "Koondstatistika on avalikult saadaval järgneval lehel.",
|
||||
"The device ID cannot be blank.": "Seadme tunnus ei tohi olla tühi.",
|
||||
"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.": "Krüptitud kasutusaruanne saadetakse kord päevas. Sellega peetakse arvet kasutatavate platvormide, rakenduste versioonide ja kaustade suuruste üle. Kui varemteatatud andmekogu muutub, siis sa näed seda vaadet uuesti.",
|
||||
"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.": "Seadme tunnus ei tundu olema korrektne. Ta peaks olema 52 või 56 märki pikk, kus leidub vaid numbreid ja tähemärke ning võib olla ka tühikuid ja kriipse.",
|
||||
"The folder ID cannot be blank.": "Kausta ID ei tohi olla tühi.",
|
||||
"The folder ID must be unique.": "Kausta ID peab olema unikaalne.",
|
||||
"The folder path cannot be blank.": "Kausta asukoht ei tohi olla tühi!",
|
||||
"The folder path cannot be blank.": "Kausta asukoht ei tohi olla tühi.",
|
||||
"The following items could not be synchronized.": "Järgnevaid üksusi ei õnnestunud sünkroniseerida.",
|
||||
"The maximum age must be a number and cannot be blank.": "Maksimaalne vanus peab olema arv ning ei tohi olla tühi.",
|
||||
"The number of connections must be a non-negative number.": "Ühenduste arv peab olema nullist suurem number.",
|
||||
"The number of days must be a number and cannot be blank.": "Päevade arv peab olema number ega tohi jääda tühjaks.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "Päevade arv failide hoidmiseks prügikastis. Null (0) tähistab piiranguteta hoidmist.",
|
||||
"The number of old versions to keep, per file.": "Alleshoitavate faili vanade versioonide arv.",
|
||||
"The number of versions must be a number and cannot be blank.": "Versioonide arv peab olema number ega tohi jääda tühjaks.",
|
||||
"The path cannot be blank.": "Asukoht ei saa jääda tühjaks.",
|
||||
"There are no devices to share this folder with.": "Pole ühtegi seadet, millega saaks seda kausta jagada.",
|
||||
"There are no file versions to restore.": "Pole ühtegi taastatavat failiversiooni.",
|
||||
"There are no folders to share with this device.": "Pole ühtegi kausta, mida selle seadmega jagada.",
|
||||
"This Device": "See seade",
|
||||
"This Month": "Sel kuul",
|
||||
"This is a major version upgrade.": "Tegemist on suurema versiooniuuendusega.",
|
||||
"Time": "Aeg",
|
||||
"Time the item was last modified": "Aeg, millal objekti viimati muudeti",
|
||||
"Today": "Täna",
|
||||
"Trash Can": "Prügikast",
|
||||
"Type": "Tüüp",
|
||||
"UNIX Permissions": "UNIX-i õigused",
|
||||
"Unavailable": "Pole saadaval",
|
||||
"Unexpected Items": "Ootamatud ja plaanivälised objektid",
|
||||
"Unignore": "Lõpeta eiramine",
|
||||
"Unknown": "Teadmata",
|
||||
"Upload Rate": "Üleslaadimise Kiirus",
|
||||
"Unshared": "Pole jagatud",
|
||||
"Unshared Devices": "Mittejagatud seadmed",
|
||||
"Unshared Folders": "Mittejagatud kaustad",
|
||||
"Untrusted": "Pole usaldusväärne",
|
||||
"Up to Date": "Uuendatud ja sünkroonis",
|
||||
"Updated {%file%}": "{{file}} fail on uuendatud",
|
||||
"Upgrade": "Uuenda",
|
||||
"Upgrade To {%version%}": "Uuenda versioonini {{version}}",
|
||||
"Upgrading": "Uuendan",
|
||||
"Upload Rate": "Üleslaadimise kiirus",
|
||||
"Uptime": "Kasulik tööaeg",
|
||||
"Use HTTPS for GUI": "Kasuta HTTPS'i GUI jaoks",
|
||||
"User": "Kasutaja",
|
||||
"User Home": "Kasutaja kodukaust",
|
||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Kasutajaliidese autentimiseks pole lisatud kasutajanime ja salasõna. Me soovitame, et kindlasti teed seda.",
|
||||
"Version": "Versioon",
|
||||
"Versions": "Versioonid",
|
||||
"Versions Path": "Versioonide asukoht",
|
||||
"Warning": "Hoiatus",
|
||||
"Watch for Changes": "Jälgi muudatusi",
|
||||
"Watching for Changes": "Jälgin muudatusi",
|
||||
"Yes": "Jah",
|
||||
"Yesterday": "Eile",
|
||||
"You have no ignored devices.": "Sul pole ühtegi eiratud seadet.",
|
||||
"You have no ignored folders.": "Sul pole ühtegi eiratud kausta.",
|
||||
"You have unsaved changes. Do you really want to discard them?": "Sul on salvestamata muudatusi. Kas sa kindlasti tahad neist loobuda?",
|
||||
"You must keep at least one version.": "Sa pead alles hoidma vähemalt ühe versiooni.",
|
||||
"days": "päeva",
|
||||
"deleted": "kustutatud",
|
||||
"deny": "keela",
|
||||
"directories": "kaustad",
|
||||
"file": "fail",
|
||||
"files": "failid",
|
||||
"folder": "kaust",
|
||||
"full documentation": "täisdokumentatsioon",
|
||||
"items": "objektid",
|
||||
"modified": "muudetud",
|
||||
"permit": "luba",
|
||||
"seconds": "sekundit",
|
||||
"theme": {
|
||||
"name": {
|
||||
"black": "Süsimust kujundus",
|
||||
"dark": "Tume kujundus",
|
||||
"default": "Vaikimisi kujundus",
|
||||
"light": "Hele kujundus"
|
||||
}
|
||||
},
|
||||
"unknown device": "tundmatu seade",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} soovib jagada kausta \"{{folder}}\".",
|
||||
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} soovib jagada kausta \"{{folderlabel}}\" ({{folder}})."
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
"External File Versioning": "Fitxategi bertsioen kanpoko kudeaketa",
|
||||
"Failed Items": "Huts egin duten fitxategiak",
|
||||
"Failed to load ignore patterns.": "Huts egin du baztertze ereduak kargatzean.",
|
||||
"Failed to setup, retrying": "Konfigurazioan huts egitea, berriro saiatuz",
|
||||
"Failed to set up, retrying": "Konfigurazioan huts egitea, berriro saiatuz",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "IPv6 zerbitzariei buruzko konexioak huts eginen du, IPv6 konektibitaterik ez bada",
|
||||
"File Pull Order": "Fitxategiak berreskuratzeko ordena",
|
||||
"File Versioning": "Fitxategiak zaintzeko metodoa",
|
||||
@@ -309,7 +309,7 @@
|
||||
"Show diff with previous version": "Erakutsi aurreko bertsioarekiko aldeak",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Tresnaren ID-aren ordez erakutsia, taldearen egoeran. Beste tresneri erakutsia izanen da, izen erabilgarri bat bezala",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Tresnaren ID-aren ordez erakutsia, taldearen egoeran. Hutsa utzia balin bada, urrun den tresnak proposatu izenarekin aktualizatua izanen da",
|
||||
"Shutdown": "Geldi",
|
||||
"Shut Down": "Geldi",
|
||||
"Shutdown Complete": "Gelditua!",
|
||||
"Simple File Versioning": "Bertsioen segitze sinplifikatua",
|
||||
"Single level wildcard (matches within a directory only)": "Hein bakar bateko jokerra (karpetaren barnean bakarrik dagokiona)",
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
"External": "Ulkoinen",
|
||||
"External File Versioning": "Ulkoinen tiedostoversionti",
|
||||
"Failed Items": "Epäonnistuneet kohteet",
|
||||
"Failed to setup, retrying": "Käyttöönotto epäonnistui, Yritetään uudelleen",
|
||||
"Failed to set up, retrying": "Käyttöönotto epäonnistui, Yritetään uudelleen",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Yhteys IPv6-palvelimiin todennäköisesti epäonnistuu, koska IPv6-yhteyksiä ei ole.",
|
||||
"File Pull Order": "Tiedostojen noutojärjestys",
|
||||
"File Versioning": "Tiedostoversiointi",
|
||||
@@ -288,7 +288,7 @@
|
||||
"Show diff with previous version": "Näytä muutokset edelliseen versioon",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Näytetään ryhmän tiedoissa laitteen ID:n sijaan. Ilmoitetaan muille laitteille vaihtoehtoisena oletusnimenä.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Näytetään ryhmän tiedoissa laitteen ID:n sijaan. Tyhjä nimi päivitetään laitteen ilmoittamaksi nimeksi.",
|
||||
"Shutdown": "Sammuta",
|
||||
"Shut Down": "Sammuta",
|
||||
"Shutdown Complete": "Sammutus valmis",
|
||||
"Simple File Versioning": "Yksinkertainen tiedostoversiointi",
|
||||
"Single level wildcard (matches within a directory only)": "Yksitasoinen jokerimerkki (vaikuttaa vain kyseisen kansion sisällä)",
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"A device with that ID is already added.": "May naidagdag na device na may ganitong ding ID.",
|
||||
"A negative number of days doesn't make sense.": "Walang saysay ang negatibong numero ng araw.",
|
||||
"A new major version may not be compatible with previous versions.": "Maaring hindi compatible ang isang bagong major na beryson sa mga nakaraang bersyon.",
|
||||
"A device with that ID is already added.": "May naidagdag na device na may ganito ring ID.",
|
||||
"A negative number of days doesn't make sense.": "Walang saysay ang negatibong bilang ng araw.",
|
||||
"A new major version may not be compatible with previous versions.": "Maaring hindi compatible ang isang bagong major na bersiyon sa mga nakaraang bersiyon.",
|
||||
"API Key": "API Key",
|
||||
"About": "Tungkol sa",
|
||||
"Action": "Aksyon",
|
||||
"Actions": "Mga Aksyon",
|
||||
"Action": "Aksiyon",
|
||||
"Actions": "Mga Aksiyon",
|
||||
"Active filter rules": "Mga aktibong tuntunin sa pag-filter",
|
||||
"Add": "Magdagdag",
|
||||
"Add Device": "Magdagdag ng Device",
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Mga Pinapayagang Network",
|
||||
"Alphabetic": "Alpabetiko",
|
||||
"Altered by ignoring deletes.": "Binago sa pamamagitan ng hindi pagpansin sa mga pagtanggal.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Palaging nakabukas kung ang uri ng folder ay nakatakda bilang \"{{foldertype}}\".",
|
||||
"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.": "Pinapamahala ng external na command ang file versioning. Kailangan nitong tanggalin ang file mula sa binabahaging folder. Kung may mga space ang path sa application, kailangan itong i-quote.",
|
||||
"Anonymous Usage Reporting": "Anonymous na Pag-uulat ng Paggamit",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Nagbago ang pormat ng anonymous na ulat ng paggamit. Gusto mo bang lumipat sa bagong pormat?",
|
||||
@@ -52,9 +53,10 @@
|
||||
"Body:": "Body:",
|
||||
"Bugs": "Mga Bug",
|
||||
"Cancel": "Kanselahin",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Hindi maaaring paganahin kapag ang uri ng folder ay \"{{foldertype}}\".",
|
||||
"Changelog": "Mga Pagbabago",
|
||||
"Clean out after": "Linisin pagkatapos",
|
||||
"Cleaning Versions": "Mga Bersyon ng Paglinis",
|
||||
"Cleaning Versions": "Mga Bersiyon ng Paglinis",
|
||||
"Cleanup Interval": "Pagitan ng Paglinis",
|
||||
"Click to see full identification string and QR code.": "I-click upang makita ang buong string ng pagkakakilanlan at QR code.",
|
||||
"Close": "Isara",
|
||||
@@ -80,6 +82,7 @@
|
||||
"Custom Range": "Custom na Saklaw",
|
||||
"Danger!": "Panganib!",
|
||||
"Database Location": "Lokasyon ng Database",
|
||||
"Debug": "Debug",
|
||||
"Debugging Facilities": "Mga Facility ng Pag-debug",
|
||||
"Default": "Default",
|
||||
"Default Configuration": "Default na Configuration",
|
||||
@@ -152,14 +155,14 @@
|
||||
"External": "Panlabas",
|
||||
"External File Versioning": "Panlabas na File Versioning",
|
||||
"Failed Items": "Mga Nabigong Item",
|
||||
"Failed to load file versions.": "Nabigong i-load ang mga bersyon ng file.",
|
||||
"Failed to load file versions.": "Nabigong i-load ang mga bersiyon ng file.",
|
||||
"Failed to load ignore patterns.": "Nabigong i-load ang mga ignore pattern.",
|
||||
"Failed to setup, retrying": "Nabigong i-set up, sinusubukan muli",
|
||||
"Failed to set up, retrying": "Nabigong i-set up, sinusubukan muli",
|
||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Inaasahan ang pagbigo sa pagkonekta sa mga IPv6 na server kapag walang konektibidad sa IPv6.",
|
||||
"File Pull Order": "Order ng Pagkuha ng File",
|
||||
"File Versioning": "File Versioning",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Nilipat ang mga file sa .stversions na direktoryo kapag pinalitan o binura ng Syncthing.",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Nililipat ang mga file sa mga naka-date stamp na bersyon sa .stversions na direktoryo kapag pinalitan o binura ng Syncthing.",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Nililipat ang mga file sa mga naka-date stamp na bersiyon sa .stversions na direktoryo kapag pinalitan o binura ng 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.": "Pinoprotektahan ang mga file mula sa mga pagbabago sa ibang device, pero ipapadala sa ibang cluster ang mga pagbabago na ginawa sa device na ito.",
|
||||
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Sini-synchronize mula sa cluster ang mga file, pero hindi ipapadala sa mga ibang device ang mgaanumang pagbabago.",
|
||||
"Filesystem Watcher Errors": "Mga Error sa Taganood ng Filesystem",
|
||||
@@ -208,12 +211,13 @@
|
||||
"Incoming Rate Limit (KiB/s)": "Rate Limit ng Papasok (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Maaring sirain ng maling pagsasaayos ang nilalaman ng iyong mga folder at gawing inoperable ang Syncthing.",
|
||||
"Incorrect user name or password.": "Maling user name o password.",
|
||||
"Info": "Impormasyon",
|
||||
"Internally used paths:": "Mga internal na ginamit na path:",
|
||||
"Introduced By": "Ipinakilala Ni/Ng",
|
||||
"Introducer": "Tagapagpakilala",
|
||||
"Introduction": "Panimula",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Pabaliktad ng ibinigay na kundisyon (hal. huwag ibukod)",
|
||||
"Keep Versions": "Panatilihin ang mga Bersyon",
|
||||
"Keep Versions": "Panatilihin ang mga Bersiyon",
|
||||
"LDAP": "LDAP",
|
||||
"Largest First": "Pinakamalaki Muna",
|
||||
"Last 30 Days": "Huling 30 Araw",
|
||||
@@ -225,6 +229,7 @@
|
||||
"Learn more": "Matuto pa",
|
||||
"Learn more at {%url%}": "Matuto pa sa {{url}}",
|
||||
"Limit": "Limitasyon",
|
||||
"Limit Bandwidth in LAN": "Limitahan ang Bandwidth sa LAN",
|
||||
"Listener Failures": "Mga Pagbibigo ng Listener",
|
||||
"Listener Status": "Status ng Listener",
|
||||
"Listeners": "Mga Listener",
|
||||
@@ -240,12 +245,12 @@
|
||||
"Log In": "Mag-Log In",
|
||||
"Log Out": "Mag-Log Out",
|
||||
"Log in to see paths information.": "Mag-log in upang makita ang impormasyon ng mga path.",
|
||||
"Log in to see version information.": "Mag-log in upang makita ang impormasyon ng bersyon.",
|
||||
"Log in to see version information.": "Mag-log in upang makita ang impormasyon ng bersiyon.",
|
||||
"Log tailing paused. Scroll to the bottom to continue.": "Na-pause ang tailing ng tala. Mag-scroll pababa para magpatuloy.",
|
||||
"Login failed, see Syncthing logs for details.": "Nabigo ang pag-login, tignan ang mga tala ng Syncthing para sa mga detalye.",
|
||||
"Logs": "Mga Tala",
|
||||
"Major Upgrade": "Major na Upgrade",
|
||||
"Mass actions": "Mga maramihang aksyon",
|
||||
"Mass actions": "Mga maramihang aksiyon",
|
||||
"Maximum Age": "Pinakamataas na Edad",
|
||||
"Maximum single entry size": "Pinakamataas na laki ng isang entry",
|
||||
"Maximum total size": "Pinakamataas na kabuuang laki",
|
||||
@@ -284,7 +289,7 @@
|
||||
"Password": "Password",
|
||||
"Path": "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 papunta sa folder sa computer na ito. Gagawin kapag hindi umiiral. Maaring gamitin ang tilde na character (~) bilang shortcut sa",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Path kung saan ilalagay ang mga bersyon (iwanang walang laman para sa default .stversions na direktoryo sa binabahaging folder).",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Path kung saan ilalagay ang mga bersiyon (iwanang walang laman para sa default .stversions na direktoryo sa binabahaging folder).",
|
||||
"Paths": "Mga Path",
|
||||
"Pause": "I-pause",
|
||||
"Pause All": "I-pause Lahat",
|
||||
@@ -311,7 +316,7 @@
|
||||
"Receive Encrypted": "Makatanggap Naka-Encrypt",
|
||||
"Receive Only": "Makatanggap Lamang",
|
||||
"Received data is already encrypted": "Naka-encrypt na ang natanggap na data",
|
||||
"Recent Changes": "Mga Kamakilang Pagbabago",
|
||||
"Recent Changes": "Mga Kamakailang Pagbabago",
|
||||
"Reduced by ignore patterns": "Binabawasan ng mga ignore pattern",
|
||||
"Relay LAN": "Relay na LAN",
|
||||
"Relay WAN": "Relay na WAN",
|
||||
@@ -330,7 +335,7 @@
|
||||
"Restart Needed": "Kinakailangan ng Restart",
|
||||
"Restarting": "Nagre-restart",
|
||||
"Restore": "I-restore",
|
||||
"Restore Versions": "I-restore ang Mga Bersyon",
|
||||
"Restore Versions": "I-restore ang Mga Bersiyon",
|
||||
"Resume": "I-resume",
|
||||
"Resume All": "I-resume Lahat",
|
||||
"Reused": "Ginamit muli",
|
||||
@@ -340,13 +345,13 @@
|
||||
"Saving changes": "Sine-save ang mga pagbabago",
|
||||
"Scan Time Remaining": "Natitirang Oras sa Pag-scan",
|
||||
"Scanning": "Sina-scan",
|
||||
"See external versioning help for supported templated command line parameters.": "Tingnan ang tulong sa external na pag-bersyon para sa mga sinusuportahang naka-template na parameter ng command line.",
|
||||
"See external versioning help for supported templated command line parameters.": "Tingnan ang tulong sa external na pag-bersiyon para sa mga sinusuportahang naka-template na parameter ng command line.",
|
||||
"Select All": "Piliin Lahat",
|
||||
"Select a version": "Pumili ng bersyon",
|
||||
"Select a version": "Pumili ng bersiyon",
|
||||
"Select additional devices to share this folder with.": "Pumili ng mga karagdagang device para ibagagi ang folder na ito.",
|
||||
"Select additional folders to share with this device.": "Pumili ng mga karagdagang folder para ibagagi sa device na ito.",
|
||||
"Select latest version": "Piliin ang pinakabagong bersyon",
|
||||
"Select oldest version": "Piliin ang pinakalumang bersyon",
|
||||
"Select latest version": "Piliin ang pinakabagong bersiyon",
|
||||
"Select oldest version": "Piliin ang pinakalumang bersiyon",
|
||||
"Send & Receive": "Magpadala at Makatanggap",
|
||||
"Send Extended Attributes": "Magpadala ng mga Pinalawak na Attribute",
|
||||
"Send Only": "Magpadala Lamang",
|
||||
@@ -365,10 +370,10 @@
|
||||
"Show QR": "Ipakita ang QR",
|
||||
"Show detailed discovery status": "Magpakita ng detalyadong status sa pagtuklas",
|
||||
"Show detailed listener status": "Ipakita ang detalyadong status sa listener",
|
||||
"Show diff with previous version": "Ipakita ang diff sa nakaraang bersyon",
|
||||
"Show diff with previous version": "Ipakita ang diff sa nakaraang bersiyon",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Ipinapakita sa halip na Device ID sa status ng cluster. Ia-advertise sa iba pang mga device bilang opsyonal na default na pangalan.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Ipinapakita sa halip na Device ID sa status ng cluster. Ia-update sa pangalan na ina-advertise ng device kung iiwanang walang laman.",
|
||||
"Shutdown": "I-shutdown",
|
||||
"Shut Down": "I-shutdown",
|
||||
"Shutdown Complete": "Tapos na ang Shutdown",
|
||||
"Simple": "Simple",
|
||||
"Simple File Versioning": "Simpleng File Versioning",
|
||||
@@ -403,7 +408,7 @@
|
||||
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Ang Syncthing ay Libre at Open Source na Software na nakalisensya sa 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.": "Ang Syncthing ay isang continuous na file synchronization na program. Sini-synchronize nito ang mga file sa pagitan ng dalawa o higit pang mga computer sa totoong oras, ligtas na protektado mula sa prying na mata. Ang iyong data ay iyong data at nararapat kang pumili kung saan sila ilalagay, kung binabahagi ito sa third party, at kung paano ito pinapadala sa Internet.",
|
||||
"Syncthing is listening on the following network addresses for connection attempts from other devices:": "Nakikinig ang Syncthing sa mga sumusunod na network address para sa mga tangka sa koneksyon mula sa ibang device:",
|
||||
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Hindi nakikinig ang Syncthing sa mga tangka sa koneksyon mula sa ibang mga device sa anumang address. Ang mga palabas na koneksyon lamang ay maaring gumana.",
|
||||
"Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Hindi nakikinig ang Syncthing sa mga tangka sa koneksyon mula sa ibang mga device sa anumang address. Ang mga palabas na koneksyon lamang ay maaaring gumana.",
|
||||
"Syncthing is restarting.": "Nagre-restart ang Syncthing.",
|
||||
"Syncthing is saving changes.": "Nagse-save ng mga pagbabago ang Syncthing.",
|
||||
"Syncthing is upgrading.": "Naga-upgrade ang Syncthing.",
|
||||
@@ -417,18 +422,18 @@
|
||||
"The Syncthing Authors": "Ang Mga Awtor ng Syncthing",
|
||||
"The Syncthing admin interface is configured to allow remote access without a password.": "Naka-configure ang Syncthing admin interface na payagan ang remote access nang walang password.",
|
||||
"The aggregated statistics are publicly available at the URL below.": "Available nang publiko ang pinagsama-samang istatistika sa URL sa ibaba.",
|
||||
"The cleanup interval cannot be blank.": "Hindi maaring walang laman ang pagitan ng paglinis.",
|
||||
"The cleanup interval cannot be blank.": "Hindi maaaring walang laman ang pagitan ng paglinis.",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Na-save na ang configuration ngunit hindi naka-activate. Kailangang mag-restart ang Syncthing para i-activate ang bagong configuration.",
|
||||
"The device ID cannot be blank.": "Hindi maaring walang laman ang Device ID.",
|
||||
"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).": "Mahahanap ang device ID na ilalagay dito sa \"Mga Aksyon > Ipakita ang ID\" na dialog sa isa pang device. Opsyonal ang mga puwang at gitling (hindi pinapansin).",
|
||||
"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.": "Araw-araw na pinapadala ang naka-encrypt na ulat sa paggamit. Ginagamit ito sa pag-track ng mga karaniwang platform, laki ng folder, at bersyon ng app. Kapag nabago ang tinakdang data ng ulat ipo-prompt kang muli ng dialog na ito.",
|
||||
"The device ID cannot be blank.": "Hindi maaaring walang laman ang Device ID.",
|
||||
"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).": "Mahahanap ang device ID na ilalagay dito sa \"Mga Aksiyon > Ipakita ang ID\" na dialog sa isa pang device. Opsyonal ang mga puwang at gitling (hindi pinapansin).",
|
||||
"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.": "Araw-araw na pinapadala ang naka-encrypt na ulat sa paggamit. Ginagamit ito sa pag-track ng mga karaniwang platform, laki ng folder, at bersiyon ng app. Kapag nabago ang tinakdang data ng ulat ipo-prompt kang muli ng dialog na ito.",
|
||||
"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.": "Mukhang hindi angkop ang inilagay na device ID. Dapat itong 52 o 56 na character na string na binubuo ng mga titik at numero, na may mga puwang at gitling bilang opsyonal.",
|
||||
"The folder ID cannot be blank.": "Hindi maaring walang laman ang folder ID.",
|
||||
"The folder ID cannot be blank.": "Hindi maaaring walang laman ang folder ID.",
|
||||
"The folder ID must be unique.": "Kailangang kakaiba ang folder ID.",
|
||||
"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.": "Io-overwrite ang nilalaman ng folder sa mga ibang device para maging magkapareho sa device na ito. Ang mga file na hindi nandito ay buburahin sa mga ibang device.",
|
||||
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Io-overwrite ang nilalaman ng folder sa mga ibang device para maging magkapareho sa device na ito. Ang mga file na kamakilang dinagdag dito ay buburahin sa mga ibang device.",
|
||||
"The folder path cannot be blank.": "Hindi maaring walang laman ang path ng folder.",
|
||||
"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.": "Ang mga sumusunod na pagitan ay ginagamit: sa unang oras ang isang bersyon ay pinapanatili bawat 30 segundo, sa unang araw ang isang bersyon ay pinapanatili bawat oras, sa unang 30 araw ang isang bersyon ay pinapanatili bawat araw, hanggang sa pinakamataas na edad ang isang bersyon ay pinapanatili bawat linggo.",
|
||||
"The folder path cannot be blank.": "Hindi maaaring walang laman ang path ng folder.",
|
||||
"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.": "Ang mga sumusunod na pagitan ay ginagamit: sa unang oras ang isang bersiyon ay pinapanatili bawat 30 segundo, sa unang araw ang isang bersiyon ay pinapanatili bawat oras, sa unang 30 araw ang isang bersiyon ay pinapanatili bawat araw, hanggang sa pinakamataas na edad ang isang bersiyon ay pinapanatili bawat linggo.",
|
||||
"The following items could not be synchronized.": "Hindi ma-synchronize ang mga sumusunod na item.",
|
||||
"The following items were changed locally.": "Binago ng lokal ang mga sumusunod na item.",
|
||||
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "Ang mga sumusunod na paraan ay ginagamit upang tumuklas ng mga ibang device sa network at ipahayag ang device na ito na mahanap ng iba:",
|
||||
@@ -436,14 +441,14 @@
|
||||
"The following unexpected items were found.": "Nahanap ang mga sumusunod na hindi inaasahang item.",
|
||||
"The interval must be a positive number of seconds.": "Dapat positibong numero ng segundo ang pagitan.",
|
||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Ang pagitan, bilang segundo, para sa pagtakbo ng paglinis sa versions na direktoryo. Sero para i-disable ang periodical na paglinis.",
|
||||
"The maximum age must be a number and cannot be blank.": "Dapat numero ang pinakamataas na edad at hindi maaring walang laman.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Ang pinakamataas na oras para panatilihin ang bersyon (bilang araw, itakda sa 0 para panatilihin ang mga bersyon magpakailanman).",
|
||||
"The maximum age must be a number and cannot be blank.": "Dapat numero ang pinakamataas na edad at hindi maaaring walang laman.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Ang pinakamataas na oras para panatilihin ang bersiyon (bilang araw, itakda sa 0 para panatilihin ang mga bersiyon magpakailanman).",
|
||||
"The number of connections must be a non-negative number.": "Dapat hindi negatibong numero ang bilang ng mga koneksyon.",
|
||||
"The number of days must be a number and cannot be blank.": "Dapat numero ang bilang ng araw at hindi maaring walang laman.",
|
||||
"The number of days must be a number and cannot be blank.": "Dapat numero ang bilang ng araw at hindi maaaring walang laman.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "Ang bilang ng araw para panatilihin ang mga file sa basurahan. Ang sero ay ibig sabihin ay magpakailanman.",
|
||||
"The number of old versions to keep, per file.": "Ang bilang ng mga lumang bersyon na dapat panatilihin, bawat file.",
|
||||
"The number of versions must be a number and cannot be blank.": "Dapat numero ang bilang ng mga bersyon at hindi maaring walang laman.",
|
||||
"The path cannot be blank.": "Hindi maaring walang laman ang path.",
|
||||
"The number of old versions to keep, per file.": "Ang bilang ng mga lumang bersiyon na dapat panatilihin, bawat file.",
|
||||
"The number of versions must be a number and cannot be blank.": "Dapat numero ang bilang ng mga bersiyon at hindi maaaring walang laman.",
|
||||
"The path cannot be blank.": "Hindi maaaring walang laman ang path.",
|
||||
"The rate limit is applied to the accumulated traffic of all connections to this device.": "Ina-apply ang rate limit sa naipon na traffic ng lahat ng mga koneksyon sa device na ito.",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "Dapat hindi negatibong numero ang rate limit (0: walang limitasyon)",
|
||||
"The remote device has not accepted sharing this folder.": "Hindi tinanggap ng remote device ang pagbahagi ng folder na ito.",
|
||||
@@ -457,7 +462,7 @@
|
||||
"This Month": "Itong Buwan",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Madali nitong mabibigyan ang mga hacker ng access na basahin at baguhin ang anumang mga file sa iyong computer.",
|
||||
"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.": "Hindi awtomatikong tutuklasin ng device na ito ng mga ibang device o i-annouce ang sarili nitong address para mahanap ng iba. Makakakonekta lamang ang mga device na may static na naka-configure na address.",
|
||||
"This is a major version upgrade.": "Ito ay isang upgrade sa major na bersyon.",
|
||||
"This is a major version upgrade.": "Ito ay isang upgrade sa major na bersiyon.",
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "Kinokontrol ng setting na ito ang kinakailangan na bakanteng espasyo sa home (hal., index database) disk.",
|
||||
"Time": "Oras",
|
||||
"Time the item was last modified": "Oras na huling binago ang file",
|
||||
@@ -496,10 +501,10 @@
|
||||
"Using a QUIC connection over WAN": "Gumagamit ng QUIC na koneksyon mula sa WAN",
|
||||
"Using a direct TCP connection over LAN": "Gumagamit ng direktang TCP na koneksyon mula sa LAN",
|
||||
"Using a direct TCP connection over WAN": "Gumagamit ng direktang TCP na koneksyon mula sa WAN",
|
||||
"Version": "Bersyon",
|
||||
"Versions": "Mga Bersyon",
|
||||
"Versions Path": "Path ng Mga Bersyon",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Awtomatikong buburahin ang mgs bersyon kapag mas matanda sila kaysa sa pinakamataas na edad o lumalagpas sa numero ng mga file na pinapayagan sa pagitan.",
|
||||
"Version": "Bersiyon",
|
||||
"Versions": "Mga Bersiyon",
|
||||
"Versions Path": "Path ng Mga Bersiyon",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Awtomatikong buburahin ang mgs bersiyon kapag mas matanda sila kaysa sa pinakamataas na edad o lumalagpas sa numero ng mga file na pinapayagan sa pagitan.",
|
||||
"Waiting to Clean": "Naghihintay para Linisin",
|
||||
"Waiting to Scan": "Naghihintay para Mag-scan",
|
||||
"Waiting to Sync": "Naghihintay para Mag-sync",
|
||||
@@ -524,7 +529,7 @@
|
||||
"You have no ignored devices.": "Wala kang mga hindi pinapansin na device.",
|
||||
"You have no ignored folders.": "Wala kang mga hindi pinapansin na folder.",
|
||||
"You have unsaved changes. Do you really want to discard them?": "Mayroon kang mga hindi na-save na pagbabago. Gusto mo ba talagang i-discard ang mga ito?",
|
||||
"You must keep at least one version.": "Kailangan mong magpanatili ng kahit isang bersyon.",
|
||||
"You must keep at least one version.": "Kailangan mong magpanatili ng kahit isang bersiyon.",
|
||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Hindi ka dapat magdagdag o magpalit ng anumang lokal sa folder na \"{{receiveEncrypted}}\".",
|
||||
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Magbubukas ang iyong SMS app para hayaan kang pumili ng tatanggap at ipadala ito mula sa sarili mong numero.",
|
||||
"Your email app should open to let you choose the recipient and send it from your own address.": "Magbubukas ang iyong email app para hayaan kang pumili ng tatanggap at ipadala mula sa sarili mong address.",
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
"Show QR": "Afficher l'image QR",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Affiché à la place de l'ID de l'appareil dans l'état du groupe. Sera diffusé aux autres appareils comme nom convivial optionnel par défaut.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Affiché à la place de l'ID de l'appareil dans l'état du groupe. Si laissé vide, il sera renseigné par le nom convivial proposé par l'appareil distant.",
|
||||
"Shutdown": "Arrêter",
|
||||
"Shut Down": "Arrêter",
|
||||
"Shutdown Complete": "Arrêté !",
|
||||
"Simple File Versioning": "Suivi simplifié des versions",
|
||||
"Single level wildcard (matches within a directory only)": "Joker à un seul niveau (correspond uniquement à l’intérieur du répertoire)",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user